Vedúci po veľmi dlhej dobe začali upratovať kostýmy zo sústredení. Všetky boli v neoptimálnom stave. Boli zablatené, špinavé a podobne. Preto ich bolo potrebné oprať. Potom ale museli vyschnúť. Na to sme využili dve laná pri matfyze, na ktoré sme jednotlivé kostýmy zavesili.
Vedúci potrebujú usušiť $n$ kostýmov. Máme $2$ rovnako dlhé rovnobežné laná dlhé $l$, na ktorých sa budú kostýmy sušiť. $i$-ty kostým má šírku $w_i$, zaberie teda $w_i$ miesta na lane. Každý kostým môžeme sušiť na jednom alebo na oboch lanách. Časy sušenia sú $t_{i1}$ (na jednom lane) a $t_{i2}$ (na oboch lanách). Platí, že každý kostým sa na oboch lanách vysuší rýchlejšie ako len na jednom. Ako najrýchlejšie vieme vysušiť všetky kostýmy iba na týchto dvoch lanách? Všetky kostýmy musíme zavesiť už na začiatku.
Na prvom riadku vstupu sú dve medzerou oddelené čísla $n$ a $l$, kde $n$ je počet kostýmov a $l$ je dĺžka lán. Potom nasleduje $n$ riadkov, kde každý riadok obsahuje tri medzerou oddelené čísla - $w_i$, $t_{i1}$ a $t_{i2}$, kde $w_i$ je šírka $i$-tého kostýmu, $t_{i1}$ je čas sušenia na jednom lane a $t_{i2}$ je čas sušenia na oboch lanách. Pre každý kostým platí $t_{i1} > t_{i2}$.
V jednotlivých sadách platia nasledujúce obmedzenia:
| Sada | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| $1 \leq n \leq$ | $20$ | $100$ | $1\,000$ | $10^4$ |
| $1 \leq l \leq$ | $20$ | $200$ | $2\,000$ | $2*10^4$ |
| $1 \leq \max t_{i1} \leq$ | $1000$ | $1000$ | $10^9$ | $10^9$ |
Vypíšte jedno číslo - minimálny čas, po ktorom sa všetky kostýmy usušia. Ak kostýmy nie je možné vysušiť, vypíšte -1.
5 12
4 9 3
3 9 2
2 9 3
4 8 5
4 10 1
9
Posledný kostým zavesíme na obe laná a zvyšné musia byť zavesené na jednom lane, aby sa zmestili. Napríklad zavesíme prvý a druhý kostým na prvé lano a tretí so štvrtým na druhé lano.
3 10
8 3 2
7 9 2
7 8 3
-1
Kostýmy nemáme ako usušiť - nezmestia sa na laná
Vieme, že nejaké “najhoršie” riešenie bude, že každý kostým bude na jednom lane. Keďže sa všetky kostýmy sušia naraz, tak celkový čas sušenia bude vždy rovnaky ako čas sušenia toho “najpomalšieho” kostýmu. Ak by sme tento čas chceli vylepšiť, tak jediná možnossť je, že zoberieme najpomalší kostým a prevesíme ho cez obe laná.
Všetky kostýmy povešané cez obe laná budeme ukladať na začiatok lán. Vieme, že ak nejaké riešenie bude existovať, tak určite bude existovať rovnako dobré s tým, že kostýmy zavesené na oboch lanách sú na začiatku. Vieme si ich totiž v tom nejakom riešení posunúť na začiatok a stále to bude fungovať. Naopak to však nemusí platiť. Každé optimálne riešenie teda vieme poposúvať tak, že na začiatku bude niekoľko kostýmov na oboch lanách a potom zvyšok bude zavesený na jednom lane.
Tieto pozorovania využijeme pri zisťovaní riešenia. Začneme s “najhorším” riešením a postupne ho budeme vylepšovať. Jeden krok vylepšenia je, že si zoberieme ten najpomalší kostým a prehodíme ho cez obe laná. Preto si kostýmy utriedime podľa pomalšieho času, aby sme vedeli rýchlo povedať, ktorý z nich máme prehodiť. Povedzme, že prvých niekoľko kostýmov na oboch lanách nám zaberie šírku $x$. Na zvyšok potom ostáva $2l - x$ lana. V každej iterácii vylepšovania potrebujeme zistiť, či vieme rozvešať zvyšné kostýmy. Naša úloha sa teda zjednodušuje na to, že kostýmy vešiame iba na jedno lano a chceme zistiť, či sa dajú povešať na dve laná s dĺžkou $l - x$. V nasledovnej časti riešime, iba to, ako zodpovieme túto otázku.
Najprv môžeme vyskúšať priamočiary prístup. Pre každé $k$ vezmeme zvyšných $n-k$ kostýmov a vyskúšame všetky možné rozdelenia na prvé a druhé lano. Pre každú podmnožinu spočítame sumu šírok. Ak sa táto suma zmestí do zostávajúcej kapacity prvého lana a zvyšok sa zmestí do kapacity druhého lana, našli sme platné rozdelenie. Pre každé z $n$ možných umiestnení hranice generujeme všetky podmnožiny, čo trvá $O(2^{n-k})$. Celkovo teda $O(n \cdot 2^n)$.
Problém rozdelenia zvyšných kostýmov na dve laná je podobný známemu problému batohu (Knapsack problem). Môžeme použiť dynamické programovanie. Budeme mať tabuľku $dp$, kde $dp[i][j]$ bude true práve vtedy, ak je možné zaplniť šírku $j$ na prvom lane len s prvými $i$ kostýmami. Túto tabuľku vieme jednoduchým prechádzaním dopĺňať - $dp[i][j] = dp[i-1][j] \lor dp[i-1][j-w_i]$. Znamená to, že buď sme danú šírku $j$ na prvom lane dosiahli už s prvými $i-1$ kostýmami, alebo sme použili $i$-ty kostým so šírkou $w_i$ a potrebujeme doplniť šírku $j-w_i$ z $i-1$ kostýmov, čo už máme vypočítané. Je jasne vidno, že tabuľku si vieme vyplniť v čase $O(n \cdot l)$, keďže každý prvok tabuľky sa vypočíta v konštantnom čase. Odpoveď na otázku, či vieme kostýmy doplniť na dve laná bude, že prejdeme cez všetky dosiahnuteľné šírky na prvom lane (tie, kde $dp[n][j]$ je true) a zistíme, či sa šírka zostávajúcich kostýmov (tých, ktoré nie sú na oboch lanách a ani na prvom) zmestí na druhé lano. Celková časová zložitosť tohto riešenia je $O(n^2 \cdot l)$, lebo pre každý počet kostýmov na začiatku musíme spúšťať tento problém batoha nanovo.
Pamäťová zložitosť by bola $O(n \cdot l)$, lebo to je veľkosť našej tabuľky. Všimnime si, že na zistenie nového stĺpca potrebujeme len ten predošlý. Takže zvyšné si nemusíme pamätať, lebo ich už nebudeme potrebovať pri ďalších výpočtoch. To nám zníži pamäťovú zložitosť na $O(l + n)$, lebo si pamätáme jeden stĺpec tabuľky a usporiadané kostýmy.
V predošlom riešení sme pri každom pridaní kostýmu na obe laná znova počítali rozdeľovanie takmer tých istých kostýmov. Konkrétne pri každej iterácii sa odobral jeden kostým z jedného lana a prehodil na dve. To znamená, že počet kostýmov v “batohu” sa nám postupne odoberal. Keď sa pozrieme bližšie na problém batoha, tak by sa nám hodilo keby sa kostýmy postupne pridávali. Pridanie kostýmu nebude znamenať všetko prepočítať, ale dopočítať jeden stĺpec.
Skúsme teda otočiť poradie vonkajšieho cyklu - namiesto pridávania kostýmov na obe laná začneme s tým, že všetky už sú na oboch lanách, a budeme ich odoberať. To nám spôsobí, že pri batohu nám budú kostýmy pribúdať a po prehodení kostýmu stačí dopočítať jeden stĺpec. Takže sme sa posunuli z času $O(n^2 \cdot l)$ na $O(n \cdot l)$. Pamäť zostáva rovnaká ako v minulom riešení.
Korešpondenčný seminár z programovania zastrešuje občianske združenie Trojsten.
Trojsten, o.z.
FMFI UK, Mlynská dolina
842 48 Bratislava
Programátorská súťaž pre základoškolákov
Materiály a úlohy na výučbu programovania
Intenzívny programátorský zážitok v lete