Aller au contenu

Un robot nettoyeur

Dans ce problème, on cherche à résoudre le plan de trajet pour un robot mobile qui nettoie le sol d'une pièce rectangulaire avec des meubles, des murs, des obstacles.

Présentation⚓︎

Le sol de la pièce est pavé de carreaux carrés dont la taille convient au robot nettoyeur (1 × 1). Il y a des carreaux propres et des carreaux sales, et le robot peut transformer un carreau sale en carreau propre en visitant ce carreau. Il peut également y avoir des obstacles (meubles...) dont la taille correspond à celle d'un carreau. Si un obstacle est sur un carreau, le robot ne peut pas le visiter. Le robot se déplace vers un carreau adjacent avec un seul mouvement. Le carreau sur lequel le robot se déplace doit être l'un des quatre carreaux adjacents (c'est-à-dire est, ouest, nord ou sud). Le robot peut visiter un carreau plusieurs fois.

Objectif⚓︎

Il s'agit d'un problème d'optimisation pour lequel on cherche le trajet qui passe sur tous les carreaux sales en un minimum de mouvements.

Un pièce à nettoyer est représentée par une matrice de caractères :

  • o pour le robot
  • x pour un obstacle
  • * pour un carreau sale
  • . pour un carreau propre.

Exemple d'entrées⚓︎

Python
7 5
.......
.o...*.
.......
.*...*.
.......

15 13
.......x.......
...o...x....*..
.......x.......
.......x.......
.......x.......
...............
xxxxx.....xxxxx
...............
.......x.......
.......x.......
.......x.......
..*....x....*..
.......x.......

10 10
..........
..o.......
..........
..........
..........
.....xxxxx
.....x....
.....x.*..
.....x....
.....x....

Sortie attendue⚓︎

Le nombre de mouvement minimal, ou -1 si un des carreaux n'est pas accessible.

Comment résoudre ?⚓︎

Avant d'avoir une approche exhaustive, on peut chercher une première solution approximant le minimum par une stratégie gloutonne.

Trouver si un obstacle est présent sur le chemin le plus direct entre deux carreaux.⚓︎

Python
def sans_obstacle(d:(int,int), a:(int,int), liste_obstacles)->bool:
    """
    Renvoie True si le chemin de d vers a ne croise pas d'obstacle
    Les chemins possibles sont soit
     - horizontal puis vertical
     - vertical puis horizontal
    """

    ld,cd = d # ligne et colonne départ
    la, ca = a # ligne et colonne arrivée
    obstacle = False
    # Test d'abord déplacement horizontal puis vertical (chemin 1)
    for o in liste_obstacles:
        lo, co = o # ligne et colonne obstacle
        # test sur la ligne horizontale depuis le point de départ
        if lo == ld and  min(cd,ca) <= co <= max(cd, ca):
            obstacle = True # obstacle rencontré sur le déplacement horizontal
        # test sur la ligne verticale qui va au point d'arrivé
        if co == ca and  min(ld,la) <= lo <= max(ld, la):
            obstacle = True # obstacle rencontré sur le déplacement horizontal
    # test si aucun obstacle rencontré sur le chemin 1
    if not obstacle:
        return True

Plusieurs solutions on été proposées :

  • chercher un carreau sale le plus proche possible atteignable directement sans obstacle, (cette situation n'existe pas toujours)
  • déplacer le robot au minimum pour qu'il se retrouve sur un carreau permettant d'accéder un carreau sale le plus proche possible sans obstacle...

Et un parcours en largeur (BFS)⚓︎

Et si, en partant de la position du robot, on cherchait à l'aide d'un parcours en largeur la distance de tous les carreaux atteignables de la pièce

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5
.128013it3a;dv,n2.S5éwb*cy!+14q: f-up08)_j9eklohxrPè=[s6(]/mg7050g0L0c0e0b0N0W0A0s0N0e0W0W0U010c0b0E010406050W0D0#0#0e0R0t040m0O0N0D0{0O0j050!12141618100E04051o1h1r0!1o100g0b0h0:0=0@0_0=0j0$0D0e0$0L0C0E0t0c0P1f0A0P0b0$0P0N1T0P0c0~050+0q0N0L1A0?0^011S1U1W1U0c1$1(1!0c0R1p1O0:1b0W0E0e0j0_0k011*1C010B0-0L0j0e0#0L1!1 21261,291(2c2e0~0a0A0S0R0O0E0O0W0b1e0j0A0)1}0R0R0L0s2z1h2h0j1p0!1O2M1_1{1`1#0g2j1D0b0j2b2w1!1x1z0;1+2W2Y0j0O2$1!0E2F1p2K2M2?11202A2(272,0R150N1!0e1R2F0B0_030I0I0s2-0L1W2+0O0C0k0C0w0~0A0w1h0e2@2`0 2_2i2|1,2~3032340L3601383a3c3e2Z3h3h3l0k3o3q213s2K2V013x0e311p330P3537393b0)3H2,3J0d3l0d3N2J3r103R3v0_3U3W053Y3!3D3$3G2X3I3i0x3l0x3/1i3;3t2{1B3w0O2 3V3z3Z3B3#3F3(413*3i0n3l0n472?3=2`3S3_4h3}3E3%3d4n3g3i0X3l0X4t493?4c3^4e3y3X3A3C4B403f3J0%3l0%4K3P4v3u4N3T4P4g4R4i4T3 4m4W3i0G3l0G4#2L4%4b2)4*4f3`3|4j3~4l4D4=0C0K3l0K4`3Q4w3@4 4Q3{4S4k4C3)4F3j0F0~0w0F5c4|4x4+515j545l4E3J0w3k045D5t4a5v504z534U4;423j245F3M0!3p3:4$5I5f4y4-4A4:565P0w3,5F3.5U3O4{5Y4)5!5i4.5k4V5)445F465.5W5:4M4~5?524/555m5C4q5F4s5 485X622}5w5L665A570w4H5F4J6d4u5;636i5#5M5%683i0w4Y5F4!6r4L5e5=6v5@5$675B6A4@5F4_6F6f6H6u5K6w6k5`4o3j595F5b6S616U6h6W6K6x6M570k5p046=5H6g4d6-655_5O6!0k5E716_6+6{5h6}5z6Z5n0k5R7c744(6V775y5N5(705+0k5-5V6e6*7g6,7i5^796 7b5|0k5~7q6s6`4O6|7j6y6N3h6a0k6c7D6G7t764,6.6Y7y3J0k6o7Y7f4}7u7T787k6z3h6C0k6E7P6T7R7G7v6L6l5P0k6P7{7#5J7@6/7_706$0k6(7:7s7$7S5x7w7*7K0d6?8f7~5Z6J7)7J570d5E8o8i6I7H8c8m5P0d5R8x8r7h7(7I6:8w5+0d7p5/5d7=5g8C8u8E6!0d5|8R8A7%8b7^7a3+6a0d7O8J5u8j8t8X7W3i0d6o8.8U8a6j6~7l5n0d6C8`8;7?8N8+8^3+6P0d6R878K898~8W818Y8-6$0d868%6t8V8?7x91436?0x5s6)977 8 9b8,0C0x5E9y8}8M9a7V9m9x5R0x5T968(8s809E7+0x5+9Q9B648D825n0x5|9Y9T8k9V9c9x6a0x8$609s8)9N8@9P6o0x6q9K9i8=6X9;7K0x6C9 5c1s2;1h2$2P0g1{2U5f4C2#1y1p2:0L2=3r9-054Cai2i0b0g0_392K5C3zapar8P5n3k0A2n0Lax9Wau2M5V7F3T0~0E0b2G0LakaJ0O0~0U9-0A9`0_0W5R020y0D0O0c0fa#a%a)a+a(0f0la;a=0l0Qa?a=9-107ran2Aaw01as2`7Xavaqb1ayb4aB2daDb7aF3i5R3/aJaZ0~a=0Oa=a^a=0ra;a{aQ4wb0b2213+b5aE9(3,ba2ebA9w5+bh75aY5Ra_a;boa_bsa}5Ibv0Iat43bzbd9(44bDbc8v6!5|bI8Lbj04bMa@b.bQ2^3RbTbV0C6a5ibTbeb_25aCbF9Fb`5 bibLbMbOa?b;ajb?b6bw0j3J6ob{cdb84Gb bbc17+chc4bJ01b,b.cvca3PbScjb^6Ccico7K4Yb#cE57cCcrb+5R0QcOcOa_cPcOcx2Lczaxb^6PcDbY9w4@cHc#9FcZcL98ctc6cvbPa}a|b=bucAb33i6$c!b%5n59c(c~3Jc|c,3Scuc7b:c=btaoc_bx6A6?c}ck5ocmbEc)7+5r1!d55fd7cRd96sbRcccXc`3j5Edhb}5Ddkb$didEaHa~d6c/a?c8a`dadwc^dyde5QbXd26A24d1dHbgdq4)d7bqbna?d*cUdKb@dz5*dWdHbCc0dm7Kd=dJaXc.bkd8bMa.a-a$a/e30fd.cWb7b^0wb)33b|9(eddFcI5{dpaIcs0jaLaN0saPa}aWaRaTaVd~ep04aMaOdbc-aSb-eE4x0~1 1W0ceI5f0}040YeOd(5E02039fe8dRb+2f04091ge!c-0WeVeX0KeZc@8LeQ0He9dxebd;b`efcjdD4qd!e em3se*0Ad:dU6nd?dD4Hf1ehcqen8L0M0~0)0BeT4~0p3lfm2}0BaL2w0I2t0qa(fq1,eQeSf55ZeqeDfD4)e?0ze^dSe`f8cCe}ek6!6Bejd`6mcK3p0AfZevcsfi040B4eeyaJeA0Nf+cs0Ofo042Xf/8L0j0q0~0R211Jfz0_fBg03Tf{042mg3g2fH63fFesg80~0H0HfKdQe;a ddcf6Ofaehc%d_dX3jc+fYf!gxd~f%f)0Rf^c-eA0sgD3Sf;0~f@euezg5f}1Fetgk3Sg9gS5Zg5g7ga27gUcbeogcgRg$e=0~0Vg30#0b0~9qgVfI0~0ZgggidvgSf7gn6#gp9w0wd0gsdHd4gwgxh9f#fhgKflgMf,g(ge04g-gZ3w0~f.hlg1g^hkg?gb04gGhp01eQ0ZgH5feG0UaUhfcse,0~eWeY0OhKe/hig{49f5g~7XdgfQfV7`5pfd9w6=f3hah(f!gzf|0*0D0Re)2?hbc-g#cyhgg6hi0ig3gFhie@gjg*gldTg 71h19Fi6h!i8dB5.c?i2f6gm7XbghWgt7cfUild$fgc-f%fkg3f=aWhx0jfs040q0B0WhifCht2}fj0L200ReNhxeQh|hGf_hhiNgfhQ5XhSihbfbHikdi7oini$bHd%fn3lh*hx0W0ghJe5e4a,a*i?i_i^fLdci47XeeigfR7zi(b}7Bf3d~i:i-fZ2F0c0Oh.2Y0A0=2B0b0W0+0jes2B1)je1d0L0/1(0/0s162F0e0D0Q0A0e1=0L1T2c1%ju2B210/0Djh151_0bjpjBjD1x2e0j2x1f0/2CjjaM0Tgdi1h^fMce7Xe|j2hX70f0h5j6c3iqd6i;04gxe7k1i`i}i3fNi5chi#j6fcj_9(7Yh%h*aJ0MeA0B2t0#iufpixgF1b1(2H0bj#knj g3kigK0#2viLhB5=iIa$g)iWg}iY7,i77+7.j5kdfXj kgg%f(2akE4~hDkYiH040)kHiEi0g|ifhTbfcZk9kdgrcnj?7bgvkTfZez0~0Pk#1,k!iQgEgX2biEh}iSiGfAgfk4j=j/bfc|k;h#h4k@ilh7k{h=eJh`l3gIexlsgWhnl6iTeRl8eBerkIcVaJeQhsif5fg/g;hig_ldk.0C8fkN8ehZkcbGdg5.gyh_1xjm21esiDlv4)l2h;d~lHlI3PlphC0~0Cg.g:5Fl00_gAf*l+hu0Il}01gJf?h:3rl?5=gOf~lEdKeP0~iFlJkFlrlahq040HhAm127l gCmrhm04m3mv0_m6gLl.f,mcgQl7ixhnh mqk,j-i~k63+dBli9F8okQbGich8mahu0tmummhy0~iPmDkV0Qm$m9d~l-m/l#iJ16iMmMlFj.bUdz8xlT8ndZlWmTiplok}k%jljnl)hil;m{iR04m#lNndmfmkm-lNm4m;l=d~lL04g=hRkKi 8-i!ledi8HmVmTi*3piemNk5lflRj1egbGb!n37+8Rkfk|khiIhem%ivh}iziB0Wfv2GmHm%gFjz0LjBh iV5:iXnxlRj;nNmTj^llnBj{lol!f$hdm4f`lxm8nIgTmhlB2lmel/iUlPkL8.n08wkbn}b}oidJh)o1nf0e2H0D3dnolum+nfocg3eG0llB2v0E1(0Bm_mj4~fBk+nvk-ohfPnAoncGnQ8ekSoqkUnf0t0eh{o40~0Qo%mzm5oym=kVot0covhom`d/ohk:oTbBk?dlgt94nToqh+f(m0ozgEfj0Qo(o-eA0g0tnof=mCo:g+hjmio8lKl{nupog@04m*oL27eGl_hxns3nlz0HpvnqlGoapAl{pCm%iOl`lMpDpF2LmZg!pIm%nsprneh?m)pOl|pQm4h@pZlt04pzpWpKpcp/pPpMgf0Zn;kxaJ0s5E030A2F0s0P0L0Rq21)0)jxn-jVjE0g0Jot2b0cl*o_eanK9foj8Qlkp0nBlnoZhan70t0hp;pkp9040Qqxo-nppSoelAmIngo,p8p,0vo)k%pglzpRp{m,qLqzqNqP0gpbpDaVnHp+lQ9pqn9XlVombZlYmYqtpT1,f%0bnXqXfEngqDqMhCpio7qGmEf|mdn*pwmwk q$qEf=210gqPqCphgKr3qUf_mFf lzpnp+q}o^r9mnoOpGf:re0jrgpdl9rvm(hjlBqwnilBrilzmLq|l,0~0uhFr0eUhJe.0f0QhNe:rPkZrArCrUhul$najuncrIq rFhznjn7rMp@04rOry8LhDrTr$pxl^p$pLr=0~p`q@0_p}0~p 0h0Ojl2X0/2,2A2b3b2F2B0o3b0D0hq4oKkJoQn@9yq,3Jsyia9PmXo0q?o!qAoBhxoDlB0e0E0E2br)s6eRrrnkm!r;psoMp#qJr_sSggogsxijo}9w9InD9Pn5sGh(n7r,l(r.lzr@h_rJrNs|m,sXrsptr|r4f:o/r}qAs^jos`r`t0o#qWsYpUr{tfqAo+nnqE0~qOo-pBm4sb04p e7esq5jwe71}es0jjSkHj$jLjy2En.jCqcqetEqhs)mP43nzn`9Pd^q/s-nFsFquh_n$n(hwr`sUn7sJr`rxpSf!q(o`sxnMe~bZnPtZ9F9Yf3t@qkm}dU9+sz43n|qqb}u6opsHlqt/rFsLqJsNsPrBr8timw0)iKsut3sZmotSnK9@u79xoluabZffn6t(iCt*unutk$uguorwuwu4g 9 uzuSsC9~oYue5ff%jdjgqZn9s_qioPcy0!ama3aha5ae1h0ca8u?2S2N0ejK2Ma6a|0)0+0-0W04.

Résolution exhaustive⚓︎

Voici une solution qui teste tous les trajets possibles en permutant l'ordre des poussières...

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5
.128013it3a;djv,n{2.S5wb*cy+14: f-up08)_}9eklohxrOP=[s6(]/mg7050g0K0c0e0b0M0V0z0t0M0e0V0V0T010c0b0D010406050V0C0!0!0e0Q0u040o0N0M0C0`0N0k050Z111315170 0D04051n1g1q0Z1n0 0g0b0i0/0;0?0^0;0k0#0C0e0#0K0B0D0u0c0O1e0z0O0b0#0O0M1S0O0c0}050*0r0M0K1z0=0@011R1T1V1T0c1#1%1Z0c0Q1o1N0/1a0V0D0e0k0^0m011)1B010A0,0K0k0e0!0K1Z1~20251+281%2b2d0}0a0z0S0Q0N0D0N0V0b1d0k0z0(1|0Q0Q0K0t2y1g2g0k1o0Z1N2L1^1`1_1!0g2i1C0b0k2a2v1Z1w1y0:1*2V2X0k0N2#1Z0D2E1o2J2L2=101 2z2%262+0Q140M1Z0e1Q2E0A0^030H0H0t2,0K1V2*0N0B0d0B0w0}0z0w1g0e2?2_0~2^2h2{1+2}2 31330K350137393b3d2Y3g0B23040z0m3n3p203r2J2U013w0e301o320O3436383a0(3G2+3I0d3k0d3O2I3q0 3S3u0^3V3X053Z3#3C3%3F2W3H3h0x3k0x3:1h3=3s2`1A3v0N2~3W3y3!3A3$3E3)423+3h0p3k0p482=3?2_3T3`4i3~3D3(3c4o3f3h0W3k0W4u4a3@4d3_4f3x3Y3z3B4C413e3I0$3k0$4L3Q4w3t4O3U4Q4h4S4j4U404n4X3h0F3k0F4$2K4(4c2(4+4g3{3}4k3 4m4E4?0B0J3k0J4{3R4x3^504R3|4T4l4D3*4G3i0E0}0w0E5d4}4y4,525k555m4F3I0w3j045E5u4b5w514A544V4=433i3K0w3N0Z3o3;4%5J5g4z4.4B4;575Q0w3-5G3/5V3P4|5Z4*5#5j4/5l4W5*455G475/5X5;4N4 5@534:565n5D4r5G4t60495Y632|5x5M675B580w4I5G4K6e4v5=646j5$5N5(693h0w4Z5G4#6s4M5f5?6w5^5%685C6B4^5G4`6G6g6I6v5L6x6l5{4p3i5a5G5c6T626V6i6X6L6y6N580m5q046?5I6h4e6.665`5P6#0m5F726`6,6|5i6~5A6!5o0m3K7d754)6W785z5O5)715,0m5.5W6f6+7h6-7j5_7a707c5}0m5 7r6t6{4P6}7k6z6O3J6b0m6d7E6H7u774-6/6Z7z3I0m6p7Z7g4~7v7U797l6A3J6D0m6F7Q6U7S7H7w6M6m5Q0m6Q7|7$5K7^6:7`716%0m6)7;7t7%7T5y7x7+7L0d6@8g7 5!6K7*7K580d5F8p8j6J7I8d8n5Q0d3K8y8s7i7)7J6;8x5,0d7q5:5e7?5h8D8v8F6#0d5}8S8B7(8c7_7b3,6b0d7P8K5v8k8u8Y7X3h0d6p8/8V8b6k6 7m5o0d6D8{8=7@8O8,8_3,6Q0d6S888L8a8 8X828Z8.6%0d878(6u8W8@7y92446@0x5t6*9880909c8-0B0x5F9z5d1r2:1g2#2O0g1`2T5g4D2!1x1o2/0K2;3q611o4D9S2h0b0g0^382J5D3y9Z9#8Q5o3j0z2m0K9+839-1Z607G010L0}0(0A9U9j1+0q3ka09`0k0A0}2u0C0?0b0K2E0Va5760^0|040Xah8M0ka9ad0t0Kan9901ak0G0y9U0 7s9X2z9*019$2_7Y9)9!aG9,aJ9/2c9;aM9?aJ2L3o0zaX0za13_a90NabagaCaZ9`0N0}0T9Ua+aiaw0}0U0Ya:aYaB2@3SaFaH203,aK9=9d3g249:b49x5,3OaYa;8M9|040A4fa:a!3U0}0Mbka,a3042Wbpa=0k0r0}0Q201Iau3TakamaCblbwbn2abC5gbEbL5?aq2FatbG9`axayaAbCa 0H9%44b3aSb545aP2db99n9y9^aWbdaYblbgbi0Qbuao0}0tb{av0Nbrbta*bHbx04bz1EbSa}a=bNbTbvc62lbO4 cdcbb|040Darca9TbUa@ci260!0b0}9rclavak0Y0GbWaCa|cr4xbZb#0B6b5jbZaT4qb7aQb-7,cN5/b=cYbeavbg0b9 c4a6bQascu1+ak0Uc-a#04boce8McCc:c^avap04b~c|bD0}a_c)a=a-040Ta/d58M0V5F02039g0f0sdfdhc;a?04azcGbYaLb00k3I6pcOdsaN4HcSb,b(9xdwcXcZdHbHa$a(dmd70ndmc~0e0D0D2a0gdmbEbFcA4ybndW0}0jdPb}d$04cEbXd1cKaI3h6DdxcU7L4Zb+aR8w6#d?dGaXb@by0)0C0Q1fdbc}dK0?d.dZd:b14@b%d}5o4^d{d^586Q3OcH3Q5Jeedu3h6%d@dDb.5aeley7,ew9_a=bg9~dmbraZd15!a8041w0V0*0kas0V0H0(0D0C0beV0t152E0e0C0Pd+dYcIcmcobRd+cF6td/dycL5sehdz5pdBd|e}e{aV3Lb?c*ePe!eSeU0Haaebe83Td7da2=c!d2040l0Ib 3T0t5F030z0R2zdT1c0z1c0,e!aefy2z1w2G0b1e2b0b2Eece.aEe_d;3i5Fexei5D9.b8eC7L5Eb:f4e1f6eQf90KeVfca)dZbMcteMbPcn2v0H2s0r0N0ce,d)cncpe=d4fiblfgfo5!0}0r0Af.fNfke-erf62ueVf`f|f~f=64c+cqggcc0}0Gd(feg8g0e;gn26axfMgr9YfPef5Re|cQgIeBfU6B3Kbcf5eG0}b_g7f?0DgV4 c10}c3g4gha%acaef+gmf/gWg1gAc.gtdpe@edgGeu3ibb32cPb55+e em5*bbe0b=dJf7eR20faf-d+c{g.gocnd+g33qfj5gg6gwf?gagcgEcB0}gf2Kh8gXg;ajd%gY2|gpe=gDhxa~g`5D5}fTf1b*fXgN3ihMh6e2c7e4e6hD3v9}f8hbf+fbg)hs5;e^9+e`cNg~dygK0w4rgMf1cW3oeqhIcJhK6Bdwh;h36#6oh2fY6ndFh|drh.fQ6CgJh0d`hQf1d 5W9`eH3AeJa4hA3UeOafa%dVisckgdgxe:c,iyg?hHaD0zet5Deoi3i85*ekijh?eohUf60V3Wg,hpgZa.hZc=f-adafg-iAg/gzhggBgtiGesi06$ig9x0weAiQh0eEb;f%bvby0Nf{f}iY26hog%j1f@gjj3gliEalf iCgqh~c_i:dqh-aMcL6?i^b.jrh_gKjrf3bdh8f)h%h+3Lg5i!j6h!h9f*eVeXeZe#e%0Ke)e+jfhwiHiBg:i.g=d,i;hJidgH72js7,j)jvb5j)f3h}iHiJ3h7dj*7Lj^j-9xj^jygRbf9}ipiseKdPeO0M1e1F0Ke50H1^0e0h0)i*htd!eP0K1 0Qj5jYhB04gvj9e/h*i(iXkqdne?4ajodt7Yg}iIh=j.3-j|jth5i h7f6kekgkphljFd8i#01cwcyj#h j%g{7Cj_6=hPcTiN71hTkOjzf64f0g2Eb!iWjDhm4*j8kUcsjgisbIc?f8kijke9f@a(kxjDblbVdM0}dOl3b}2t0ue,0Gk#gFk%7Yh:kHi47ch^i|j}h{f$kPcfa90Q2WkTkjf:l2kzc~k@k_iV1%ldl1lojng_lrj@i2luk.7c4IkLj+ialBk=ja0e2G0C3ckXk 3Qk}hhgklHh,lVjpfQ7/k*7{iik-hRl j cYhV0q1R1%kXl4chjTf lN0Kk`lQe=g@kCl|kEj@iLlZm4iPm3e}7|f#dHl+cm111xl;jGkul9mhmjkyi+iZ04lilLa$hzkzbElTg^gdj?3JewiMm4i{mvjwi~l*cZh80ue7mShCjHc=0Pm.l0d6mFm^mBeZ0ilpfOlW3g6@hNgK8gi7hRn6m6mzh8kRkhm;01d70vfhm{l9jBeTmKlI4*c/f l-0cl/c@m/040YhfmLhE04m-hjnAnphhm?hjm msb!fQ8pm08RfWm%b5nPnadIf6ntnvmEkWnfc~mCm~lUmWi?8ynQ8`23l%8fgP5/j;i=n18In/3,kKlyb.n|nWk0c#e3nuhYn%0}ndl`3rkDnNgH8Sn}8.k,dCn8k:odmoofg{8$oi3glxnTbalAmAl9ob0Hcwm@l?kVnjoFf6k81El/kcobl7jVf?l_d+ktnkkklPnol8fkmUmnn,n{lYg bal$o07,8/myo43Tb^bjo9cnae0!mJk|g5c2oE2Kl@nCdT2~1c0e2ylRgslKnBjIoXp9jld,mm5YoecL8{otpmn=8oilm*j0k1bsc(mGkkoKkaoNbzkSoPh8oSjfoUoIjap4o{pehGnfd7020M0c0foHp1ncpC0)oCg$pckrpil{o%l}ogmro*o1muole}95o:mzp2jIoBoDn#pVjEoJk9oM0QkdpYoclehvf pGnxpIpWgho`o|pOmVkjmX9gpnm$p;n5m)p^ptmH0Qk^jjp m_n$pxiBqfpNn+qji?9qotqHpp5QqHj:icp+g{9zqInSqob)fSiTgShWo7p0qxcmp{p#nHi/ksmc0}mhi;0Z9W9D9R9F9O1g0c9Iq`2R2M0e1$q@0Z9GaB0(0*0,0V04.