Informatika gyűjtemény

Egy szinttel feljebb Megoldás

2004050607080910

NézetNyomtat

A megoldás

A Zombik című feladatban használt legrövidebbút kereső algoritmust átalakításást fogjuk itt is használni. De előbb találjuk ki, hogy hogyan tároljuk el a térképet:

Tárolás a memóriában

Az egy városhoz kapcsolódó, és a két város közötti karavánkapcsolatokra nincsen semmilyen korlát, csak annyit tudunk, hogy az összes kapcsolatok száma legfeljebb 5000. Ezért felveszünk néhány 5000-es tömböt nekik. A városokat és a kapcsolatokat is 1-től sorszámozzuk. A tömbök: az i-edik karavánkapcsolat a v1[i] sorszámú városból a v2[i]-edik városba visz, és az év n1[i]-edik napjától az n2[i]-edik napjáig közlekedik.

Az első részfeladat

Írjuk fel minden városhoz, hogy mennyi idő alatt érhetőek el. Pontosabban azt, hogy melyik nap reggelére érhetőek el. Kezdetben ez minden városhoz végtelen, kivéve A-t, amihez 1. A végtelen azt jelenti, hogy a város nem érhető el, vagy legalábbis azt, hogy még nem tudjuk, hogy elérhető-e. Jelölhetjük pl. -1-el, vagy egy olyan nagy számmal, ami biztosan nem fordulhat elő. (Ezt a nagy számot mindenképpen rögzítsük konstansként, és a legjobb az, ha a számábrázolás felső határának állítjuk be.) A továbbiakban a "-1-es" megoldást fogjuk megnézni.
Az algoritmus a következő, már jól ismert lépés ismételgetéséből fog állni: vegyük sorra a városokat, és ha olyat találunk, aminek nem végtelen már az elérési ideje, akkor nézzük meg, hogy a karaván-kapcsolatain keresztül el lehet-e jutni belőle más városokba. Ha valamelyik ilyen városba hamarabb el lehet így jutni, mint a feljegyzett ideje, akkor ezt a hamarabbi napot jegyezzük fel neki elérési időnek. Az elérési idők számolásánál figyelni kell arra, hogy két város között csak bizonyos időszakokban van út. Ilyenkor természetesen a lehető legkorábbi időpontban ki kell használni az utat. És az előző órákhoz hasonlóan, ez az algoritmus itt is akkor fog megállni, ha már lefutott egy olyan lépés, amiben nem volt elérési idő csökkentés.
A következő függvény megmondja, hogy ha t napon reggel indulunk az A városból, akkor legkorábban melyik nap estére érkezhetünk meg B-be. Felhasználja még az ido[] tömböt is, aminek kezdetben minden eleme -1, és később az i-edik eleme az i-edik város elérési ideje lesz. (Tehát az, hogy melyik nap reggel lehet belőle továbbindulni.) Annyit érdemes még módosítani az algoritmuson, hogy nem a városokat veszi sorra egy lépésben, hanem a karaván-kapcsolatokat. Ez a két dolog valójában ekvivalens, de a másodikat könnyebb gyors(abb)ra megírni.
Eljárás Leghamarabb(t)
    ido[a]:= t;
    kesz := Hamis;
    Ciklus Amíg (kesz = Hamis)
        kesz := Igaz;
        Ciklus i := 1-től M-ig 
            Ha Csokkent(i) Akkor
                kesz := Hamis;
            Elágazás Vége
        Ciklus Vége
    Ciklus Vége
    Ha ido[b] = -1 Akkor Leghamarabb:= -1
    Különben Leghamarabb := ido[b] - 1;
Eljárás Vége
A kesz fogja jelezni, ha nem volt csökkentés egy körben. A Csokkent(k) pedig megnézi, hogy csökken-e a v2[k] város elérési ideje, ha v1[k] városból a k karavánnal megyünk oda. (Természetesen úgy, hogy ha v1[k]-ból legkorábban csak ido[ v1[k] ] napon indulhatunk el.) Ha igen, akkor csökkenti, és Igazzal tér vissza, ha nem, akkor nem csinál semmit, és Hamissal tér vissza. A Csokkent eljárás:
Eljárás Csokkent(k)
    start := v1[k];
    cel := v2[k];
    Ha (ido[start] > n2[k]) vagy (ido[start] = -1) Akkor
        Csokkent := Hamis;
    Különben
        Ha (ido[start] <= n1[k]) Akkor
            startnap := n1[k];
        Különben
            startnap := ido[start];
        Elágazás Vége
        Ha (startnap + 1 < ido[cel]) vagy (ido[cel] = -1) Akkor
            ido[cel] := startnap + 1;
            Csokkent := Igaz;
        Különben
            Csokkent := Hamis;
        Elágazás Vége
    Elágazás Vége
Eljárás Vége
Ha ido[v] > n2[k] akkor túl későn érkeztünk a v városba, és lekéstük a karavánt. Ha elértük, akkor megnézzük, hogy a karaván kezdőnapja előtt, vagy után érkeztünk, és ez alapján meghatározzuk a lehetséges legkorábbi indulás időpontját, startnap-ot. Így az első részfeladat megoldása:
Kiir( Leghamarabb(1) );
...ha nem felejtkezünk el az ido[] tömb inicializációjáról.

A másik két részfeladat

Ezeket a részfeladatokat az előző rész megoldásának Leghamarabb(t) a felhasználásával lehet könnyen megcsinálni. Meg kell keresni a legkésőbb befejeződő karaván utolsó napját: legyen pl. utso-ban. Majd nézzük végig a napokat 1-től utso-ig, és nézzük meg, hogy egy adott napon indulva A-ból, mikorra érkezhetünk B-be. Ezek közül kell kiválasztani a legrövidebb utazási időtartamú utazást, és a legkésőbb kezdődő, de H határidőre megérkező utazást.
leg_rovidebb := -1;
leg_kesobb := -1;
Ciklus i := 1-től usto-ig
    erk := Leghamarabb(i);
    Ha erk <= H Akkor
        Ha (> leg_kesobb) Akkor
            leg_kesobb := i;
        Elágazás vége
    Elágazás vége
    Ha (erk - i + 1 < leg_rovidebb) vagy (leg_rovidebb = -1) Akkor
        leg_rovidebb := erk - i + 1;
    Elágazás Vége
Ciklus vége
Kiir(leg_rovidebb);
Kiir(leg_kesobb);

Megoldások