[ RooTeR @ 10.10.2004. 22:30 ] @
na sajtu www.yuoi.nis.edu.yu mogu da se skinu zadaci.
zadatak koji mene najvishe zanima je sledeci :
Citat:

Zadatak 3. Terorista
Najveća zgrada u svetskoj prestionici Beonisu ima N spratova. Na svakom spratu se nalazi bazen. Terorista Joca želi da sruši zgradu, tako što će porušiti prvi sprat a samim tim i celu zgradu. Za svaki sprat su poznate sledeće informacije:
Wi – težina vode koja se nalazi u bazenu na i-tom spratu na početku
Li – najveća težina vode koju i -ti sprat može da izdrži
Ci – cena dinamita potrebnog da se poruši i-ti sprat
Sva voda iz srušenog sprata odlazi na sledeći niži. Ako težina vode na nekom spratu postane veća od dozvoljene koju može da izdrži, pod se ruši i sva voda prelazi nadole.
Vaš zadatak je da pomognete teroristi Joci da sa što manje novca sruši prvi sprat.
Ulaz: U prvom redu ulaza je prirodan broj N (1≤N≤100000), koji predstavlja broj spratova. U svakom od sledećih N redova nalaze se po tri cela broja Wi, Li, Ci (0≤Wi≤Li<2x109, 0<Ci<2x109), veličine naznačene u tekstu zadatka za i-ti sprat.
Izlaz: Na standardni izlaz ispisati minimalnu količinu novca potrebnu da se banka potopi. Zatim u sledećih nekoliko redova upisati redne brojeve spratova koje treba srušiti za minimalno rešenje, u redosledu rušenja.
Primer:
Ulaz:-----------------------------------------Izlaz:
4---------------------------------------------5
10 50 100-------------------------------------3
0 100 3---------------------------------------2
100 100 2
10 50 6
Objašnjenje: Terorista ruši treći, pa drugi sprat sa minimalnom cenom 5. Drugi sprat se ne ruši sam, jer je težina vode nakon eksplozije jednaka trećini izdržljivosti. Kada bi se rušio samo prvi sprat cena bi bila 100, a kada bi se rušio četvrti cena bi bila 6.

Mene ovo najvishe asocira na dinamichko programiranje, ali nikako ne mogu
da provalim formulu...
Inache, zanimaju me ideje josh i za 4. i 5. zadatak.
[ zvrba @ 11.10.2004. 13:53 ] @
Citat:
srdjandakic: OFFTOPIC:
Ne znam da li sam to samo ja, ali ovakva ideja za zadatak za savezno takmicenje je vrhunac neukusa i primitivizma.


Slazem se da je neukusno. Politiku na stranu, ajmo po zadatku.

Moja ideja - ne garantiram da je tocna ili da ce dati rjesenje u prihvatljivom vremenskom roku. Neka svaki kat predstavlja jedan cvor grafa (broj kata neka je odmah i oznaka cvoar grafa).

Pretpostavimo miniranje kata i. Ovisno o tezini vode na tom katu, tezini vode i izdrzljivosti katova ispod, proces rusenja katova ce se zaustaviti na nekom katu j. Tada se u grafu povuce brid od cvora i do cvora j. Bridu se dodjeljuje oznaka (w, c) gdje je w ukupna tezina vode na j-tom katu nakon rusenja, a c je cijena rusenja kata i.

Iz svakog kata i=2..n povucemo brid sa oznakom (w, c) do odgovarajuceg kata j.

Kada je graf konstruiran, nad grafom se izvrsi Dijkstrin algoritam pocevsi od vrha 1 uzimajuci za udaljenost komponentu c para (w, c) kojim je oznacen brid. Rezultat ce biti "najjeftiniji" put do svakog vrha i=2..n (ukupna cijena puta je najjeftinija cijena rusenja kata 1 ako se pocinje od kata i).

Pocevsi od vrha s najjeftinijom cijenom, pratimo put od tog vrha do vrha 1 i usput se zbrajaju tezine w kojima su obiljezeni bridovi. Najjeftiniji put ciji zbroj tezina vode (w) nadmasuje limit L_1 je rjesenje zadatka.

Sad, ima nekoliko problema.

1. MISLIM da je moguce konstruirati bridove grafa u jednom prolazu kroz vrhove, ali jos nisam razradio rjesenje.

2. Dijkstrin algoritam nad cijelim grafom ima slozenost O(n^2) gdje je n broj vrhova grafa. Zadatak dopusta do n=100000=10^5 katova. To daje oko 10^10 operacija. Nemam ideje koliko dugo bi to trajalo.

3. Odabir najjeftinijeg redoslijeda katova. U najgorem slucaju potrebno je ispitati svih n-1 puteva (n-1vi ce ispasti jedini koji ima dovoljnu tezinu da srusi sve katove ispod).

Vremenom izvrsavanja dominira 2. korak ako se 1. moze napraviti u O(n) (a mislim da moze uz puno dodatne memorije).
[ RooTeR @ 11.10.2004. 14:22 ] @
Ovaj, bez uvrede, ali nisam bash skontao shta znache neke rechi kljuchne
za objasnjenje zadatka koje je dao zvrba.
Shta znachi "kat" i "brid"?

Moderatora bih zamolio da pobrishe offtopic teme ...
[ Mihailo Kolundzija @ 11.10.2004. 15:11 ] @
Kat - sprat.
Brid - (iz konteksta) grana ili ivica.

Ne znam koliko je efikasno, ali da probam.

Koliko vidim, ti moraš da izazoveš lančanu reakciju, pošto dizanje nekog sprata u vazduh ne koristi mnogo ako se ta bujica vode zaustavi negde iznad prvog sprata. Pošto ta najjeftinija lančana reakcija mora da ima najviši sprat (pa makar on bio i prvi), kreni od najvišeg sprata i za svaki sprat (počevši od poslednjeg) izazovi lavinu do (ispod) prvog i zapamti cenu i taj sprat (čisto da bi na kraju rekonstruisao scenario).

Lančana izgleda ovako:
Digneš n-ti sprat u vazduh, povećaš cenu i gledaš gde će voda da se zaustavi. Zatim digneš u vazduh taj sprat, zaračunaš cenu i opet pratiš dokle se spuštaš (ne zaboraviš da sabiraš zapreminu vode). Tako nastaviš do propadanja prvog.

[ chupcko @ 12.10.2004. 08:01 ] @
Uf, opet mi je obrisana upadica o dinamiskom programiranju, mislim da nije offtopic jer se spominje u postavci teme :).

Dakle sta je to dinamicko programiranje, ako ti mirise na to, onda pojasni sta je tacno, da li je to odrejeneo secenje brute force varijante ili pak neko unosenje slucajnosti ?

Uzgred zasto nisi probao grubom silom da resis zadatak ?
[ zvrba @ 12.10.2004. 11:54 ] @
Gle, dinamicko programiranje je metoda optimizacije. Primjenjiva je u slucaju da optimalno rjesenje manjeg problema vodi optimalnom rjesenju veceg problema. Problem se pocinje rjesavati od najmanje instance, pamte se sva rjesenja i onda se na temelju tih zapamcenih rjesenja rjesavaju vece instance problema.

Za detalje trazi na googleu "dynamic programming" i mozes dodati "tutorial".
[ chupcko @ 12.10.2004. 12:11 ] @
Ne ne ne, nije prepoznata moja ironija, oko metode i naziva metode :).
Ali ajde, cini mi se da je dinamicko programiranje, ono sto je bilo objektno orjentisano pre nekih 10 goidna, magicna mantra koja sve resava :).

P.S. Cak sam pre nekih 5 godina pisao radove koji koriste te metode, ali nisam znao da se to zove tako :)...

Da se vratimo na temu, da li si probao metodu grube sile ?
[ Rapaic Rajko @ 12.10.2004. 14:01 ] @
Dinamicko programiranje je citava jedna nauka za sebe. Stvorena je u cilju pronalazenja resenja sa sto je moguce manje koraka. Ovog trenutka mogu da se setim samo 'metode severozapadnog ugla' (ipak, davno bese), ali ima tu jos dosta toga. Dinamicko programiranje NEMA nikakve veze sa objektnim programiranjem ili bilo kojom drugom vrstom IMPLEMENTACIJE resenja; mozes da pises postupak resenja u cemu god hoces (recimo Fortranu).

Rajko
[ RooTeR @ 12.10.2004. 14:14 ] @
Ej bre ljudi, ala ga vi zakomplikovaste ovde :)

Pa Chupcko, zar ne mislish da je ogranichenje malo preveliko da bi se
probao brute force? I shta ti imash toliko protiv dinamichkog? :)

[ chupcko @ 12.10.2004. 14:23 ] @
Pa samim tim mu je ime pogresno , to je opis metodologije kako se resava :), kao rekurzija recimo :).

Mozda da si samo pazljivije procitao sta sam napisao shvatio da sam pricao o mistifikaciji pojma.

Dakle defintino dinamicko programiranje nije programiranje, nego metodologija filtriranja medju svim resenjima (obicno gruba sila).

A poredjenje sa objektno orjentisanim je samo podsecanje na jos jednu mistifikaciju (koja traje jos i danas, zar ne :) ).

Ali ajde, da kazemo da je sve ustvari dinamicko programiranje :).

Recimo racunanje kvadratnog korena je dinamicko programiranje, kako, pa lepo:

Napisemo za pocetak neki prost petlja program (oo evo novog izraza: petlja programiranje) koji ide od 0 do n, uvecavajuci za 1 i ceka da i^2 bude vece od n.

Primeticemo da je algoitam puno spor (mada za 1 radi lepo :) ). Primenimo neke ustede ...

Trt mrt i eto nam dinamickog programiranja :).

Naci cu recimo jedan moj stari esej, pa cemo svi prepoznati dinamicko programiranje u njemu .

Dakle lepo je to dinamicko programiranje :)))), ali daj mi primer nekog koji nije dinamicko :), ja cu ti prepoznati metode dinamickog u njemu :)))).

P.S. Jel se seca neko "samomodifikujuceg programiranja"
[ mucky @ 12.10.2004. 14:36 ] @
Preuzecu rizik da gresim, ali moram opet da offtopic-ujem:

Chupcko, kad tako lepo shvatas znacenje izraza, onda objasni deci zasto
se Linearno programiranje tako zove?
Funkcionalno programiranje takodje? Kako su mene ucili, Dynamic
programming se ne koristi samo u programiranju,
vec i u nekim granama matematike :) Nebitno, Joca terorista moze zgradu
srusiti na razne nacine, a jedan od njih
je i "ono nesto dinamicko" :)

--
Visit my photo log at http://www.fotolog.net/mucky
[ srki @ 12.10.2004. 14:45 ] @
Citat:
chupckoRecimo racunanje kvadratnog korena je dinamicko programiranje, kako, pa lepo:

Napisemo za pocetak neki prost petlja program (oo evo novog izraza: petlja programiranje) koji ide od 0 do n, uvecavajuci za 1 i ceka da i^2 bude vece od n.

Primeticemo da je algoitam puno spor (mada za 1 radi lepo :) ). Primenimo neke ustede ...
Kakve ustede?

Citat:
Trt mrt i eto nam dinamickog programiranja :).

Pa ako si pocetni algoritam izmenio tako da pamtis resenja preklapajucih podproblema onda je to dinamicko programiranje. Ja uopste ne shvatam sta je problem sa pojmom dinamicko programiranje?

Citat:
Dakle lepo je to dinamicko programiranje :)))), ali daj mi primer nekog koji nije dinamicko :), ja cu ti prepoznati metode dinamickog u njemu :)))).

Npr. algoritam za nalazenje Fibonacijevog broja rekurzijom. Gde tu vidis dinamicko programiranje?
[ srki @ 12.10.2004. 15:07 ] @
Da ne bih bio potpuno offtopic da odgovorim na postavljeni zadatak.
Mozes da krenes od poslednjeg sprata i za svaki sprat da pravis listu od kojih svaki clan sadrzi dva podatka: kolicinu vode koja ode sa tim spratom i minimalna cena za to. Posle za sprat ispod koristis listu sa prethodnog da napravis novu listu. Lista ne treba da ti sadrzi podatke za kolicine vode preko 219 jer nema potrebe. Ako dobijes kolicinu vecu od 210 onda samo zapamti za 219.

Na ovaj nacin ce ti program brzo naci resenje. Pocnes od najviseg sprata za koji ces imati listu od samo jednog clana koji sadrzi kolicinu vode sa tog sprata i cenu za rusenje tog sprata.

Drugi nacin za resenje je da pamtis listu cena za rusenje tog sprata i maksimalnu kolicinu vode koja pri tome moze da ode na donji sprat.

[Ovu poruku je menjao srki dana 12.10.2004. u 16:27 GMT+1]
[ chupcko @ 12.10.2004. 15:16 ] @
Naci cu negde, ali dok nadjem :)...

Dinamicko programiranje je "postupal" kojim od jednog algoritma dobijas drugi (gde se menja slozenost), zar ne :).

E sada, ja kazem da za svaki algoritam B postoji algoritam A tako da je algortiam B nastao primenjujuci postupke dinamickog programiranja nad algoritmom A.

E jos da to dokazem :).

Sada mi je mnogo jasnije sta je to dinamicko programiranje, steta samo sto nisam pre znao da ga tako zovem dok sam ga primenjivao :).

A offtopic, ja i dalje nisam video da je iko pokusao brute force :) slozenost je samo n! na prvu loptu :).
[ srki @ 12.10.2004. 15:26 ] @
Citat:
chupcko:Dinamicko programiranje je "postupak" kojim od jednog algoritma dobijas drugi (gde se menja slozenost), zar ne :).

Ne, nije svejedno kako menjas postupak. Fora je da koristis preklapajuce osobine podproblema. Tek onda bi mogao da menjanje slozenosti nazoves dinamickim programiranjem.

Citat:
A offtopic, ja i dalje nisam video da je iko pokusao brute force :) slozenost je samo n! na prvu loptu :).

Samo? Pa nije ni cudo sto niko nece to da pokusava da implementira. A ako mislis na ideju, ne verujem da nisu pokusali ali sto bi se zamarali ljudi da pisu na forum ideju koja nije dobra za implementaciju?

[Ovu poruku je menjao srki dana 12.10.2004. u 16:45 GMT+1]
[ chupcko @ 12.10.2004. 15:38 ] @
Pa da, koristeci tu metodu dinamickog programiranja :)

To jel domaci za doktorske studije negde u americi :).

Brute force je lep jer tek kada pogledas tako napisan program mozes pronaci invarijatne i odredjene podprobleme i razne metodologije ....

Ono sto meni smeta je naziv, nije to programiranje, nego metodologija analize ipoboljsanja perfomansi koristeci razne funkcije. Mozda jednom ispricam o mom programu za trazenje pokrivanja pentomimima (lep primer secenja brute force metode, a mngo bi rekli, pa to je dinamicko programiranje).

Meni smeta sto neko daje tako idiotski naziv necemu sto je normlano :).
[ Danica Porobic @ 13.10.2004. 13:56 ] @
Pozdrav svima!

Prvo offtopic o DP: svi kojima smeta naziv dinamicko programiranje neka lepo smisle neki lepi naziv u duhu srpskog jezika, pa neka to razglase medju programerskom zajednicom...

A sad o zadacima:

3: ideja koja donosi 90 poena: napisete greedy koji ide na obrnutu stranu od logicne ( ne znam tacno kako ali meni je proslo na takmicenju ), i nadate se da su test primeri debilni ( kao sto su i bili ); ideja za 100 poena: DP u nlogn, primenom kumulativnih tabela...

4: najjednostavnija Dijkstra, samo sto ( skoro ) niko nije dobro implementirao obrtanje kockice, zadatak je trivijalan....

5: divide and conquer, primeni se ideja slicna quicksort-u ( nosi 86 poena ) ili randomized quicksort-u ( 100 poena ) iako je zadatak uradjen po strategiji sa papira nosio oko 60 poena....

Offtopic 2: Rajko jako si zaboravan, sve smo ti ovo ispricali jos dok smo se vracali sa saveznog... Ako ti treba neki info, pomoc, algoritam.... slobodno pisi posto "obozavani direktor" nece organizovati pripreme ove godine ( izgleda )....

Offtopic 3: Kakve veze ima koji je tekst zadatka sa tim da li je zadatak dobar ili ne? Prica o teroristima je naravno jako glupa, ali nije najgluplja, pogledajte zadatak Artemis ( prvi dan IOI 2004 ), tekst je ultra glup narocito deo sa fudbalskim igralistem ( koji je tu zbog velike ljubavi jednog od clanova ISC-a prema fudbalu )...
[ chupcko @ 13.10.2004. 20:23 ] @
Setih se onogo vica o morskom prasetu. Znate vec, to je onaj vredjajuci vic o slicnosti izmedju zene programera i morskog praseta :).

E od sada ga pricam ali u varijanti sa morskim prasetom i dinamickim programiranjem :).

Dakle nije u pitanju duh srpskog jezika (jel nam se tako zove jezik ?) nego smislenosti :).

Salu na stranu, izgleda da je Danica jasno rekla koje metode mogu da se koriste :).
[ StMilan @ 19.10.2004. 11:56 ] @
Dinamicko programiranje:
http://en.wikipedia.org/wiki/Dynamic_programming

Naziv ni na engleskom nema mnogo smisla, ali to i nije bitno. Nauci sta je i to je to.
Zapravo predstavlja resavanje problema indukcijom.

[ chupcko @ 19.10.2004. 20:37 ] @
Nauka nekada podrazumeva i kriticki odnos ka necemu sto posmatras :), pa makar moja zamerka bila ime :).

Kako bese, morsko prase niti je morsko niti prase :).

I da li na kraju ima neko da postuje resenje :))))
[ Rapaic Rajko @ 21.10.2004. 07:30 ] @
Sve u svemu, vrlo lepa ideja, i neverovatno glupa formulacija zadatka.
Sad sam se bas zainteresovao; pokusacemo nesto...pa mozda i postujemo.

Rajko
[ Reljam @ 21.10.2004. 17:30 ] @
Srki, mislim da tvoje resenje sa listom nece raditi, ne vidim kako uzima u obzir da gornji sprat nije optimalan za rusenje.

Citajuci i ostale postove, izgleda da nismo uspeli da nadjemo neko dobro resnje :/
[ srki @ 21.10.2004. 18:06 ] @
Citat:
Reljam: Srki, mislim da tvoje resenje sa listom nece raditi, ne vidim kako uzima u obzir da gornji sprat nije optimalan za rusenje.

Uzima u obzir ali mozda nisam lepo objasnio. Rekao sam da listu za odredjen sprat pravis na osnovu prethodnog sprata i kolicine vode koja ode sa prethodnog sprata i cenu za to.
[ Reljam @ 21.10.2004. 18:28 ] @
Zar to onda nije opet losije od O(n^2)? Ako te ne mrzi, molim te napisi alogirtam u pseudokodu - jasan mi je deo za liste, ne moras to da razradjujes.
[ srki @ 21.10.2004. 18:54 ] @
Ako imamo niz cene[n], voda[n], maksimalnavoda[n] onda uradimo nesto ovako:
Code:

lista[n+1]:={ (0,0) };   //to predstavla cenu i kolicinu vode koja ode sa tim spratom)
for i=n downto 1 do
   begin
    lista[i]= { (voda[i], cena[i]) } ;
    for each k in lista[i+1] do if (k.voda+voda[i] > maksimalnavoda[i]) then dodaj u lista[i] clan (k.cena, k.voda+voda[i])
    for each k in lista[i+1] do if lista[i] ne sadrzi clan sa kolicinom vode (k.voda+voda[i]) dodaj u lista[i] clan (k.cena+cena[i], k.voda+voda[i] 
   end;

I to je to.
Na kraju samo treba videti clan sa najmanjom cenom iz lista[1].

E da, podrazumeva se da u listu ne ubacujemo clanove kod kojih je kolicina vode veca od maksimalne za jedan sprat koja je u uslovima zadatka tj. 218.
Pod takvim uslovima slozenost zadatka je O(n)

Ajde sada da prodjemo kroz pseudokod koristeci primer iz prve poruke:
Imamo 4 sprata.
10 50 100 (tezina vode na tom spratu, maksimalna moguca tezina vode, cena)
0 100 3
100 100 2
10 50 6

E u prvoj liniji lista[5]={ (0,0) }
Posle imamo lista[4]={ (10,6) };
lista[3]={ (100,2), (110,6) }
lista[2]={ (100,5), (110,6)}
lista [1]={ (10, 100) , (100,5), (110,6) }

Iz lista[1] vidimo da je najmanja cena 5.
[ filmil @ 21.10.2004. 20:21 ] @
Evo da probam da kažem nešto na zadatu temu.

Najpre u vezi s dinamičkim programiranjem. Slažem se da je naziv pomalo nesrećan, pošto je jelte svaki program posledica programiranja; što bi sad pa to dinamičko bilo posebnije od svih ostalih? Ne bih se složio sa onima koji kažu da je dinamičko programiranje korišćenje rekurzije, jer se zapravo svaki program može izraziti rekurzivno. O tome govori ta, u par navrata pominjana, Čerčova teza.

Kada pišete program, rekurzije se obično izravnaju da bi cela stvar bila manji zalogaj za računar, pa se zato u samim sorsovima pojavljuju retko. Ali rekurzivni programi su neverovatno važni kada se algoritam izmišlja. To je zato što svaki rekurzivni program možemo da izrazimo preko samo tri bitna sastojka: početnog uslova, induktivne hipoteze i induktivnog koraka. To su jedine tri stvari koje treba zapamtiti.

Dosta često ljudi o programima razmišljaju tako što pokušavaju u glavi da izvrše sve instrukcije, korak po korak, vodeći računa o međurezultatima. Upoznao sam nekoliko ljudi koji mogu da isprate program do u neverovatnu dubinu, ali IMHO to je rasipanje resursa. Gde bi im bio kraj da tu metodologiju zamene samo sa tri gorepomenute stvari; definišite ih i pustite da indukcija odradi ostatak posla za vas.

Naravno da je problem nabosti pravu induktivnu hipotezu, i induktivni korak da bi se sve razmotalo kako treba (obično početni uslov nije glavobolja), ali jednom kad ste to uradili, prepevavanje na jezik petlji i uslova je čista nizbrdica. (Ovde ću malo da se ponovim pa da kažem da Udi Manber u svojoj knjizi Introduction to Algorithms: a creative approach objašnjava isto ali mnogo tačnije, bolje i lepše; pogledajte ako možete, nećete se razočarati:)

Evo jedan bez veze primer koji (kao) objašnjava onaj deo sa rekurzijama. Treba da se napiše program koji štampa sve brojeve od 1 do 10. Retko ko bi trepnuo dvaput pre nego što bi smislio nešto poput:

Code:
for(int i=1; i =10; i++)
     printf("%dn", i);


Ajde sad da probamo da isto to napravimo, samo rekurzivno. Ideja je da se napravi funkcija koja uradi parčence posla a ostatak svede na sebe, samo malo manju. Ovde nije preterano teško da se sredi ispis. Brojeve od 1 do 10 ispisaćemo tako što ispišemo prvo 1, a zatim ispišemo sve ostale (od 2 do 10). Kada ova dva koraka posmatramo zajedno, treba da dobijemo funkciju koja je ista kao drugi korak, samo malo „veća“. Nije teško da se vidi da je ta funkcija nešto kao: ispisi(int i). Ona treba da ispiše sve brojeve od i do 10.

Nezgodan deo je u tome što naša funkcija ispisi() nekako lebdi u vazduhu. Poziva se na samu sebe, deluje malo kao baron Minhauzen koji pokušava da se izvuče iz živog blata vukući se za čizme. Da bismo rešili ovaj „konflikt“ moramo da funkciju zakačimo za nešto realno. Kazaćemo da funkcija ispisi(10) ispisuje samo 10 i završava sa radom.

Ovim smo našli sva tri bitna sastojka odozgo: početni uslov (to je poziv ispisi(10)), induktivnu hipotezu (imamo funkciju ispisi(i) koja ispisuje sve brojeve od i do 10) i induktivni korak (ispisi(i) se može izraziti kao ispisivanje broja i i zatim pozivanje funkcije ispisi(i+1)). Kad ove tri stvari objedinimo u program, dobijamo:

Code:
void ispisi(int i) {
     printf("%dn", i);
     if (i  10) 
        ispisi(i+1); // ispisi sve brojeve od i+1 do 10
 }


E sad nadam se da nećete da mi se mnogo smejete zbog ovog ekstra glupog primera. Dabome da niko pri zdravoj pameti ne bi ovako napisao program koji broji od 1 do 10. Probajte da sagledate obrt: šta se dešava kada probamo da razumemo šta radi prvi primer? Nema mnogo pomoći nego da sagledamo svaku iteraciju ponaosob. Pošto petlja ide od 1 do 10 i telo je prilično jednostavno, neće biti problem da se isprati. Šta međutim kada je gornja granica 10000? Šta ako u telu petlje imamo nešto više od printfa? Ko će to sve da zapamti? (U stvari ispravan način za razumevanje petlje svodi se na to da se „vaskrsne“ rekurzija iz iteracije pa onda opet „podmiti“ indukcija da odradi prljavi posao, o tome je zamalo bilo reči pre neki dan u forumu C/C++.)

S druge strane, rekurzivna varijanta je „otporna“ na sva pomeranja granice. Razumevanje programa se ne menja sa promenom one desetke s početka: stavite 10000 umesto 10 ali ćete i dalje da koristite samo tri sastojka da biste razumeli funkciju.

Dakle, svaki program je bio rekurzija kad je bio mali, bez obzira da li je tu korišćeno „dinamičko programiranje“ ili nekakvo drugo.

Otkud onda dinamičko programiranje? Izraz verovatno dolazi iz vremena pre pravog „kompjuterskog“ programiranja. Slično kao što linearno programiranje nema posebne veze sa programskim jezicima itd, već je valjda neka matematička zezalica. Evo i zašto to mislim: jednom sam tako čitao neku knjigu iz automatike. U jednom delu se govori o nekakvom sistemu koga treba prevesti iz jednog stanja u drugo, uz minimalan utrošak energije (dakle recimo uz najmanju potrošnju struje ili tako nečeg sličnog, valjda je otud jasno što se inženjeri cimaju oko toga). Gle čuda, metoda za to zove se upravo dinamičko programiranje. (a još uvek smo negde na početku 20. veka, dakle nema nikakve šanse da je izmišljator metode imao računare na umu!)

Tamo je objašnjeno otprilike da dinamičko programiranje koristi nekakav „princip optimalnosti“ koji kaže: ako između početnog i krajnjeg stanja postoji najbolja putanja, ako se sistem jednom nađe na najboljoj putanji (bez obzira kako je dotle došao), od te tačke na dalje, mora da se kreće po njoj da bi i ceo put bio najbolji mogući. Štos koji se koristi da se reši stvar će verovatno da vam se učini poznat: krene se od krajnje tačke putanje i nekako, na mišiće, izračuna sam kraj te optimalne putanje. Zatim se izmaknemo još malo unazad i opet na mišiće izračunamo, ali samo deo putanje koji nas od te tačke vodi do malopre izračunate među-tačke. Princip optimalnosti se brine da, kad jednom izađemo na pravi put, sve ostalo klizi kako treba. Malo po malo, računajući parče po parče puta i krećući se unazad sve vreme i koristeći „princip optimalnosti“, na kraju dolazimo do početne tačke. Put kog smo (unazad) ocrtali od cilja do starta je ta najbolja putanja.

E sad, programi koji koriste sličnu ideju su (kao) to dinamičko programiranje. Dakle ima tu rekurzije (pošto je jelte ima u svakom programu), ali ima i još malo više. Sad, da li je ime nesrećno ili ne, ne raspravljam, po knjigama se dobro ukopalo i teško da će skoro neko uspeti da ga odatle pomeri.

I napokon da se vratimo na zadatak posle ove digresije.

Rekoh već da nam za rešenje trebaju samo tri elementa. Mali problem je što na početku pojma nemam kako će ta tri elementa da izgledaju. Zato ću probati da stvar rešim postavljajući (nadam se prava) pitanja u nadi da ću nekako uspeti da napipam koji su pravi početni uslov, induktivna hipoteza i induktivni korak.

Glavni zadatak teroriste (jeste bez veze postavka, šta ćemo...) jeste zapravo da sruši sprat broj 1 na najjeftiniji mogući način.

Da vidimo prvo kako da se sruši sprat i po kojoj ceni, posle ćemo da vidimo šta je sa najjeftinijim rušenjem. Postoje tri načina:
- da se izminira sam sprat, u kom slučaju rušenje košta Ci;
- da se sa gornjih spratova dopremi dovoljno vode;
- da se uradi i jedno i drugo.

Treća varijanta otpada pošto sigurno košta više od primene bilo varijante 1 ili varijante 2 ponaosob.

Što se tiče varijante 2, ona košta najmanje onoliko kolika je ukupna najmanja cena rušenja svih spratova iznad ovog, a koji u zbiru imaju dovoljno vode da sruše ovaj sprat. Ako je ta cena manja od Ci, onda ovaj sprat ne miniramo, već samo čekamo da se voda sruči. Cena miniranja ovog sprata onda postaje 0. Prosto biramo između varijanti 1 i 2 onu koja manje košta. Pošto postoje samo dve mogućnosti za rušenje sprata, ako izaberemo jevtiniju, sigurno smo izabrali minimalnu.

Primetite da se ovde upravo pojavio jedan deo induktivne hipoteze: da bismo izračunali najmanju cenu za miniranje ovog sprata, potrebno nam je da nekako znamo najmanje cene za miniranje gornjih spratova.

Međutim, da bismo to sračunali uveli smo još par pretpostavki koje nam sad štrče, a to je da znamo koliko spratova iznad moramo da imamo srušene (bez obzira na koji način, dovoljno je da znamo da se sprat „minimalno“ srušio, metod 1 ili 2 nebitno je!) kao i da znamo koliki je zbir cena rušenja tih spratova kao i da znamo koliko vode ukupno ima u njima.

Dakle u principu smo rešili minimalno rušenje sprata, sad još treba da sredimo ove novouvedene stvari. I one mogu da se reše rekurzivno, a njihove induktivne hipoteze ćemo da sastavimo sa početnom. To se zove ojačavanje (strengthening) induktivne hipoteze (vidite gorepomenutu knjigu od Manbera). Prosto rečeno: moramo u jednoj petlji da sračunamo više od jedne stvari.

Negde gore ste primetili da nam na par mesta trebaju zbirovi nekakvih veličina od sprata a do sprata b (cena i količina vode će se računati na taj način). Ovaj podproblem se lako reši ako ubacimo induktivnu hipotezu da znamo koliki je zbir recimo svih količina vode od sprata x do sprata N. Neka je to recimo V(x). Količinu vode od sprata a do sprata b ćemo onda lako da sračunamo kao V(b) - V(a), ako je b a. Slično uradimo i sa cenom; obe stvari pomerimo u induktivnu hipotezu.

Inventar:

Induktivna hipoteza:
Za sprat i N znamo:
- zbir minimalnih cena rušenja svih spratova od i+1 do N
- zbir količina vode svih spratova od i+1 do N

Početni uslov:
za sprat N znamo:
- minimalna cena rušenja svih spratova iznad je 0 (jer iznad nema ničega)
- ukupan zbir količina vode svih spratova iznad je 0 (jer iznad nema ničega)

Da bismo sastavili ceo algoritam treba nam još da napravimo induktivni korak. On mora da bude takav da, pošto se izvrši, saznamo:
- zbir minimalnih cena rušenja svih spratova od i do N
- zbir količina vode svih spratova od i do N

Induktivni korak:
Drugi deo lako računamo sabirajući zbir voda sa svih spratova od i+1 do N sa Wi. To je O(1).

Prvi deo je malo nezgodan, jer treba da lociramo najbliži sprat j iznad, takav da kada se saberu količine vode na spratovima između i+1 i j, to sve bude veće od limita Li na ovom spratu. Pošto je zbir količina vode od i+1 ka N monotono rastući, ovaj sprat može da se pronađe binarnom pretragom. Pošto zbir količina vode može direktno da se pronađe kao razlika zbirova količine voda, onda nam za to treba samo O(1), dobili smo to vrlo jeftino. Ostaje samo složenost binarne pretrage, O(log(N-i)) = O(logN).

Kada smo našli taj sprat iznad, uporedimo cenu rušenja sprata i sa cenom „urušavanja“ sprata pomoću vode. Na osnovu toga izaberemo varijantu rušenja 1 ili 2, zavisno koja je jeftinija.

Time smo završili induktivni korak i opisali sva tri bitna elementa programa. Nije zgoreg još jedna provera da sve pasuje (nisam proveravao, slobodno me izrešetajte ako sam nešto omanuo!).

Dakle, ako sa izvršavanjem algoritma krenemo od sprata N ka spratu 1, kada stignemo do 1 i završimo, znaćemo:
- zbir minimalnih cena rušenja svih spratova od i do N
- zbir količina vode svih spratova od i do N

Za minimalnu cenu nam treba samo da oduzmemo min. ukupne cene rušenja od 2 do N od min ukupne cene rušenja od 1 do N.

Na kraju, procena složenosti:
- induktivni korak se izvšava N puta, jednom za svaki od N spratova.
- složenost induktivnog koraka zavisi od i, ali nije veća od složenosti koraka za sprat 1, gde je otprilike O(logN): većina operacija je O(1) pošto smo koristili induktivnu hipotezu da svedemo sva računanja na minimum; jedini korak koji „iskače“ je ona binarna pretraga koja je O(logN). Kada saberemo tih par O-ova dobijamo samo O(logN).
- dakle, ukupna složenost je manja od N O(logN), što je ukupno O(NlogN).

Nadam se da nisam baš mnogo lupio...

f
[ srki @ 22.10.2004. 00:42 ] @
Citat:
filmil
Da bismo sastavili ceo algoritam treba nam još da napravimo induktivni korak. On mora da bude takav da, pošto se izvrši, saznamo:
- zbir minimalnih cena rušenja svih spratova od i do N
- zbir količina vode svih spratova od i do N

A zasto?

Citat:
Prvi deo je malo nezgodan, jer treba da lociramo najbliži sprat j iznad, takav da kada se saberu količine vode na spratovima između i+1 i j, to sve bude veće od limita Li na ovom spratu.

Sta smo tacno time dobili?

Citat:
Nije zgoreg još jedna provera da sve pasuje (nisam proveravao, slobodno me izrešetajte ako sam nešto omanuo!).

ratatata!
Salim se, ali samo nije zgoreg da jos malo pojasnis algoritam.

Citat:
Za minimalnu cenu nam treba samo da oduzmemo min. ukupne cene rušenja od 2 do N od min ukupne cene rušenja od 1 do N.

Ne razumem.
[ filmil @ 22.10.2004. 08:56 ] @
Citat:
A zasto?
Kod rekurzivnog rešenja moramo da imamo funkciju koja se induktivnim korakom svede na istu tu funkciju, samo sa malo drugačijim argumentom.

(1) U iteraciju sa i-tim spratom ulazimo znajući:
- zbir minimalnih cena rušenja svih spratova od i+1 do N
- zbir količina vode svih spratova od i+1 do N

(2)U iteraciju sa i-1-vim spratom moramo da uđemo znajući:
- zbir minimalnih cena rušenja svih spratova od i do N
- zbir količina vode svih spratova od i do N
da bi nam stalno važila induktivna hipoteza, to je uslov da indukcija radi. Primeti: (1) i (2) su ista funkcija, samo je u (1) argument i, a u (2) argument i-1.

Znači da telu iteracije za i-ti isprat moramo od (1) da nekako napravimo (2) da bismo održali važnost induktivne hipoteze.

Citat:
Sta smo tacno time dobili?
Time smo sračunali koliko košta metod  rušenja br. 2. (urušavanje, iliti rušenje gornjih spratova na neki način i čekanje da voda uradi ostalo).  Zatim to uporedimo sa cenom miniranja sprata, metod br. 1. Zavisno od toga koja cena je manja, za sprat i trošimo ili Ci (ako mora da se minira) ili 0, ako može da se uruši.

Citat:
Ne razumem.
Pa, igrom slučaja je u zadatku lakše da se pamti zbir koštanja svih spratova od i do N umesto samo koštanja svih spratova. Zato cenu sprata 1 dobijamo kao (zbir od 1 do N) - (zbir od 2 do N).

f
[ Mihailo Kolundzija @ 22.10.2004. 11:45 ] @
Hm, zar nije malo sumnjivo sto stoji 2x109? Pre bih rekao da je u pitanju , sto ce reci da Srkijevo resenje ipak ima kvadratnu slozenost (kao i ono koje sam ja predlozio).

Filipe, mislim da se princip optimalnosti u ovom zadatku ne moze primeniti. Metoda koju si opisao je i meni pala na pamet, ali sam nekako sebe ubedio da tako ne ide. Ispravi me ako gresim, ali evo jednog primera konfiguracije zgrade koja bi mogla da je zbuni:

1. sprat: vode ima 10 litara, kapacitet bazena 20 litara, cena rusenja 30.
2. sprat: vode ima 5 litara, kapacitet bazena 30 litara, cena rusenja 15.
3. sprat: vode ima 10 litara, kapacitet bazena 20 litara, cena rusenja 10.
4. sprat: vode ima 20 litara, kapacitet bazena 30 litara, cena rusenja 20.

E sad, minimalne cene rusenja pojedinih spratova:
4. - 20
3. - 10
2. - 15
1. - 20

Kao sto vidis, problem nam pravi onaj 2. sprat, posto ima preveliki kapacitet. Kad bi gledali po kolicini vode, za rusenje prvog sprata cemo (binarnom pretragom) naci da je dovoljno da dignemo samo treci sprat u vazduh. Medjutim, ta voda ce se zaustaviti na drugom spratu, pa zato moramo da rusimo samo cetvrti.

Voleo bih da vidim ono resenje sa primenom "kumulativnih tabela" (ne mogu da pogodim sta se krije iza tog pojma) koje je Danica pomenula.
[ filmil @ 22.10.2004. 12:27 ] @
Citat:
Kao sto vidis, problem nam pravi onaj 2. sprat, posto ima preveliki kapacitet.
Da, pogrešio sam. :( Minimalna cena stoji „iznad“ gornje granice za spratove koju sam postavio pa sam je promašio. Učinilo mi se da će minimalna cena da sama „siđe“ ali izgleda da nije tako...

f
[ srki @ 23.10.2004. 16:39 ] @
Citat:
filmil
Citat:
A zasto?

Kod rekurzivnog rešenja moramo da imamo funkciju koja se induktivnim korakom svede na istu tu funkciju, samo sa malo drugačijim argumentom.

To je ok ali dalje mi nije jasno jer ne vidim kako iz date funkcije mozemo da nadjemo resenje zadatka.

Citat:
Pa, igrom slučaja je u zadatku lakše da se pamti zbir koštanja svih spratova od i do N umesto samo koštanja svih spratova. Zato cenu sprata 1 dobijamo kao (zbir od 1 do N) - (zbir od 2 do N).

Cek, sta ti je cena jednog sprata? Ako je cena rusenja spratova od 1 do N i od 2 do N ista onda bi ti ispalo da je cena sprata 1 u stvari nula. Ili te ipak nisam dobro razumeo?
[ Sinalco @ 23.10.2004. 23:36 ] @
Hey!

Ja sam tek sada saznao za ovu raspravu... Ubacujete se previse!
Necu da komentarisem text zadatka, ali niko nije imao nista protiv njega...
Test primeri... Ako hocete da vidite debilne test primere - www.acm.ro
Meni jos uvek nije jasno zasto je prolazilo 90 poena na takmicenju, ali verovatno je neka greska u kodu ili algoritmu.
Inace, zadatak je vrlo simpatican! A i ne bi ste se vi ovoliko jebavali da vas ne interesuje!
Bodovanje je bilo sledece:

- za proveru rusenja svakog sprata i kretanjem na dole se dobija 30 poena
- za pametniju proveru, ali i dalje kvadratni algoritam se dobija 50 poena (sa koriscenjem niza u kojem smestamo spratove koje smo rusili za prethodni sprat)
- za resenje koriscenjem hipa i naravno dinamicki odozdo nagore se dobija svih 100 poena

Ako vas bas zanima, mogu malo i da pojasnim resenje zadatka...
Pozdrav!
[ srki @ 24.10.2004. 09:36 ] @
Citat:
Mihailo Kolundzija: Hm, zar nije malo sumnjivo sto stoji 2x109? Pre bih rekao da je u pitanju , sto ce reci da Srkijevo resenje ipak ima kvadratnu slozenost (kao i ono koje sam ja predlozio).

Ako je tako onda moje resenje ne vajla jer bi mogao da se napravi slucaj da moje resenje predugo radi. Onda je najbolje dinamicki popunjavati matricu cena[i,j] gde je cena[i,j] najmanja cena rusenja spratova izmedju "i" i "j" (i<=j uz dozvoljeno rusenje spratova iznad j ako je ukupna cena urusavanja manja). To je malo izmenjeno Filipovo resenje jer gornja granica nije konstantna. U tom slucaju je slozenost resenja O(n*n). Tada se ide rekurzijom odozdole ka gore. Napisacu kasnije pseudokod.
[ Sinalco @ 24.10.2004. 09:58 ] @
Inace, ja sam autor zadatka (texta i resenja).