You are on page 1of 6

HONI 2016/2017

4. kolo, 17. prosinca 2016.

Opisi algoritama
Zadatak Planovi Autor: Nikola Pintarić

Pri upisu je B​1​ manji od B​2​ pa ako vrijedi da B​1​ nije tijekom obaveza onda je to i rješenje. U
suprotnom je rješenje B​2​.

Programski kod (pisan u Python 3.x):


a1, a2 = map(int, input().split())
b1, b2 = map(int, input().split())

if (b1 >= a1 and b1 <= a2):


print(b2)
else:
print(b1)

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


Kategorija:​ ad-hoc

Zadatak Sreća Autor: Marija Gegić

Uočimo naprije da učenik koji je X-ti u redu očekuje da će dobiti točno X bombona. Kako
bismo odredili broj sretnih učenika, zapravo trebamo odrediti koliko postoji pozicija X u
zadanom nizu brojeva takvih da je X >= B​x ​. Jedan od načina na koji to možemo učiniti je da
svaki učitani broj uspoređujemo s njegovim rednim brojem (koji je to po redu učitani broj) te
povećavamo neki brojač ukoliko je učitani broj veći ili jednak njegovom rednom broju.
Vrijednost tog brojača nakon učitavanja svih N brojeva će biti traženo rješenje.

Potrebna znanja:​ for petlja


Kategorija:​ ad-hoc

Zadatak Bridž Autor: Branimir Filipović

Za rješavanje zadatka sve što je bilo potrebno je prebrojati koliko se puta pojavljuju znakovi
‘A’, ‘K’, ‘Q’, i ‘J’ u svim kartama K​i​ zajedno.

Traženo rješenje zadatka R dano je prema sljedećoj formuli:

R = 4 * A + 3 * K + 2 * Q + 1 * J

Pseudokod (pisan u Python 3.x):


n = int(input())
r = 0
for i in range(n):
s = input()

for j in s:
if j == 'A':
r += 4
elif j == 'K':
r += 3
elif j == 'Q':
r += 2
elif j == 'J':
r += 1

print (r)

Potrebna znanja:​ for petlja, rad sa stringovima


Kategorija:​ ad-hoc

Zadatak Kartomat Autor: Nikola Dmitrović

Prvo ćemo u niz učitati sva odredišta koja su zadana, a u posebnu varijablu prvih nekoliko
početnih znakova odabranog odredišta. Za svako mjesto/odredište koje nam je zadano i koje
smo mogli birati provjerimo počinje li tim nizom znakova. Ako počinje, odredimo slovo koje
može biti birano u sljedećem odabiru tj. slovo koje slijedi nakon zadanog niza znakova. Tako
određeno slovo dodamo na prethodna odabrana slova.

Za kraj u stringu ‘'***ABCDEFGHIJKLMNOPQRSTUVWXYZ***'’ svako slovo koje nismo


pronašli da može biti birano u sljedećem odabiru slova zamijenimo znakom ‘*’. Još je
potrebno pažljivo ispisati slova abecede u paketima po osam znakova.

Programski kod (pisan u Python 3.x):


N = int(input())

L = []
for i in range(N):
L += [input()]

odrediste = input()

prva_sljedeća_slova = ''
d = len(odrediste)

for i in range(N):
if L[i][:d] == odrediste:
prva_sljedeća_slova += L[i][d]

tipkovnica = '***ABCDEFGHIJKLMNOPQRSTUVWXYZ***'

for i in tipkovnica:
if i not in prva_sljedeća_slova:
tipkovnica = tipkovnica.replace(i,'*')

print(tipkovnica[0:8],tipkovnica[8:16],tipkovnica[16:24],tipkovnic
a[24:32], sep = '\n')

Potrebna znanja:​ stringovi


Kategorija:​ ad-hoc

Zadatak Kas Autor: Ivan Paljak

Cijeli se zadatak svodi na ispravnu podjelu novčanica kako je opisano u prvom odlomku
teksta zadatka. Dakle, potrebno je raspodijeliti ​neke od ​N novčanica u dva dijela tako da je
svota novca u svakom od dijelova jednaka, a ukupna svota novca na neiskorištenim
novčanicama je najmanja moguća.

Naivnim algoritmom mogli smo isprobati sve moguće kombinacije i tako ostvariti 50%
bodova na zadatku. Budući da svaka novčanica može pripasti ili Kiletu ili Pogiju ili nijednom
od njih, zaključujemo da vremenska složenost takvog algoritma iznosi O(3​n​).

Slično prethodnom toku misli, zamislimo da prolazimo redom po novčanicama i pokušavamo


ih dodijeliti Kiletu, Pogiju ili nijednom od njih. U nekom koraku takvog algoritma trebali bismo
znati koju novčanicu trenutno obrađujemo te koliku svotu novca su do sada skupili Kile i
Pogi. Neka je f (k, a, b) funkcija koja nam vraća najveću svotu novca koju Kile i Pogi mogu
skupiti ako nakon (k − 1) raspoređenih novčanica Kile ima a kn, a Pogi ima b kn.
Očigledno, f (k, a, b) = max{f(k + 1, a, b), f(k + 1, a + c[k], b), f(k + 1, a, b + c[k])} gdje c[k]
označava vrijednost k-te novčanice. Naravno, f (n + 1, a, b) iznosi 0 ako je a različit od b ,
odnosno a u suprotnom. Implementiramo li takvo rješenje koristeći tehniku dinamičkog
programiranja ostvarili smo algoritam složenosti O(ns2) , gdje s predstavlja sumu svih
novčanica.

Za osvajanje svih bodova potrebno je primijetiti da je u stanju dinamike dovoljno čuvati


apsolutnu vrijednost razlike svota novca koje su do sada skupili Kile i Pogi. Iz stanja
f (k, diff) radimo prijelaze u stanja f (k + 1, diff) , f (k + 1, diff + c[k]) i f (k, |diff − c[k]|) koji redom
predstavljaju preskakanje novčanice, dodjelu novčanice onome koji trenutno ima više
novaca te dodjelu novčanice onom koji trenutno ima manje novaca. Budući da imamo O(ns)
stanja, a prijelaz je konstantan, zaključujemo da se radi o algoritmu vremenske složenosti
O(ns) što je dovoljno za osvajanje svih bodova.

Iako se u zadatku to nije tražilo, savjetujemo da osmislite algoritam koji rješava ovaj zadatak
sa dodatnim ograničenjem memorije na 32 MB.

Potrebna znanja:​ dinamičko programiranje, optimizacija stanja


Kategorija:​ dinamičko programiranje

Zadatak Rekonstruiraj Autor: Adrian Satja Kurdija

Znajući da brojevi na ulazu imaju najviše pet decimalnih mjesta, množeći ih sa 100 000
pretvaramo ih u prirodne brojeve. Mirkovi nepoznati brojevi (također pomnoženi sa 100 000)
očito su neki od djelitelja dobivenih brojeva.

Dakle, za svaki od dobivenih brojeva, redom od najmanjeg, treba pronaći neki njegov djelitelj
koji je “dobar”, tj. može pripadati traženom skupu Mirkovih početnih brojeva. Djelitelj je
“dobar” ako se svi njegovi višekratnici u zadanome intervalu nalaze među zadanim
brojevima, što je moguće efikasno provjeriti.

Kako efikasno pronalazimo djelitelje broja X? Tako da prolazimo po svim njegovim


potencijalnim djeliteljima ​d od 2 do korijen(X) i provjeravamo jesu li X i X/​d djelitelji broja X.
Potom ćemo sortirati pronađene djelitelje i pronaći najmanji onaj koji je “dobar”. Međutim,
ako je broj X već “objašnjen” (dobiven kao višekratnik) nekim od prethodno odabranih
djelitelja, nećemo ni tražiti njegove druge djelitelje jer želimo minimizirati veličinu odabranog
skupa.

Potrebna znanja:​ decimalni brojevi, traženje djelitelja


Kategorija:​ teorija brojeva, greedy

Zadatak Rima Autor: Domagoj Bradač

Dobivene riječi ćemo okrenuti, te ih ubaciti u prefiksno stablo. Dvije riječi se rimuju ako i
samo ako je čvor koji predstavlja jednu od njih roditelj čvora koji predstavlja drugu ili ta dva
čvora imaju zajedničkog roditelja.

Promatrajmo neki niz u kojem se svake dvije susjedne riječi rimuju. Niz počinje na nekom
čvoru u stablu te čini nekoliko koraka prema gore ili u stranu, tj. prema roditelju ili čvorovima
sa zajedničkim roditeljem. Jednom kad napravi korak prema dolje, tj. prema djetetu, može
raditi samo korake u stranu ili prema dolje.
Sada zadatak možemo riješiti dinamičkim programiranjem. Za svaki čvor izračunamo najveći
broj čvorova u njegovom podstablu koje možemo proći krećući se samo gore ili dolje
(svejedno je), te u stranu. Za svaki čvor možemo pretpostaviti da je on najviši čvor u nizu, te
za njega pronaći dva najduža puta među njegovom djecom. Također trebamo paziti da
brojimo sve čvorove sa zajedničkim roditeljem. Za detalje pogledajte službenu
implementaciju.

Ukupna složenost je O(S), pri čemu je S suma duljina svih stringova.

Potrebna znanja:​ trie


Kategorija:​ stringovi, dinamičko programiranje

Zadatak Osmosmjerka Autor: Adrian Satja Kurdija

Fiksirajmo neki od blokova osmosmjerke. Možemo pretpostaviti da uvijek biramo početno


slovo baš iz tog bloka. Vjerojatnost se definira kao broj povoljnih odabira podijeljen s brojem
mogućih odabira. Broj mogućih odabira dviju riječi jednak je kvadratu broja mogućih odabira
jedne riječi, a on je jednak broju mogućnosti za početno polje (M puta N) pomnoženom s
brojem mogućih smjerova (8).

Veći je problem izračunati broj povoljnih odabira, tj. onih koji daju dvije jednake riječi. Ako se
neka riječ R pojavljuje na X mogućih mjesta, tada je jednom možemo pročitati na X načina, a
dvaput na X*X načina. Potrebno je, dakle, zbrojiti kvadrate svih ovih brojeva X, tj.
identificirati različite moguće riječi i znati na koliko se načina svaka od njih može pročitati.

To činimo koristeći ​hash funkciju koja string pretvara u broj radi lakše usporedbe. Hashiranje
je ovdje problematično zbog veličine broja K (duljine riječi). U slučaju gdje je M = N možemo
primijetiti da su sve riječi periodične s periodom N pa je dovoljno gledati duljinu K % N, a svi
se takvi hashevi mogu dovoljno brzo izračunati koristeći ​rolling hash pristup. To rješenje
nosilo je 100 bodova.

U općem slučaju možemo, kao pomoćni korak, izračunati sve hasheve čija je duljina
potencija broja 2, tako da hash duljine 2^(i+1) dobivamo kombinacijom dvaju hasheva duljine
2^i. Potom K zapišemo binarno, kao zbroj potencija broja 2, i time hash svake moguće riječi
dobivamo kao kombinaciju nekoliko izračunatih hasheva. Sortiranjem skupa hasheva
mogućih riječi možemo saznati koliko se puta svaki od njih pojavljuje i tako doći do tražene
vjerojatnosti.

Potrebna znanja: ​hash, definicija vjerojatnosti


Kategorija: ​stringovi

You might also like