Pamätáš si ešte na intergalaktického zlodeja Okäňa “Bzučiaka” Hruškového?
…
Kým si si pozeral hyperlinky v jeho mene, Okäň “Bzučiak” Hruškový ukradol priamo spod tvojho nosa príbeh tejto úlohy. Neostáva ti nič iné, než vyriešiť nezarozprávkovanú úlohu o bodoch v rovine…
V rovine je na celočíselných súradniciach (nie nutne na rôznych) $n$ bodov typu A očíslovaných $0$ až $n-1$ a $n+1$ bodov typu B očíslovaných $0$ až $n$. Bod typu A sa dá aktivovať, čím sa odstráni on, a jemu najbližší bod typu B v euklidovskej vzdialenosti (ak je viacero bodov typu B rovnako vzdialených, vieme si vybrať, ktorý z nich bude odstránený). Rozhodnite, či existuje spôsob, ako každý bod typu A práve raz aktivovať tak, aby jediný bod typu B, ktorý na konci zostane neodstránený, bol ten s číslom $n$, a ak áno, tento postup nájdite.
V prvom riadku vstupu je číslo $n$ ($1 \leq n \leq 3000$) udávajúce počet bodov typu A.
Na $i$-tom z ďalších $n$ riadkov sú vždy dve medzerou oddelené celé čísla - súradnice $i$-teho bodu typu A, vždy ležiace v intervale $-10^9$ až $10^9$. Na $i$-tom z ďalších $n+1$ riadkov potom sú opäť dve medzerou oddelené celočíselné súradnice z intervalu $-10^9$ až $10^9$ - súradnice $i$-teho bodu typu B.
V sadách platia nasledujúce obmedzenia:
| Sada | 1, 2 | 3, 4 | 5, 6 | 7, 8 |
|---|---|---|---|---|
| $1 \leq n \leq$ | $10$ | $100$ | $1\,000$ | $3\,000$ |
Ak neexistuje spôsob, ako odstrániť všetky body typu B až na $n$-tý, vypíšte na jediný riadok výstupu slovo NIE.
Inak vypíšte na prvý riadok výstupu slovo ANO a potom na $n$ nasledujúcich riadkoch konštrukciu riešenia: vždy dve celé medzerou oddelené čísla $0 \leq a,b \leq n-1$ označujúce aktiváciu bodu typu A číslo $a$ a s ním odstránenie bodu typu B číslo $b$. Každý bod typu A musí byť aktivovaný práve raz, a v tom momente nesmie existovať žiaden neodstránený bod typu B, ktorý by k nemu bol bližšie než ten, ktorý ide táto aktivácia odstrániť.
4
1 1
1 -1
-1 1
-1 -1
0 0
0 -1
1 0
-1 0
0 1
ANO
0 2
2 3
1 1
3 0
Všetky body sú umiestnené v mriežke 3x3. Najprv odstránime body typu A v dvoch vrchných rohoch (s kladnou y súradnicou), a s nimi body typu B v stredoch ľavej a pravej strany (s nulovou y súradnicou). Potom už len odstránime zvyšné štyri body s nekladnou y súradnicou, a nakoniec zostane iba bod 4, ktorý je na súradniciach (0,1).
5
-1 1
1 1
-1 -1
1 -1
0 1
-1 1
1 1
-1 -1
1 -1
0 -1
0 0
NIE
Najjednoduchší spôsob, ako úlohu riešiť bez ohľadu na efektivitu, je hrubou silou. Konkrétne sa to dá stihnúť v $O(n^22^{2n})$ dynamickým programovaním - mám $O(2^{2n})$ stavov, podľa toho, ktoré body v rovine sú už odstránené. Pre každý takýto stav vypočítam, či je možné odstrániť všetky ostatné body, až na ten jeden, ktorý má na konci zostať neodstránený, a to tak, že vyskúšam aktivovať každý z $O(n)$ bodov typu A, ktoré zostávajú, v kombinácii s každým bodom typu B, čo sa dá s trochou predpočítania vzdialeností stihnúť v čase $O(n^2)$. Pokiaľ takto nájdem nejaký stav, ktorý vedie k riešeniu, uložím si, ktoré body treba aktivovať tak, aby som sa k riešeniu dostal. Na konci sa len pozriem na stav, kde žiadne body nie sú odstránené, a uvidím, či existuje cesta vedúca k riešeniu. Ak áno, konštrukciu zhotovím jednoducho tak, že budem po týchto odkazoch skákať, kým neodstránim všetky potrebné body.
Teraz sa zamyslime, ako túto úlohu riešiť omnoho efektívnejšie. Zabstraktnime najprv zadanie - máme nejaké body typu A. Každý takýto bod má nejaké vzdialenosti od každého bodu typu B. Uvedomme si, že vzdialenosti od bodov typu B, ktoré sú vzdialenejšie ako ten, ktorý nechceme odstrániť, sú irelevantné, pretože určite nebude možnosť ich nikdy odstrániť. Rovnako tak môžeme potom odignorovať aj vzdialenosť od toho bodu samotného, keďže ho nikdy nebudeme odstraňovať. Po takejto úvahe sme zadanie úlohy zjednodušili na nasledovný formát: každý z $n$ bodov typu A má niekoľko susedných bodov typu B (ktorých je tiež $n$), ktoré majú od neho rôzne vzdialenosti. Našou úlohou je odstrániť všetky takéto body postupne tak, že vždy s bodom typu A odstránime aj jeden zo susedných bodov typu B takých, že všetky bližšie body typu B sme už odstránili.
Teraz si uvedomme, že k riešiteľnosti takéhoto zadania poznáme očividnú nutnú podmienku - v grafe susedov musí existovať úplné párovanie. Tak môžeme zbehnúť algoritmus Hopcroft-Karp, ktorý nám nájde maximálne párovanie v bipartitnom grafe v čase $O(n^{2.5})$. Pokiaľ výsledné párovanie nebude úplné, môžeme hneď odpovedať NIE. Ale čo ak úplné bude? Môžeme skúsiť na základe tohto úplného párovania zostrojiť riešenie zadania…
Máme teda bipartitný graf, poznáme jeho úplné párovanie, a navyše máme pre každý bod typu A dané, ktoré body typu B sú s ním momentálne aktivovateľné (sú mu najbližšie z tých neodstránených). Pokiaľ niektorá dvojica v párovaní pozostáva z bodu typu A, a bodu typu B, ktorý je s ním aktivovateľný, môžeme oba odstrániť a pokračovať v konštrukcii zredukovaným prípadom. Komplikovanejší prípad je, keď ani jedna dvojica párovania nie je spolu aktivovateľná. Vtedy ale vieme párovanie upraviť tak, aby sa taká dvojica našla - pre každý bod typu A určíme nejaký bod typu B, s ktorým je aktivovateľný. Vždy to bude iný bod, než ten, s ktorým je spárovaný, preto určite vznikne nejaký cyklus, v ktorom sa striedajú hrany z párovania s hranami, ktoré spájajú dva spolu aktivovateľné body. Jednoducho párovanie upravíme tak, že invertujeme hrany podľa tohto cyklu - odstránime tie, ktoré v ňom sú, a pridáme tie zvyšné hrany tohto cyklu. Stále pôjde o úplné párovanie, a navyše budú zaručene existovať spárované spolu aktivovateľné body. Tento postup teda v čase $O(n^2)$ určite povedie ku správnemu riešeniu.
Vidíme teda, že ak úplné párovanie neexistuje, odpoveď je záporná, a ak existuje, určite vieme na jeho základe nájsť konštrukciu, a teda odpoveď je kladná. Časová zložitosť riešenia je $O(n^{2.5})$, a pamäťová stačí $O(n^2)$.
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