Tűzoltók

Használható segédanyagok: Haskell könyvtárainak dokumentációja, (lokális!) Hoogle, a tárgy honlapja és a BE-AD rendszerbe feltöltött beadandók. Ha bármilyen kérdés, észrevétel felmerül, azt a felügyelőknek kell jelezni, nem a diáktársaknak!

A feladatok egymásra épülnek ezért érdemes ezeket a megadásuk sorrendjében megoldani, de legalább megérteni az aktuális feladatot megelőző feladatokat! A függvények definíciójában lehet, sőt javasolt is alkalmazni a korábban definiált függvényeket (függetlenül attól, hogy sikerült-e azokat megadni).

Tekintve, hogy a tesztesetek, bár odafigyelés mellett íródnak, nem fedik le minden esetben a függvény teljes működését, határozottan javasolt még külön próbálgatni a megoldásokat beadás előtt, vagy megkérdezni a felügyelőket!

Részpontszámokat csak az elégséges szint elérése után lehet kapni!

A feladat összefoglaló leírása

A Piripócsi Tűzoltóság megbízott minket, hogy segítsük őket a felvonulási útvonalak megtervezésében. Az útvonal tervezésre egy leírást bocsátnak a rendelkezésünkre, amely tartalmazza az aktuálisan járható útszakaszokat.

A könnyebb feldolgozhatóság érdekében, a kapott leírásból készítettünk egy gráfot. A leírásban szereplő kereszteződéseket a gráf csúcsaiként (esetleg csomópontjaiként), a kereszteződéseket összekötő útszakaszok pedig a gráf éleiként fogjuk jelölni. Egyszerűsítésként, a gráf két csúcsa között megadott élek mindkét irányba járható útszakaszokat jelölnek (nincsenek egyirányú utak), illetve ezek egységnyi hosszúak. A feladat megoldása során feltételezhetjük, hogy a gráf összefüggő, azaz két tetszőlegesen kiválasztott csúcsa között létezik út.

A gráf reprezentációja

A gráfot (Graph) egy rendezett párok (Edge) listájával reprezentáljuk, a rendezett párok a gráf éleit adják meg. Egy rendezett pár (Edge) két csúcsból (Node) áll, amely a köztük lévő közvetlen utat adja meg. Ha egy (a,b) rendezett pár megtalálható a listában, ez mindkét irányú utat megadja, azt jelenti hogy az a-ból b-be és b-ből a-ba is át tudunk jutni.

(A type kulcsszó segítségével a gráf csúcsainak azonosítására használt Node típus az Int típus, a Edge típus a Node típusból képzett párok (Node, Node) típusának egy másik neve lesz. A Graph típus a Edge típusból alkotott listákat/sorozatokat fogja jelenteni, hasonlóan a String és [Char] viszonyához. A programban ez semmilyen további megszorítást nem indukál, csupán a beszédesebb függvénytípusok kialakításában segít.)

A megoldás teszteléséhez bevezetjük a következő három gráfot:

Firefightersce2f997fd98520db05dadd87af074127.png

Firefighters13b13aa3f9519b39aa8a9dda4bb00f8d.png

Firefighters9d10b169a4dd9acf64ac2d27116a6615.png

Gráf csúcsainak meghatározása

Az egyes műveletekhez szükségünk lesz a gráfban található összes csúcspontra. Ennek meghatározásához szükségünk lesz akövetkező két műveletre.

Elem rendezett beszúrása (1 pont)

Adjuk meg azt a függvényt, amely egy rendezett listába beszúr egy elemet, amennyiben az még nem szerepel a listában! A megoldásnál használjuk, hogy a paraméterül kapott lista rendezett, így az új elem a lista legfeljebb egy bejárásával beszúrható!

FONTOS: Csak az a megoldás számít teljes értékűnek, amelyik kihasználja hogy a lista rendezett és csak a feltétlenül szükséges lépéseket teszi meg!


Test>
[4] :: [Integer]
Test>
[4] :: [Integer]
Test>
[1, 4, 5, 7] :: [Integer]
Test>
[1, 2, 3, 4, 5] :: [Integer]

Gráf csomópontjai (2 pont)

Definiáljuk azt a függvényt, amelyik megadja egy gráf összes csúcsát! Az eredmény minden csomópontot egyszer tartalmazzon és ezek érték szerinti növekvő sorrendben szerepeljenek!

Megjegyzés: Próbáljuk meg használni az előbb megadott uInsert műveletet!


Test>
[1, 2, 3, 4, 5, 6] :: [Node]
Test>
[1, 2, 3, 4, 5, 6, 7, 8, 9] :: [Node]
Test>
[3, 4, 5] :: [Node]

Szomszédos csúcsok megadása

A tűzoltók által kért útvonalak megadásához szükségünk lesz arra a műveletre, amely megadja egy csomópontból kivezető éleket. Ezt a feladatot a következő két függvény segítségével fogjuk megoldani.

Közvetlen kapcsolat vizsgálata (1 pont)

Adjuk meg azt a függvényt, amely megadja, hogy a paraméterül kapott csúcs szomszédos-e!

Megjegyzések:


Test>
True :: Bool
Test>
[False, True, True, False, False, False, True, False, True, True, False, False, True, True, False, True, True, False, False, True, True, False, False, True, False, False, True, False, False, True, False, False, False, True, True, False] :: [Bool]

Közvetlen kapcsolatok meghatározása (1 pont)

Adjuk meg azt a függvényt, amely egy adott csúcs szomszédait adja meg!

Segítség: Használjuk az areNeighbouringNodes és a nodes függvényt!


Test>
[2, 3] :: [Node]
Test>
[1, 2, 4, 5] :: [Node]
Test>
[1, 2, 4] :: [Node]

Útvonalak előállításának előkészítése

A kód olvashatóságának növelése céljából bevezetjük a Path típus, amely a gráfban található szomszédos csúcsok sorozatát fogja jelenteni.

type Path = [Node]

FONTOS! Az útvonalak fordított sorrendben adottak, azaz a kiindulópont mindig az utolsó, a végpontja pedig az első eleme! Tehát, a [4,3,2,1] útvonal az 1-es csúcsból indul és a 4-es csúcsban ér véget.

Firefighters387beba03261e2e9f6959ae56b8eb322.png

Adott útvonal lehetséges folytatásai (2 pont)

Definiáljuk azt a műveletet, amely adott útvonal információit felhasználva megadja, hogy mely irányokba haladhatunk tovább!

A függvény csak azokat a csomópontokat adja meg, amelyek közvetlenül elérhetőek az aktuális csúcsból és még nem szerepelnek az útvonalban (hogy elkerüljük az esetleges köröket).


Test>
[2, 3] :: [Node]
Test>
[2, 4, 5] :: [Node]
Test>
[2, 6] :: [Node]

Elágazások kezelése (2 pont)

Amennyiben egy csúcsból több irányba is tovább tudunk haladni, úgy minden irányhoz egy új útvonalat fogunk megadni.

Adjuk meg azt a műveletet, amely egy adott útvonalat az összes lehetséges módon megpróbál egy újabb rendelkezésre álló iránnyal bővíteni!

Amennyiben az út nem bővíthető tovább, azaz minden szomszédos csúcsa szerepel az útvonalban, úgy adjuk vissza ezt az útvonalat változatlan formában!


Test>
[[2, 1], [3, 1]] :: [Path]
Test>
[[1, 2], [3, 2], [4, 2]] :: [Path]
Test>
[[2, 3, 1], [4, 3, 1], [5, 3, 1]] :: [Path]
Test>
[[2, 4, 3, 1], [6, 4, 3, 1]] :: [Path]

Segítség:

Amennyiben a jelenlegi útvonalunk [3,1] úgy a graph1 esetében a 2-es, 4-es és 5-ös sorszámú (az ábrán pirossal jelzett) csúcsokkal bővíthetjük az útvonalat.

Firefighters8a25510fe8925fdab001a40c1b9f2494.png

Tehát az eredmény három útvonalat fog tartalmazni, ez a [[2, 3, 1], [4, 3, 1], [5, 3, 1]] lista. Szemléltetve:

Firefightersecd784253053844a45ff9dbe21e1cb43.png

Firefighters7ecd0c7ae0793df9b73ce7ea219d613c.png

Firefighters76b954057c54069a0de8dc17879f1c4e.png

Útvonalak listájának inicializálása adott pontból (1 pont)

Adjuk meg azt a függvényt, amely inicializál egy útvonal sorozatot a megadott csúcsból! A függvény eredménye egy lista, amely egy egyelemű útvonalat tartalmaz (a megadott kiindulóponttal).

Amennyiben a csúcs nem szerepel a megadott gráfban, úgy adjunk egy hibaüzenetet az error függvény segítségével!

Segítség: Az error függvénynek egy String típusú értéket kell paraméterül adni, amely maga a hiba szövege.


Test>
[[3]] :: [Path]
Test>
[[5]] :: [Path]
Test>
[[7]] :: [Path]
Test>
⊥₁ :: [Path]
⊥₁: initPaths: Invalid node!
CallStack (from HasCallStack):
  error, called at ./Firefighters.lhs:342:26 in main:Firefighters

Útvonalak bővítése (2 pont)

Adott egy útvonal sorozat, amelynek minden elemét folytatni szeretnénk.

Definiáljuk azt a függvényt, amely az adott útvonal sorozat minden elemét az összes lehetséges módon megpróbál bővíteni! Használjuk a fork műveletet!


Test>
[[1, 3], [2, 3], [4, 3], [5, 3]] :: [Path]
Test>
[[3, 2], [5, 2]] :: [Path]
Test>
[[1, 3, 4], [2, 3, 4], [1, 6, 4], [9, 6, 4]] :: [Path]
Test>
[[5, 1, 3, 4], [6, 1, 3, 4], [8, 1, 3, 4], [5, 2, 3, 4], [3, 1, 6, 4], [5, 1, 6, 4], [8, 1, 6, 4], [8, 9, 6, 4]] :: [Path]

Útvonalak meghatározása

A feladat ezen részében megpróbáljuk megadni egy tetszőleges csúcsból induló összes felvonulási utat, amely a teljes gráfot lefedi. Ezen információk ismeretében pedig megpróbáljuk a legrövidebb utat/utakat megadni (az esetleges alternatívák figyelembe vételével).

Teljes útvonalak megadása (3 pont)

Definiáljuk azt a műveletet, amely egy paraméterül kapott csúcsból kiindulva megadja az összes útvonalát a gráfnak! A megoldás azokat az utak sorozatát fogja megadni, amelyek már nem folytathatóak az aktuális végpontból.

Segítség: A függvénynek addig kell ismételnie az útvonalak bővítését, amíg az fixpontba nem jut, azaz nem változik az eredmény az előző lépéshez viszonyítva.


Test>
[[5, 6, 4, 2, 1, 3], [1, 2, 3], [5, 6, 4, 2, 3], [1, 2, 4, 3], [5, 6, 4, 3], [1, 2, 4, 6, 5, 3]] :: [Path]
Test>
[[4, 6, 9, 8, 7, 5, 2, 3, 1], [2, 5, 7, 8, 9, 6, 4, 3, 1], [7, 8, 9, 6, 4, 3, 2, 5, 1], [2, 3, 4, 6, 9, 8, 7, 5, 1], [9, 8, 7, 5, 2, 3, 4, 6, 1], [4, 3, 2, 5, 7, 8, 9, 6, 1], [9, 6, 4, 3, 2, 5, 7, 8, 1], [7, 5, 2, 3, 4, 6, 9, 8, 1]] :: [Path]
Test>
[[2, 5, 1, 3], [4, 6, 9, 8, 7, 5, 1, 3], [4, 6, 1, 3], [2, 5, 7, 8, 9, 6, 1, 3], [2, 5, 7, 8, 1, 3], [4, 6, 9, 8, 1, 3], [4, 6, 1, 5, 2, 3], [7, 8, 9, 6, 1, 5, 2, 3], [7, 8, 1, 5, 2, 3], [4, 6, 9, 8, 1, 5, 2, 3], [4, 6, 1, 8, 7, 5, 2, 3], [9, 6, 1, 8, 7, 5, 2, 3], [1, 6, 9, 8, 7, 5, 2, 3], [4, 6, 9, 8, 7, 5, 2, 3], [2, 5, 1, 6, 4, 3], [9, 8, 7, 5, 1, 6, 4, 3], [2, 5, 7, 8, 1, 6, 4, 3], [9, 8, 1, 6, 4, 3], [2, 5, 1, 8, 9, 6, 4, 3, ……], [7, 5, 1, 8, 9, 6, 4, …, ……], [1, 5, 7, 8, 9, 6, …, ……], [2, 5, 7, 8, 9, …, ……]] :: [Path]

Útvonal tervezés (3 pont)

Adjuk meg azt a műveletet, amely megadja egy kezdőcsúcs és egy célcsúcs közötti összes lehetséges útvonalat! Ha a a célcsúcs megegyezik a kiinduló csúccsal, akkor az eredmény csak egy egyelemű út, ahol az út csupán a kiinduló csúcsot tartalmazza.

A működése:


Test>
[[5, 6, 4, 3, 2, 1], [5, 3, 2, 1], [5, 3, 4, 2, 1], [5, 6, 4, 2, 1], [5, 6, 4, 2, 3, 1], [5, 6, 4, 3, 1], [5, 3, 1]] :: [Path]
Test>
[[5, 1, 3, 4], [5, 1, 3, 4], [5, 7, 8, 9, 6, 1, 3, 4], [5, 7, 8, 1, 3, 4], [5, 2, 3, 4], [5, 2, 3, 4], [5, 2, 3, 4], [5, 2, 3, 4], [5, 2, 3, 4], [5, 2, 3, 1, 6, 4], [5, 1, 6, 4], [5, 1, 6, 4], [5, 7, 8, 1, 6, 4], [5, 2, 3, 1, 8, 9, 6, 4], [5, 1, 8, 9, 6, 4], [5, 1, 8, 9, 6, 4], [5, 7, 8, 9, 6, 4], [5, 7, 8, 9, 6, 4]] :: [Path]

Legrövidebb útvonal (2 pont)

Adjuk meg a legrövidebb utat a megadott két pont között!

A legrövidebb út alatt azt az utat értjük, amely a kiinduló csúcsból a célcsúcsig a legkevesebb kereszteződést érinti. Amennyiben több legrövidebb út is lehetséges, úgy az elsőt adjuk meg ezek közül!


Test>
[5, 3, 1] :: Path
Test>
[1, 3, 5] :: Path
Test>
[6, 4, 3] :: Path
Test>
[6, 1, 3] :: Path

Pontozás