Twee bergbeklimmers starten aan weerszijden van een bergtop. Kunnen ze samen de bergtop bereiken als ze zich op hun tocht altijd op dezelfde hoogte van elkaar moeten bevinden?

bergbeklimmers
Twee bergbeklimmers starten aan weerszijden van een bergtop. Kunnen ze samen de bergtop bereiken als ze zich op hun tocht altijd op dezelfde hoogte van elkaar moeten bevinden?

Het profiel van een (tweedimensionale) berg wordt voorgesteld door een reeks (list of tuple) natuurlijk getallen (int) die de hoogtes van de lokale pieken en dalen voorstellen. Daarbij moeten de volgende voorwaarden gelden:

Er bestaat altijd minstens één manier waarop de bergbeklimmers samen de top kunnen bereiken van een berg waarvan het profiel aan deze voorwaarden voldoet. Hieronder zie je bijvoorbeeld de grafische voorstelling van een berg met profiel [0, 3, 2, 8, 3, 5, 0], waarbij de lokale pieken en dalen worden aangeduid door zwarte stippen. Op de top hebben we een groene vlag geplant en hebben we de zwarte stip een groene rand gegeven. Aan de linkerkant worden de hoogtes van de lokale pieken en dalen in het lichtgrijs weergegeven, met blauwe horizontale hoogtelijnen die aangeven welke punten op dezelfde hoogte liggen als de pieken en dalen.

profiel
Grafische voorstelling van een berg met profiel [0, 3, 2, 8, 3, 5, 0]. De lokale pieken en dalen worden aangeduid door zwarte stippen. Op de top hebben we een groene vlag geplant en hebben we de zwarte stip een groene rand gegeven. De hoogtes van de lokale pieken en dalen worden aan de linkerkant in het lichtgrijs weergegeven, met blauwe horizontale hoogtelijnen die aangeven welke punten op dezelfde hoogte liggen als de pieken en dalen.

De mogelijke routes die de bergbeklimmers kunnen volgen, kunnen gevonden worden door een verbindingsgraaf op te stellen voor het profiel. Ter herinnering: een graaf is een datastructuur $$G = (K, B)$$ die bestaat uit een verzameling knopen $$K$$ en een verzameling bogen $$B$$ die telkens twee knopen met elkaar verbinden.

Een verbindingsgraaf is een graaf waarvan elke knoop bestaat uit een koppel punten $$(P_L, P_R)$$ die zich op dezelfde hoogte bevinden. Daarbij geldt ook nog dat $$P_L$$ zich links van de top bevindt en $$P_R$$ rechts van de top, en dat minstens één van beide punten een lokale piek of een lokaal dal is (het andere punt mag ook een lokale piek of een lokaal dal zijn). Twee knopen $$(P_L, P_R)$$ en $$(P'_L, P'_R)$$ van een verbindingsgraaf worden met elkaar verbonden als en slechts als de twee bergbeklimmers zich in dezelfde richting (beide naar boven of beide naar onder) kunnen begeven, respectievelijk van $$P_L$$ naar $$P'_L$$ en van $$P_R$$ naar $$P'_R$$.

tussenpunten
De punten die zich op dezelfde hoogte bevinden als een lokale piek of een lokaal dal van het profiel, vormen de basis voor het bepalen van de knopen in de verbindingsgraaf van het profiel.

Om de verbindingsgraaf op te stellen, bepalen we eerst alle punten langs het profiel die zich op dezelfde hoogte bevinden als een lokale piek of een lokaal dal van het profiel. Dit zijn uiteraard de pieken en dalen zelf, maar ook enkele bijkomende punten tussen de pieken en dalen die we in bovenstaande afbeelding hebben aangeduid in het oranje. Deze punten worden van links naar rechts genummerd vanaf nul. Als de linkse klimmer zich bijvoorbeeld op punt 2 bevindt, dan kan de rechtse klimmer zich op punt 8, 10 of 12 bevinden. De koppels $$(2, 8)$$, $$(2, 10)$$ en $$(2, 12)$$ zijn dus knopen van de verbindingsgraaf, omdat de bergbeklimmers zich op een bepaald moment op deze punten kunnen bevinden. Hieronder hebben we ook de bogen getekend tussen de knopen van de verbindingsgraaf voor een berg met profiel [0, 3, 2, 8, 3, 5, 0].

verbindingsgraaf
Verbindingsgraaf voor een berg met profiel [0, 3, 2, 8, 3, 5, 0].

Rest ons nu nog de vraag of er in de verbindingsgraaf een pad kan gevonden worden tussen de startknoop $$(0, 14)$$ waarin de twee bergbeklimmer zich aan de voet van de berg bevinden (aangeduid met een oranje rand) en de knoop $$(6, 6)$$ die de top aanduidt waar de twee klimmers samenkomen (aangeduid met een groene rand). Voor de verbindingsgraaf hierboven is het antwoord duidelijk ja. Als we het pad volgen van $$(0, 14)$$ naar $$(6, 6)$$, dan zien we dat beide bergbeklimmers eerst stijgen naar de eerste piek $$(2, 12)$$, waarna de linkse bergbeklimmer verder afdaalt richting de top, terwijl de rechtse bergbeklimmer op zijn stappen terugkeert totdat ze op $$(3, 13)$$ uitkomen. Daarna stijgen beide bergbeklimmers naar $$(5, 11)$$, waarna de linkse bergbeklimmer op zijn stappen terugkeert en de rechtse bergbeklimmer verder stijgt richting de top, totdat ze op $$(3, 9)$$ uitkomen. Van daaruit kunnen de twee bergbeklimmers samen de laatste klim naar de top inzetten, om op $$(6, 6)$$ uit te komen.

Opgave

Bepaal de verbindingsgraaf voor een gegeven bergprofiel. Hiervoor ga je als volgt te werk:

Voorbeeld

>>> profiel = [0, 3, 2, 8, 2, 5, 0]
>>> is_profiel(profiel)
True
>>> piek(profiel)
8
>>> niveaus(profiel)
[0, 2, 3, 5, 8]
>>> tussenpunten(profiel)
[(0, True), (2, False), (3, True), (2, True), (3, False), (5, False), (8, True), (5, False), (3, False), (2, True), (3, False), (5, True), (3, False), (2, False), (0, True)]
>>> hoogtelijnen(profiel)
{0: ({(0, True)}, {(14, True)}), 2: ({(1, False), (3, True)}, {(9, True), (13, False)}), 3: ({(2, True), (4, False)}, {(10, False), (8, False), (12, False)}), 5: ({(5, False)}, {(11, True), (7, False)}), 8: ({(6, True)}, {(6, True)})}
>>> knopen(profiel)
{(3, 13), (5, 11), (0, 14), (2, 8), (6, 6), (2, 10), (3, 9), (1, 9), (2, 12)}
>>> zijn_verbonden((0, 14), (2, 12), profiel)
True
>>> zijn_verbonden((2, 12), (0, 14), profiel)
True
>>> zijn_verbonden((5, 11), (2, 8), profiel)
False
>>> verbindingsgraaf(profiel)
{(3, 13): {(2, 12), (5, 11)}, (5, 11): {(3, 9), (3, 13)}, (2, 12): {(0, 14), (3, 13)}, (3, 9): {(6, 6), (2, 8), (2, 10), (5, 11)}, (0, 14): {(2, 12)}, (2, 8): {(3, 9), (1, 9)}, (1, 9): {(2, 8), (2, 10)}, (6, 6): {(3, 9)}, (2, 10): {(3, 9), (1, 9)}}

>>> is_profiel([0, 3, 2, 8, 2, 5, 2])
False
>>> is_profiel([0, 3, 2, 8, 2, 8, 0])
False
>>> is_profiel([1, 3, 0, 8, 2, 5, 1])
False
>>> is_profiel([0, 3, 5, 8, 2, 5, 0])
False

Bronnen