Zadanie

Ak máte akékoľvek otázky ohľadom tejto úlohy, napíšte Miškovi na [email protected]

Táto úloha je o dátovej štruktúre - Halda. Ak nevieš čo Halda je, môžeš si o nej viac prečítať v študíjnom texte o Halde.

Po tom ako v Krtkove požiadali o pomoc s naprogramovaním čakárne na očkovanie, začali sa programátori a programátorky zo širokého okolia predbiehať, kto túto čakáreň naprogramuje. Keďže čakáreň môže byť iba jedna, musia sa rozhodnúť, kto z nich ju má naprogramovať. To však nie je jednoduché, keďže takýto človek musí mať dostatok skúseností, no zároveň nemôže mať príliš veľa skúseností, lebo potom by cena systému prekročila rozpočet Krtkova.

Úloha

Postupne budú prichádzať ponuky od programátorov a programátoriek s rôznym počtom skúseností. Vašou úlohou bude po každej ponuke zistiť, koľko skúseností má vhodný kandidát. Vhodný kandidát je ten, ktorý má stredne veľa skúseností. To znamená, že polovica kandidátov je horšia a polovica lepšia. Presnejšie sa tejto strednej hodnote hovorí medián. Formálne, medián \(M\) prvkov je taký prvok, ktorý je po utriedení vzostupne na \(\lceil M/2 \rceil\)-tom1 mieste.

Formát vstupu

Na prvom riadku vstupu je jedno číslo \(1 \leq n \leq 10^5\) - počet programátorov. Na nasledujúcich \(n\) riadkoch sa nachádza vždy jedno číslo, \(0 \leq s_i < 10^9\), počet skúseností programátorov v poradí, v ktorom sa ponúkajú naprogramovať čakáreň.

Formát výstupu

Vypíšte \(n\) riadkov, v \(i\)-tom počet skúseností vhodného kandidáta po \(i\) ponukách.

Príklad

Input:

10
158
182
112
92
52
201
193
167
177
167

Output:

158
158
158
112
112
112
158
158
167
167

Input:

10
83
36
16
58
65
80
52
83
73
71

Output:

83
36
36
36
58
58
58
58
65
65

  1. Hranaté zátvorky okolo \(M/2\) znamenajú hornú celú časť, teda prvé celé číslo, ktoré je väčšie alebo rovné \(M/2\). Napríklad horná celá časť \(3.2\) je \(4\) a horná celá časť \(8\) je \(8\).↩︎

V tejto úlohe potrebujeme udrživiať čísla tak trochu usporiadané. To sa dá robiť pomocou rôznych pomerne zložitých dátových štruktúr. My si ukážeme riešenie, ktoré využíva iba haldu. To ako, prečo, ako rýchlo… halda funguje bolo v študíjnom texte o Halde.

Riešenie s jednou haldou

Vieme že táto úloha má byť o halde. Skúsime sa teda zamyslieť ako by sme vedeli haldu využiť. Vlastnosť haldy je, že vieme jednoducho nájsť ktorý prvok je najväčší (prípadne najmenší). O ostatných prvkov však nevieme skoro nič povedať. Jedno z riešení, by teda mohlo byť že si budeme skúseností programátorov držať v halde. Potom v každom kroku z našej haldy vyberieme polovicu prvkov, zistíme hodnotu mediánu, a následne všetky prvky naspať vrátime. Takéto riešenie je však veľmi pomalé.

Optimalizácia predošlého riešenia

Môžeme si všimnúť, že v predošlom riešení robíme veľa “roboty” zbytočne. Na konci každého kroku najprv polovicu prkov vrátime len na to, aby sme ich neskôr skoro všetky znovu z haldy vybrali. Toto je samozrejme zbytočné. Nechajme teda polovicu prvkov stále vybranú.

Máme teda prvky rozdelené na dve rovnako veľké skupiny, v jednej sú prvky väčšie ako medián, v druhej prvky menšie alebo rovné ako medián. Teraz vieme jednoducho vypísať hodnotu mediánu, je to totiž najväčší prvok z druhej skupiny. Ako si však vieme udržiavať tieto skupiny, keď budú pribúdať nový programátori?

Keď nám príde nové číslo(nový programátor) najprv zistíme do ktorej skupiny by malo patriť. Tak ho tam teda pridáme. Teraz by mohol nastať problém, a to, že v tejto skupine je teraz veľa prvkov. Vtedy treba jeden prvok presunúť do druhej skupiny (môžete si premyslieť, prečo určite stačí presunúť jeden prvok). Prvok, ktorý musíme presunúť je ten najväčší/najmenší z danej skupiny, aby stále platilo, že skupiny sú rozdelené podľa veľkosti. Keďže z času na čas potrebujeme zistiť, a vybrať najväčší resp. najmenší prvok, budeme si naše skupiny ukladať ako haldy.

Zhrnutie

Budeme mať teda dve haldy, jednu maximovú a jednu minimovú. V maximovej bude polovica(zaokrúhlené nahor) všetkých prvkov, a budú to tie menšie. V minimovej zase budú tie väčšie prvky. Vždy keď dá svoju ponuku nový programátor najprv ho pridáme a upravíme skupiny, a potom jednoducho nájdeme vhodného programátora.

Časová zložitosť

V každom kroku, teda vždy keď pridávame nejakého programátora spravíme niekoľko operácií s haldami. Každá táto operácia nám zaberie najviac \(O(log n)\), a teda keďže týchto krokov spravíme \(n\), celková zložitosť bude \(O(nlog n)\).

Implementácia

V kóde využijeme to, že haldu niekto pred nami už naprogramoval. V c++ využijeme priority_queue.

#include <iostream>
#include <queue>

using namespace std;

int main()
{
    int n;
    cin >> n;

    priority_queue<int, vector<int>, greater<int>> minimova;
    priority_queue<int, vector<int>, less<int>> maximova;

    for(int i = 0; i < n; i++)
    {
        int x;
        cin >> x;

        if(maximova.size() == 0 || x <= maximova.top())
        {
            maximova.push(x);
        }
        else minimova.push(x);

        if(maximova.size() < minimova.size())
        {
            maximova.push(minimova.top());
            minimova.pop();
        }
        else if(maximova.size() >= minimova.size() + 2)
        {
            minimova.push(maximova.top());
            maximova.pop();
        }

        cout << maximova.top() << endl;
    } 
    
    return 0;
}

Diskusia

Tu môžte voľne diskutovať o riešení, deliť sa o svoje kusy kódu a podobne.

Pre pridávanie komentárov sa musíš prihlásiť.