Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

HONI 2016/2017

2. kolo, 5. studenog 2016.

Opisi algoritama
Zadatak Oči Autor: Mihael Liskij

Zadatak zahtijeva poznavanje osnovnih matematičkih operacija, konkretno zbrajanje,


oduzimanje i dijeljenje. Pažljivim čitanjem zadatka dolazimo do sljedeće formule:

gdje su U, O i L oznake iz teksta zadatka.

Potrebna znanja: osnovne matematičke operacije


Kategorija: ad-hoc

Zadatak Pikado Autor: Luka Barišić

Potrebno je pomnožiti brojeve S i K kako bi se dobio ukupan broj bodova koje je Mirko
osvojio u bacanju.
Nakon toga se rezultat bacanja uspoređuje s brojem N i ovisno o rezultatu usporedbe te
posebnim uvjetima navedenima u zadatku ispisuje se odgovarajuća poruka. Konkretno, ako
je rezultat bacanja strogo manji od N-1 ispisuje se ocjena "Dobro", ako je rezultat točno N-1
ili je veći od N ispisuje se ocjena "Lose", a ako je rezultat točno N potrebno je dodatno
provjeriti vrijednost učitanog broja K. Ako je K jednak 2 tada je sukladno uvjetima zadatka,
Mirko ostvario najbolje moguće bacanje i ocjena je "Odlicno", u protivnom ocjena je "Lose".

Potrebna znanja: višestruka if provjera, if-else konstrukcija


Kategorija: ad-hoc

Zadatak Go Autor: Branimir Filipović

Kako bi se izračunao Ei, tj. broj Pokémona vrste Pi koje Mirko može evoluirati, potrebno je
implementirati algoritam opisan sljedećim pseudokodom:

Ei = 0
dok je Mi >= Ki :
Mi = Mi - Ki + 2
Ei = Ei + 1

Alternativno, može se koristiti i sljedeća formula:

Zašto je ta formula dobra ostavljamo čitateljima za vježbu.

Sada, kada imamo izračunate sve Ei , sve što je potrebno u zadatku je ispisati zbroj svih Ei ,
te prvog Pokémona Pi koji ima najveći Ei.
Pseudokod (pisan u Python 3.x):
N = int(input())

maks = ukupno = 0

for i in range(N):
pokemon = input()
Ki, Mi = map(int, input().split())

evoluiraj = 0
while Mi - Ki >= 0:
evoluiraj += 1
Mi -= Ki
Mi += 2

ukupno += evoluiraj

if evoluiraj > maks:


maks = evoluiraj
koji = pokemon

print(ukupno)
print(koji)

Potrebna znanja: osnovne matematičke operacije, for-petlje, algoritam za nalaženje


maksimuma
Kategorija: ad-hoc

Zadatak Tavan Autor: Stjepan Požgaj

Za 60% bodova tinta je razlivena po samo jednom slovu pa je dovoljno abecedno sortirati
slova koja bi ga mogla zamijeniti i zamijeniti ga X-tim po redu. Ovo je dobar primjer zadatka
u kojem se na jednostavan način može dobiti velik broj bodova.

Postoji više rješenja kojima je moguće dobiti sve bodove. Jedno od njih je da broj X - 1
pretvorimo u broj u brojevnom sustavu s bazom K. Da bismo lakše implementirali dopunimo
ga nulama s prednje strane tako da ima M znamenki. Neka su znamenke dobivenog broja
redom a1, a2, a3 ,..., am, tada je i-to nepoznato slovo potrebno zamijeniti ai-tim slovom u
sortiranom poretku slova koje bi mogla zamijeniti i-to slovo (slova u sortiranom poretku su 0-
indeksirana).

Vremenska složenost ovog rješenja je O(M * K lg K).

Potrebna znanja: stringovi


Kategorija: ad-hoc
Zadatak Nizin Autor: Ivan Paljak

Analizirajmo najprije suboptimalna rješenja iz sekcije “BODOVANJE”.

Za 30% bodova zadatak se mogao riješiti iscrpnom pretragom, odnosno mogli smo na sve
moguće načine mijenjati niz i na kraju ispisati najmanji broj poteza koji nas je doveo do
rješenja. Budući da nad nizom duljine N možemo napraviti (N - 1) različitih poteza, a nakon
svakog se duljina niza smanji za točno 1, zaključujemo da ovo rješenje radi u složenosti
O(n!).

Promotrimo prvi i posljednji član niza. Budući da niz želimo u konačnici pretvoriti u
palindrom, prvi i posljednji član na kraju moraju biti jednaki. Ako trenutno nisu jednaki, očito
barem jednog od njih trebamo spojiti sa svojim susjedom. Ovakvo razmišljanje nas navodi
na rekurzivnu formulaciju rješenja. Neka f(l, r) označava najmanji broj poteza koji je potreban
da se elementi A[l], A[l+1], …, A[r - 1], A[r] pretvore u palindrom. Prema prethodnim
primjedbama,

l > r, f(l,r) = 0
l <= r, f(l,r) = min(1 + f(l+1, r), 1 + f(l, r - 1), f(l+1, r-1))

pri čemu masno otisnuti dio uzimamo u obzir samo ako je A[l] = A[r]. Primijetimo da je u
svakom koraku algoritma vrijednost A[l] jednaka sumi svih svojih prethodnika, a A[r] je
jednak sumi svih svojih slijedbenika, dok su elementi u sredini netaknuti. Koristeći dinamičko
programiranje, odnosno tehniku memoizacije, ovo rješenje implementiramo u složenosti
O(n^2) što je dovoljno za 60% bodova na zadatku. Implementacija ovog algoritma nalazi se
u datoteci nizin_n2.cpp.

Za osvajanje svih bodova potrebno je iskoristiti činjenicu da su svi brojevi u ulazu pozitivni.
Kao i u prošlom odlomku, zadatku pristupamo izvana prema unutra, a kad rješavamo neki
interval [l, r] razlikujemo par slučajeva:

Ako je A[l] = A[r], tada nije potrebno krajnje elemente spajati, već nastavljamo rješavati
interval [l+1,r-1].

Ako je A[l] < A[r], tada sigurno ne možemo profitirati spajanjem elemenata A[r] i A[r - 1] zato
što je njihova suma nužno veća od A[l] koji ne može ostati ne spojen. Dakle, spojit ćemo
elemente A[l] i A[l+1] te nastaviti s rješavanjem intervala [l + 1, r]. Analogno rješavamo slučaj
gdje A[l] > A[r].

Budući da ćemo u svakom koraku algoritma smanjiti interval koji rješavamo za najmanje 1,
dolazimo do zaključka da se radi o algoritmu složenosti O(n) koji će osvojiti sve bodove.

Potrebna znanja: Analiza složenosti, rastavljanje na slučajeve.


Kategorija: Ad-hoc
Zadatak Prosječni Autor: Mislav Balunović

Zadatak je moguće riješiti na mnogo različitih načina.

Jedno od mogućih rješenja je sljedeće:



● U prvi redak zapišimo brojeve 1, 2, . . . , − 1,
● Svaki sljedeći redak osim posljednjeg dobijemo tako da brojevima u prethodnom

retku dodamo
● Posljednji redak dobijemo na sljedeći način:
Za svaki stupac, ako su brojevi a1, …, an - 1 brojevi zapisani u tom stupcu dosad, u
n-ti redak u tom stupcu zapišemo broj n * an - 1 - (a1 + a2 + … + an - 1).
Time smo postigli da je prosjek tog stupca jednak upravo pretposljednjem broju u
stupcu.

Jedino što preostaje je uvjeriti se da se prosjek posljednjeg retka nalazi u tom retku. Čitatelju
ostavljamo za vježbu dokazati da je taj prosjek jednak upravo broju koji se nalazi u
posljednjem retku i pretposljednjem stupcu.

Potrebna znanja: aritmetika, kombinatorika


Kategorija: matematika

Zadatak Zamjene Autor: Mislav Balunović

Za rješenje zadatka koristit ćemo union-find strukturu podataka.

Opišimo prvo rješenje koje nije dovoljno brzo, ali će služiti kao motivacija za pravo rješenje.

Koje podatke moramo pamtiti u svakoj komponenti?


Svakoj komponenti odgovara neki skup pozicija.
Označimo sa PK multiskup svih vrijednosti koje se nalaze u nizu p na pozicijama koje se
nalaze u komponenti K, a sa QK multiskup svih vrijednosti koje se nalaze u sortiranom nizu q
na pozicijama koje se nalaze u komponenti K.
Kada spajamo komponente A i B u novu komponentu C, vidimo da je PC = PA ∪ PB i QC =
Q A ∪ Q B.

Ključno je primijetiti da niz možemo sortirati ako i samo ako za svaku komponentu vrijedi PK
= Q K.

Nakon toga, možemo uočiti kako nije potrebno pamtiti točne multiskupove brojeva nego
samo njihovu hash vrijednost. Ako se u multiskupu S broj 1 nalazi c1 puta, 2 c2 puta, …, n cn
puta onda definiramo hash skupa S kao:
h(S) = c1 * H + c2 * H2 + … + cn - 1* Hn - 1
Kada spajamo komponente, jednostavno zbrojimo hash vrijednosti originalnih komponenata.

No, dosad nismo spominjali kako odgovoriti na upit broj 4.


Nas zapravo zanima broj parova takvih da vrijedi:

PA + PB = QA + QB ( gdje sada P i Q označavaju hash vrijednosti)



PA - QA = - ( PB - QB )

Sada vidimo kako na upit možemo odgovoriti održavajući mapu M gdje nam M[d] kaže koliko
postoji čvorova čija komponenta ima razliku P - Q točno d.

Vremenska složenost ovog rješenja je O(Q lg N).


Za implementacijske detalje pogledajte službeno rješenje.

Potrebna znanja: union-find, hash


Kategorija: teorija grafova

Zadatak Burza Autor: Domagoj Bradač

Ukorijenit ćemo stablo u čvoru broj 1. Za početak primijetimo da će se nakon i-tog poteza
novčić nalaziti u nekom čvoru na dubini i. Očito će Danielu biti optimalno označiti neki čvor
na dubini i u i-tom potezu. Sada možemo preformulirati zadatak: postoji li skup čvorova, od
kojih niti jedan nije korijen, na različitim dubinama tako da ne postoji čvor na dubini k čiji niti
jedan predak nije označen?

Čvorove na dubini k nazvat ćemo listovima. Možemo ukloniti sve čvorove koji u svojem
podstablu nemaju niti jedan list(ovo uključuje i čvorove na dubini većoj od k) jer Daniel
sigurno pobjeđuje ako se novčić nalazi u takvom čvoru. Sada su listovi zaista listovi u
dobivenom stablu. Odsada ćemo promatrati samo stabla dobivena nakon uklanjanja
nepotrebnih čvorova.

Analizirajmo sljedeći jednostavni algoritam: u i-tom potezu označit ćemo neki čvor na dubini i
te ukloniti sve čvorove u njegovom podstablu. Time smo u i-tom koraku uklonili barem k - i +
1 čvorova, što znači da smo u k koraka uklonili barem čvorova. To znači da u slučaju
≤ znamo da Daniel može sigurno pobijediti.

To nam je motivacija da pronađemo i bolju ogradu, a za to ćemo koristiti sljedeći algoritam:


za početak, za svaki čvor v definirat ćemo f(v) kao najmanju dubinu na kojoj neki potomak
od v ima više od jednog djeteta. U i-tom potezu označit ćemo čvor v na dubini i s najmanjom
vrijednošću f(v) te iz stabla ukloniti sve njegove potomke.

Indukcijom ćemo dokazati da ovim algoritmom Daniel pobjeđuje ako vrijedi ≥ . Za


potrebe indukcije, nećemo promatrati jedno stablo, nego više njih, koji će zapravo
predstavljati podstabla originalnog stabla. S d ćemo označiti najmanji broj za koji vrijedi da
nakon d poteza postoji neuklonjeni čvor v za koji je f(v) = d. Ako ne postoji broj d za koji to
vrijedi, to znači da smo ukupno napravili k poteza, a u svakom smo uklonili barem k čvorova
(brojimo potomke, ali i pretke tog čvora koji su svi međusobno različiti). No, to znači da nije
preostao nijedan čvor. S druge strane, ako je d neki prirodan broj, to znači da smo u svakom
od d poteza uklonili barem 2k - d čvorova (najgori slučaj je za f(v) = d, a tada smo uklonili d
čvorova do dubine d te barem 2(k - d) ispod dubine d jer se stablo grana na toj dubini.
Pogledajmo što nam je preostalo: nova šuma stabala ima najviše n2 ≤ n - d * (2k - d)
čvorova, te nova dubina do koje novčić ne smije doći je k2 = k - d. Sada iz uvjeta ≥ ,
slijedi ≥ , pa dalje tvrdnja vrijedi po pretpostavci indukcije.

Pokazali smo da za ≥ Daniel uvijek pobjeđuje, što znači da nam preostaje riješiti slučaj
kada to ne vrijedi. No, tada je < 20, pa ne trebamo tražiti polinomijalno rješenje!

Označimo listove redom kako se pojavljuju u dfs obilasku stabla. Tada svaki čvor u stablu
pokriva neki interval listova, tj. to su oni koji se nalaze u njegovom podstablu. Zadatak ćemo
riješiti dinamičkim programiranjem. Stanje ćemo prikazati brojem i bitmaskom veličine k
bitova. Neka dp[T][mask] označava možemo li pokriti prvih T listova tako da smo označavali
čvorove samo na dubinama koje pišu u mask. Prijelaz je jednostavan, u nekom trenutku
možemo odabrati bilo koji od najviše k čvorova kojima prvi list u podstablu ima oznaku T.
Primijetimo da, iako za neku poziciju T, prijelaz može biti k različitih čvorova, svaki čvor će
se u prijelazu pojavljivat na samo jednoj poziciji.

Ukupna složenost je 2√ ∗ .

Potrebna znanja: bitmaske, dinamičko programiranje


Kategorija: ad-hoc, dinamičko programiranje

You might also like