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)\) où \(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\) où \(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))\) où \(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

.128013:pbv(40i2+edmx3;s7= 1}o5w_lSnatf-)9h]{6,crg.[éyPku8/050m0l0F0E0i0B0r0u0P0B0E0r0r0t010F0i0c010406050r0Y0n0n0E0Q0V040C0x0B0Y0^0x0D050!0 1113150}0c041e1l051o0!1o1q1l0}0m0i0e0-0/0;0?0K0i0R0K0B1E0K0F0{050(0d0B0l1z0:0=011D1F1H1F0F1N1P1L0F0d0x0m151M0Q1m0F0K0-180r0c0E0D0?0j011R1B010G0*0l0D0E0n0l1L1?1^1}1T201P23250{0a0u0W0Q0x0c0x0r0i1b0D0u0$1;0Q0Q0l0P2q1e280D1m0!1/2D0F1-1,1.0m2a0?1H0D222n1L1w1y0.1S2N0i2P0D1)1x1L0c2w1m2B2D2+0~1@2r2V1~2!0Q120B0{0u0v2A2/0|2.292;1T2?2^2`0j2}1^2 2B2M01340E2_040u0p382C0}3b320?3e3g0u0g3k3a2/3c3q2`0y3u3m3w3o3d0x2@3f2`0N3B302:1A333G353h0s3L3n3O3p3Q3I3h0Z3U3D3W3F3H3r0J3$313(3y040v0h3-3N2W3)3R0v2|1f2~3C3.3_3:0v373~39403^2=3Y3g0v3j463l3M3x4b0{0v3t4f3v414a3*4k3A4n1n2)1e2T2G0m2K3c0P1)261m4y1p4w2-4u4D0$2*3%3_0X0{0$0G3u4h3E0z2`4V3V420G4S0i0r0(0D0P0l0r0A3}2-4#1~0`040f4!4P2=0{0R0Q0E0c0K0l4{4p1T4^0O3u0u4W3/0{0r0x0Y0Q4-5549570{0I0b3B0u5r5b4?334(4*5a5c3_0x0{0t5y5u0?4^0M4`4u5F3d5e5k3c585E4|1T0n0i0{3?5K5S5G5n5p4n5t5Z3d0d0{0G0B0x0E0F5O3E4^5J4=5)0r1{04012Y4%5=3(4^0I5R560?4R040G3G655l3p5N5%5z1~0x4Y042Y6c3x4~5052545Y66014^0w5q5s6h5v041w5x6t6d6v0{0T5^2~6A6e045f5h5j6F5P0{596g5L5U5W613_630L6n3E5B045D6W5)6Y045X2+06065s5(6u686a0Q6(5d6l6~5A6k6m6-6u0D5+04500D0R6s5_6u5@6!1~6/4;6L5L5Q756G770{2d7h5m4_7t6N4 51537w6H040I5o5a6_6G0P0v0{030u2!0n0d2w0u120o0i2^2s00130P0,1a0*4)0U0r6y6^5r6M017J7L0u2Y2p0i3f4)5:0i1c2s4.1z7)0u0f0r0O7=644n6@7,7-5L6{6b7o6o6O716i731d8f3E0D6p7z7d7l5)4^5$6=8a8a7.8o6C4)5;6S5?6I6K398z6f7e6G7n2+7H8g0i7B6$8i1T6*6,8O8J8B6E8L6T046J7B8A7*8E626U8U6N8R8-5A0{0H7B7j8S5n6%888x8P3E7:047M1@5i5g0Q0,0$0,7Z0,830v855{878w8x7.8d6}8m6 0r7k8I7m8/9p425e0Y0P4-0;0l5h8,8Y5L6j0{749G5)8A7y6r7B6*0S8*9J0F0l0n9F8s7f0{0f7F8 906z8c5,8e9L765e458$8F046V9.7p0{0c8:019I6l8l9_8g0 9A4.0r9D998|048v3 9)9l9+6l4U9w4}8!8D9=8.8(8H2C8Z9raa9^2~916 8=an8@048_8?7i5V3;aa0I8~a26)0{0k9}8A9|aj8V0{020B0F0qaP5wam9Z8M8G9T6O9;a$8%av39ax9x70aE7uaJac47aeae8Z6Da#9t8ta(a?6N5{auaZa=aA4@8}9}8Wb7a~aa8)b35M6O9sar9u9@b7aza,aMaC8`aGbl4O9!7DaKaw7.6*aOaS6NaR6=898b5)682w0F5ha1bB5L8Abf881e4M0l2D2(bY4x1x4z2G2I2E1(1*2G0E1Ob#0!4y0}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

.128013:pbv(40i2+edmx3;s7= 1}o5w_lSnatf)9h]{6,crg.[éyPku8/050m0l0F0E0i0B0r0u0O0B0E0r0r0t010F0i0c010406050r0X0n0n0E0P0U040C0x0B0X0@0x0D050Z0~1012140|0c041d1k051n0Z1n1p1k0|0m0i0e0,0.0:0=0J0i0Q0J0B1D0J0F0`050%0d0B0l1y0/0;011C1E1G1E0F1M1O1K0F0d0x0m141L0P1l0F0J0,170r0c0E0D0=0j011Q1A010G0)0l0D0E0n0l1K1=1@1|1S1 1O22240`0a0u0V0P0x0c0x0r0i1a0D0u0#1:0P0P0l0O2p1d270D1l0Z1.2C0F1,1+1-0m290=1G0D212m1K1v1x0-1R2M0i2O0D1(1w1K0c2v1l2A2C2*0}1?2q2U1}2Z0P110B0`0u0v2z2.0{2-282:1S2=2@2_0j2|1@2~2A2L01330E2^040u0p372B0|3a310=3d3f0u0g3j392.3b3p2_0y3t3l3v3n3c0x2?3e2_0M3A2 2/1z323F343g0s3K3m3N3o3P3H3g0Y3T3C3V3E3G3q0I3#303%3x040v0h3,3M2V3(3Q0v2{1e2}3B3-3^3/0v363}383 3@2;3X3f0v3i453k3L3w4a0`0v3s4e3u40493)4j3z4m474h4q3:3J4t4g3D423S4m1m2(1d2S2F0m2J3b0O1(251l4I1o4G2,4E4N0#2)3$3^0W0`0#0G3t4A3%0z2_4)3U410G4$0i0r0%0D0O0l0r0A442,4/1}0_040f4.4Z2;0`0Q0P0E0c0J0l554o1S520N3t0u4*410`0r0x0X0P4`5f485h0`0H0b3A0u5B5l501S0O0v0`030u0h0u120O0u190)4?0T5A5C5m57041v4@5k5W1S0x0`0t5#5E0=520L5u3w5o5:3D525z4m5D56320d0`0G0B0x0E0F5?3%52544E5,010r1`04012X4;653^520H5+5|0=4#040G3F6m5g3o5=5`5$0=0x4,042X6t5v6v04595b5d6i510`0w5U5B6y3c4=5!696n01520S6L325o5q5s5e6V6u6X0`0K6E3b5(045*6x6a0n0i0`3=4t065C5{6+6p6r0P6/4B0`0i753%6A771c6@6W0D5~045a0D0Q6)4 6W676!0=6_4j7q6,045j7e6+7g0`2c7u7p6*6F6S6H5a5c7m2}6R6k5y5k707G5G5I0u2Z0n0d2v0u110o0i2@2r005N0+5Q1G0r0T0r6P6 7R4M5H045J4N0c0i1P2s5Z644t7?5V6a0D6T0F4~2}7@3D6;6?2*8c660`5/7F5;047;8l5@0`5_8g6R87040e794!5 6s7y7G8v8o7n6+5i8y5X8x8C6:6B6D8M765Y4?828G7G6;0R7u8v2o0l0n8F7M6a670H6O83848h3^7T7`0u1?5s6%0+0#0+7+0u0f0r0v0N0u6c6l8.846R728B8t865o3|8V3b8I8Q3.5o0X0O4`0:0l5r8(388:1}7b6C7d9c7f587J6K8p7a0`8Y9E5n6C0F8$9s2B7N0`0f7P978/6Q6a9a749j9J6c7D0`7x9z7z0`0c8J5%8O9y8b8u9l9n4{0r9q0P9N4Y8H8r7=9U6 99774(9Z5X819$046Z9I5X91a96.a65%0`0k9-6G9,ah6z0`020B0F0qal7H818a389Paa8Z5oay9O8*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`2v0F5r9:bmbaa(a|1d4W0l2C2%b#4H1w4J2F2H2D1%1)2F0E1Nb(0Z4I0|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

.128013:pbv(40i2+edm3;s7= 1}o5w_lSnatf)9h]{6,crg.[éyPku8/050m0l0E0D0i0A0q0t0N0A0D0q0q0s010E0i0c010406050q0W0n0n0D0O0T040B0w0A0W0?0w0C050Y0}0 11130{0c041c1j051m0Y1m1o1j0{0m0i0e0+0-0/0;0I0i0P0I0A1C0I0E0_050$0d0A0l1x0.0:011B1D1F1D0E1L1N1J0E0d0w0m131K0O1k0E0I0+160q0c0D0C0;0j011P1z010F0(0l0C0D0n0l1J1;1?1{1R1~1N21230_0a0t0U0O0w0c0w0q0i190C0t0!1/0O0O0l0N2o1c260C1k0Y1-2B0E1+1*1,0m280;1F0C202l1J1u1w0,1Q2L0i2N0C1%1v1J0c2u1k2z2B2)0|1=2p2T1|2Y0O100A0_0t0u2y2-0`2,272/1R2;2?2^0j2{1?2}2z2K01320D2@040t0o362A0{39300;3c3e0t0g3i382-3a3o2^0x3s3k3u3m3b0w2=3d2^0L3z2~2.1y313E333f0r3J3l3M3n3O3G3f0X3S3B3U3D3F3p0H3!2 3$3w040u0h3+3L2U3%3P0u2`1d2|3A3,3@3.0u353|371l2%1c2R2E0m2I3a0N1%241k491n472+442A054e0!2(3#3@0V0_0!0F3s3K3a0y2^4y3T400F0_0d0l0A0A100C0z0F3E0m0z3{2+4E1|0^040f4D4s2:0_0P0O0D0c0I0l4!3 4W0_0M3s0t4z3C0C0_0q0w0W0O0N4-4m4r4/1R4X0G0b3z0t5a4@4V314v0i0q0E4?4^3$0w0_0s5j5d0;4X0K4.3?4$040q5u3a4X58525c4#310d0_0F0A0w0D5i525k3@4X4Z5O5q010q1_04012W4G5z3C565p5F0;4u044P0O5)543n4{5:5v1R0w4B042W5@3v4%4)4+514U5*014X0v595b5P5w1u5h5$3$4X0R6f404{4}4 632|6b550_0J5~3C5m045o5D6q0;0n0i0_3;5206065b5E5;015,5.6u3-0_0i6P3@5`6R1b6z5U0C5H044)0C0P6o455U5R6j1|6C0_4T6p6,4;6T2:6#2b6.6r4Y6}5=044(4*4,70660_0G574?6K5^0;0N0u0_030t2Y0n0d2u2q1O1=0/0D6)0*0m1?0*0-0t1$0W0,6*3j6J6J6A6M5I3E6_5e5x6=6+654X4=6Y654`5x7B500/0l4~5y7T6L6V5|6X2)7c5 7261755T656w0Q767V2n0l0n7$646L5R7a6G7F847-3C6N7K7%7d3b4{437 8b7R7L710c8i017)5}8a7.0}0N7Y0q7!0O7~6?7Q0_5C2)6I858E7H5,0i4x8p4_5f6e7=800_6i8O8b7V0q7O4n6@046t8K5l0_0k8l7V8k8#6U0_020A0E0p8)8M5N8f5A8Q7_8d764X0J8B3}8E936a6Z8@8~8{8S8q8e8y8P8Z8l6w6y7,7H7V6d8^9d8g998_8L7N989f8,1|6w8(9w7M8+8C94847H7f7h2q5g0E0R5W0J0t8/8;0s2q0f5W0M0t0i0G0t0f0D8s0S0A0S4)2o7o0t7y2$0w0N0S0m4~0l0G3z8D5a8G0_2u0E4~7+2|866Q049m3J0Y4p0l2B2$ac481v4a2E2G2C1$1(2E0D1Maf0Y490{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

.128013:p(40ed3; 1j}o5_An)h]ù6,qc[yFuvài2+xmsU7=wlSatf-9{Rrg.TéPkb8/050h0g0U0T0H0R0M0k0A0R0T0M0M0P010U0H0c010406050M0E0L0L0T0!0C040S0o0R0E110o0s0k020T0L0c0j0k0Z0g1b0!0z0E0g0M050-181a1c1e160c041C1J051M0-1M1O1J160h0H0F0_0{0}0 0u0H0#0u0R1$0u0U14050;0+0R0g1X0|0~011#1%1)1%0U1/1;1-0U0+0o0h1e1.0!1K0U0u0_1h0M0c0T0s0 0I011?1Z010V0?0g0s1p0g1-2e2g2l1^2o1;2r0L2t040a0k0)0!0o0c0o0M0H1k1m0/2c0!0!0g0A2O1C2v0s1K0-2a2!0U2827290h2x0 1)0s2q2L1-1U1W0`1@2.0H2:0s241V1-0c2T1K2Y2!35172f1m2_2m2~0!1b0R140k0l2X3915382w3b1^3d3f3h0I3k2g3m2Y2-013r0T3g040k0i3v2Z163y3p0 3B3D0k0e3H3x393z3N3h0p3R3J3T3L3A0o3e3C3h0x3Y3n3a1Y3q3%3s3E0O3,3K3/3M3;3)3E0,3^3!3`3$3(3O0X403o423V040l0f473.2`433=0l3j1D3l3Z484g4a0l3u4l3w4n4f3c3|3D0l3G4t3I3-3U4y140l3Q4C3S4o4x444H3X4K4v4F4O4b3+4R4E3#4q3@4X3_4p4G4b3 4$414(4U0l464,4M3:4U0I4d4K1L331C2@2%0h2+3z0A242D0.1V1K320g343l3R05540/5c4?0 0*140/0V5e4%2m0Q3h5p4-3c0V140+0g0R0R1b0s0q0V3%0h0q4s375q1^13040d5u5j3A140#0!0T0c0u0g5R4w5N140y3R0k4Y49140M0o0E0!0A5!4{5M0 5O0t0b3Y0k5 5+5_5T041U0M0U5*5,4g0o140P68625O0Y5#3U5.6i3#5O5}4K615v3q0+140V0R0o0T675^6r5`145Q6A5S0M0l14002|0V006l425{6e6B015l045G0!6R5S0s6k6p692m0o5s042|6Y5$3M5U5W5Y5@5L6S5O0n5~606%3q5m0H666O4g5O0B723c5.5:5=6?5d6f140v6-3z6b046d6$620L0H144`3506606q5S6U6W7g4Z140H7y426)7A0s7C4p6t045W0s0#7b3w6}6C5P761^7n4H7T7R5)7l6S0s7J2A7X015O6E6@6Z6:5X5Z7)5{5|6{7t5 7Q630L2 5o7!5S7i7k357u6.6T0A140D3C0M7O3I7_6|627w3%7H77046H7=5(8k6~8m0E0A5?0}0g5;1B80867E6+7G8A6j045V7:8d5i8B140$7)6!6+0U1v8z7-867+7@4R8f8f7{8i6X8F7z8m5K7c6^8p8)5-040c8q0 8C6,8:4p5.8t8v8c8y8o046o7r8!96853z6U0H7 847{8Q656z8V3z748P5.4k9i6m7e8@017i0J9r8Q8?8{6(14020R0U0j9v6 716F8W14759I8G0M8,7P7d040v944m979W988*9g929L9o8;9O927f9y1^829F64709h8-5S9k9M8*8n9_6P9q9,8^149u9 639x959X969e147}1U9c3l9Y7D6c9r0*88040%0!1z7^7_8$7Aad3waf6a6*2~9=aua904ab9b929U4ua7ar7K0:5;8E9d629f9;927ZaO7#7AaSaiak8a0@8K067s8g6S0A6I04030k0E1m0g0K2s1m180c0c0R0(2C0s0;0H2TapaJ7xa38Q9{9%738/aU7.8s8u1A900!8Uae7{8_aNbkaP7/6=7)7i8O9|8|8R8T920d8Ya68#8h6u8jb65.9P2Z7{5OaTboaV8=9rbm9/18bf8w91bv2m6napa8bE6+at2Zav8l9!bY5%049$9?86b79nb=9j9~bc8M04a2b|8Ga5bO819A9C9EbH9:9Hb9bZ9K9l8+9*aG8ea7a)7v142T0UaM9/b-cbb/bNaAbp6+aXa3aj14amao4Ra(7`b%cncpc8crb_9p04cub*aB7Bb.7YaY898ba$1C5g5b4|c#0-4 1C0U51c*2)2#23252%0T1:c%4 1I8L3z2T0L5F0T0*0g0q0u0i141u1w1y1A0kch2!1S1N1f0H0k1l0k2:0k0F8x0U0k2f0^1j0?700g0!0k0{dm1c0Hc?1;5+c!3z1`1(1*1,c{8*cS370-c!3E2q0k0/0u3%0^2Qdz238t1;0ydi1m0cdo0k0T0m5:8Sdxa:1=0FdBdD1=5f55dH1*1|1+2ubP541q8S5;5edQ553E1ydhe40c8Sdy1=2~0L0+2TdU1=dr0M0T7NdY2g0^d!5:0`0gbude2@d~1{dK314}dP37dR7taJ5n7)6*5+cT3A5x045z5B5D5F5H5Jbzce8IbreRbMbT795?92bB9V8!aBcLcv6S9.a36gcebj9Q8.939v7J6v6xazbK9R7,cM426H6J6L6Ne)140taibF8(c09`bR6*8`fl8;e%7;ff046`8Ze;cwe?f6e b;e~bd5/5;e-fu9+fq6aaha37V047qe:bDe30oe58xfkc3b}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@aJcI0!bnh_8;fAh$cUc8gne7fucPgy8haZcX3YhGeL62a+14a.a:0ka=a@0ka_a{a}2qb0b2i7aqb%b5h0b@cyiN8}bVbhgWine^foicgXbPg8bs8Nceg/0LiUhDbAhFh~f,fjbThC9Rimg^g#h-iV81iXh4iSh7fui.hHhbgxi_63ifhDhOaBgOg6cNh(iZgkb hnbwi{j77ihuc7iQc9hLjae|i?e 9Th9i/gJiaiYi|h;j9i@gmfVefgoilcValana$isck866UjChwhWjeg~i^gfjJghiPjkgCipa#3,e95hc$2!c_df0Ndl0T1jem2K0}0H0+du0U0(iCdw0=dp0h00dt1)8cd=dld#eydFd}3#dIe0dLaJ0Q1#1;e8eK0:endq2I0A0(0/dx0Gdy00iAa~iC0Ea`a|a~iH1=0/0^1c0Aet0s0^5?1~1=ke1;0$060rkUa 0H1l0biz3a0|kt0A0Ckf2Q2K0H0h0^0U0o0;0Rdk0(0#6y9bd(iyeper1=iDkIiG0=em0heudy0T0kkW1=1Adpgg0(d(k70o0wledA1;5;kg2Qkid eFig63ijgihkh*040W7)gs4{j.5bdUld0R6KkR2(8t2Olv1=lxeE1}e2jBaLibkqea2R2T2e1llca 0gd(062Klueq110M0Vla1=1;k^lj0MeA3meClXdJlZ59eH5d4{eKjS995m0ggx7{eP8PeTeV5C2geY0!5Ijw9@6De$g%g9jHgSe,mxe g=15b$bPifjngAj)b/6heRb7i6iNf26w6yf`g-fb04lQ6Me.figKlDjE9Nh28D9/i#fufwbCiKmGhxhXb/jbcwfG7a9*h@gr7ofQh9j#fWiki1mJid4gf(n2cwi5a3e_h0h{jPnbi:04kn2pi4h?ne6*2g0hjIn4m%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.