Beadandó programozási feladatok

A beadandó programok követelményei


1. feladat: Kifejezésfa (10 pont)

Készítsünk programot, amely felépíti egy megadott aritmetikai kifejezés kifejezésfáját. Az inputkifejezés operandusokat (változónevek vagy számok), műveleti jeleket és zárójeleket tartalmazhat.

A kifejezésfa egy aritmetikai kifejezéshez egyértelműen hozzárendelhető (bináris) fa, amely jól mutatja a kifejezés szerkezetét és a helyes kiértékelésének menetét. A kifejezésfa belső csúcsai műveleti jeleket tartalmaznak, míg levél csúcsai a kifejezés operandusainak feleltethetők meg. Egy műveleti jel gyerekei a művelet operandusai. (Segítség: a kifejezésfa úgy építhető fel a legkönnyebben, ha először a kifejezést lengyelformára alakítjuk.)

Az inputkifejezést a kifejezesfa.in szöveges fájl első és egyetlen sora tartalmazza. A kifejezesfa.out szöveges fájlba négy sort kell kiírni, amelyek a felépített kifejezésfa bejárásait tartalmazzák az alábbi sorrendben: szintfolytonos, preorder, inorder, postorder. Az egyes elemeket a kimenetben egy-egy szóközzel válasszuk el egymástól (a sorok végén is lehet szóköz).

1.1. feladat (8 pont)

Az inputkifejezés a +, -, *, /, ^, = műveleteket tartalmazhatja a szokásos precedenciákkal és zárójeleződési (asszociativitási) szabályokkal. Megengedett a többszörös értékadás is (pl. a=b=0). Egy operandus egy egyetlen betűből álló változónév vagy egy egyetlen számjegyből álló természetes szám lehet.

Minta: kifejezesfa1.in | kifejezesfa1.out

1.2. feladat (+ 2 pont)

Az előző feladat általánosabb változata. Egy változónév betűkből, számjegyekből és _ (aláhúzás) jelekből álló karaktersorozat lehet, de számjeggyel nem kezdődhet, egy konstans tetszőleges természetes szám lehet, továbbá a kifejezés tartalmazhat szóközöket is.

Minta: kifejezesfa2.in | kifejezesfa2.out


2. feladat: Labirintus (15 pont)

Készítsünk programot, amely egy megadott labirintus két pontja között megkeresi a legrövidebb utat. Egy labirintus N×M mezőből áll (N sor és M oszlop), amelyek között vannak falmezők, a többin pedig szabadon lehet mozogni. Egy szabad mezőről négy irányba lehet lépni: balra, jobbra, fel, le, amennyiben a megfelelő szomszédos mező szintén szabad, és nem lépünk ki a labirintus szélén.

A feladatot az alábbi három változatban lehet megoldani, de külön kell programot küldeni mindegyikhez.

2.1. feladat (6 pont)

A program bemenete a labirintus.in szöveges fájlban található (illetve paraméterként megadható a fájlnév). A bemeneti fájl első sora két egész számot tartalmaz egy szóközzel elválasztva: a labirintus 1<=N<=1000 és 1<=M<=1000 méreteit. A második sor négy egész számot tartalmaz egy-egy szóközzel elválasztva, amelyek a kiindulási mező és a célmező pozícióját adják meg. Egy pozíció egy 1<=i<=N sorindexből és egy 1<=j<=M oszlopindexből áll. A fájl következő N sora a labirintust írja le. Mindegyik sor M karaktert tartalmaz, '.' jelöli a szabad mezőket és '#' a falakat.

A programnak az eredményt a labirintus.out szöveges fájlba kell kiírnia. Ha a kezdőpontból a célmező nem érhető el (vagy valamelyik mező fal), akkor az eredmény egyetlen sorból álljon: "Nem erheto el". Ellenkező esetben a fájl első sora egy egész számot tartalmazzon: a legrövidebb út hosszát (a lépések számát). A következő N sor pedig adja meg a labirintust, megjelölve benne a megtalált legrövidebb utat. Mindegyik sor M karakterből álljon az alábbiak szerint: '#' - falmező, 'o' (kis 'o' betű) - az út egy mezője, '.' - az út során nem használt szabad mező.

Minta: labirintus1.in | labirintus1.out

2.2. feladat (+ 4 pont)

A 2.1. feladathoz képest annyi változik, hogy lehetőségünk van a labirintusban található falak felrobbantására is. Az inputfájl egy sorral többet tartalmaz: a labirintus méretei után egy új sorban található egy 1<=C<=1000 egész szám, amely egy szomszédos falmező felrobbantásának és a mezőre lépésnek teljes költségét (idejét) adja meg. A kimeneti fájl formátuma pedig csak annyiban változik, hogy a felrobbantott falakat '@' jellel kell jelöli. Amennyiben a kezdőpont vagy a célmező fal, akkor az eredmény itt is a "Nem erheto el" sor legyen.

Minta A: labirintus2a.in | labirintus2a.out

Minta B: labirintus2b.in | labirintus2b.out

2.3. feladat* (+ 5 pont)

A 2.2. feladathoz képest annyi változik, hogy a bombák száma (vagyis az egy út során felrobbantható falmezők száma) korlátozva van. Az inputfájl második sorában a robbantás 1<=C<=1000 költsége után még szerepel egy 1<=K<=100 egész szám, amely megadja az egy út során felhasználható bombák számát. A kimeneti fájl formátuma nem változik.

Minta A: labirintus3a.in | labirintus3a.out

Minta B: labirintus3b.in | labirintus3b.out


3. feladat*: Metró (15 pont)

Adott egy tömegközlekedési hálózat, pl. metróvonalak hálózata, amelyben szeretnénk egy megadott megállóból egy másikba utazni a lehető legkevesebb átszállással, valamint azon belül a lehető leggyorsabban.

A program bemenete két fájlból áll. A metro-vonalak.in szöveges fájl a közlekedési hálózatot írja le a következő formában: az első sor a metóvonalak 1<=K<=100 számát és a megállók 1<=N<=10000 számát adja meg, az utána következő sorok mindegyike pedig egy-egy szakaszt határoz meg. Egy szakaszt leíró sor a kezdő- és célállomás 1<=a, b<=N azonosítóit, a metróvonal 1<=v<=K azonosítóját, valamint a szakasz 1<=c<=100 költségét (utazási idő) tartalmazza ebben a sorrendben, egy-egy szóközzel elválasztva. Egy szakaszon mindkét irányban lehet közlekedni (ugyanazzal a költséggel). Átszállásról akkor beszélünk, ha egy utazás két egymást követő szakasza nem azonos járathoz (metróvonalhoz) tartozik. A metro-kerdesek.in szöveges fájl pedig soronként egy-egy feladatot tartalmaz: a kezdőállomás és a célállomás azonosítóját.

Készítsünk programot, amely a metro-kerdesek.in fájlban megadott megállópárok között megkeresi a legkevesebb átszállással járó és azon belül legrövidebb utat. Az eredményeket írja ki a metro-kerdesek.out szöveges fájlba a következő formában. Minden válasz két sorból áll: az első tartalmazza az átszállások számát és a szakaszok összköltségét egy szóközzel elválasztva; a második pedig adja meg az érintett megállók azonosítóját az utazásnak megfelelő sorrendben.

Minta: metro-vonalak.in | metro-kerdesek.in | metro-kerdesek.out

Itt van egy kép is a mintaként megadott közlekedési hálózatról, amellyel az eredmények könyebben ellenőrizhetők: metro-vonalak.png.

Megjegyzés: Az input a moszkvai metróhálózat alapján készült.


4. feladat: Minimális költségű feszítőfa (10 pont)

4.1. feladat (8 pont)

Adott egy irányítatlan, élsúlyozott, összefüggő, de nem feltétlenül egyszerű gráf. Keressük meg egy minimális költségű feszítőfáját.

A feszitofa.in szöveges fájl első sora tartalmazza a csúcsok 1<=N<=10000 és az élek 1<=M<=1000000 számát, az utána következő M sor pedig egy-egy élt ír le három egész számmal: az él két végpontja (1<=u, v<=N) és költsége (1<=c<=1000).

A feszitofa.out szöveges fájl első sorába a megtalált feszítőfa összköltségét kell írni. A további N-1 sorba pedig a feszítőfa éleit kell megadni ugyanolyan formában, ahogyan az inputban szerepeltek, de a két végpont azonosítója közül mindig a kisebb (nem nagyobb) legyen az első, továbbá az élek lexikografikusan legyenek rendezve!

Minta: feszitofa1.in | feszitofa1.out

4.1. feladat (+ 2 pont)

Az előző feladathoz képest csak annyi változik, hogy a gráf nem feltétlenül összefüggő. Ha nem összefüggő, akkor egy minimális költségű feszítőerdőjét keressük meg, vagyis minden komponensében keressünk egy-egy minimális költségű feszítőfát.

A kimeneti fájl első sora a feszítőerdő összköltségét tartalmazza, a további N-K sor pedig a feszitőerdő éleit az előző pontban megadott szabályok szerint (ahol K a gráf komponenseinek száma).

Ez a feladat nyilván az előző általánosítása, így aki ezt is megoldja, annak elég az általánosabb programot beküldenie.

Minta: feszitofa2.in | feszitofa2.out


5. feladat: Erősen összefüggő komponensek (10 pont)

Adott egy irányított gráf, határozzuk meg az erősen összefüggő komponenseit.

A komponensek.in szöveges fájl első sora tartalmazza a csúcsok 1<=N<=10000 és az élek 1<=M<=1000000 számát, az utána következő M sor pedig egy-egy él kezdő- és végpontját adja meg két egész számmal (1<=u, v<=N).

A komponensek.out szöveges fájl első sorába a gráf erősen összefüggő komponenseinek K számát kell kiírni, a következő K sorban pedig az egyes komponenseket kell megadni a csúcsaik felsorolásával. A komponensek csúcsait növekvő sorrendben írjuk ki, de a komponensek sorrendje tetszőleges.

Minta: komponensek.in | komponensek.out


Kiegészítő feladat: Hash-elés

A hash-elés témaköréből azt a speciális feladatot vizsgáljuk, amikor pontosan ismert a kulcshalmaz, amelyhez hash-függvényt keresünk. Ez a probléma olyan alkalmazásokban merülhet fel, amikor egy meglehetősen statikus kulcshalmazzal szeretnénk sokat és hatékonyan dolgozni. A legtöbb gyakorlati esetben azonban csak hozzávetőleges információnk van a kulcsok várható eloszlásáról.

Adott személynevek egy listája a input.txt fájlban. Ezen személyek adatait szeretnénk tárolni egy hash-táblában (láncolással). Tekintsük a neveket kulcsoknak (amelyek többnyire nem alkalmasak az egyedi azonosításra, de jelen esetben igen, mert az inputban nincsenek ismétlődő nevek).

A feladat az, hogy erre a kulcshalmazra nézve keressünk egy minél jobb hash-függvényt. Két szempont szerint kell optimalizálni: átlagos és maximális keresési idő szerint. Vagyis azt szeretnénk, hogy ha az adott hash-függvényt használva felépítenénk a hash-táblát, akkor abban a listák hossza minél egyenletesebb legyen, pontosabban az egyes elemek keresésének átlagos és maximális ideje minimális legyen.

Az input.txt fájl 16077 nevet tartalmaz, ezeket a kulcsokat kell leképezni egy 1613 méretű tömb indexeire. A hash.cpp fájl egy C++ program, amelyben a különböző hash-függvényeket kipróbálhatjuk és összehasonlíthatjuk. A fájl tartalmaz egy triviális hash-függvényt, amelyet le kell cserélni. Olyan implementációt keresünk, amelyre a program által kiszámolt átlagos és maximális keresési idő minél kisebb, és a függvény hatékonyan számítható, vagyis a fájlban található triviális változatnál nem lehet lényegesen lassabb, és a tárigénye sem lehet lényegesen nagyobb.

A maximális keresési idő (maximális listahossz) elméleti optimuma 10, az átlagos keresési idő elméleti optimuma pedig 5.485, a cél ezt minél jobban megközelíteni. Olyan hash-függvényeket lehet beküldeni, amelyekre a maximális keresési idő legfeljebb 22 és/vagy az átlagos idő legfeljebb 6,00. Mindenki több függvényt is küldhet.

Megjegyzés: A program csak a hash-táblában tárolandó listákon való lineáris keresések idejét (pontosabban lépésszámát) számolja. Ehhez azonban még minden esetben hozzáadódik a hash-függvény kiszámításának ideje, éppen ezért alapvető kritérium, hogy ez is kicsi legyen.

Értékelés: Ez a feladat tulajdonképpen egy verseny. A pontszámokat a beküldött hash-függvények statisztikai eredményei alapján állapítom meg, a legjobb megvalósításokkal 5-6 pontot lehet szerezni.

Inputfájl: input.zip
Tesztelő program: hash.cpp