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
.128013oèPf 8lkg5:j+,bry2a1éh3tvw!=u6e[(*Sn9]x_s-c)7iqm./4p0d050$0F0y0t0U0h0P0f0R0h0t0P0P0C010y0U0!010406050P0D0W0W0t0q0r040J0b0h0D0`0b0K050Y111315170 0!04051n1g1q0Y1n0 0$0U0z0/0;0?0^0;0K0j0D0t0j0F0Q0!0r0y0w1e0f0w0U0j0w0h1S0w0y0}050*0p0h0F1z0=0@011R1T1V1T0y1#1%1Z0y0q1o1N0/1a0P0!0t0K0^0s011)1B010e0,0F0K0t0W0F1Z1~20251+281%2b2d0}0a0f0d0q0b0!0b0P0U1d0K0f0(1|0q0q0F0R2y1g2g0K1o0Y1N2L1^1`1_1!0$2i1C0U0K2a2v1Z1w1y0:1*2V2X0K0b2#1Z0!2E1o2J2L2=101 2z2%262+0q140h1Z0t1Q2E0e0^030O0O0R2,0F1V2*0b0Q0s0Q0u0}0f0u1g0t2?2_0~2^2h2{1+2}2 31330F350137393b3d2Y3g3g3k0s3n3p203r2J2U013w0t301o320w3436383a0(3G2+3I0x3k0x3M2I3q0 3Q3u0^3T3V053X3Z3C3#3F2W3H3h0Z3k0Z3.1h3:3s2`1A3v0b2~3U3y3Y3A3!3E3%403)3h0k3k0k462=3;2_3R3^4g3|3D3$3c4m3f3h0E3k0E4s483=4b3@4d3x3W3z3B4A3 3e3I0T3k0T4J3O4u3t4M3S4O4f4Q4h4S3~4l4V3h0g3k0g4!2K4$4a2(4)4e3_3{4i3}4k4C4;0Q0L3k0L4_3P4v3?4~4P3`4R4j4B3(4E3i0#0}0u0#5b4{4w4*505i535k4D3I0u3j045C5s495u4 4y524T4:413i235E3L0Y3o3/4#5H5e4x4,4z4/555O0u3+5E3-5T3N4`5X4(5Z5h4-5j4U5(435E455-5V5/4L4}5=514.545l5B4p5E4r5~475W612|5v5K655z560u4G5E4I6c4t5:626h5!5L5$673h0u4X5E4Z6q4K5d5;6u5?5#665A6z4?5E4^6E6e6G6t5J6v6j5_4n3i585E5a6R606T6g6V6J6w6L560s5o046;5G6f4c6,645^5N6Z0s5D706^6*6`5g6|5y6Y5m0s5Q7b734%6U765x5M5%6 5*0s5,5U6d6)7f6+7h5@786~7a5{0s5}7p6r6_4N6{7i6x6M3g690s6b7C6F7s754+6-6X7x3I0s6n7X7e4|7t7S777j6y3g6B0s6D7O6S7Q7F7u6K6k5O0s6O7`7!5I7?6.7^6 6#0s6%7/7r7#7R5w7v7)7J0x6=8e7}5Y6I7(7I560x5D8n8h6H7G8b8l5O0x5Q8w8q7g7%7H6/8v5*0x7o5.5c7;5f8B8t8D6Z0x5{8Q8z7$8a7@793*690x7N8I5t8i8s8W7V3h0x6n8-8T896i6}7k5m0x6B8_8:7=8M8*8@3*6O0x6Q868J888}8V808X8,6#0x858$6s8U8=7w90426=0Z5r6(967~8~9a8+0Q0Z5D9x8|8L997U9l9w5Q0Z5S958%8r7 9D7*0Z5*9P9A638C815m0Z5{9X9S8j9U9b9w690Z8#5 9r8(9M8?9O6n0Z6p9J9h8;6W9:7J0Z6B9~5b1r2:1g2#2O0$1`2T5e4B2!1x1o2/0F2;3q9,054Bah2h0U0$0^382J5B3yaoaq8O5m3j0f2m0Faw9Vat2L5U7E3S0}0!0U2F0FajaI0b0}0C9,0f9_0^0P5Q01a!0Xa$a%0X0Na(a%9,0 7qam2zav01ar2_7Wauapa?axa_aA2caCa|aE3h5Q3.aIaY0}a%0ba%a*a%0Ia$a-aP4va=a@203*a`aD9%3+a 2dbp9v5*b674aX5Qa+a$bda+bha/5Hbk0Oas42bob29%43bsb18u6Z5{bx8Kb804bBa)bZbF2@3QbIbK0Q695hbIb3b+24aBbu9Eb,5~b7bAbBbDa(b$aib(a{bl0K3I6nb-c2a}4Fb;b0b?7*c6b_by01bXbZckb 3ObHc8b*6Bc7cd7J4XbQct56crcgbW5Q0NcDcDa+cEcDcm2Kcoawb*6OcsbN9v4?cwcQ9EcOcA97cib{ckbEa/a.b%bjcpa^3h6#cPbS5m58cTc:3Ic.cX3Rcjb|b#c%bianc+bm6z6=c/c95ncbbtcU7*5q1Zc`5ec|cGc~6rbGc1cMc,3i5Dd6b/5Cd9bRd7dtaGa:c{c!a(b}a,c dlc*dnd35PbMc@6z23c?dwb5df4(c|bfbca(dVcJdzb)do5)dLdwbrb=db7Jd%dyaWcZb9c}bBa!01dZcLa|b*0ubU32b.9%d~ducx5`deaHch0KaKaM0RaOa/aVaQaSaUd:ea04aLaNd0cYaRbYep4w0}1~1V0yet5e0|040HezdT5D00eE4}0P2e04091fdGbWeGeI26eB0Sd`dmd|d$b,e0c8ds4pdPe%e73rePa;d2c46zc6e#e56Z6me4d,6lcfe88K0i0}0(0eeS1+0A3kf43@0eaK2v0O2s0p0beye-3ReBeDfi5Yebeofm4(eU0leWdHeYdJ6Ad(dscvd+dM3icz3o0ffHegchf0040e4dejaIel0hfPch0bf6042WfT8K0K0p0}0q201If801fkf+f#0}2lf+f-fq62foedf=0}0S0SftdFc)d1dIe:3icOe?e{5(cSfCdwcWfGfIgfd:fLfN0qfZcYel0Rgl3RfV0}fYefekf$04f(1Eeeg1cYf?gB4wgwf;f@eT0}flgEfnemecgAc0cheB0Gf+0W0U0}9pgMfr0}0Mf}f dkgEd#fxc.g7fD0uc=gbdsc_gegfg`fJe gsf3gufQf_gQcnaIgTf.f:f{040MgUgI3v0}gohd0^eB0Mgp5eer0CaTh0ch0PeG0beHhhf,0}g)48fig,g46;fz9%hEe)hGd55-g{hMfIghf%0)0D0qeO2=g|gCgKh704fShweB0ohZhgg!4}eUfug2fwhDdqg/d770e`fDh^dyc(gRh/c37Wb5h?b/7bh_h@dRe~cYfLf2f+fWaVhw0Kfa040p0e0Ph9gLh~gmf10F1 0qfhh+gJ04h(hqf!h2h9f~h.e.g37Wbwi3hGd*ccg87le+d:iehOhw0P0$0}d^d_g0ip0fhC7Wd i$e$hGbPg?i,iQb7iW04gf2E0y0bhS2X0f0;2A0U0P0*0Ked2A1(i_1c0F0.1%0.0R152E0t0D0N0f0t1;0F1S2b1$j92A200.0Di|141^0Uj4jgji1w2d0K2w1e0.2Bi~aL0cf`i!h4fvi0b4e!i*e@7ae(i.9v7Mi:hri=gfd^iFjTh:7We=j*h`4GhIjYe}i?hOaI0iel0e2s0Widf7iggn1a1%2G0UjGj i?f+j`gs0W2uiuhl5;ir0V0Dh35/hBe/7WcriKjYfBiNh`fFj@fHek0}2kklk9eh04hphVkz040(kjkCd:fkeVjNcKeXjQ3gg6j.h@gakukXe+ggh1040wkg4}hnk*2|gG2ainhZenjMiw1+h-kQd!kob4g.kWi4g=kZl1k#j^e9h8iAeqeil9gFf:k:h$hYigiClg04hck^0^gWgYh9g%j)i%8,d5kr9E8ei6b/lzdyk$l7kJj0j2edimlchmlbkHh50}0Glm3qhWgq0}0QgVgX5Ek-1+gifOlLkh040Ol#0^grfXhUlTgvf%f)kMlPeChZh#lnhx040Shkl)4}l%gkm32|0}l,m71+l/gtlOe9gwgyf*lkiojOiBh!iDm2g*i#lu0Q8nhFbvazjXlydqhLl6mn0rm6l~h%l-aJ040NmGl=kEkGmOlF0(itivhAg+k}mui2l0bqdOmz7*8wl5kyk%1wj120lJh9lSmmiq04mFlrm?kRlFmMlrmJk,mblolZgZmWmsmY8GmwlyiMdafDnah|etmt8Qnbm)i-l3bqbUmCm,fKirg l~ief.iiik0Pfd2Fk;li04jd2D0FjgiDhz5WkniH8,jSe1bvjWnonTm+g`hPfXnvmRf!k/l;m@fjlhl~j{29nLltn9j-nSlyj:m(8dj?hNlEmn0t2Gkkl}n$lakFmJn.1%f+er0Xk=2t1%0emVn*eAgKkPmrn*njkqm#bvktned78_nXhNkI0r0th9izmfmn0NoAn301n2oDm^o00yo2n;nPmukVn@m)kYotlBgdkxn}nZgjo7f10NoBo%kJ0rn1fWmeo4n+llmlm}8Klp04n6oig#iyoalWlY5piDoCo;ojl{hwo`3mlkp5o}4}o`o|o^hXm0pd2KlUp7o@dz5epgo*p9lZpbmHf|plkDgSn,i#lM04lXpup3pcp2o{mpnMpmd:0R5D030f2E0R0w0F0qpV1(0(jcjenJjhjj0$0mo02a0ylKompikWb*9enl8dl2oWbqg^oZn}mDm^0r0zptoKeumLq4oHoJp6o~ppoyoGq6pE0no+0$o-pJoHeloFn10}qjqoo(iDaUh}onmY9op_56qBj;9EqBlDq0nYj_g~o+q3o.gsn)pOfQmhl^nEn-0}k)lkolqck+fW200$o+0Nq9qh4(mdqRpAn%l@gzqWpDl*o3peixq#3Opnq/q(0Kq*qugOfppxllhZqOlkhbhZq,n0qa0}0BmQr0d:hs0}000Nhvq.q%0}q)qklHm:j9m=rbq-q`h,g$m|pql*rgrdqrkFrlqSfUp1pHl!lkpNq=cYpQ0}pS0z0bj02W0.2+2z2a3a2E2A0v3a0D0zpXohkmmXoQ9xqC5Or|qF9OmBg_qJg{kIkBp0esnF0t0!0!2ar5r90Hqek%rcr9pzkIrKr9f}oPj+42m!oT9}m%nVqGi8p s4q1q7m.lIrAlkrHoyrDq}k_rGrfsLp=p7mqq$26qbrmm-ryj3sHr9sJsjqgrEixrenFqqrLri04qtrt26pamJrY04pS01edpYjb243aed0KjxkLjHjqnHjfp)0tp+p-t4p:n7qzr{iJoqqGnddvb/9PowhMkInAnCh*s*sNp8qXfMn/q!aUfIqyp=mt9Xr}bTnnp|9vtJnhnOss9(tK9WnUtNqGb^s3s6tCl~obhZsbsdr4q_sM3@irmUqwk{d{kT9?tU3It_s09}n|sDgNtupUt-sRl*s7tDt?kSbJdo9~t`42ostobOkwn~iahQoNhTrxm/s!tgnN2@0Yala2aga4ad1g0ya7uB2R2M0tjp2La5a.0(0*0,0P04.

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
.128013oP;f 8lkg5:j+,bry2a1}h3tvw=u6{e[(*Sn9]x_s-c)7im./4p0Od050$0F0y0t0U0h0P0f0R0h0t0P0P0B010y0U0Z010406050P0C0V0V0t0q0r040J0b0h0C0`0b0K050X111315170 0Z04051n1g1q0X1n0 0$0U0z0/0;0?0^0;0K0j0C0t0j0F0Q0Z0r0y0w1e0f0w0U0j0w0h1S0w0y0}050*0p0h0F1z0=0@011R1T1V1T0y1#1%1Z0y0q1o1N0/1a0P0Z0t0K0^0s011)1B010e0,0F0K0t0V0F1Z1~20251+281%2b2d0}0a0f0c0q0b0Z0b0P0U1d0K0f0(1|0q0q0F0R2y1g2g0K1o0X1N2L1^1`1_1!0$2i1C0U0K2a2v1Z1w1y0:1*2V2X0K0b2#1Z0Z2E1o2J2L2=101 2z2%262+0q140h1Z0t1Q2E0e0^030O0O0R2,0F1V2*0b0Q0x0Q0u0}0f0u1g0t2?2_0~2^2h2{1+2}2 31330F350137393b3d2Y3g0Q23040f0s3n3p203r2J2U013w0t301o320w3436383a0(3G2+3I0x3k0x3O2I3q0 3S3u0^3V3X053Z3#3C3%3F2W3H3h0Y3k0Y3:1h3=3s2`1A3v0b2~3W3y3!3A3$3E3)423+3h0k3k0k482=3?2_3T3`4i3~3D3(3c4o3f3h0D3k0D4u4a3@4d3_4f3x3Y3z3B4C413e3I0T3k0T4L3Q4w3t4O3U4Q4h4S4j4U404n4X3h0g3k0g4$2K4(4c2(4+4g3{3}4k3 4m4E4?0Q0L3k0L4{3R4x3^504R3|4T4l4D3*4G3i0!0}0u0!5d4}4y4,525k555m4F3I0u3j045E5u4b5w514A544V4=433i3K0u3N0X3o3;4%5J5g4z4.4B4;575Q0u3-5G3/5V3P4|5Z4*5#5j4/5l4W5*455G475/5X5;4N4 5@534:565n5D4r5G4t60495Y632|5x5M675B580u4I5G4K6e4v5=646j5$5N5(693h0u4Z5G4#6s4M5f5?6w5^5%685C6B4^5G4`6G6g6I6v5L6x6l5{4p3i5a5G5c6T626V6i6X6L6y6N580s5q046?5I6h4e6.665`5P6#0s5F726`6,6|5i6~5A6!5o0s3K7d754)6W785z5O5)715,0s5.5W6f6+7h6-7j5_7a707c5}0s5 7r6t6{4P6}7k6z6O3J6b0s6d7E6H7u774-6/6Z7z3I0s6p7Z7g4~7v7U797l6A3J6D0s6F7Q6U7S7H7w6M6m5Q0s6Q7|7$5K7^6:7`716%0s6)7;7t7%7T5y7x7+7L0x6@8g7 5!6K7*7K580x5F8p8j6J7I8d8n5Q0x3K8y8s7i7)7J6;8x5,0x7q5:5e7?5h8D8v8F6#0x5}8S8B7(8c7_7b3,6b0x7P8K5v8k8u8Y7X3h0x6p8/8V8b6k6 7m5o0x6D8{8=7@8O8,8_3,6Q0x6S888L8a8 8X828Z8.6%0x878(6u8W8@7y92446@0Y5t6*9880909c8-0Q0Y5F9z5d1r2:1g2#2O0$1`2T5g4D2!1x1o2/0F2;3q611o4D9S2h0U0$0^382J5D3y9Z9#8Q5o3j0f2m0F9+839-1Z607G010i0}0(0e9U9j1+0A3ka09`0K0e0}2u0C0?0U0F2E0Pa5760^0|040Hah8M0Ka9ad0R0Fan9901ak0S0l9U0 7s9X2z9*019$2_7Y9)9!aG9,aJ9/2c9;aM9?aJ2L3o0faX0fa13_a90babagaCaZ9`0b0}0B9Ua+aiaw0}0G0Ma:aYaB2@3SaFaH203,aK9=9d3g249:b49x5,3OaYa;8M9|040e4fa:a!3U0}0hbka,a3042Wbpa=0K0p0}0q201Iau3TakamaCblbwbn2abC5gbEbL5?aq2FatbG9`axayaAbCa 0O9%44b3aSb545aP2db99n9y9^aWbdaYblbgbi0qbuao0}0Rb{av0bbrbta*bHbx04bz1EbSa}a=bNbTbvc62lbO4 cdcbb|040Zarca9TbUa@ci260V0U0}9rclavak0M0SbWaCa|cr4xbZb#0Q6b5jbZaT4qb7aQb-7,cN5/b=cYbeavbg0U9 c4a6bQascu1+ak0Gc-a#04boce8McCc:c^avap04b~c|bD0}a_c)a=a-040Ba/d58M0P5F000I00c;a?04azcGbYaLb00K3I6pcOdoaN4HcSb,b(9xdscXcZdDbHa$a(did70Wdic~0t0Z0Z2a0$dibEbFcA4ybndS0}0odLb}dY04cEbXd1cKaI3h6DdtcU7L4Zb+aR8w6#d/dCaXb@by0)0C0q1fdbc}dG0?d*dVd,b14@b%d_5o4^d@d;586Q3OcH3Q5Jeadq3h6%d:dzb.5aeheu7,es9_a=bg9~dibraZd15!a8041w0P0*0Kas0P0O0(0Z0C0UeR0R152E0t0C0Nd%dUcIcmcobRd%cF6td+ducL5seddv5pdxd^e_e@aV3Lb?c*eLeWeOeQ0Oaae7e43Td7da2=c!d2040E0vb 3T0R5F030f0#2zdP1c0f1c0,eWaefu2z1w2G0U1e2b0U2Ee8e*aEe=d-3i5Fetee5D9.b8ey7L5Eb:f0d}f2eMf50FeRf8a)dVbMcteIbPcn2v0O2s0p0b0ye(d#cncpe.d4feblfcfk5!0}0p0ef*fJfge)enf22ueRf?f^f`f.64c+cqgccc0}0Sd!fag4f|e-gj26axfIgn9YfLeb5Re^cQgEexfQ6B3Kbcf1eC0}b_g3f/0ZgR4 c10}c3g0gda%acaef%gif+gSf}gwc.gpdle:e9gCeq3ibb32cPb55+e{ei5*bbd|b=dFf3eN20f6f)d%c{g*gkcnd%f 3qff5gg2gsf/g6g8gAcB0}gb2Kh4gTg-ajdZgU2|gle.gzhta~g?5D5}fPe}b*fTgJ3ihIh2d~c7e0e2hz3v9}f4h7f%f7g#ho5;e;9+e?cNg`dugG0u4rgIe}cW3oemhEcJhG6Bdsh-g 6#6og~fU6ndBh^dnh*fM6CgFg|d?hMe}d{5W9`eD3AeFa4hw3UeKafa%dRiockg9gte,c,iug/hDaD0fep5Dekh i45*egifh/ekhQf20P3Wg(hlgVa.hVc=f)adafg)iwg+gvhcgxgpiCeoh|6$ic9x0uewiMg|eAb;fZbvby0bf@f_iU26hkgZi}f:gfi ghiAalf{iygmh`c_i,dmh)aMcL6?i;b.jnh=gGjne bdh4f#hZh%3Lg1iWj2hWh5f$eReTeVeXeZ0Fe#e%jbhsiDixg,i*g.d(i-hFi9gD72jo7,j#jrb5j#e h_iDiF3h7dj$7Lj;j)9xj;jugNbf9}ilioeGdLeK0h1e1F0Fe10O1^0t0m0)i$hpdWeL0F1 0qj1jUhx04grj5e+h$i!iTkmdje/4ajkdp7Yg_iEh.j*3-j^jph1i{h3f2kakcklhhjBd8iX01cwcyjXh{jZg@7Cj=6=hLcTiJ71hPkKjvf24f0$2Eb!iSjzhi4*j4kQcsjciobIc?f4kejge5f:a(ktjzblbVdI0}dKk b}2t0re(0SkXgBkZ7Yh,kDi07ch;i^j_h@fYkLcfa90q2WkPkff,k~kvc~k:k=iR1%l9k}lkjjg=lnj:h~lqk*7c4IkHj%i6lxk.j60t2G0C3ckTk{3Qk_hdgglDh(lRjlfM7/k$7{iek)hNl{j{cYhR0A1R1%kTl0chjPf{lJ0Fk?lMe.g:kyl^kAj:iHlVm0iLl e_7|fXdDl%cm111xl-jCkql5mdmfkui%iV04lelHa$hvkvbElPg;g9j/3JesiIm0i@mrjsi`l$cZh40re3mOhyjDc=0Nm*k|d6mBm;mxeV0zllfKlS3g6@hJgG8gi3hNn2m2mvh4kNkdm-01d70nfdm@l5jxePmGlE4*c/f{l)0yl+c@m+040MhbmHhA04m)hfnwnlhdm/hfm{mob!fM8pl|8RfSmZb5nLn6dEf2npnrmAkSnbc~mym`lQmSi/8ynM8`23lZ8fgL5/j-i.m}8In+3,kGlub.n^nSj|c#d nqhUnZ0}n9l?3rkznJgD8Sn_8.k(dyn4k,o9mkobg@8$oe3gltnPbalwmwl5o70Ocwm:l/kRnfoBf2k41El+k8o7l3jRf/l=d%kpngkglLnkl4fgmQmjn(n@lUg{balYn|7,8/muo03Tb^bjo5cnae0VmFk^g1c2oA2Kl:nydP2~1c0t2ylNgolGnxjEoTp5jhd(mi5YoacL8{oppin.8oihm$i|j}bsc(mCkgoGk6oJbzkOoLh4oOjboQoEj6p0o@pahCnbd7020h0y0doDo}n8py0)oygYp8knpel@oZl_ocmno$n}mqohe_95o,mvo~jEoxoznXpRjAoFk5oI0qk9pUo8lahrf{pCntpEpSgdo?o^pKmRkfmT9gpjmYp-n1m#p;ppmD0qk;jfp{m=nYptixqbpJn%qfi/9qopqDpl5QqDj,i8p%g@9zqEnOqkb)fOiPgOhSo3o|qtcmp@pXnDi+kom80}mdi-0X9W9D9R9F9O1g0y9Iq?2R2M0t1$q:0X9GaB0(0*0,0P04.