Bellman-Ford

On considère dans cet exercice des graphes pondérés.

Si un graphe n'est pas orienté, on considère le graphe obtenu en remplaçant chaque arête par deux arcs d'orientations opposées avec les mêmes extrémités.

Un graphe est représenté par les listes de successeurs à l'aide d'un dictionnaire : les clés sont les sommets du graphe et pour chaque clé \(s\), la valeur associée est la liste des couples \((u, p)\)\(u\) est un successeur du sommet \(s\) et \(p\) est le poids de l'arc \((s, u)\).

Par exemple, avec les deux graphes dessinés ci-dessous, le graphe orienté à gauche est représenté par le dictionnaire
{"A": [("B", 5), ("C", 4)], "B": [("C", 3), ("D", 3), ("E", 2)], "C": [("B", -2), ("D", 2)], "D": [("C", 3), ("E", -2)], "E": []}
et le graphe orienté (non connexe) à droite, par le dictionnaire
{"A": [("B", 3), ("C", 6)], "B": [("A", 3), ("C", 2), ("D", 5)], "C": [("A", 6), ("B", 2), ("D", 4)], "D": [("B", 5), ("C", 4)], "E": []}.

Exemples de graphes

Un chemin est une suite d'arcs \((s_0, s_1), (s_1, s_2), (s_2, s_3), ..., (s_{q-1}, s_q)\). Un circuit est un chemin tel que \(s_0 = s_q\).

Le poids d'un chemin est la somme des poids des arcs constituant le chemin. Certains arcs peuvent avoir des poids négatifs mais on suppose pour les questions 1, 2, 3 que le graphe ne contient pas de circuit dont le poids total est négatif.

La distance entre deux sommets \(u\) et \(v\) est le poids minimal d'un chemin allant de \(u\) à \(v\). S'il n'existe pas de chemin entre deux sommets, la distance entre ces deux sommets est infinie.

Le problème posé est : déterminer les distances entre un sommet source et chaque sommet du graphe.

1. Programmation dynamique

Pour résoudre le problème posé, on va résoudre tous les problèmes : déterminer les distances entre un sommet source et chaque sommet du graphe en utilisant au plus \(i\) arcs, \(i\) allant de \(0\) à \(n-1\)\(n\) est l'ordre du graphe.

Si \(S\) est le sommet source et \(u\) un sommet quelconque, on note \(d(u, i)\) la distance entre \(S\) et \(u\) en utilisant au plus \(i\) arcs. On a alors les relations :

  • \(d(S, 0) = 0\);
  • \(d(u, 0) = \infty\) si \(u\neq S\);
  • pour \(i\) allant de \(1\) à \(n-1\), \(d(u, i) = min(d(u, i-1), d(v, i-1) + p(v, u))\)\(p(v, u)\) est le poids de l'arc \((v, u)\).
    En effet, pour atteindre \(u\) avec au plus \(i\) arcs, on peut atteindre \(u\) avec au plus \(i-1\) arcs ou, s'il existe un arc \((v, u)\), atteindre \(v\) avec au plus \(i-1\) arcs et terminer le chemin par l'arc \((v, u)\).

La solution au problème initial est alors donnée pour tout sommet \(u\) par \(d(u, n-1)\).

Compléter la fonction distances_1 ci-dessous, qui prend en paramètres un dictionnaire représentant un graphe et un sommet source, et construit un dictionnaire dist dont les clés sont les couples de la forme \((s, i)\) avec pour valeur associée \(d(s, i)\), pour tout \(s\) sommet du graphe et tout \(i\) entier allant de \(0\) à \(n-1\). Le dictionnaire dist est renvoyé à la fin.

Une double boucle permet de parcourir les arcs : parcours des sommets puis, pour chaque sommet, parcours de la liste de successeurs.

Pour représenter \(\infty\), une variable inf a été définie par inf = float("inf"). Elle est directement utilisable dans le code de la fonction.

Exemple
>>> g = {"A": [("B", 2), ("C", 5)], "B": [("C", 1)], "C": []}
>>> distances_1(g, "A")
{('A', 0): 0, ('B', 0): inf, ('C', 0): inf, ('A', 1): 0, ('B', 1): 2, ('C', 1): 5, ('A', 2): 0, ('B', 2): 2, ('C', 2): 3}

###(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 : 10/10

.128013s3o_8;bcdufvgx/0ly n7apS.r1-me,(P2=4:}+twki9][5h{)é6050j0E0O0w0R0r0b0t0i0r0w0b0b0J010O0R0x010406050b0k0D0D0w0A0s040y0d0r0k0^0d0u050p0 1113150}0x041e1l051o0p1o1q1l0}0j0R0m0-0/0;0?0W0R0n0W0r1E0W0O0{050(0h0r0E1z0:0=011D1F1H1F0O1N1P1L0O0h0d0j151M0A1m0O0W0-180b0x0w0u0?0I011R1B010l0*0E0u0w0D0E1L1?1^1}1T201P23250{0a0t0H0A0d0x0d0b0R1b0u0t0$1;0A0A0E0i2q1e280u1m0p1/2D0O1-1,1.0j2a0?1H0u222n1L1w1y0.1S2N0R2P0u1)1x1L0x2w1m2B2D2+0~1@2r2V1~2!0A120r0{0t0B2A2/0|2.292;1T2?2^2`0I2}1^2 2B2M01340w2_040t0c382C0}3b320?3e3g0t0K3k3a2/3c3q2`0V3u3m3w3o3d0d2@3f2`0!3B302:1A333G353h0v3L3n3O3p3Q3I3h0f3U3D3W3F3H3r0S3$313(3y040B0q3-3N2W3)3R0B2|1f2~3C3.3_3:0B373~39403^2=3Y3g0B3j463l3M3x4b0{0B3t4f3v414a3*4k3A4n1n2)1e2T2G0j2K3c0i1)261m4y1p4w2-4u4D0$2*3%3_0Q0{0$0l3u4h3E0P2`4V3V420l4S0R0b0(0u0i0E0b0e3}2-4#1~0`040G4!4P2=0{0n0A0w0x0W0E4{4p1T4^0F3u0t4W3/0{0b0d0k0A4-5549570{0Y0L3B0t5r5b4?334(4*5a5c3_0d0{0J5y5u0?4^0X4`4u5F3d5e5k3c585E4|1T0D0R0{3?5K5S5G5n5p4n5t5Z3d0h0{0l0r0d0w0O5O3E4^5J4=5)0b1{04012Y4%5=3(4^0Y5R560?4R040l3G655l3p5N5%5z1~0d4Y042Y6c3x4~5052545Y66014^0M5q5s6h5v041w5x6t6d6v0{0U5^2~6A6e045f5h5j6F5P0{596g5L5U5W613_630T6n3E5B045D6W5)6Y045X2+06065s5(6u686a0A6(5d6l6~5A6k6m6-6u0u5+04500u0n6s5_6u5@6!1~6/4;6L5L5Q756G770{2d7h5m4_7t6N4 51537w6H040Y5o5a6_6G0i0B0{030t2!0D0h2w0t120o0R2^2s00130i0,1a0*4)0Z0b6y6^5r6M017J7L0t2Y2p0R3f4)5:0R1c2s4.1z7)0t0G0b0F7=644n6@7,7-5L6{6b7o6o6O716i731d8f3E0u6p7z7d7l5)4^5$6=8a8a7.8o6C4)5;6S5?6I6K398z6f7e6G7n2+7H8g0R7B6$8i1T6*6,8O8J8B6E8L6T046J7B8A7*8E626U8U6N8R8-5A0{0C7B7j8S5n6%888x8P3E7:047M1@5i5g0A0,0$0,7Z0,830B855{878w8x7.8d6}8m6 0b7k8I7m8/9p425e0k0i4-0;0E5h8,8Y5L6j0{749G5)8A7y6r7B6*0z8*9J0O0E0D9F8s7f0{0G7F8 906z8c5,8e9L765e458$8F046V9.7p0{0x8:019I6l8l9_8g0 9A4.0b9D998|048v3 9)9l9+6l4U9w4}8!8D9=8.8(8H2C8Z9raa9^2~916 8=an8@048_8?7i5V3;aa0Y8~a26)0{0N9}8A9|aj8V0{020r0O0gaP5wam9Z8M8G9T6O9;a$8%av39ax9x70aE7uaJac47aeae8Z6Da#9t8ta(a?6N5{auaZa=aA4@8}9}8Wb7a~aa8)b35M6O9sar9u9@b7aza,aMaC8`aGbl4O9!7DaKaw7.6*aOaS6NaR6=898b5)682w0O5ha1bB5L8Abf881e4M0E2D2(bY4x1x4z2G2I2E1(1*2G0w1Ob#0p4y0}b=0%0)0+04.
2. Simplification

Dans cette question, on reprend la fonction distances_1 de la question précédente et on la modifie pour obtenir la fonction distance_2.

Principe de la modification: pour évaluer la valeur associée à une clé \((s, i)\), \(i >0\), on n'utilise que les valeurs associées aux clés de la forme \((s, i-1)\). Il n'est donc pas nécessaire de garder en mémoire toutes les clés (au nombre de \(n^2\) en fin de boucle, avec \(n\) sommets et \(n\) valeurs possibles pour \(i\)).
À chaque passage dans la boucle externe, on dispose d'un dictionnaire dist, dont les clés sont les sommets \(s\), correspondant à l'utilisation d'au plus \(i-1\) arcs, on construit, lors du parcours des arcs à l'aide des deux boucles internes, un dictionnaire dist2, dont les clés sont les sommets \(s\), mais correspondant à l'utilisation d'au plus \(i\) arcs, puis on remplace dist par dist2 avant le passage suivant dans la boucle externe. On n'utilise ainsi plus que \(2\times n\) clés, \(n\) pour chacun des deux dictionnaires. C'est une économie importante sur le coût en mémoire.

Compléter la fonction distances_2.

###(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 : 10/10

.128013s3o_8;bcdufvgx/0ly n7apS.r1me,(P2=4:}+twki9][5h{)é6050j0D0N0w0Q0r0b0t0i0r0w0b0b0I010N0Q0x010406050b0k0C0C0w0A0s040y0d0r0k0@0d0u050p0~1012140|0x041d1k051n0p1n1p1k0|0j0Q0m0,0.0:0=0V0Q0n0V0r1D0V0N0`050%0h0r0D1y0/0;011C1E1G1E0N1M1O1K0N0h0d0j141L0A1l0N0V0,170b0x0w0u0=0H011Q1A010l0)0D0u0w0C0D1K1=1@1|1S1 1O22240`0a0t0G0A0d0x0d0b0Q1a0u0t0#1:0A0A0D0i2p1d270u1l0p1.2C0N1,1+1-0j290=1G0u212m1K1v1x0-1R2M0Q2O0u1(1w1K0x2v1l2A2C2*0}1?2q2U1}2Z0A110r0`0t0B2z2.0{2-282:1S2=2@2_0H2|1@2~2A2L01330w2^040t0c372B0|3a310=3d3f0t0J3j392.3b3p2_0U3t3l3v3n3c0d2?3e2_0Z3A2 2/1z323F343g0v3K3m3N3o3P3H3g0f3T3C3V3E3G3q0R3#303%3x040B0q3,3M2V3(3Q0B2{1e2}3B3-3^3/0B363}383 3@2;3X3f0B3i453k3L3w4a0`0B3s4e3u40493)4j3z4m474h4q3:3J4t4g3D423S4m1m2(1d2S2F0j2J3b0i1(251l4I1o4G2,4E4N0#2)3$3^0P0`0#0l3t4A3%0O2_4)3U410l4$0Q0b0%0u0i0D0b0e442,4/1}0_040F4.4Z2;0`0n0A0w0x0V0D554o1S520E3t0t4*410`0b0d0k0A4`5f485h0`0X0K3A0t5B5l501S0i0B0`030t0q0t120i0t190)4?0Y5A5C5m57041v4@5k5W1S0d0`0I5#5E0=520W5u3w5o5:3D525z4m5D56320h0`0l0r0d0w0N5?3%52544E5,010b1`04012X4;653^520X5+5|0=4#040l3F6m5g3o5=5`5$0=0d4,042X6t5v6v04595b5d6i510`0L5U5B6y3c4=5!696n01520T6L325o5q5s5e6V6u6X0`0S6E3b5(045*6x6a0C0Q0`3=4t065C5{6+6p6r0A6/4B0`0Q753%6A771c6@6W0u5~045a0u0n6)4 6W676!0=6_4j7q6,045j7e6+7g0`2c7u7p6*6F6S6H5a5c7m2}6R6k5y5k707G5G5I0t2Z0C0h2v0t110o0Q2@2r005N0+5Q1G0b0Y0b6P6 7R4M5H045J4N0x0Q1P2s5Z644t7?5V6a0u6T0N4~2}7@3D6;6?2*8c660`5/7F5;047;8l5@0`5_8g6R87040m794!5 6s7y7G8v8o7n6+5i8y5X8x8C6:6B6D8M765Y4?828G7G6;0z7u8v2o0D0C8F7M6a670X6O83848h3^7T7`0t1?5s6%0+0#0+7+0t0F0b0B0E0t6c6l8.846R728B8t865o3|8V3b8I8Q3.5o0k0i4`0:0D5r8(388:1}7b6C7d9c7f587J6K8p7a0`8Y9E5n6C0N8$9s2B7N0`0F7P978/6Q6a9a749j9J6c7D0`7x9z7z0`0x8J5%8O9y8b8u9l9n4{0b9q0A9N4Y8H8r7=9U6 99774(9Z5X819$046Z9I5X91a96.a65%0`0M9-6G9,ah6z0`020r0N0gal7H818a389Paa8Z5oay9O8*6-8s3~a1aK9u6#8S4@aE9}7G6YaC8naQaAag9)8W5)av8va8ac5waBa)6Gaea,7vaY9;6a6;akao7Han2*6~8/6R8=7{2k7~2r7 8TaQa}989daO8Ua=6W8eav5.aU9|aAaI9t9=8wav9Xa$6w9g8q7wbrboa_9w8PaZ8max7u8XaU8#8%a99R8-a|b8aM6o0`2v0N5r9:bmbaa(a|1d4W0D2C2%b#4H1w4J2F2H2D1%1)2F0w1Nb(0p4I0|b^0$0(0*04.
3. Bellman-Ford

Dans cette question on procède à une nouvelle modification: on ne crée plus un nouveau dictionnaire dist2, mais on modifie le dictionnaire initial dist.

Dans ce cas, on obtient pour chaque valeur de i, une valeur pour dist[s2] qui est inférieure ou égale à \(d(s2, i)\). Donc la procédure est "accélérée". La variable i ne représente plus le nombre maximal d'arcs utilisés, mais simplement le nombre de passages dans la boucle externe.

Compléter la fonction bellman_ford_1. Cette fonction implémente l'algorithme de Bellman-Ford.

###(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 : 10/10

.128013s3o_8;bcdufvg/0ly n7apS.r1me,(P2=4:}+twki9][5h{)é6050j0C0M0v0P0q0b0s0i0q0v0b0b0H010M0P0w010406050b0k0B0B0v0z0r040x0d0q0k0?0d0t050o0}0 11130{0w041c1j051m0o1m1o1j0{0j0P0m0+0-0/0;0U0P0n0U0q1C0U0M0_050$0h0q0C1x0.0:011B1D1F1D0M1L1N1J0M0h0d0j131K0z1k0M0U0+160b0w0v0t0;0G011P1z010l0(0C0t0v0B0C1J1;1?1{1R1~1N21230_0a0s0F0z0d0w0d0b0P190t0s0!1/0z0z0C0i2o1c260t1k0o1-2B0M1+1*1,0j280;1F0t202l1J1u1w0,1Q2L0P2N0t1%1v1J0w2u1k2z2B2)0|1=2p2T1|2Y0z100q0_0s0A2y2-0`2,272/1R2;2?2^0G2{1?2}2z2K01320v2@040s0c362A0{39300;3c3e0s0I3i382-3a3o2^0T3s3k3u3m3b0d2=3d2^0Y3z2~2.1y313E333f0u3J3l3M3n3O3G3f0f3S3B3U3D3F3p0Q3!2 3$3w040A0p3+3L2U3%3P0A2`1d2|3A3,3@3.0A353|371l2%1c2R2E0j2I3a0i1%241k491n472+442A054e0!2(3#3@0O0_0!0l3s3K3a0N2^4y3T400l0_0h0C0q0q100t0e0l3E0j0e3{2+4E1|0^040E4D4s2:0_0n0z0v0w0U0C4!3 4W0_0D3s0s4z3C0t0_0b0d0k0z0i4-4m4r4/1R4X0W0J3z0s5a4@4V314v0P0b0M4?4^3$0d0_0H5j5d0;4X0V4.3?4$040b5u3a4X58525c4#310h0_0l0q0d0v5i525k3@4X4Z5O5q010b1_04012W4G5z3C565p5F0;4u044P0z5)543n4{5:5v1R0d4B042W5@3v4%4)4+514U5*014X0K595b5P5w1u5h5$3$4X0S6f404{4}4 632|6b550_0R5~3C5m045o5D6q0;0B0P0_3;5206065b5E5;015,5.6u3-0_0P6P3@5`6R1b6z5U0t5H044)0t0n6o455U5R6j1|6C0_4T6p6,4;6T2:6#2b6.6r4Y6}5=044(4*4,70660_0W574?6K5^0;0i0A0_030s2Y0B0h2u2q1O1=0/0v6)0*0j1?0*0-0s1$0k0,6*3j6J6J6A6M5I3E6_5e5x6=6+654X4=6Y654`5x7B500/0C4~5y7T6L6V5|6X2)7c5 7261755T656w0y767V2n0C0B7$646L5R7a6G7F847-3C6N7K7%7d3b4{437 8b7R7L710w8i017)5}8a7.0}0i7Y0b7!0z7~6?7Q0_5C2)6I858E7H5,0P4x8p4_5f6e7=800_6i8O8b7V0b7O4n6@046t8K5l0_0L8l7V8k8#6U0_020q0M0g8)8M5N8f5A8Q7_8d764X0R8B3}8E936a6Z8@8~8{8S8q8e8y8P8Z8l6w6y7,7H7V6d8^9d8g998_8L7N989f8,1|6w8(9w7M8+8C94847H7f7h2q5g0M0S5W0R0s8/8;0H2q0E5W0D0s0P0W0s0E0v8s0X0q0X4)2o7o0s7y2$0d0i0X0j4~0C0W3z8D5a8G0_2u0M4~7+2|866Q049m3J0o4p0C2B2$ac481v4a2E2G2C1$1(2E0v1Maf0o490{as0#0%0)04.
4. Recherche de circuit

Cette question traite de deux améliorations possibles de la fonction bellman_ford_1.

  1. Les distances cherchées peuvent être obtenues en moins de \(n-1\) passages dans la boucle. Dans ce cas il est possible d'interrompre le programme.
    Pour cela, on ajoute une variable modif initialisée à False avant l'examen des arcs (les deux boucles internes). Si lors d'un examen une distance est modifiée, la valeur de la variable modif devient True. Si cette variable n'a pas été modifiée après un examen des arcs, alors on interrompt la boucle externe, sinon on continue.

  2. Si le graphe contient un circuit dont le poids total est négatif, alors un examen supplémentaire des arcs va entraîner une diminution de la valeur pour certaines distances.
    On ajoute donc un nouvel examen des arcs, après la boucle externe, pour tester s'il y a une modification d'une distance. Si c'est le cas, le graphe contient un cycle de poids total négatif.

Compléter la fonction bellman_ford_2 avec les deux modifications proposées. Cette fonction doit renvoyer un triplet constitué du dictionnaire des distances dist, du nombre de passages effectivement réalisés dans la boucle externe et de la valeur True ou False selon que le graphe contient un cycle négatif ou pas.

Remarque importante

Après la fin d'une boucle for i in range(a, b), la variable i reste accessible et a pour valeur b-1. Par exemple:

>>> for i in range(1, 7):
        pass

>>> i
6

Dans d'autres langages, la variable déclarée dans la boucle est limitée à la boucle. On dit qu'elle est dans la portée du bloc for et elle n'est pas visible en dehors.

###(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 : 10/10

.128013s3_8ufvy n7aS1me(P24:jtwi][h)6o;bcdUgù/T0làqAp.rF-,}=+k{95Rxé050J0q0x0m0z0Q0b0j0I0Q0m0b0b0#010x0z0U010406050b0f0p0p0m0W0i040n0F0Q0f110F0k0j020m0p0U0G0j0+0q1b0W0S0f0q0b050N181a1c1e160U041C1J051M0N1M1O1J160J0z0h0_0{0}0 0C0z0L0C0Q1$0C0x14050;0H0Q0q1X0|0~011#1%1)1%0x1/1;1-0x0H0F0J1e1.0W1K0x0C0_1h0b0U0m0k0 0t011?1Z010g0?0q0k1p0q1-2e2g2l1^2o1;2r0p2t040a0j0s0W0F0U0F0b0z1k1m0/2c0W0W0q0I2O1C2v0k1K0N2a2!0x2827290J2x0 1)0k2q2L1-1U1W0`1@2.0z2:0k241V1-0U2T1K2Y2!35172f1m2_2m2~0W1b0Q140j0o2X3915382w3b1^3d3f3h0t3k2g3m2Y2-013r0m3g040j0c3v2Z163y3p0 3B3D0j0u3H3x393z3N3h0*3R3J3T3L3A0F3e3C3h0E3Y3n3a1Y3q3%3s3E0l3,3K3/3M3;3)3E0e3^3!3`3$3(3O0)403o423V040o0P473.2`433=0o3j1D3l3Z484g4a0o3u4l3w4n4f3c3|3D0o3G4t3I3-3U4y140o3Q4C3S4o4x444H3X4K4v4F4O4b3+4R4E3#4q3@4X3_4p4G4b3 4$414(4U0o464,4M3:4U0t4d4K1L331C2@2%0J2+3z0I242D0.1V1K320q343l3R05540/5c4?0 0%140/0g5e4%2m0y3h5p4-3c0g140H0q0Q0Q1b0k0d0g3%0J0d4s375q1^13040r5u5j3A140L0W0m0U0C0q5R4w5N140Z3R0j4Y49140b0F0f0W0I5!4{5M0 5O0D0v3Y0j5 5+5_5T041U0b0x5*5,4g0F140#68625O0(5#3U5.6i3#5O5}4K615v3q0H140g0Q0F0m675^6r5`145Q6A5S0b0o14002|0g006l425{6e6B015l045G0W6R5S0k6k6p692m0F5s042|6Y5$3M5U5W5Y5@5L6S5O0!5~606%3q5m0z666O4g5O0B723c5.5:5=6?5d6f140A6-3z6b046d6$620p0z144`3506606q5S6U6W7g4Z140z7y426)7A0k7C4p6t045W0k0L7b3w6}6C5P761^7n4H7T7R5)7l6S0k7J2A7X015O6E6@6Z6:5X5Z7)5{5|6{7t5 7Q630p2 5o7!5S7i7k357u6.6T0I140X3C0b7O3I7_6|627w3%7H77046H7=5(8k6~8m0f0I5?0}0q5;1B80867E6+7G8A6j045V7:8d5i8B140V7)6!6+0x1v8z7-867+7@4R8f8f7{8i6X8F7z8m5K7c6^8p8)5-040U8q0 8C6,8:4p5.8t8v8c8y8o046o7r8!96853z6U0z7 847{8Q656z8V3z748P5.4k9i6m7e8@017i0$9r8Q8?8{6(14020Q0x0G9v6 716F8W14759I8G0b8,7P7d040A944m979W988*9g929L9o8;9O927f9y1^829F64709h8-5S9k9M8*8n9_6P9q9,8^149u9 639x959X969e147}1U9c3l9Y7D6c9r0%88040O0W1z7^7_8$7Aad3waf6a6*2~9=aua904ab9b929U4ua7ar7K0:5;8E9d629f9;927ZaO7#7AaSaiak8a0@8K067s8g6S0I6I04030j0f1m0q0,2s1m180U0U0Q0-2C0k0;0z2TapaJ7xa38Q9{9%738/aU7.8s8u1A900W8Uae7{8_aNbkaP7/6=7)7i8O9|8|8R8T920r8Ya68#8h6u8jb65.9P2Z7{5OaTboaV8=9rbm9/18bf8w91bv2m6napa8bE6+at2Zav8l9!bY5%049$9?86b79nb=9j9~bc8M04a2b|8Ga5bO819A9C9EbH9:9Hb9bZ9K9l8+9*aG8ea7a)7v142T0xaM9/b-cbb/bNaAbp6+aXa3aj14amao4Ra(7`b%cncpc8crb_9p04cub*aB7Bb.7YaY898ba$1C5g5b4|c#0N4 1C0x51c*2)2#23252%0m1:c%4 1I8L3z2T0p5F0m0%0q0d0C0c141u1w1y1A0jch2!1S1N1f0z0j1l0j2:0j0h8x0x0j2f0^1j0?700q0W0j0{dm1c0zc?1;5+c!3z1`1(1*1,c{8*cS370Nc!3E2q0j0/0C3%0^2Qdz238t1;0Zdi1m0Udo0j0m0w5:8Sdxa:1=0hdBdD1=5f55dH1*1|1+2ubP541q8S5;5edQ553E1ydhe40U8Sdy1=2~0p0H2TdU1=dr0b0m7NdY2g0^d!5:0`0qbude2@d~1{dK314}dP37dR7taJ5n7)6*5+cT3A5x045z5B5D5F5H5Jbzce8IbreRbMbT795?92bB9V8!aBcLcv6S9.a36gcebj9Q8.939v7J6v6xazbK9R7,cM426H6J6L6Ne)140DaibF8(c09`bR6*8`fl8;e%7;ff046`8Ze;cwe?f6e b;e~bd5/5;e-fu9+fq6aaha37V047qe:bDe30Fe58xfkc3b}83fZ3zfPfRaHfy6S8%9/dOf$3#bSb67J7L7Ne#eRfPb^fE9JcOf1147(fuf8f~8Gfs8KbLfge/f*9WaBeee6fYe@c47j9/ggfXbRa19rf|b#f+bdaDb)3EblfNfL2mcA04a!8cgtfTcl6VbGgB8rb8f9bag0c8bU8 fXe}cQ62f=gM6/8H6;ftcsa004bug)632Nbyg4gccicjb+1^f-gSbJdM9}gRg!a4fn7FbT8~bggVaFgHcjaJaEcKaRfufDfBfFf}hhf fKf:agb~9/c2gjb}9B9Dcqheg-9^g-b7g|ga9SdccFg@gub?9Gf5g}gQhghM8l9)fJgpglhdcagPccb:e|hjhPb/hmhs7hgqc8hrg?hHgIhJaC7~hTf#h)3#gDcCa$aIb%hch08Cay9/gwh8fxg@aJcI0Wbnh_8;fAh$cUc8gne7fucPgy8haZcX3YhGeL62a+14a.a:0ja=a@0ja_a{a}2qb0b2i7aqb%b5h0b@cyiN8}bVbhgWine^foicgXbPg8bs8Nceg/0piUhDbAhFh~f,fjbThC9Rimg^g#h-iV81iXh4iSh7fui.hHhbgxi_63ifhDhOaBgOg6cNh(iZgkb hnbwi{j77ihuc7iQc9hLjae|i?e 9Th9i/gJiaiYi|h;j9i@gmfVefgoilcValana$isck866UjChwhWjeg~i^gfjJghiPjkgCipa#3,e95hc$2!c_df0Kdl0m1jem2K0}0z0Hdu0x0-iCdw0=dp0J00dt1)8cd=dld#eydFd}3#dIe0dLaJ0y1#1;e8eK0:endq2I0I0-0/dx0Rdy00iAa~iC0fa`a|a~iH1=0/0^1c0Iet0k0^5?1~1=ke1;0V060TkUa 0z1l0viz3a0|kt0I0ikf2Q2K0z0J0^0x0F0;0Qdk0-0L6y9bd(iyeper1=iDkIiG0=em0Jeudy0m0jkW1=1Adpgg0-d(k70F0MledA1;5;kg2Qkid eFig63ijgihkh*040Y7)gs4{j.5bdUld0Q6KkR2(8t2Olv1=lxeE1}e2jBaLibkqea2R2T2e1llca 0qd(062Klueq110b0gla1=1;k^lj0beA3meClXdJlZ59eH5d4{eKjS995m0qgx7{eP8PeTeV5C2geY0W5Ijw9@6De$g%g9jHgSe,mxe g=15b$bPifjngAj)b/6heRb7i6iNf26w6yf`g-fb04lQ6Me.figKlDjE9Nh28D9/i#fufwbCiKmGhxhXb/jbcwfG7a9*h@gr7ofQh9j#fWiki1mJid4gf(n2cwi5a3e_h0h{jPnbi:04kn2pi4h?ne6*2g0JjIn4m%mI04jpg104g3hymumNbqg(m@7RmDjRh:8GlCm~iij$jLn6hpfOn0h#nLm;gvnpnTh^jhjTj+gGiJmFgJiMmKg#jdlEcNj!m`h5bWbim*fpn:63m-g-bti(8Si*bznKh/b4gLn hBj(n88ljmbli~gSn`iTmPfSh/j79aj6e=m?jYhNh!m}neh+jrohgYc5hvhVjt9Rm_bPhRnDhEjzj4h oscwnOoz7jn%m(4Z7%2qmUnIo0mwe.j3op97i9l$jDj7aQjXn?jZnujKn5o$7ilHf{nWoen(99akh|oOi8fzouo?ownFcfhSoUoWo:hK9#oxpdnTjjof8ri{nYo9ncn#n nfn nhcDm:n-jTasm*i3c8ndoMo*hacHo.jWoHe n^fUnvp0oX42gDgFjQpAmeaKcol%oGpSpg04oToMpP7vn*cYlLdd4}c(5816c(0:0=0@04.