Documente Academic
Documente Profesional
Documente Cultură
Olimpiada - cls8
2022-12
PROBLEME DE INFORMATICĂ
date la olimpiade
OJI + ONI
ı̂n
2022-12-27
Dedication
( in ascending order! )
1
https://www.femalefirst.co.uk/books/carol-lynne-fighter-1034048.html
2
https://otiliaromea.bandcamp.com/track/dor-de-el
3
https://en.wikipedia.org/wiki/To_be,_or_not_to_be
4
https://www.youtube.com/watch?v=eMtcDkSh7fU
ii
Prefaţă
Stilul acestor cărţi este ... ca şi cum aş vorbi cu nepoţii mei (şi chiar cu mine ı̂nsumi!) ... ı̂ncercând
ı̂mpreună să găsim o rezolvare cât mai bună pentru o problemă dată la olimpiadă.
Ideea, de a scrie aceste culegeri de probleme date la olimpiadele de informatică, a apărut acum
câţiva ani când am ı̂ntrebat un student (care nu reuşea să rezolve nişte probleme foarte simple):
”Ce te faci dacă un elev, care ştie că eşti student şi că studiezi şi informatică, te roagă, din când ı̂n
când, să-l ajuţi să rezolve câte o problemă de informatică dată la gimnaziu la olimpiadă, sau pur
şi simplu ca temă de casă, şi tu, aproape de fiecare dată, nu ı̂l poţi ajuta? Eu cred că nu este prea
bine şi poate că ... te faci ... de râs!” Pe vremea mea (!), când eram elev de gimnaziu, un student
era ca un fel de ... zeu! Cu trecerea anilor am ı̂nţeles că nu este chiar aşa! Şi ı̂ncă ceva: nu am
reuşit să ı̂nţeleg de ce, atunci când cineva nu poate să rezolve corect o problemă de informatică
de clasa a 6-a, dată la olimpiada de informatică sau ca temă de casă, foloseşte această scuză: ”eu
nu am făcut informatică ı̂n liceu!” şi acest cineva este ”zeul” sau ”zeiţa” despre care vorbeam!.
5
Sunt convins că este important să studiem cu atenţie cât mai multe probleme rezolvate! . Cred
că sunt utile şi primele versiuni ale acestor cărţi ... ı̂n care sunt prezentate numai enunţurile şi
indicaţiile ”oficiale” de rezolvare. Acestea se găsesc ı̂n multe locuri; aici ı̂ncerc să le pun pe ”toate
la un loc”! Fiecare urcă spre vârf ... cât poate! Sunt multe poteci care duc spre vârf iar această
carte este ... una dintre ele!
Limbajul de programare se alege ı̂n funcţie de problema pe care o avem de rezolvat. Cu nişte
ani ı̂n urmă alegerea era mai simplă: dacă era o problemă de calcul se alegea Fortran iar dacă era
o problemă de prelucrarea masivă a datelor atunci se alegea Cobol. Acum alegerea este ceva mai
6 7
dificilă! :-) Vezi, de exemplu, IOI2020 şi IOI2019 , IOI2015 .
Cred că, de cele mai multe ori, este foarte greu să gândim ”simplu” şi să nu ”ne complicăm”
atunci când cautăm o rezolvare pentru o problemă dată la olimpiadă. Acesta este motivul pentru
care vom analiza cu foarte mare atenţie atât exemplele date ı̂n enunţurile problemelor cât şi
”restricţiile” care apar acolo (ele sigur ”ascund” ceva interesant din punct de vedere al algoritmului
8
de rezolvare!) .
Am ı̂nceput câteva cărţi (pentru clasele de liceu) cu mai mulţi ani ı̂n urmă, pentru perioada
2000-2007 ([29] - [33]), cu anii ı̂n ordine crescătoare!). A urmat o pauză de câţiva ani (destul de
mulţi!). Am observat că acele cursuri s-au ”ı̂mprăştiat” un pic ”pe net” ([48] - [56])! Încerc acum
să ajung acolo unde am rămas ... plecând mereu din prezent ... până când nu va mai fi posibil ...
aşa că, de această dată, anii sunt ı̂n ordine ... descrescătoare! :-)
”Codurile sursă” sunt cele ”oficiale” (publicate pe site-urile olimpiadelor) sau publicate pe alte
site-uri (dacă mi s-a părut că sunt utile şi se poate ı̂nvăţa câte ceva din ele).
Pentru liceu perioada acoperită este de ”azi” (până când va exista acest ”azi” pentru mine!)
până ı̂n anul 2000 (aveam deja perioada 2000-2007!).
Pentru gimnaziu perioada acoperită este de ”azi” până ı̂n anul 2010 (nu am prea mult timp
9
disponibil şi, oricum, calculatoarele folosite la olimpiade ı̂nainte de 2010 erau ceva mai ’slabe’ şi
iii
11
depăşeşte pragul ”exascale”! (1.102 Exaflop/s). ”Peta” a fost depăşit ı̂n 2008, ”tera” ı̂n 1997,
12
”giga” ı̂n 1972. . Pentru ce a fost mai ı̂nainte, vezi https://en.wikipedia.org/wiki/Li
st_of_fastest_computers.
13
O mică observaţie: ı̂n 2017 a fost prima ediţie a olimpiadei EJOI ı̂n Bulgaria şi ... tot
14
ı̂n Bulgaria a fost şi prima ediţie a olimpiadei IOI ı̂n 1989. Dar ... prima ediţie a olimpiadei
15
IMO (International Mathematical Olympiad) a fost ı̂n România ı̂n 1959. Tot ı̂n România s-au
ţinut ediţiile din anii 1960, 1969, 1978, 1999 şi 2018. Prima ediţie a olimpiadei BOI (Balkan
Olympiad in Informatics) a fost ı̂n România ı̂n 1993 la Constanţa. Prima ediţie a olimpiadei
CEOI (Central-European Olympiad in Informatics) a fost ı̂n România ı̂n 1994 la Cluj-Napoca.
Revenind la ... “culegerile noastre” ... mai departe, probabil, va urma completarea unor
informaţii ı̂n ”Rezolvări detaliate” ... pentru unele probleme numai (tot din cauza lipsei timpului
necesar pentru toate!). Prioritate vor avea problemele de gimnaziu (nu pentru că sunt mai ’uşoare’
ci pentru că ... elevii de liceu se descurcă şi singuri!). Acum, ı̂n martie 2022, am ı̂nceput şi
redactarea unei culegeri de probleme date la bacalaureat ı̂n ultimii câţiva ani (câţi voi putea!).
Îmi aduc aminte că exista o interesantă vorbă de duh printre programatorii din generaţia mea:
”nu se trage cu tunul ı̂ntr-o muscă” . Sensul este: nu se scrie un cod complicat dacă se
poate scrie un cod simplu şi clar! Asta ı̂ncerc eu ı̂n ”Rezolvări detaliate”.
Vom ı̂ncerca, ı̂mpreună, şi câteva probleme de ... IOI ... dar asta este o treabă ... nu prea
uşoară! Cred totuşi că este mai bine să prezint numai enunţuri ale problemelor date la IOI ı̂n
ultimii câţiva ani! (asta aşa, ca să vedem cum sunt problemele la acest nivel!). Cei care ajung
acolo sau vor să ajungă acolo (la IOI) sigur nu au nevoie de ajutorul meu! Se descurcă singuri!
”ALGORITMI utili la olimpiadele de informatică”, separat pentru gimnaziu şi liceu, sper să
fie de folos, aşa cum cred că sunt [1] - [28], [34] - [47], [57] - [83], ... şi multe alte cărţi şi site-uri!.
Ar fi interesant să descoperim noi ı̂nşine cât mai mulţi algoritmi ... ı̂n loc să-i ı̂nvăţăm pur şi
simplu!
O altă mică observaţie: ce am strâns şi am scris ı̂n aceste cărţi
se adresează celor interesaţi de aceste teme! Nu cârcotaşilor! Sunt
evidente sursele ”de pe net” (şi locurile ı̂n care au fost folosite) aşa
că nu sunt necesare ”ghilimele anti-plagiat”, referinţe şi precizări
suplimentare la fiecare pas!
Şi un ultim gând: criticile şi sfaturile sunt utile dacă au valoare!
Dacă sunt numai aşa ... cum critică lumea la un meci de fotbal ...
sau cum, pe bancă ı̂n parc, ”ı̂şi dă cu părerea” despre rezolvarea
problemelor economice ale ţării ... atunci ... !!!
16
”I’m only responsible for what I say, not for what you understand.”
Adrese interesante (rezultatele elevilor români):
https://stats.ioinformatics.org/halloffame/
https://stats.ioinformatics.org/tasks/
http://stats.ioinformatics.org/results/ROU
Adresele acestor cursuri:
https://www.scribd.com/user/550183580/Adrian-Răbâea
https://www.scribd.com/user/552245048/Adi-Rabaea
https://drive.google.com/drive/folders/1hC5PZuslCdS95sl37SW46H-qy59GRDGZ
Adrese utile (programe şcolare):
http://programe.ise.ro/Portals/1/Curriculum/Progr_Lic/TH/Informatica_teoretic_vocatio
nal_intensiv_clasa%20a%20IX-a.pdf
http://programe.ise.ro/Portals/1/Curriculum/Progr_Lic/TH/Informatica_teoretic_vocatio
nal_intensiv_clasa%20a%20X_a.pdf
http://programe.ise.ro/Portals/1/Curriculum/Progr_Lic/TH/Informatica_teoretic_vocatio
nal_intensiv_clasa%20a%20XI-a.pdf
17
”I want to thank God most of all because without God I wouldn’t be able to do any of this.”
Adrian R.
17
I.d.k.: ”I don’t know who the author is.”
v
Despre autor
18
nume: Răbâea Aurel-Adrian, 18.03.1953 - ...
vi
Cuprins
Prefaţă iii
Cuprins vii
Lista tabelelor xv
3 OJI 2020 17
3.1 datorii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.2 triunghi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4 OJI 2019 29
4.1 cate3cifre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.2 paralele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
vii
4.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5 OJI 2018 38
5.1 Cruce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.2 pal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
5.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6 OJI 2017 47
6.1 tablou . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
6.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
6.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
6.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.2 triunghiuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
6.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
7 OJI 2016 55
7.1 arma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
7.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
7.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
7.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
7.2 ks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
7.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
7.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
7.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
8 OJI 2015 66
8.1 dominant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
8.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
8.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
8.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
8.2 pavare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
8.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
8.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
8.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
9 OJI 2014 81
9.1 arrows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
9.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
9.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
9.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
9.2 tcif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
9.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
9.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
9.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
10 OJI 2013 95
10.1 maxp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
10.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
10.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
10.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
10.2 puncte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
10.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
10.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
10.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Index 355
Bibliografie 357
6.1 triunghiuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
9.1 arrows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
xiii
A.38 Execuţie test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
A.39 Rezultat execuţie test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
A.40 Fişiere apărute după compilare! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
A.41 Creare test02.cpp gol! + ¡dublu click¿ sau ¡Enter¿ . . . . . . . . . . . . . . . . . . 344
A.42 Lista programelor de utilizat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
A.43 Selectare Code::Blocks IDE pentru fişierele .cpp . . . . . . . . . . . . . . . . . . 345
A.44 Editare+Compilare+Execuţie pentru test02 . . . . . . . . . . . . . . . . . . . . . 345
A.45 Selectare tab ce conţine test01.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 346
4.1 cate3cifre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.2 pal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
xv
Lista programelor
xvi
11.1.1deal 100 ch.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
11.1.2deal 100 ct.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
11.1.3DEAL 100 em.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
11.2.1ozn stream100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
11.2.2ozn100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
11.2.3oznarbint100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
11.2.4oznEvenimente100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
12.1.1 ADUNSCAD.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
12.1.2 adrian1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
12.1.3 adrian2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
12.1.4 adrian3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
12.1.5 adrian4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
12.1.6 ADDSUBBK.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
12.1.7 ADUNSCADM.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
12.1.8 ADSC nodea.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
12.1.9 ADUNSC A.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
12.1.10 dana ad.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
12.1.11 vi adunscad.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
12.2.1 comp.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
12.2.2 alin.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
12.2.3 comp r.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
12.2.4 em.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
12.2.5 nodea.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
12.2.6 p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
12.2.7 vi comp.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
13.1.1 sol2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
13.1.2 sol3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
13.2.1 BERATO.PAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
13.2.2 BPREPRO.PAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
14.1.1proeminenta.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
17.1.1 criptografie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
17.2.1 drept.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
17.3.1 lumini.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
19.1.1 boatsAB.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
19.1.2 boatsCM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
19.1.3 boatsLC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
19.2.1 doilan.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
19.3.1 z.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
20.1.1 cercetasi ema.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
20.1.2 cercetasi2 RBalasa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
20.2.1 farma ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
20.2.2 farma raluca.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
20.2.3 lucia farma.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
20.3.1 stele ema 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
20.3.2 stele raluca.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
21.1.1 magicN2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
21.1.2 sursaGenerarePas1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
21.1.3 sursaGenerarePasC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
21.1.4 sursaNumarareBKT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
21.1.5 sursaPutereLogN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
21.1.6 sursaPutereN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
21.2.1 restaurare.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
21.2.2 restaurare stdio.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
21.2.3 restaurareBrut1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
21.2.4 restaurareLT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
21.3.1 2dist alin.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
21.3.2 sort2 distBrute1FNM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
21.3.3 sort2 distBSFBM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
21.3.4 sort2dist.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
21.3.5 sort2dist1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
21.3.6 sort2distBS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
21.3.7 sort2distFNM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
21.3.8 sort2distFNMDouble.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
21.3.9 sort2distFNMLongDouble.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
21.3.10 sort2gv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
22.1.1 cifre carmen ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
22.1.2 cifre ema.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
22.1.3 cifre GCC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
22.1.4 cifre sofia.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
22.2.1 solitar cp.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
22.2.2 solitar ema.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
22.3.1 tdrept ema n2logn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
22.3.2 tdrept ema ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
22.3.3 tdrept eman3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
22.3.4 tdrept emanlogn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
22.3.5 tdrept n sofia.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
22.3.6 tdrept n2logn sofia.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
24.1.1 alune.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
24.1.2 alune DT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
24.1.3 aluneBrute.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
24.1.4 aluneBrute1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
24.1.5 aluneBrute2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
24.1.6 aluneBruteParse.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
24.1.7 alunedanal.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
24.1.8 aluneS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
24.2.1 cubmin.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
24.2.2 cubminC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
24.2.3 cuburiBackMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
24.2.4 cuburiDL.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
24.2.5 cuburiDL20.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
24.2.6 cuburi-iv4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
24.2.7 cuburiStackMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
24.3.1 optim back100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
24.3.2 optim dinamica100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
24.3.3 optim0MN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
24.3.4 optim1MN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
24.3.5 optim2MN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
24.3.6 optim3MN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
24.3.7 optimDT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
24.3.8 optim-iv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
26.1.1 FLORE7.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
26.2.1 neuroni1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
26.2.2 neuroni2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
26.3.1 RAZE2.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
26.3.2 RAZE3.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
B.4.1exponentiere rapida1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
B.4.2exponentiere rapida2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
B.4.3exponentiere rapida3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
B.4.4exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
B.5.1exponentiere naiva MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
B.5.2exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
B.6.1secventa cod.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
Part I
1
Capitolul 1
OJI 2022
1.1 pelican
Problema 1 - Pelican 100 de puncte
Într-o minunată zi de primăvară, P răţuşte au ieşit la plimbare pe lac. Un pelican milităros,
care stătea pe mal, a decis să facă instrucţie cu nevinovatele raţe. Pentru aceasta, a cartografiat
imediat lacul şi l-a reprezentat ca o matrice cu N linii (numerotate de la 0 la N 1 de sus ı̂n jos)
şi N coloane (numerotate de la 0 la N 1 de la stânga la dreapta).
Astfel, poziţia oricărei raţe pe lac poate fi identificată prin
linia şi coloana pe care se află raţa. Raţele sunt orientate cu
faţa spre una dintre direcţiile Nord, Sud, Est, Vest. Pelicanul a
codificat direcţiile 1, 2, 3, 4 ca ı̂n figură.
Când pelicanul strigă: ”Comanda la mine!” raţele trebuie să
execute simultan cele K comenzi pe care le dă pelicanul. Comenzile pelicanului sunt codificate
astfel:
Cerinţe
Scrieţi un program care, cunoscând poziţia iniţială pe lac a celor P raţe şi succesiunea comen-
zilor pelicanului, determină poziţia finală a fiecărei raţe.
Date de intrare
2
CAPITOLUL 1. OJI 2022 1.1. PELICAN 3
Date de ieşire
Fişierul de ieşire pelican.out va conţine P linii. Pe linia i va fi scrisă poziţia celei de a i-a
raţe din fişierul de intrare (linia şi coloana separate printr-un singur spaţiu) după executarea ı̂n
ordine a celor K comenzi.
# Punctaj Restricţii
1 76 Pentru teste valorând 76 de puncte fişierul de intrare nu conţine comanda Z.
2 20 Pentru teste valorând 20 de puncte N 100, P 100 şi K1000.
3 36 Pentru teste valorând 36 de puncte N % 100, 1000P 5000 şi K 50000.
Exemple:
pelican.in pelican.out
534 24
112 44
231 23
314
A3
R3
A1
A3
Explicaţii:
Lacul are 5 linii şi 5 coloane. Pe lac există 3 raţe poziţionate ca ı̂n figură.
Pelicanul dă 4 comenzi pe care toate cele 3 raţe le execută ı̂n ordine.
Raţele execută comanda A 3
CAPITOLUL 1. OJI 2022 1.1. PELICAN 4
o
Raţele execută comanda R 3 (se rotesc cu 270 ı̂n sens orar)
Propusă de: prof. Nistor Moţ, şcoala Gimnazială ”Dr. Luca”, Brăila
Problema admite mai multe abordări. O posibilă abordare este de a citi succesiv comenzile
pelicanului şi de a executa fiecare comandă pentru fiecare dintre cele P raţe. Este evident o abor-
dare corectă, dar care va depăşi timpul de execuţie. Totuşi această abordare poate fi ı̂mbunătăţită
bazându-ne pe următoarele observaţii:
(1) Dacă ı̂n fişierul de ieşire există o comandă Z, atunci vom executa această comandă pentru
toate raţele. Dacă există mai multe comenzi Z, doar ultima contează, deci doar aceasta va fi
executată. Toate comenzile A până la comanda Z executată pot fi ignorate, doar comenzile
R contează. În plus, observăm că nu trebuie să executăm fiecare comandă R separat, putem
cumula numărul de grade şi putem executa o singură rotaţie finală pentru toate raţele.
(2) Ideea de cumulare poate fi utilizată mai departe. Pentru a reduce numărul de comenzi
executate de toate raţele, putem lucra pe secvenţe de comenzi A, respectiv secvenţe de
comenzi R. Pentru fiecare secvenţă de comenzi identice vom cumula nr şi la finalul secvenţei
executăm o deplasare/rotaţie cumulată pentru toate raţele. Desigur, pentru deplasări lucrăm
modulo N , iar pentru rotaţii lucrăm modulo 4.
Aceste observaţii pot conduce la un punctaj bun, dar pentru 100 de puncte trebuie observat că
două raţe care au iniţial aceeaşi orientare se vor deplasa ”solidar”, adică păstrează aceeaşi poziţie
relativă.
Dacă avem două raţe cu aceeaşi orientare ı̂n poziţiile iniţiale i1 , j1 şi i2 , j2 , comanda A nr
va duce ambele raţe ı̂n poziţii care păstrează aceeaşi distanţă ı̂ntre ele.
De exemplu, pentru orientarea Sud cele două raţe vor ajunge ı̂n poziţiile i1 nr, j1 şi respectiv
i2 nr, j2 . Această observaţie ne permite să efectuăm comenzile doar cu 4 raţe ”virtuale” ce
pornesc din poziţia 0, 0 având orientarile N, E, S, respectiv V.
CAPITOLUL 1. OJI 2022 1.2. STRIPS 5
La final, pentru fiecare raţă vom determina poziţia sa finală ı̂n funcţie de poziţia raţei virtuale
care avea iniţial aceeaşi orientare cu raţa respectivă. Dacă raţa virtuală ajunge ı̂n poziţia finală
x, y , atunci o raţă care iniţial avea aceeaşi orientare cu cea virtuală şi era plasată ı̂n poziţia
i1 , j1 va ajunge ı̂n poziţia finală i1 x, j1 y .
Evident, toate deplasările se fac modulo N . La fel, rotaţiile se execută modulo 4.
Mai mult, dacă dorim, putem renunţa la a calcula poziţia finală pentru toate cele 4 direcţii şi
să efectuăm mutările doar pentru o singură direcţie, celelalte putând fi deduse prin rotaţii cu câte
90 de grade. De exemplu, considerând că o raţă ce porneşte din poziţia 0, 0 având orientarea
iniţială N ajunge ı̂n poziţia finală x, y , atunci raţa din pozitia i1 , j1 ajunge:
dacă are orientarea iniţială N , ı̂n poziţia finală i1 x, j1 y
dacă are orientarea iniţială S, ı̂n poziţia finală i1 x, j1 y
dacă are orientarea iniţială E, ı̂n poziţia finală i1 y, j1 x
dacă are orientarea iniţială V , ı̂n poziţia finală i1 y, j1 x.
1.2 strips
Problema 2 - Strips 100 de puncte
Ana şi Bogdan au inventat un nou joc, pe care l-au denumit Strips. Este un joc de strategie,
dar şi de antrenare a memoriei, deoarece se joacă pe o tablă care nu este vizibilă pentru cei doi
jucători ı̂n timpul jocului.
Tabla de joc este o bandă albă de lungime N cm, pe care sunt marcate poziţii de lungime 1 cm.
Poziţiile sunt numerotate pe tablă de la 0 la N 1, poziţia 0 fiind marcată la ı̂nceputul tablei
(capătul din stânga), iar poziţia N 1 fiind marcată la sfârşitul tablei (capătul din dreapta).
La ı̂nceputul jocului fiecare jucător are N r benzi colorate, toate de aceeaşi lungime L cm.
Benzile Anei sunt de culoare roşie, iar benzile lui Bogdan sunt de culoare verde.
Jucătorii mută alternativ, prima la mutare fiind Ana. La o mutare, jucătorul care este la rând
alege o poziţie de pe tabla de joc şi dacă poziţia este validă, pe tabla de joc va fi plasată o bandă a
jucătorului respectiv, cu capătul din stânga ı̂n poziţia aleasă. Dacă poziţia nu este validă, mutarea
nu va fi executată, iar jucătorul respectiv va primi 1 punct de penalizare şi pierde banda care ar
fi trebuit plasată pe tablă la poziţia respectivă (aceasta este eliminată din joc).
O poziţie este considerată validă, dacă pe tabla de joc poate fi plasată o bandă de lungime
L cu capătul din stânga al benzii fixat la poziţia specificată, astfel ı̂ncât banda să fie integral
pe tabla de joc, fără a se suprapune sau a se atinge cu o zonă de pe bandă colorată ı̂n culoarea
adversarului. Jocul se termină când jucătorii nu mai au benzi.
Fiecare jucător are ca scop să obţină o zonă pe bandă de lungime cât mai mare colorată ı̂n
culoarea sa. O zonă de pe bandă este constituită din poziţii consecutive, colorate cu aceeaşi
culoare.
Cerinţe
Scrieţi un program care citeşte lungimea tablei de joc, numărul de benzi colorate pe care le are
fiecare jucător la ı̂nceputul jocului, lungimea benzilor, precum şi poziţiile specificate de jucători
pe parcursul jocului şi rezolvă următoarele două cerinţe:
1. determină numărul de puncte de penalizare pentru fiecare dintre cei doi jucători;
2. determină pentru fiecare jucător care este lungimea maximă a unei zone de pe tabla de joc
colorată ı̂n culoarea sa la sfârşitul jocului.
Date de intrare
CAPITOLUL 1. OJI 2022 1.2. STRIPS 6
# Punctaj Restricţii
1 50 Pentru teste valorând 50 de puncte cerinţa este 1.
2 40 Pentru teste valorând 40 de puncte 1 & N & 106 , 1 & L & 1 000 şi 1 & N r & 1 000.
Fişierul de intrare strips.in conţine pe prima linie un număr natural C care reprezintă cerinţa
care urmează a fi rezolvată (1 sau 2). Pe cea de-a doua linie se află trei numere naturale separate
prin câte un spaţiu N N r L, cu semnificaţia din enunt. Celelalte linii ale fişierului de intrare
conţin ı̂n ordine poziţiile specificate de jucători pe parcursul jocului, câte o poziţie pe o linie.
Date de ieşire
Fişierul de ieşire strips.out va conţine o singură linie pe care vor fi scrise două numere naturale
rezA rezB, separate printr-un singur spaţiu.
Dacă C 1 atunci rezA este numărul de puncte de penalizare acumulate de Ana, iar rezB
numărul de puncte de penalizare acumulate de Bogdan.
Dacă C 2 atunci rezA este lungimea maximă a unei zone de culoare roşie la sfârşitul jocului,
iar rezB este lungimea maximă a unei zone de culoare verde la sfârşitul jocului.
1 & N & 10
9
Exemple:
strips.in strips.out
1 01
20 4 3
9
15
2
13
5
17
0
12
2 87
20 4 3
9
15
2
13
5
17
0
12
Explicaţii:
CAPITOLUL 1. OJI 2022 1.2. STRIPS 7
Propusă de: prof. Emanuela Cerchez, Colegiul Naţional ”Emil Racoviţă” Iaşi
Soluţia 1 - 40 de puncte. Pentru N & 10 , putem utiliza un vector pentru a reprezenta
6
culorile benzilor plasate pe tabla de joc (0 pentru poziţie neocupată, 1 pentru o poziţie ocupată
de o bandă roşie, respectiv 2 pentru o poziţie ocupată de o bandă verde).
Citim succesiv mutările.
Pentru fiecare mutare verificăm dacă este validă. Dacă da, plasăm pe tabla de joc o bandă ı̂n
poziţia respectivă. Dacă nu, contorizăm un punct penalizare pentru jucătorul care este la rând.
Pentru cerinţa 2 este suficient să parcurgem tabla de joc şi să determinăm lungimea maximă
a unei secvenţe formată numai din 1, respectiv lungimea maximă a unei secvenţe formată numai
din 2.
Soluţia 2 - 100 de puncte. Pe măsură ce plasăm benzi pe tabla de joc formăm practic
zone continue de aceeaşi culoare, care nu se suprapun. În loc să reţinem tabla de joc, vom reţine
pentru fiecare jucător zonele obţinute din benzile plasate de acesta pe tabla de joc. O zonă va fi
reprezentată prin cele două extremităţi (poziţia de ı̂nceput şi poziţia de sfârşit a zonei).
NRMAX este numărul maxim de zone ale unui jucător (se garantează că la finalul jocului
NRMAX nu depăşeşte 5 000, dar pe parcursul jocului poate fi mai mare).
Vom reţine zonele ı̂n ordinea crescătoare a poziţiilor de ı̂nceput.
Vom citi succesiv mutările celor doi jucători. La o mutare verificăm mai ı̂ntâi validitatea
acesteia. Să notăm poz poziţia corespunzătoare mutării curente şi cu cine jucătorul care este la
mutare. Evident, adversarul va fi 1 cine.
Pentru a verifica validitatea vom căuta pe tabla adversarului cea mai mică poziţie (unde)
pentru care poz & Z adversarunde.inc. Zonele fiind sortate, vom face o căutare binară.
Dacă o bandă plasată ı̂n poziţia poz se intersectează cu, sau se lipeşte de, cea de pe poziţia
unde (la dreapta) sau cu cea de pe poziţia unde 1 (la stânga) atunci mutarea nu este validă.
În cazul ı̂n care mutarea este validă, plasăm o bandă pe tabla jucătorului care este la mutare.
CAPITOLUL 1. OJI 2022 1.2. STRIPS 8
Pentru aceasta facem din nou o căutare binară, de data aceasta ı̂n zonele jucătorului care este
la mutare şi determinăm cea mai mică poziţie (unde) pentru care poz & Z cineunde.inc.
Pot apărea următoarele situaţii:
(1) banda poate fi alipită zonei Z cineunde (dacă această zonă există şi poz L 1 '
Z cineunde.inc 1;
(2) banda poate fi alipită zonei Z cineunde 1 (dacă această zonă există şi poz &
Z cineunde 1.sf 1;
(3) ı̂n urma alipirii zonele Z cineunde 1 şi Z cineunde pot fi şi ele alipite, formând astfel
o singură zonă (le alipim şi eliminăm una dintre ele);
(4) dacă nicio alipire nu este posibilă, inserăm o nouă zonă cu ı̂nceputul poz şi sfârşitul poz L 1
ı̂n poziţia unde (astfel zonele rămânând sortate).
Eficienţa timp a acestui algoritm este afectată de situaţiile 3 sau 4 (eliminarea, respectiv
inserarea fiind liniară).
Utilizarea unor structuri de date avansate (de exemplu, structura de date set din STL) poate
optimiza acest pas, dar o astfel de implementare depăşeşte nivelul clasei a VIII-a şi nu este necesară
pentru a obţine 100 de puncte.
Este posibilă o implementare bazată pe aceeaşi idee, asociind fiecărei zone culoarea acesteia
şi reţinând zonele sortate după extremitatea iniţială ı̂ntr-un singur vector. Dar ı̂n acest caz
dimensiunea vectorului este mai mare şi timpul necesar pentru eliminare/inserare creşte.
2.1 Cartofi
Problema 1 - Cartofi 100 de puncte
Fermierul Feder cultivă cartofi pe un teren dreptunghiular de lăţime N metri şi lungime M
metri, compartimentat ı̂n N M zone pătratice identice de lungime 1 metru, dispuse alăturat,
câte N pe lăţime (pe N linii, numerotate de la 1 la N ) şi câte M pe lungime (pe M coloane,
numerotate de la 1 la M ).
În fiecare zonă pătratică se află câte o plantă de cartofi. Parcurgând terenul de la prima
linie către ultima, fiecare linie cu număr impar parcurgând-o de la coloana 1 către coloana M ,
iar fiecare linie cu număr par parcurgând-o de la coloana M către coloana 1, fermierul (pasionat
de matematică) a scris numerele cartofilor produşi de fiecare plantă, ı̂n ordinea parcurgerii, şi a
constatat că a obţinut şirul cifrelor unităţilor primilor N M termeni ai şirului Fibonacci (vezi
Figura 1 ı̂n care N 3 şi M 6).
Cerinţe
Scrieţi un program care citeşte numerele N şi M (cu semnificaţia din enunţ), iar apoi determină:
Date de intrare
Fişierul cartofi.in conţine pe prima linie un număr natural C reprezentând cerinţ,a din prob-
lemă care trebuie rezolvată (1, 2 sau 3). A doua linie a fişierului conţine cele două numere naturale
N şi M , separate printr-un spaţiu, cu semnificaţia din enunţ. Dacă C 3, atunci fişierul va mai
conţine pe a treia linie numărul natural Q, iar pe fiecare linie dintre următoarele Q, câte două
numere naturale separate printr-un spaţiu, reprezentând câte o pereche de numere A, B dintre
cele Q.
Date de ieşire
9
CAPITOLUL 2. OJI 2021 - OSEPI 2.1. CARTOFI 10
N &M
Q & 10
5
1&A&B&M
Pentru cerinţa 1 se acordă 20p, iar pentru cerinţele 2 şi 3 se acordă câte 40p.
Pentru teste ı̂n valoare de 17 puncte: N & 50, M & 100 şi Q & 20
Pentru alte teste ı̂n valoare de 24 puncte: N & 100, M & 1000 şi Q & 10000
Pentru alte teste ı̂n valoare de 31 puncte: N & 1000, M & 10000 şi Q & 100000
Observaţii:
Şirul Fibonacci este definit astfel: f 1 1, f 2 1 şi fn fn1 fn2 , dacă n ' 3, (n este un
număr natural nenul).
O suprafaţa pătratică din teren este formată din K K zone pătratice alăturate dispuse câte
K pe linie şi câte K pe coloană, oricare ar fi 1 & K & minrN, M x.
Exemple:
Propunător: prof. Flavius Boian, Colegiul Naţional ”Spiru Haret”, Târgu Jiu
1.1 Solut, ie cu punctaj parţial
Cerinţa 1 (20 de puncte)
a O soluţie cu punctaj parţial se poate obţine generând şirul cifrelor unităţilor primilor N M
termeni ai şirului Fibonacci, contorizându-se apariţia fiecărei cifre 0. Complexitate: O N M .
Cerinţa 2 (40 de puncte)
a O soluţ ie cu punctaj parţ ial se poate obţine utilizând matricea N M a numerelor cartofilor
produşi de plante. Se observă că suprafaţa pătratică de sumă maximă trebuie căutată printre
submatricele N N ale matricei. Astfel, suma numerelor din prima submatrice (formată din
coloanele 1, N ) este egală cu suma valorilor din primele N coloane. Submatricea formată din
coloanele 2, N 1 va avea suma egală cu suma valorilor din coloanele 2, N 1. Această sumă
se poate obţine şi direct, din suma anterioară scăzând suma primei coloane şi adăugând suma
coloanei N 1. Procedăm asemănător pentru celelalte submatrice, ultima fiind formatu a din
ultimele N coloane. Cea mai mare sumă dintre acestea reprezintă răspunsul la cerinţ,a 2.
Pentru a evita calculul repetat al sumelor valorilor din fiecare coloană, putem calcula mai ı̂ntâi
aceste sume ı̂ntr-un vector de sume (SC K = suma valorilor din coloana K, K 1, 2, ..., M ).
Se observă inutilitatea formării matricei. Calculul sumei fiecărei coloane ı̂n vector se poate
face simulând construirea matricei, adunând ultima cifră a termenului Fibonacci la suma ce ı̂i
corespunde. Complexitate O M .
Cerinţa 3 (40 de puncte)
a O soluţie cu punctaj parţial se poate utilizând matricea, calculând pentru fiecare pereche
A, B suma elementelor situate ı̂ntre aceste coloane.
a O soluţ ie cu un punctaj parţial mai mare ca anteriorul se obţine utilizând un alt mod
de calculare a sumelor elementelor cuprinse ı̂ntre coloanele A, B şi constă ı̂n calculul sumelor
parţiale ale vectorului SC intr-un alt vector S cu M componente. Astfel, suma elementelor dintre
coloanele A, B va fi egală cu S B S A 1, iar S 0 0. Complexitate O Q M .
1.2 Soluţie cu punctaj maxim
Propusă de stud. Ioan-Cristian Pop (UPB) şi stud. Ioan-Bogdan Iordache (UB)
General
a Ştim că şirul cifrelor unităţilor termenilor din şirul lui Fibonacii este periodic cu perioada
60.
Fie R 60. Pe baza acestei proprietăţi, putem construi strict primele 60 de elemente din acest
şir, iar pe baza acestora putem calcula răspunsul la fiecare cerinţă.
De exemplu, al 500-lea termen din şir va fi egal cu al 20-lea termen.
Cerinţa 1 (20 de puncte)
a Plecând de la observaţia de mai sus, construim vectorul alcătuit din primele 60 de numere
din şir. De aici aflăm că cifra 0 apare de 4 ori ı̂n acest set. Ştiind că există N M ©R seturi
complete, rămâne să determinăm numărul de apariţii ale cifrei 0 ı̂n secvenţa formată din ultimii
N M %R termeni ai vectorului.
De exemplu, pentru N 50 şi M 100, obţinem N M = 5 000. Ştim că vor fi 5000©60 83
seturi complete, iar ı̂n ultimii 20 de termeni, 0 se află o singură dată. Deci, răspunsul va fi
83 4 1 333.
a Complexitate: O R.
Obs: https://ebooks.infobits.ro/culegere_OJI_2021.pdf
2.2 Tunel
Problema 2 - Tunel 100 de puncte
Tommy este un motan alintat care adoră să se plimbe prin orice tunel. De
aceea, stăpânii lui i-au construit o nouă jucărie, formată din N tuneluri inter-
conectate (etichetate cu numerele distincte de la 1 la N ). Toate tunelurile au
aceeaşi lungime, sunt formate din M elemente unitare identice (numerotate
cu numerele distincte de la 1 la M ) şi au ieşiri la ambele capete. Conectarea
dintre două tuneluri alăturate se face printr-un element unitar numit pasaj.
În exemplul din Figura 1, jucăria este formată din 4 tuneluri, fiecare tunel
fiind format din 9 elemente unitare. Pentru a fi mai provocator, stăpânii motanului plasează ı̂n
ultimul element unitar al ultimului tunel o recompensă.
Motan isteţ, Tommy a ı̂nvăţat deja toate regulile jocului:
poate intra prin capătul din stânga al oricărui tunel (prin elementul unitar 1);
CAPITOLUL 2. OJI 2021 - OSEPI 2.2. TUNEL 13
dacă ajunge ı̂n ultimul element unitar al tunelului etichetat cu N , atunci Tommy iese din
acest tunel cu recompensă, chiar dacă ar exista un pasaj ce conectează acest ultim element
la ultimul element din tunelul N 1 (vezi Figura 2.b);
dacă ajunge ı̂n ultimul element unitar al tunelului etichetat cu N 1 şi există un pasaj
care conectează acest element cu ultimul element unitar al tunelului etichetat cu N , atunci
Tommy trece prin acest pasaj ı̂n ultimul element din ultimul tunel, ia recompensa şi iese din
tunel (vezi Figura 2.a). În cazul ı̂n care acest pasaj nu există, Tommy iese din tunelul N 1
fără recompensă;
dacă ajunge ı̂n ultimul element unitar al unui tunel cu eticheta strict mai mică decât N 1,
atunci Tommy iese din tunel fără recompensă.
Ajutaţi-l pe Tommy să ajungă cât mai repede la recompensă respectând regulile jocului!
Cerinţe
Scrieţi un program care citeşte numerele naturale N , M şi X, iar apoi determină:
1. eticheta tunelului prin care iese Tommy dacă intră prin tunelul cu eticheta X, respectând
regulile jocului;
2. numărul minim L de elemente unitare (ale tunelurilor şi ale pasajelor) prin care Tommy ar
trebui să treacă, respectând regulile jocului, pentru a ajunge la recompensă.
Date de intrare
Fişierul tunel.in conţine pe prima linie un număr natural C reprezentând cerinţa din problemă
care trebuie rezolvată (1 sau 2). A doua linie a fişierului conţine cele trei numere naturale N , M
şi X, separate prin câte un spaţ, iu, cu semnificaţ ia din enunţ. Următoarele N 1 linii descriu
pasajele dintre tuneluri. Prima linie dintre cele N 1 indică pasajele dintre tunelurile etichetate cu
1 şi 2, următoarea linie indică pasajele dintre tunelurile etichetate cu 2 şi 3, ..., ultima dintre cele
N 1 linii indică pasajele dintre tunelurile etichetate cu N 1 şi N . Primul număr din fiecare astfel
de linie reprezintă numărul P de pasaje, iar următoarele P numere distincte, scrise ı̂n ordine
crescătoare, reprezintă poziţ iile elementelor unitare (dintre cele două tuneluri) conectate prin
cele P pasaje.
Date de ieşire
1&P &M 2
Pot exista cel mult 150 000 pasaje care interconectează tunelurile.
Pot exista pasaje ı̂nvecinate care să conecteze elementele unitare din două tuneluri alăturate
(vezi Figura 1 ı̂n care tunelurile 1 şi 2 sunt interconectate prin pasajele ı̂nvecinate dintre
elementele 6, respectiv 7).
Primul element unitar din fiecare tunel nu este conectat la niciun pasaj.
Ultimul element unitar din tunelurile etichetate cu 1, 2, ..., N 2 nu este conectat la niciun
pasaj.
Pentru fiecare test există cel puţin o intrare ı̂ntr-un tunel prin care Tommy poate ajunge la
ieşirea cu recompensă din tunelul N .
Pentru cerinţa 1 se acordă 40 de puncte iar pentru cerinţa 2 se acordă 60 de puncte.
Exemple:
decenii ne-a fost o foarte apreciată şi respectată colegă ı̂n comisiile de organizare a competiţiilor
naţionale şi internaţionale de informatică din ţara noastră.
2.1 Soluţie cu punctaje variate
Soluţia propusă utilizează o matrice binară A de dimensiune 2N 1 M , ı̂n care valorile de
1 din liniile impare reprezintă elementele unitare, iar valorile 1 din liniile pare reprezintă pasajele.
Observaţii: a) umplerea inutilă cu 1 a liniilor din matrice corespunzătoare tunelurilor va mări
timpul de executare, diminuând punctajul; b) simularea deplasării ı̂n matrice se poate face din
element ı̂n element sau din pasaj ı̂n pasaj.
Cerinţa 1 (40 de puncte)
Pentru fiecare dintre aceste aceste trasee vom proceda astfel: din Ai, j trecem ı̂n a) Ai
2, j 1 dacă Ai 1, j 1 sau b) Ai 2, j 1 dacă Ai 1, j 1 sau c) Ai, j 1. Doar
una dintre situaţiile a), b) sau c) poate să apară. Astfel, sunt parcurse cel mult două trasee din
cele N posibile (complexitate O M ).
2.2 Soluţie cu punctaj maxim
Propusă de drd. Diana Ghinea (ETH Zürich) şi ş.l. Stelian Ciurea (ULB Sibiu)
Complexitatea soluţiei, atât pentru cerinţa 1 cât şi pentru cerinţa 2 este O M cu memorie
<
N
suplimentară O N M , sau O M log N cu memorie suplimentară O i 1 Pi , unde Pi reprezintă
numărul de pasaje din tunelul i.
Cerinţa 1 (40 de puncte)
Soluţia se poate obţine simulând traseul lui Tommy element cu element de la intrarea dată
până la ieşire. Notăm cu T, E elementul unitar cu numărul E din tunelul cu eticheta T . Orice
traseu conţine O M elemente ı̂ntrucât fiecare element unitar al oricărui tunel este conectat la cel
mult un pasaj. Pentru fiecare element i, j j N, M , verificăm dacă există un pasaj spre tunelul
i 1 sau spre tunelul i 1 din elementul i, j . În caz afirmativ, Tommy va merge prin pasajul
corespunzător către elementul i 1, j , respectiv i 1; j . Altfel, Tommy va continua să meargă
către dreapta: fie ı̂n elementul i, j 1, fie iese din pasaj.
Putem verifica dacă există un pasaj din elementul i, j j N, M ı̂n mai multe moduri:
CAPITOLUL 2. OJI 2021 - OSEPI 2.2. TUNEL 16
Reţinem o matrice bool P cu N linii şi M coloane, unde P ij = true dacă şi numai dacă
există un pasaj din celula i, j către celula i 1, j . Astfel, putem verifica dacă există un
pasaj ı̂n complexitate O 1, ı̂nsă cu memorie suplimentară O N M .
Utilizăm structura vector din C++: pentru fiecare coloană j, vector-ul P j reţine pasajele
de pe coloana j (cel mult O N pasaje). Putem verifica dacă există un pasaj folosind căutare
N
<
binară, ı̂n complexitate O log N , cu memorie suplimentară O i 1 Pi , unde Pi reprezintă
numărul de pasaje din tunelul i.
Utilizăm structura set din C++: pentru fiecare coloană j, set-ul P j reţine pasajele de pe
coloana j (cel mult O N pasaje). Putem verifica dacă există un pasaj (pentru elementul
i, j , dacă i aparţine set-ului P j ) ı̂n complexitate O log N , cu memorie suplimentară
< N
O i 1 Pi , unde Pi reprezintă numărul de pasaje din tunelul i.
Astfel, utilizând prima modalitate, obţinem complexitatea O M , iar utilizând una dintre
celelalte două modalităţi, obţinem complexitatea O M logN .
Cerinţa 2 (60 de puncte)
O soluţie naivă ar simula traseul lui Tommy pornind din intrarea fiecărui tunel. Fără a lua ı̂n
calcul timpul pentru verificarea pasajelor, o astfel de soluţie ar avea complexitate O N M .
Pentru a obţine o soluţie ı̂n complexitatea de timp dorită, avem nevoie de următoarea observaţie:
Există cel mult două tuneluri din care Tommy poate ajunge la recomensă.
Orice traseu care ajunge la recompensă (ı̂l vom denumi traseu cu succes) fie trece prin elementul
N 1, M (dacă există un pasaj către N, M ), fie trece prin elementul N, M 1.
Presupunem că există cel puţin trei trasee distincte cu succes. Atunci, două dintre aceste
trasee trec prin acelaşi element N 1, M sau N, ; M 1. Vom presupune că cele două trasee
trec prin elementul N 1, M (celălalt caz este analog).
Vom nota aceste trasee cu A al x, 1, al1 , ..., a2 N 1, M , a1 N, M şi B
¬
bl ¬ x , 1, bl¬ 1 , ..., b2 N 1, M , a1 N, M (acestee trasee nu includ pasajele).
Din moment ce A şi B sunt două trasee distincte, dar ultimele două elemente ale lui A şi B
sunt comune, există un element C ak bk iC , jC astfel ı̂ncât ai bi pentru 1 & i & k şi
ak1 j bk1 . Acest lucru este posibil doar dacă unul dintre cele două trasee a trecut aici printr-un
pasaj. Întrucât un element poate fi conectat la maxim un pasaj, doar unul dintre traseele A şi
B poate trece aici printr-un pasaj: presupunem că acest traseu este A şi că ak1 iC 1 , jC
(analog pentru celelalte cazuri). Atunci, bk1 iC , jC 1 şi, din moment ce aici este un pasaj,
bk1 iC 1, jC j iC , jC 1 ak1 , ceea ce contrazice modul ı̂n care am ales elementul C şi
demonstrează observaţia noastră.
Astfel, nu este necesar să simulăm toate traseele posibile pentru a obţ ine răspunsul la această
cerinţa: trebuie să simulăm cel mult două trasee.
Adăugăm ı̂ncă o observaţie:
Există un traseu cu succes ı̂n care Tommy trece prin elementul N, M 1.
Dacă există un pasaj ı̂ntre N 1, M şi N, M , atunci există un traseu cu succes ı̂n care
Tommy trece prin elementul N, M 1.
În concluzie, o soluţie pentru această cerinţă va simula ı̂n sens invers deplasarea lui Tommy
ı̂n cel mult două trasee (de la recompensă către intrare) şi va calcula lungimea acestora:
unicul traseu cu succes ce trece prin celula N, M 1, dacă acest traseu există (ı̂n cazul ı̂n
care există un pasaj ı̂ntre celulele N, M şi N, M 1);
unicul traseu cu succes ce trece prin celula N, M 1.
Lungimea unui traseu poate fi calculată ca M 2p, unde p este numărul de pasaje prin care
Tommy trece ı̂n acel traseu.
Obs: https://ebooks.infobits.ro/culegere_OJI_2021.pdf
OJI 2020
3.1 datorii
Problema 1 - datorii 100 de puncte
Într-o ţară ı̂ndepărtată, economia este ı̂n criză. Cea mai mare problemă este lipsa de capital
care creează blocaje financiare. De exemplu, o firmă X poate avea datorii către o firmă Y pe care
nu le poate plăti, deoarece o altă firmă Z are datorii către firma X pe care nu le-a plătit, ş.a.m.d.
Există o listă cu toate datoriile firmelor sub forma următoare:
X %YS
cu semnificaţia ”firma X datorează firmei Y suma S”.
Este posibil ca X să aibă mai multe datorii la firma Y (ı̂n funcţie de contractele derulate
ı̂mpreună) sau chiar ca X să aibă datorii la Y şi Y să aibă datorii la X.
Cerinţe
Cunoscând lista cu datoriile firmelor, scrieţi un program care să rezolve următoarele cerinţe:
Date de intrare
Fişierul de intrare datorii.in conţine pe prima linie un număr natural C reprezentând cerinţa
care trebuie să fie rezolvată (1 sau 2). Pe a doua linie se află un număr natural D care reprezintă
numărul de ı̂nregistrări existente ı̂n lista datoriilor firmelor. Pe următoarele D linii sunt descrise
datoriile firmelor, ı̂n forma specificată ı̂n enunţ, câte o datorie pe o linie.
Date de ieşire
Fişierul de ieşire datorii.out va conţine răspunsul la cerinţa C specificată ı̂n fişierul de intrare.
Dacă C 1 fişierul va conţine un număr natural, reprezentând numărul de firme distincte care
apar ı̂n lista menţionată.
Dacă C 2 fişierul va conţine pentru fiecare dintre firmele distincte din lista menţionată câte
un singur triplet de forma X SD SP , unde X este numele firmei, iar SD şi SP au semnificaţia
din enunţ pentru firma X; tripletele vor fi scrise astfel ı̂ncât numele firmelor să apară ı̂n ordine
lexicografică, fiecare triplet pe câte o linie a fişierului, iar X, SD şi SP vor fi separate prin câte
un singur spaţiu.
a Există ı̂n total cel mult 6 000 de firme distincte ı̂n lista menţionată de datorii.
17
CAPITOLUL 3. OJI 2020 3.1. DATORII 18
a Numele unei firme este format din maximum 20 de caractere (litere mari şi mici ale alfa-
betului englez, cifre, spaţii); se face distincţie ı̂ntre literele mari şi literele mici ı̂n numele
firmelor; nu există alte restricţii referitoare la numele firmelor.
a Două firme distincte au nume distincte. O firmă nu poate avea datorii la ea ı̂nsăşi.
a În descrierea unei datorii X % Y S există un singur spaţiu ı̂ntre X şi %, un singur spaţiu
ı̂ntre % şi Y , respectiv un singur spaţiu ı̂ntre Y şi S.
a 1&D & 80 000
a Sumele datorate de firme sunt numere naturale nenule & 106 .
a Dacă X şi Y sunt numele a două firme distincte, iar k (k '
0) este valoarea maximă cu
proprietatea că secvenţa formată din primele k caractere din X este identică cu secvenţa
formată din primele caractere din Y , spunem că X precedă din punct de vedere lexicografic
pe Y dacă X are doar k caractere sau dacă al k 1-lea caracter din X este mai mic decât
al k 1-lea caracter din Y .
a Pentru teste valorând 30 de puncte cerinţa este 1. Pentru teste valorând 60 de puncte cerinţa
este 2. Pentru teste valorând 40 de puncte D &1 000. Pentru teste valorând 45 de puncte
numele firmelor nu conţin spaţii. 10 puncte se acordă din oficiu.
Exemple:
Vom citi datoriile firmelor succesiv, linie cu linie, ı̂ntr-un şir de caractere s.
Suntem interesaţi doar de firmele distincte, ca urmare vom reţine evidenţa firmelor distincte
ı̂ntr-un vector F .
O firmă va fi o structură cu 3 câmpuri (un şir de caractere reprezentând numele acesteia şi
două numere naturale SD şi SP , reprezentând totalul datoriilor firmei către alte firme, respectiv
suma totală pe care firma trebuie să o primească de la alte firme).
Vom prelucra şirul s, care reprezintă o datorie, ı̂n scopul de a extrage numele celor două firme
(X şi Y ) şi suma datorată S.
Deoarece numele firmelor pot conţine şi ele cifre, vom parcurge şirul s de la sfârşit către ı̂nceput,
construind suma S, din cifrele ı̂ntâlnite până la primul spaţiu.
Eliminăm din şirul s suma S, prin trunchierea acestuia. şirul s va avea acum forma X ¿ Y .
Separăm şirul s ı̂n două subşiruri (unul care să reprezinte numele firmei X, celălalt numele
firmei Y ), ştiind că cele două nume sunt separate prin ¿.
CAPITOLUL 3. OJI 2020 3.1. DATORII 19
Vom căuta numele firmei X ı̂n vectorul F . Dacă acesta există deja ı̂n vectorul F , doar adăugăm
suma S la câmpul SD al firmei cu numele X. Dacă nu există o firmă cu numele X, o adăugăm ı̂n
vectorul F , iniţializând SD cu S şi SP cu 0.
ı̂n mod similar procedăm cu firma Y : o căutăm ı̂n vectorul F ; dacă există deja adăugăm suma
S la câmpul SP al firmei cu numele Y ; dacă nu există o firmă cu numele Y , o adăugăm la vectorul
F şi iniţializăm câmpul SD cu 0, iar SP cu S.
Căutarea numelui unei firme se poate face secvenţial ı̂n vectorul F , dar ı̂n acest caz se obţine
punctaj parţial, din cauza depăşirii timpului de execuţie.
Observaţie: pentru teste valorând 45 de puncte, numele firmelor nu conţin spaţii şi deci etapa
de prelucrare a şirului s este eliminată, citind separat X, Y , S.
Pentru un algoritm eficient, va fi necesar să facem o căutare binară ı̂n vectorul F . Pentru ca
acest lucru să fie posibil, ar trebui ca, la fiecare pas, vectorul F să fie deja sortat lexiicografic după
numele firmelor.
Pentru aceasta, vom face o sortare prin inserare. Mai exact, de fiecare dată când adăugăm o
nouă firmă ı̂n vectorul F , căutăm locul corect şi o inserăm direct ı̂n poziţia corectă, astfel ı̂ncât F
să rămână sortat.
49 ii = strlen(linie) - 1;
50 suma = 0;
51 while ( 48 <= linie[ii] && linie[ii] <= 57)
52 {
53 suma = suma + z * (linie[ii]-48);
54 z *= 10;
55 ii--;
56 }
57
58 ii++;
59 linie[ii-1] = ’\0’;
60 cuv = strchr(linie, ’>’);
61 strncpy(numeFirma1, linie, cuv - linie - 1);
62 numeFirma1[cuv - linie - 1] =’\0’;
63 strcpy(numeFirma2, cuv+2);
64
65 //pun firmele la locul lor
66 poz = apartine(numeFirma1);
67 if(isdigit(numeFirma1[0]))
68 lista = numeFirma1[0] - ’0’;
69 else
70 if(isupper(numeFirma1[0]))
71 lista = 10 + numeFirma1[0] - ’A’;
72 else
73 lista = 36 + numeFirma1[0] - ’a’;
74
75 if(poz)
76 v[lista][poz].SD += suma;
77 else
78 {
79 nrFirme++;
80 nrF[lista]++;
81
82 strcpy(v[lista][nrF[lista]].nume,numeFirma1);
83
84 v[lista][nrF[lista]].SP = 0;
85 v[lista][nrF[lista]].SD = suma;
86
87 //mut firma la locul ei
88 for(j = nrF[lista];
89 j>1 && strcmp(v[lista][j].nume, v[lista][j-1].nume) < 0;
90 j--)
91 tmp = v[lista][j],
92 v[lista][j] = v[lista][j-1],
93 v[lista][j-1] = tmp;
94 }
95
96 poz = apartine(numeFirma2);
97 if(isdigit(numeFirma2[0]))
98 lista = numeFirma2[0] - ’0’;
99 else
100 if(isupper(numeFirma2[0]))
101 lista = 10 + numeFirma2[0] - ’A’;
102 else
103 lista = 36 + numeFirma2[0] - ’a’;
104
105 if(poz)
106 v[lista][poz].SP += suma;
107 else
108 {
109 nrFirme++;
110 nrF[lista]++;
111 strcpy(v[lista][nrF[lista]].nume,numeFirma2);
112 v[lista][nrF[lista]].SP = suma;
113 v[lista][nrF[lista]].SD = 0;
114
115 //mut firma la locul ei
116 for(j = nrF[lista];
117 j>1 && strcmp(v[lista][j].nume, v[lista][j-1].nume) < 0;
118 j--)
119 tmp = v[lista][j],
120 v[lista][j] = v[lista][j-1],
121 v[lista][j-1] = tmp;
122 }
123 }
124
CAPITOLUL 3. OJI 2020 3.1. DATORII 21
125 if( C == 1)
126 {
127 scrie << nrFirme <<’\n’;
128 }
129 else
130 {
131 for(i = 0; i < 62; ++i)
132 if(nrF[i])
133 for(j = 1; j <= nrF[i]; ++j)
134 scrie << v[i][j].nume << " " <<
135 v[i][j].SD << " " <<
136 v[i][j].SP << ’\n’;
137 }
138
139 citeste.close();
140 scrie.close();
141 return 0;
142 }
143
144 int apartine(char *denumire)
145 {
146 int st, dr, mij;
147 int lista;
148
149 if(isdigit(denumire[0]))
150 lista = denumire[0] - ’0’;
151 else
152 if(isupper(denumire[0]))
153 lista = 10 + denumire[0] - ’A’;
154 else
155 lista = 36 + denumire[0] - ’a’;
156
157 st = 1;
158 dr = nrF[lista];
159 while(st <= dr)
160 {
161 mij = (st + dr)/2;
162 if(strcmp(v[lista][mij].nume, denumire) == 0)
163 return mij;
164 else
165 if(strcmp(v[lista][mij].nume, denumire) < 0)
166 st = mij + 1;
167 else
168 dr = mij - 1;
169 }
170
171 return 0;
172 }
3.2 triunghi
Problema 2 - triunghi 100 de puncte
Se consideră A un tablou bidimensional cu n linii, n coloane şi elemente numere naturale. O
zonă triunghiulară a tabloului, reprezentată de tripletul lin, col, k , este o zonă de forma unui
triunghi dreptunghic cu catetele de lungime egală cu ¶k ¶, definită astfel:
a ...
a pe ultima linie a zonei se află elementul Alin k 1col.
Exemple:
Figura 1
Dacă ipotenuza triunghiului se găseşte de-a lungul unei diagonale aflate sub diagonala princi-
pală, zona va fi de forma unui pentagon determinat de punctele 1, 1, i, 1, i, j , x, n şi 1, n,
unde x, n este punctul ı̂n care prelungirea ipotenuzei triunghiului intersectează ultima coloană
(vezi figura 2).
CAPITOLUL 3. OJI 2020 3.2. TRIUNGHI 25
Figura 2
Pe baza tablourilor precalculate, aflarea sumei valorilor din triunghiul determinat de tripletul
i j k, cu k % 0, se realizează, ı̂n general, cu formula:
Mai exact, scădem din suma ı̂ntregii zone (zona1 + zona2 + zona 3 + triunghiul curent) zona
compusă din alipirea zonei 1 cu zona 3 şi a zonei compuse din alipirea zonei 1 şi zonei 2, la care
adăugăm zona 1, care a fost scăzută de două ori (vezi figura 3).
Figura 3
Formula de calcul suferă unele modificări ı̂n funcţie de poziţia triunghiului şi de forma zonei.
Dacă triunghiul are k $ 0 (triunghi cu unghiul drept ”ı̂n jos”), suma elementelor sale se
calculează ca fiind diferenţa dintre suma elementelor din pătratul de latură k, având colţul din
dreapta-jos de coordonate i, j şi suma elementelor triunghiului determinat de tripletul i ¶k ¶ 1,
j ¶k ¶ 1, ¶k ¶ (calculat prin procedeul descris anterior).
Complexitatea algoritmului este O Q n n.
75 k = -k;
76 SumCrt = sum[lin][col] - sum[lin][col - k] - sum[lin-k][col] +
77 sum[lin-k][col-k] -
78 aflaSuma(lin - k + 1, col - k + 1, k - 1);
79 k = -k;
80 }
81
82 //scrie << SumCrt<<’ ’<<lin <<’ ’<<col<<’ ’<<k<<’\n’;
83 if( SumMax < SumCrt )
84 SumMax = SumCrt, linMax = lin, colMax = col, kMax = k;
85 else
86 if( SumMax == SumCrt )
87 if( lin < linMax || lin == linMax && col < colMax)
88 SumMax = SumCrt, linMax = lin, colMax = col, kMax = k;
89
90 }
91
92 scrie << SumMax << "\n" ; //<< linMax<<" "<<colMax<<" "<<kMax<<"\n";
93 citeste.close();
94 scrie.close();
95 return 0;
96 }
97
98
99 int aflaSuma(int lin, int col, int k)
100 {
101 int SumCrt;
102 if( lin == 1) // cazul 1 si 2
103 SumCrt = diag[k][col] - sum[lin + k - 1][ col - 1 ];
104 else //cazul 3
105 if(col == 1)
106 SumCrt = diag[lin + k - 1][1] - diag[lin-1][col+k];
107 else
108 if(col + k -1 == N) //cazul 4
109 SumCrt = diag[lin + k - 1][col] - sum[lin + k -1][col - 1] -
110 sum[lin - 1][col + k - 1] + sum[lin-1][col-1];
111 else // czaul 5
112 SumCrt = diag[lin + k - 1][col] - sum[lin + k -1][col - 1] -
113 diag[lin - 1][col + k] + sum[lin-1][col-1];
114
115 return SumCrt;
116 }
117
118
119 void afis(int A[Nmax][Nmax], int N)
120 {
121 int i, j;
122 for(i = 1; i <= N; ++i)
123 {
124 for(j = 1; j <= N; ++j)
125 scrie<<A[i][j]<<’\t’;
126 scrie << ’\n’;
127 }
128 scrie << ’\n’;
129 }
18
19 int smax;
20 int detsuma(int lin, int col, int k);
21
22 int main()
23 {int i, j, lin, col, k, t, suma;
24 fin>>n;
25 for (i=1; i<=n; i++)
26 for (j=1; j<=n; j++)
27 {
28 fin>>A[i][j];
29 sl[i][j]=sl[i][j-1]+A[i][j];
30 sd[i][j]=sd[i-1][j]+sd[i][j-1]-sd[i-1][j-1]+A[i][j];
31 if (j==n) st[i][j]=sd[i][j]; else st[i][j]=st[i-1][j+1]+sl[i][j];
32 }
33
34 fin>>Q;
35 for (t=0; t<Q; t++)
36 {
37 fin>>lin>>col>>k;
38 if (k>0)
39 suma=detsuma(lin,col,k);
40 else
41 suma=sd[lin][col]-sd[lin][col+k]-sd[lin+k][col]+
42 sd[lin+k][col+k]-detsuma(lin+k+1,col+k+1,-k-1);
43 if (suma>smax) smax=suma;
44 }
45 fout<<smax<<’\n’;
46 fout.close();
47 return 0;
48 }
49
50 int detsuma(int lin, int col, int k)
51 {
52 if (col+k-1==n)
53 return st[lin+k-1][col]-
54 sd[lin+k-1][col-1]-
55 sd[lin-1][col+k-1]+
56 sd[lin-1][col-1];
57
58 return st[lin+k-1][col]-
59 sd[lin+k-1][col-1]-
60 st[lin-1][col+k]+
61 sd[lin-1][col-1];
62 }
OJI 2019
4.1 cate3cifre
Problema 1 - cate3cifre 90 de
puncte
Gigel, pasionat de numere, ştie că orice număr natural se scrie ı̂ntr-o bază de numeraţie b ca
o succesiune de simboluri care au asociate valori de la 0 la b 1. De exemplu numărul 7, scris
ı̂n baza 10, se scrie ı̂n baza 2ca 111 2 , iar numărul 26732, scris ı̂n baza 10, se scrie ı̂n baza 37 ca
o succesiune de 3 simboluri, primele două având asociată valoarea 19, iar ultimul având asociată
valoarea 18. El a descoperit că există numere care au proprietatea că se scriu, ı̂n exact două baze
diferite, prin exact trei simboluri identice. De exemplu, numărul 931 10 se scrie ı̂n baza 11 ca
777 11 , iar ı̂n baza 30 se scrie 111 30 .
Cerinţe
Fiind dat un număr natural N , să se determine cel mai mare număr natural mai mic sau egal
cu N , care are proprietatea că se scrie ı̂n exact două baze diferite prin exact 3 simboluri identice.
1. Să se scrie numărul determinat
2. Să se scrie cele două baze determinate şi valorile simbolurilor respective.
Date de intrare
Fişierul de intrare cate3cifre.in conţine pe prima linie cerinţa (1 sau 2). Pe linia a doua a
fişierului de intrare se află numărul natural N .
Date de ieşire
Fişierul de ieşire cate3cifre.out va conţine pe prima linie, dacă cerinţa este 1, numărul deter-
minat. Dacă cerinţa este 2, prima şi cea de a doua linie a fişierului de ieşire au aceeaşi structură:
pe fiecare linie se vor scrie, separate printr-un spaţiu, două numere naturale b c, reprezentând baza
şi valoarea simbolului cerut din baza respectivă. Cele două baze se vor afişa ı̂n ordine crescătoare.
a 0 $ N & 1000000
a Pentru rezolvarea corectă a cerinţei 1 se acordă 60 de puncte. Pentru cerinţa 2, se acordă
30 de puncte. Pentru 50 de puncte N & 10000
a Numărul xyz b scris ı̂n baza b cu simbolurile x, y, z se scrie ı̂n baza 10 ca o valoare calculată
2
astfel: x b y b z (unde simbolurile x, y, z se ı̂nlocuiesc cu valorile asociate)
a Pentru fiecare test există soluţie.
Exemple
29
CAPITOLUL 4. OJI 2019 4.1. CATE3CIFRE 30
Solutia 1
ccc(b) = c * b * b + c * b + c = c * (b * b + b + 1) = x(10)
O optimizare importanta este aceea de a testa doar acele valori c care sunt
divizori ai lui x.
Daca pentru fiecare baza b astfel aleasa se verifica fiecare cifra c din baza
respectiva (adica 1 <= c < b), in functie de implementare se pot obtine diferite
punctaje. La indeplinirea conditiei c * (b * b + b + 1) = x s-a determinat o baza.
Daca la terminarea verificarilor pentru numarul curent s-au detectat exact 2 baze,
s-a gasit solutia si, in functie de cerinta se vor scrie datele cerute.
Solutia 2
Valorile de testat fiind in numar de cel mult un milion, putem rula un program
separat, brut, care precalculeaza toate numerele cu proprietatea ceruta. Sunt putin
peste 100 astfel de valori. Sursa predata la evaluare obtine rezultatul dintr-un
vector cu constante (cu valorile precalculate).
Grad de dificultate: 3
4.2 paralele
Problema 2 - paralele 90 de puncte
Avem o matrice de dimensiuni N M , cu elemente 0 şi 1. Numim segment o secvenţă de cel
puţin două valori 1 aflate una lângă alta, toate pe aceeaşi linie, sau toate pe aceeaşi coloană a
matricei.
Cerinţe
Date de intrare
Fişierul paralele.in conţine pe prima linie, separate prin câte un spaţiu trei valori naturale,
ı̂n ordine: T , N şi M . Dacă T este 1 se rezolvă doar cerinţa 1, iar dacă T este 2 se rezolvă doar
cerinţa 2.
Începând cu linia a doua se află elementele matricei, o linie a matricei pe o linie a fişierului.
Elementele de pe aceeaşi linie se separă prin câte un spaţiu.
Date de ieşire
Fişierul paralele.out conţine pe prima linie un număr natural reprezentând valoarea cerută.
1&T &2
Pentru 30 de puncte se garantează că T 1, N 2, 2 & M & 500 şi toate elementele 1 de pe
oricare dintre linii, dacă există, formează o secvenţă compactă.
Pentru alte 30 de puncte se garantează că T 2, 2 & N & 500, 2 & M & 500 şi pe oricare
coloană sunt maximum două valori de 1 alăturate.
Pentru alte 9 puncte se garantează că T 1, 2 $ N & 500, 2 & M & 500.
Pentru alte 9 puncte se garantează că T 2, 2 & N & 500, 2 & M & 500.
Pentru alte 12 puncte se garantează că T 1, 35000 & N & 40000 şi 8 & M & 10.
Exemple
Putem evita secventa de cod care necesita timp de calcul de ordin n*n putem proceda
astfel: stocam numarul total de segmente intr-o variabila Total (deci numarul de
segmente din toata matricea). Ne gandim sa imperechem fiecare doua segmente si avem
Total (Total - 1) / 2 variante. Aici insa sunt numarate si imperecheri de segmente
de pe aceeasi linie. Scapam de acestea scazand valorile F[i] * (F[i] 1) / 2 pentru
fiecare linie i.
O alta abordare care obtine punctajul maxim este urmatoarea: odata calculat vectoru
cu semnificatia de mai sus, pentru fiecare valoare i dintre 2 si n adunam la soluti
produsul dintre F[i] si suma valorilor F de pe pozitii de la 1 la i-1. Aceasta suma
obtinem folosind tehnica sumelor partiale in vectorul F.
Grad de dificultate: 3
30
31 int main ()
32 {
33
34 fin>>t>>n>>m;
35
36 for (i=1;i<=n;i++)
37 {
38 for (j=1;j<=m;j++)
39 {
40 fin>>c;
41 if (n<=500)
42 a[i][j] = c;
43 else
44 A[i][j] = c;
45 }
46 }
47
48 if (t == 2)
49 {
50 for (i=1;i<=n;i++)
51 for (j=1;j<=m;j++)
52 b[j][n-i+1] = a[i][j];
53
54 int aux = n;
55
56 n = m;
57 m = aux;
58 for (i=1;i<=n;i++)
59 {
60 for (j=1;j<=m;j++)
61 a[i][j] = b[i][j];
62 a[i][m+1] = 0;
63 }
64 }
65
66 for (i=1;i<=n;i++)
67 {
68 L = 0;
69 for (j=1;j<=m;j++)
70 {
71 if (getA(i, j))
72 L++;
73 else
74 L = 0;
75
76 if (L >= 2)
77 f[i] += (L-1);
78 }
79 }
80
81 for (i=n-1;i>=1;i--)
82 {
83 sol += f[i] * 1LL * f[i+1];
84 f[i] += f[i+1];
85 }
86
87 fout<<sol;
88 return 0;
89 }
13 int A[50005][22];
14
15 ifstream fin ("paralele.in");
16 ofstream fout("paralele.out");
17
18 long long gauss(int n)
19 {
20 return n*1LL*(n-1)/2;
21 }
22
23 inline int getA(int i, int j)
24 {
25 if (n <= 500)
26 return a[i][j];
27 else
28 return A[i][j];
29 }
30
31 int main ()
32 {
33
34 fin>>t>>n>>m;
35
36 for (i=1;i<=n;i++)
37 {
38 for (j=1;j<=m;j++)
39 {
40 fin>>c;
41 if (n<=500)
42 a[i][j] = c;
43 else
44 A[i][j] = c;
45 }
46 }
47
48 if (t == 2)
49 {
50 for (i=1;i<=n;i++)
51 for (j=1;j<=m;j++)
52 b[j][n-i+1] = a[i][j];
53
54 int aux = n;
55 n = m;
56 m = aux;
57 for (i=1;i<=n;i++)
58 {
59 for (j=1;j<=m;j++)
60 a[i][j] = b[i][j];
61 a[i][m+1] = 0;
62 }
63 }
64
65 for (i=1;i<=n;i++)
66 {
67 for (j=2;j<=m;j++)
68 f[j] = 0;
69
70 L = 0;
71 for (j=1;j<=m+1;j++)
72 if (getA(i,j) == 1)
73 L++;
74 else
75 {
76 if (getA(i,j-1) == 1 && L >= 2)
77 f[L]++;
78 L = 0;
79 }
80
81 long long alter = 0;
82 for (j=2;j<=m;j++)
83 {
84 alter += gauss(j) * f[j];
85 }
86
87 sol -= gauss(alter);
88 alterAll += alter;
CAPITOLUL 4. OJI 2019 4.2. PARALELE 37
89 }
90
91 sol += gauss(alterAll);
92 fout<<sol;
93 return 0;
94 }
OJI 2018
5.1 Cruce
Problema 1 - Cruce 100 de puncte
Se consideră o matrice pătratică de dimensiune N , conţinând numere naturale. Numim cruce
de lăţime K reuniunea mulţimii tuturor elementelor aflate pe K linii consecutive ale matricei
şi a mulţimii tuturor elementelor aflate pe K coloane consecutive ale matricei. Două elemente
ale matricei se consideră distincte dacă sunt situate pe poziţii distincte ı̂n matrice. Se acceptă
şi forma degenerată a unei cruci, ı̂n formă de T sau L, când una dintre liniile sau coloanele care
formează crucea sunt chiar la marginea matricei. Vom defini valoarea unei cruci ca fiind suma
elementelor din care aceasta este formată.
Cerinţe
Scrieţi un program care, pentru o valoare K dată, determină o cruce de lăţime K a cărei
valoare este maximă şi poziţia ei ı̂n matrice. Această poziţie va fi exprimată prin perechea de
indici reprezentând prima linie din cele K consecutive şi prima coloană din cele K consecutive din
care este formată crucea.
Date de intrare
Fişierul cruce.in conţine pe prima linie numerele N şi K, iar pe următoarele N linii câte N
numere ı̂ntregi reprezentând ı̂n ordine, pe linii, elementele matricei. Numerele de pe aceeaşi linie
sunt separate prin câte un spaţiu.
Date de ieşire
Fişierul cruce.out va conţine trei numere V max L C, separate prin câte un spaţiu,
reprezentând valoarea maximă determinată pentru o cruce de lăţime K, respectiv linia şi coloana
care exprimă poziţia acesteia ı̂n matrice.
Exemple
38
CAPITOLUL 5. OJI 2018 5.1. CRUCE 39
Practic nu este necesara o noua matrice, se pot memora direct sumele in matricea
initiala A. Astfel suma elementelor pentru orice dreptunghi cu colturile stanga
sus in (i1,j1) si dreapta jos in (i2,j2) se poate afla prin
S[i2,j2]-S[i1,j2]-S[i2,j1]+S[i1,j1].
13 int main()
14 {
15 freopen("cruce.in", "r",stdin);
16 freopen("cruce.out","w",stdout);
17
18 scanf("%d%d\n",&N, &K);
19 assert(N && K && N<501 && K<=N);
20
21 for(i=1; i<=N; ++i)
22 for(j=1; j<=N; ++j)
23 {
24 scanf("%d",&S[i][j]);
25 assert(S[i][j]>=-5000 && S[i][j]<=5000);
26 S[i][j]= S[i-1][j]+S[i][j-1]-S[i-1][j-1] + S[i][j];
27 }
28
29 Smax=(-5000)*500*501;
30 for(i=1; i<=N - K + 1; ++i)
31 for(j = 1; j<=N -K + 1; j++)
32 {
33 Sum= S[i+K-1][N] - S[i-1][N] + S[N][j+K-1]-S[N][j-1];
34 Dif = S[i+K-1][j+K-1]-S[i-1][j+K-1]-S[i+K-1][j-1]+S[i-1][j-1];
35 Sum-=Dif;
36 if(Sum>Smax){Smax=Sum; minL=i; minC=j;}
37 }
38
39 printf("%d %d %d \n", Smax, minL, minC);
40
41 return 0;
42 }
43 }
5.2 pal
Problema 2 - pal 100 de puncte
Micul Prinţ a ajuns ı̂n ţara numerelor palindrom cu număr impar de cifre unde a primit de la
sfetnicul regelui o listă care conţine N numere naturale, fiecare cu număr impar de cifre. Un număr
este palindrom dacă prima lui cifră este egală cu ultima, a doua cu penultima, ş.a.m.d. Acesta
i-a transmis că regele este foarte bolnav. Odată cu regele, numerele din listă s-au ı̂mbolnăvit şi
ele. Sfetnicul i-a spus că lista corectă poate fi obţinută prin ı̂nlocuirea fiecărui număr din ea cu
cel mai mic palindrom mai mare sau egal cu numărul respectiv.
După ce a urmat recomandarea sfetnicului, Micul Prinţ a constatat că ı̂n lista corectă obţinută
toate palindromurile sunt distincte. Uitându-se mai cu atenţie la palindromurile din această listă,
a observat că există perechi de palindromuri ı̂n care cel mai mic se poate obţine din cel mai mare
prin ştergerea aceluiaşi număr de cifre de la ambele capete. De exemplu pentru perechea 7531357
şi 313 palindromul 313 se obţine din 7531357 prin eliminarea a câte două cifre de la ambele capete
ale sale.
Considerăm un şir de palindromuri din lista corectă şi notăm cu X valoarea maximă a acestui
şir. Vom spune că şirul este magic dacă toate palindromurile din el se pot obţine după metoda
descrisă mai sus, din palindromul de valoare X. Un exemplu de şir magic este 4, 53435, 7534357,
89753435798, presupunând că toate aceste numere se regăsesc ı̂n lista corectă.
CAPITOLUL 5. OJI 2018 5.2. PAL 42
Cerinţe
Scrieţi un program care citeşte numerele din lista primită de la sfetnicul regelui şi afişează:
1) Lista corectă obţinută de Micul Prinţ;
2) Numărul de elemente ale celui mai lung şir magic care se poate obţine din lista corectă;
3) Palindromurile din care este format cel mai lung şir magic, afişate ı̂n ordine crescătoare.
Dacă există mai multe astfel de şiruri ı̂n lista corectă a Micului Prinţ, se va afişa cel ı̂n care ultimul
număr este cel mai mare.
Date de intrare
Fişierul de intrare pal.in conţine pe prima linie numărul natural P , care nu poate avea decât
valorile 1, 24 sau 3 şi indică numărul cerinţei care va fi rezolvată. Pe a doua linie numărul natural
N de numere de pe lista primită de la sfetnicul regelui. Pe a treia linie se află numerele naturale
din lista primită de la sfetnic, separate prin câte un spaţiu.
Date de ieşire
Fişierul de ieşire pal.out va conţine pe prima linie răspunsul la cerinţa rezolvată. Dacă s-a
rezolvat prima cerinţă, fişierul de ieşire va conţine un şir de N numere naturale, separate prin
câte un spaţiu, reprezentând numerele din lista corectă, ı̂n ordinea corespunzătoare listei iniţiale.
Dacă s-a rezolvat cerinţa 2, pe prima linie a fişierului de ieşire se va scrie lungimea celui mai lung
şir magic. Dacă s-a rezolvat cerinţa 3, fişierul de ieşire va conţine numerele determinate şi afişate
conform cerinţei.
a 0 $ N & 50000;
a Numerele de pe lista sfetnicului sunt naturale nenule şi fiecare are cel mult 17 cifre;
a Pentru rezolvarea corectă a primei cerinţe se acordă 20 de puncte, pentru rezolvarea corectă
a celei de a doua cerinţe se acordă 20 de puncte, iar pentru rezolvarea corectă a celei de a treia
cerinţe se acordă 50 de puncte.
Exemple
63 else
64 a[i]=a[n-i+1];
65
66 if(a[m+1]>9)
67 {
68 r=0;
69 for(i=m+1;i<=n;i++)
70 r+=a[i], a[i]=r%10, r/=10;
71
72 for(i=1;i<=m;i++)
73 a[i]=a[n-i+1];
74 }
75
76 for(i=n;i>=1;i--)
77 x=x*10+a[i];
78
79 return x;
80 }
81
82 int main()
83 {
84 int i, c, j;
85 f>>C>>n;
86
87 for(i=1;i<=n;i++)
88 f>>z, a[i].x=pal(z,a[i].n);
89
90 if(C==1)
91 for(i=1;i<=n;i++)
92 g<<a[i].x<<’ ’;
93 else
94 {
95 sort(a+1,a+n+1, comp);
96
97 p10[0]=1;
98 for(i=1;i<=18;i++)
99 p10[i]=p10[i-1]*10;
100
101 for(i=n;i>=1;i--)
102 {
103 m=1;
104 s[m]=z=a[i].x;
105 c=a[i].n;
106 for(j=1;j<=c/2;j++)
107 if(CB(z/p10[j]%p10[c-2*j]))
108 s[++m]=z/p10[j]%p10[c-2*j];
109
110 if(m>mx)
111 {
112 mx=m;
113 for(j=1;j<=m;j++)
114 sol[j]=s[j];
115 }
116 }
117
118 if(C==2)
119 g<<mx<<’\n’;
120 else
121 for(i=mx;i>=1;i--)
122 g<<sol[i]<<’ ’;
123 }
124
125 return 0;
126 }
OJI 2017
6.1 tablou
Problema 1 - tablou 100 de puncte
Se consideră un tablou cu N linii şi N coloane (numerotate de la 1 la N ) care conţine valoarea
1 ı̂n fiecare dintre cele N N celule. Valorile din tablou pot fi modificate prin aplicarea a două
operaţii codificate astfel:
a L nr, prin care se schimbă simultan toate semnele numerelor din linia cu numărul nr.
a C nr, prin care se schimbă simultan toate semnele numerelor din coloana cu numărul nr.
Cerinţe
Date de intrare
Date de ieşire
a Dacă p 1, atunci fişierul de ieşire tablou.out conţine pe prima linie un număr natural,
reprezentând numărul valorilor pozitive din tabloul obţinut la finalul executării celor K operaţii
asupra tabloului iniţial (răspunsul la cerinţa 1).
a Dacă p 2, atunci fişierul de ieşire tablou.out conţine pe prima linie un număr natural
reprezentând numărul minim de operaţii L nr sau C nr, care, aplicate tabloului iniţial, ı̂l modifică
astfel ı̂ncât tabloul obţinut să conţină exact Z valori negative (răspunsul la cerinţa 2). Dacă prin
aplicarea de operaţii L nr sau C nr tabloului iniţial nu se poate obţine un tablou cu Z valori
negative, atunci, fişierul va conţine pe prima linie valoarea 0 (zero).
47
CAPITOLUL 6. OJI 2017 6.1. TABLOU 48
Exemple
tablou.in tablou.out Explicaţii
1 10 N=4. La finalul aplicării succesiunii de K=4 operaţii, tablou
44 modificat are conţinutul:
L1 -1 1 1 1
L3 -1 1 1 1
C1 1 -1 -1 -1
L1 -1 1 1 1
Astfel, tabloul conţine 10 valori pozitive.
2 3 Sunt necesare minimum 3 operaţii, de exemplu:
35 L3
L1
C1
2 0 Nu există nicio succesiune de operaţii pentru care să se obţină
47 Z=7 valori negative.
Cautam prima valoare a lui x din multimea {0,1,2,,N} pentru care exista un y
din {0,1,2,..,N} si cu proprietatea ca y=(Z-N*x)/(N-2*x) si N2*x
Daca exista aceste valori x si y, atunci numarul minim de operatii este M=x+y
12
13 char x;
14 int i,y,n,v,k,l,c,s,p;
15
16 void solve1()
17 {
18 for(i=1;i<=k;++i)
19 {
20 fin>>x>>y;
21 if(x==’L’)
22 L[y]=L[y]ˆ1;
23 else
24 C[y]=C[y]ˆ1;
25 }
26
27 l=L.count();
28 c=C.count();
29 fout<<n*n-l*(n-c)-c*(n-l);
30 }
31
32 void solve2()
33 {
34 if(k>n*n)
35 {
36 fout<<0;
37 return;
38 }
39
40 for(s=k/n;s<=n;++s)
41 if((n*s-k)%2==0)
42 {
43 p=(n*s-k)/2;
44 y=sqrt(s*s-4*p);
45 if(y*y==s*s-4*p)
46 {
47 fout<<s;
48 return;
49 }
50 }
51
52 fout<<0;
53 }
54
55 int main()
56 {
57 fin>>v>>n>>k;
58
59 if(v==1)
60 solve1();
61 else
62 solve2();
63
64 fin.close();
65 fout.close();
66 return 0;
67 }
17 fin>>N>>K;
18 char LC;
19 int nr_val_pozitive,nr_val_negative,nr;
20 int i, linimpar=0, colimpar=0;
21
22 for(i=1; i<=K; i++)
23 {
24 fin>>LC>>nr;
25 if(LC==’L’)
26 linie[nr]=(linie[nr]+1)%2;
27 else
28 coloana[nr]=(coloana[nr]+1)%2;
29 }
30
31 for(i=1; i<=N; i++)
32 {
33 linimpar+=linie[i];
34 colimpar+=coloana[i];
35 }
36
37 nr_val_negative=linimpar*(N-colimpar)+(N-linimpar)*colimpar;
38 nr_val_pozitive=N*N - nr_val_negative;
39 fout<<nr_val_pozitive<<endl;
40 }
41
42 void cerinta2()
43 {
44 fin>>N>>Z;
45 int M,linimpar, colimpar,ok=0, i;
46
47 if(Z>N*N)
48 ok=0;
49 else
50 if(Z==N*N)
51 {
52 linimpar=N;
53 colimpar=0;
54 ok=1;
55 }
56 else
57 if(2*Z==N*N)
58 {
59 ok=1;
60 linimpar=N/2;
61 colimpar=0;
62 }
63 else
64 for(linimpar=0; (linimpar<=N) && (ok==0); linimpar++)
65 {
66 if(N-2*linimpar!=0 && (Z-N*linimpar)%(N-2*linimpar)==0)
67 {
68 colimpar=(Z-N*linimpar)/(N-2*linimpar);
69 if(colimpar>=0 && colimpar<=N)
70 {
71 ok=1;
72 break;
73 }
74 }
75 }
76
77 if(ok==0)
78 fout<<0<<endl;
79 else
80 fout<<linimpar+colimpar<<endl;
81 }
82
83 int main()
84 {
85 fin>>P;
86
87 if(P==1)
88 cerinta1();
89 else
90 cerinta2();
91
92 return 0;
CAPITOLUL 6. OJI 2017 6.2. TRIUNGHIURI 51
93 }
6.2 triunghiuri
Problema 2 - triunghiuri 100 de
puncte
Se consideră N puncte din plan, având coordonate numere naturale,
relativ la un reper cartezian XOY , oricare două puncte fiind distincte.
Cerinţe
Date de intrare
Datele de intrare se citesc din fişierul triunghiuri.in, care are următoarea structură:
a Pe prima linie se află numărul p, care indică cerinţa ce trebuie rezolvată (p are valoarea 1
sau 2);
a Pe a doua linie se află numărul natural N , reprezentând numărul punctelor date;
a Pe următoarele N linii se găsesc câte două valori naturale x y, separate prin câte un spaţiu,
reprezentând coordonatele punctelor date.
Date de ieşire
Exemple
Cerinta 2
sumTrParaleleOY = 0;
71 return 0;
72 }
OJI 2016
7.1 arma
Problema 1 - arma 100 de puncte
În anul 2214 a izbucnit primul război interstelar. Pământul a fost atacat de către n civilizaţii
extraterestre, pe care le vom numerota pentru simplicitate de la 1 la n.
Pentru a se apăra, pământenii au inventat o armă specială ce poate fi ı̂ncărcată cu proiectile de
diferite greutăţi, fabricate dintr-un material special denumit narun. Dacă arma este programată la
p
nivelul p, atunci un proiectil de greutate k va ajunge exact la distanţa k km (k la puterea p) faţă
de Pământ şi dacă ı̂n acel punct se află cartierul general al unui atacator, acesta va fi distrus. De
exemplu, dacă arma este programată la nivelul 2, un proiectil de greutate 10 va distruge cartierul
2
general al extratereştrilor situat la distanţa 10 100 km de Pământ.
Arma poate fi ı̂ncărcată cu proiectile de diferite greutăţi, dar cum narunul este un material
foarte rar şi foarte scump, pământenii vor să folosească proiectile cât mai uşoare pentru a distruge
cartierele generale inamice.
Cerinţe
Cunoscându-se n, numărul atacatorilor, precum şi cele n distanţe până la cartierele generale
ale acestora, să se scrie un program care determină:
1. cantitatea minimă de narun necesară pentru a distruge toate cartierele generale inamice;
2. nivelurile la care trebuie programată arma, pentru a distruge fiecare cartier general inamic
cu o cantitate minimă de narun.
Date de intrare
Fişierul de intrare arma.in conţine pe prima linie un număr natural c reprezentând cerinţa care
trebuie să fie rezolvată (1 sau 2). Pe cea de a doua linie se află numărul natural n, reprezentând
numărul atacatorilor. Pe următoarele n linii se află n numere naturale, câte un număr pe o linie;
pe cea de a i-a linie dintre cele n 1 & i & n se află distanţa faţă de Pământ a cartierului general
al celei de a i-a civilizaţii extraterestre.
Date de ieşire
Dacă cerinţa este c 1, atunci pe prima linie a fişierului arma.out va fi scris un număr
natural reprezentând cantitatea minimă de narun necesară distrugerii tuturor cartierelor generale
inamice.
Dacă cerinţa este c 2, atunci fişierul de ieşire arma.out va conţine n linii. Pe a i-a linie
(1 & i & n) se va scrie nivelul la care trebuie programată arma pentru a distruge cartierul general
al celei de a i-a civilizaţii extraterestre.
55
CAPITOLUL 7. OJI 2016 7.1. ARMA 56
Exemple
arma.in arma.out Explicaţii
1 122 Primul cartier general se poate distruge cu un proiectil de greutate
5 10, programat la nivelul 2, al doilea obiectiv cu un proiectil de
100 greutate 97 programat la nivelul 1, al treilea cu un proiectil de
97 greutate 5 programat la nivelul 4, al patrulea cu un proiectil de
625 greutate 7 programat la nivelul 9, iar ultimul cu un proiectil de
40353607 greutate 3 programat la nivelul 4. Cantitatea minimă de narun
81 necesară este 10+97+5+7+3=122.
2 2 Primul cartier general se poate distruge cu un proiectil de greutate
5 1 10, programat la nivelul 2, al doilea obiectiv cu un proiectil de
100 4 greutate 97 programat la nivelul 1, al treilea cu un proiectil de
97 9 greutate 5 programat la nivelul 4, al patrulea cu un proiectil de
625 4 greutate 7 programat la nivelul 9, iar ultimul cu un proiectil de
40353607 greutate 3 programat la nivelul 4. Nivelurile sunt ı̂n ordine: 2 1 4
81 94
Metoda 1
Pentru aceasta vom precalcula numerele prime <2*10ˆ9, folosind ciurul lui Eratosten
Metoda 2.
3 #include <fstream>
4
5 using namespace std;
6
7 ifstream f("arma.in");
8 ofstream g("arma.out");
9
10 int n,v[10002],pr[50000],nr,c[50000],opt,niv; //pr - sirul numerelor prime
11 int fp[50000]; //fp[i] - retine nivelul maxim la care trebuie programata arma
12 // pt a distuge inamicul i
13 long long s; //cantitatea minima necesara
14
15 void ciur()
16 {
17 long long i,j;
18 nr = 0;
19 pr[++nr] = 2;
20 for(i = 3; i <=48000; i = i + 2)
21 {
22 if(c[i] == 0)
23 {
24 pr[++nr] = i;
25 for(j = i; j*i <= 48000; j = j + 2)
26 c[i*j] = 1;
27 }
28 }
29 }
30
31 int cmmdc(int a, int b)
32 {
33 int r;
34 while(b != 0)
35 {
36 r = a % b;
37 a = b;
38 b = r;
39 }
40 return a;
41 }
42
43 int nivel(int d)
44 {//determin cmmdc al puterilor factorilor primi din descompunerea lui d
45 int i,rez, p;
46 rez = 0;//rezultatul
47
48 for(i = 1; i <= nr && pr[i]*pr[i] <= d;++i)
49 {
50 p = 0;
51 while(d % pr[i] == 0)
52 {//cat timp pr[i] este factor prim
53 p = p + 1;//cresc puterea
54 d = d / pr[i]; //noua valoare
55 }
56
57 if(p != 0)
58 {//daca pr[i] apare in descompunere
59 if(p == 1)
60 return 1; //daca apare la puterea 1 nu mai continui
61 else
62 {
63 rez = cmmdc(rez,p); //retin cmmdc al puterilor
64 }
65 }
66 }
67
68 if(rez == 0 || d != 1)
69 return 1;//daca e numar prim sau mai avem un factor primnivelul e 1
70 else
71 return rez;
72 }
73
74 long long x_la_p(long long x, int p, int d)
75 {
76 long long y = x, rasp = 1;
77 while(p > 1)
78 {
CAPITOLUL 7. OJI 2016 7.1. ARMA 58
79 if(p % 2 == 1)
80 rasp = rasp*y;
81 y = y * y;
82 if(y > d)
83 return d + 1;//daca depasesc d nu mai calculez
84 p = p/2;
85 }
86
87 return rasp*y;
88 }
89
90 int cant_minim(int d, int p)
91 {
92 // pentru a determina cantitatea minima trebuie sa determin
93 // o valoare x, a.i. xˆp = d
94 long long x,ls,ld,y;
95 if(p == 1)
96 return d;//daca e numar prim
97
98 x = d;
99 y = 1;
100 //calculez [sqrt(d)]
101 while(x > y)
102 {
103 x = (x + y) / 2;
104 y = d / x;
105 }
106
107 if(p == 2)
108 return x;//daca e patrat perfect
109
110 ls = 1;
111 ld = x;
112 y = x_la_p(x,p,d);
113 while(y != d)
114 {
115 if(y > d)
116 ld = x;
117
118 if(d > y)
119 ls = x;
120
121 x = ls + (ld - ls)/2;
122 y = x_la_p(x,p,d);// calculez x ˆ p
123 }
124
125 return x;
126 }
127
128 int main()
129 {
130 int i;
131 ciur();
132 f>>opt>>n;
133 for(i = 1; i <= n; ++i)
134 {
135 f>>v[i];
136 niv = nivel(v[i]);
137 fp[i] = niv;
138 }
139
140 if(opt == 1)
141 {
142 s = 0; //initial
143 for(i = 1; i <= n; ++i)
144 s = s + cant_minim(v[i],fp[i]);
145 g<<s;
146 }
147
148 if(opt == 2)
149 {
150 for(i = 1; i <= n; ++i)
151 g<<fp[i]<<"\n";
152 }
153
154 return 0;
CAPITOLUL 7. OJI 2016 7.1. ARMA 59
155 }
72 }
73
74 if (d>1)
75 {
76 fp[nrf]=d;
77 mf[nrf++]=1;
78 }
79
80 p=cmmdc();
81 c=1;
82 for (j=0; j<nrf; j++)
83 c*=putere(fp[j],mf[j]/p);
84 }
85
86 void afisare()
87 {
88 int i;
89 if (cerinta==1)
90 fout<<cant<<’\n’;
91 else
92 for (i=0; i<n; i++)
93 fout<<p[i]<<’\n’;
94 fout.close();
95 }
96
97 int cmmdc()
98 {
99 int d=mf[0], i;
100 for (i=1; i<nrf; i++)
101 d=euclid(d,mf[i]);
102 return d;
103 }
104
105 int euclid(int a, int b)
106 {
107 int r;
108 while (b)
109 {
110 r=a%b;
111 a=b;
112 b=r;
113 }
114
115 return a;
116 }
117
118 int putere(int x, int y)
119 {
120 int p;
121 if (y==0)
122 return 1;
123 p=putere(x,y/2);
124 if (y%2)
125 return x*p*p;
126
127 return p*p;
128 }
16 int fp[NRMAXPRIME];
17 int mf[NRMAXPRIME];
18 int p[NMAX];
19 long long int cant;
20
21 void eratostene();
22 void afisare();
23 void determina(int d, int &p, int &c);
24 int cmmdc();
25 int euclid(int a, int b);
26 int putere(int x, int y);
27
28 int main()
29 {
30 int i, d, c;
31 eratostene();
32 fin>>cerinta>>n;
33 for (i=0; i<n; i++)
34 {
35 fin>>d;
36 determina(d,p[i],c);
37 cant+=c;
38 }
39
40 afisare();
41 return 0;
42 }
43
44 void eratostene()
45 {int d, j;
46 for (d=2; d*d<VMAX; d++)
47 if (!ciur[d])
48 for (j=d*d; j<VMAX; j+=d)
49 ciur[j]=1;
50 //transfer intr-un vector numerele prime <=vmax
51 prim[0]=2; nrprim=1;
52 for (d=3; d<VMAX; d+=2)
53 if (!ciur[d]) prim[nrprim++]=d;
54 }
55
56 void determina(int d, int &p, int &c)
57 {int m, j;
58 nrf=0;
59 for (j=0; j<nrprim && prim[j]*prim[j]<=d; j++)
60 {
61 for (m=0; d%prim[j]==0; m++,d/=prim[j]);
62 if (m)
63 {
64 fp[nrf]=prim[j];
65 mf[nrf++]=m;
66 }
67 }
68 if (d>1)
69 {
70 fp[nrf]=d;
71 mf[nrf++]=1;
72 }
73 p=cmmdc();
74 c=1;
75 for (j=0; j<nrf; j++)
76 c*=putere(fp[j],mf[j]/p);
77 }
78
79 void afisare()
80 {int i;
81 if (cerinta==1)
82 fout<<cant<<’\n’;
83 else
84 for (i=0; i<n; i++) fout<<p[i]<<’\n’;
85 fout.close();
86 }
87
88 int cmmdc()
89 {int d=mf[0], i;
90 for (i=1; i<nrf; i++)
91 d=euclid(d,mf[i]);
CAPITOLUL 7. OJI 2016 7.2. KS 62
92 return d;
93 }
94
95 int euclid(int a, int b)
96 {int r;
97 while (b)
98 {
99 r=a%b; a=b; b=r;
100 }
101 return a;
102 }
103
104 int putere(int x, int y)
105 {int p=1;
106 while (y)
107 {
108 if (y%2) {p*=x; y--;}
109 else {x*=x;y/=2;}
110 }
111 return p;
112 }
7.2 ks
Problema 2 - ks 100 de puncte
Ana şi Bogdan au inventat din nou un joc, pe care l-au denumit ks. Pe tabla de joc sunt
plasate pe poziţii consecutive n jetoane, pe fiecare jeton fiind scris un număr natural nenul.
Ana este prima la mutare şi are voie să extragă de pe tablă exact k jetoane situate pe poziţii
consecutive.
Bogdan mută al doilea şi are şi el voie să extragă exact k jetoane, dintre cele rămase pe tablă,
situate de asemenea pe poziţii consecutive.
Punctajul asociat unei mutări este egal cu suma numerelor scrise pe jetoanele extrase la mu-
tarea respectivă.
Scopul Anei este să efectueze mutarea sa astfel ı̂ncât punctajul obţinut de Bogdan să fie cât
mai mic. Considerăm că atât Ana, cât şi Bogdan joacă optim.
Cerinţe
Cunoscând numărul de jetoane de pe tabla de joc, valorile ı̂nscrise pe acestea, precum şi
valoarea k, scrieţi un program care să determine care este cel mai bun punctaj pe care Bogdan ı̂l
poate obţine, ştiind că ambii jucători joacă optim.
Date de intrare
Fişierul de intrare ks.in conţine pe prima linie două numere naturale separate prin spaţiu n
k, având semnificaţia din enunţ. Pe cea de a doua linie se află n valori naturale nenule, separate
prin câte un spaţiu, reprezentând valorile ı̂nscrise pe cele n jetoane, ı̂n ordinea ı̂n care acestea sunt
plasate pe tabla de joc.
Date de ieşire
Fişierul de ieşire ks.out va conţine o singură linie pe care va fi scris un număr natural
reprezentând punctajul maxim pe care ı̂l poate obţine Bogdan la mutarea sa, ştiind că ambii
jucători joacă optim.
a După ce Ana extrage jetoanele sale, jetoanele rămase pe tablă ı̂şi vor păstra poziţiile iniţiale.
CAPITOLUL 7. OJI 2016 7.2. KS 63
Exemple
Vom citi elementele secventei intr-un vector a. Vom utiliza 3 vectori auxiliari,
fiecare cu cate n elemente:
S[i]=suma elementelor secventei a[i], a[i+1], ..., a[i+K-1] (1 <= i <= n-K+1)
Vectorii S si maxst se pot calcula in timp liniar chiar de la citire, dar pentru
a determina maxdr mai este necesara o parcurgere a vectorului S.
Mutarea optima pentru Ana este aceea pentru care cea mai buna varianta pe care
o are Bogdan ii aduce un punctaj minim.
Prin urmare realizam o parcurgere si alegem acel i pentru care pctmax este minim.
13
14 int main()
15 {
16 fin>>n>>k;
17
18 fill(s,s+n+2,gol);
19
20 for(i=1;i<=n;i++)
21 {
22 fin>>a[i];su+=a[i];
23 if(i>=k){su-=a[i-k];s[i-k+1]=su;}
24 }
25
26 st[k]=s[1];
27 for(i=k+1;i<=n;i++)
28 st[i]=max(st[i-1],s[i-k+1]);
29
30 dr[n-k+1]=s[n-k+1];
31 for(i=n-k;i>0;i--)
32 dr[i]=max(dr[i+1],s[i]);
33
34 for(i=2;i<=n-k+1;i++)
35 rez=min(rez,max(st[i-1],dr[i+k]));
36
37 fout<<rez;
38 return 0;
39 }
46
47 for (i=n; i>n-K+1; i--) maxdr[i]=-1;
48 maxdr[n-K+1]=S[n-K+1];
49 for (i=n-K; i>0; i--)
50 if (S[i]>maxdr[i+1])
51 maxdr[i]=S[i];
52 else
53 maxdr[i]=maxdr[i+1];
54 }
55
56 long long int rezolva()
57 {
58 long long int optim=maxdr[K+1], maxim;
59 int i;
60 for (i=2; i<K; i++)
61 if (optim>maxdr[i+K]) optim=maxdr[i+K];
62
63 for (i=K; i<=n-K+1; i++)
64 {//Ana ia o secventa de K jetoane incepand cu pozitia i
65 maxim=maxst[i-1];
66 if (maxim<maxdr[i+K]) maxim=maxdr[i+K];
67 if (optim>maxim) optim=maxim;
68 }
69
70 for (i=n-K+2; i<=n; i++)
71 if (optim>maxst[i-1]) optim=maxst[i-1];
72
73 return optim;
74 }
OJI 2015
8.1 dominant
Problema 1 - dominant 100 de puncte
Considerând un şir de valori binare, numim secvenţă dominantă un set de elemente aflate pe
poziţii consecutive ı̂n şir care are proprietatea că numărul valorilor egale cu 1 este strict mai mare
decât numărul valorilor de 0. De exemplu, ı̂n şirul 1,0,0,0,1,1,0,1,1,1,0,0 o secvenţă dominantă
este 0,1,1 şi o alta, de lungime mai mare, este 0,1,1,0,1,1,1. Secvenţa dominantă maximală este
secvenţa dominantă de lungime maximă. În şirul din exemplu secvenţa dominantă maximală este
1,0,0,0,1,1,0,1,1,1,0 (adică ı̂ntreg şirul, fără ultimul zero).
Cerinţe
Dat un şir de valori binare, să se determine lungimea unei secvenţe dominante maximale
precum şi numărul acestor secvenţe.
Date de intrare
Fişierul dominant.in conţine pe prima linie un număr natural V , iar pe linia a doua şirul de
valori binare, fără spaţii.
Date de ieşire
Fişierul dominant.out va conţine:
a varianta 1: dacă V 1, atunci pe prima linie a fişierului de ieşire va fi un singur număr
natural reprezentând lungimea unei secvenţe dominante maximale.
a varianta 2: dacă V 2, atunci pe prima linie a fişierului de ieşire va fi un singur număr
natural reprezentând numărul secvenţelor dominante maximale.
Restricţii şi precizări
a V " r1, 2x
a Lungimea şirului de valori binare este de cel mult 300 000.
a Pentru toate testele şirul binar va conţine cel puţin o valoare de 1.
a Pentru 60% din punctaj V 1.
Exemple
dominant.in dominant.out Explicaţii
1 100011011100 11 Secvenţa dominantă maximală este 10001101110 şi
are lungimea 11.
2 100011011100 1 Secvenţa dominantă maximală este 10001101110 şi
are lungimea 11. Este o singură secvenţă dominantă
maximală.
1 9 Secvenţa dominantă maximală are lungime 9;
0000110000111 aceasta este 110000111.
2 10000111000 3 Secvenţa dominantă maximală are lungimea 5. Sunt
trei secvenţe dominante maximale: 00111, 01110 şi
11100.
66
CAPITOLUL 8. OJI 2015 8.1. DOMINANT 67
Fie s sirul de biti si fie n lungimea acestuia. Prin s[i..j] vom nota secventa
si, si+1, ..., sj.
Lungimea maxima a secventei dominante este data de cele mai indepartate doua poziti
p si q cu p<q si cu proprietatea ca dif[p] = 1 + dif[q].
Pentru a determina rapid aceste doua valori p si q trebuie memorate in doi vectori,
pentru fiecare valoare x care apare in vectorul dif,
Pentru ca x poate lua valori intre n si n, atunci trebuie avut grija la modul in
care se contruiesc vectorii st si dr.
24 n = 1;
25 while (fin >> s[n])
26 n++;
27 n--;
28 s[0] = ’*’;
29 fin.close();
30
31 st = x + Nmax;
32 dr = y + Nmax;
33
34 // initializare st si dr
35 for (i = -n; i <= n; ++i)
36 {
37 st[i] = 10000000;
38 dr[i] = -10000000;
39 }
40
41 // calcul st si dr
42 // st[i] = cea mai din stanga pozitie unde apare valoarea i
43 // dr[i] = cea mai din dreapta pozitie unde apare valoarea i
44 st[0] = dr[0] = 0;
45 suma = 0;
46 vmin = 10000000;
47 vmax = -10000000;
48 for (i = 1; s[i]; ++i)
49 {
50 if (s[i] == ’0’)
51 suma--;
52 else
53 suma++;
54
55 st[suma] = min(st[suma], i);
56 dr[suma] = max(dr[suma], i);
57 vmin = min(vmin, suma);
58 vmax = max(vmax, suma);
59 }
60
61 //for (i = vmin; i <= vmax; i++)
62 // cout << i << " : " << st[i] << " " << dr[i] << "\n";
63
64 // lungimea maxima a secventei
65 lgMax = 0;
66 for (i = vmin; i < vmax; i++)
67 lgMax = max(lgMax, dr[i+1] - st[i]);
68
69 // numarul de aparitii ale secventei maximale
70 int s0, s1;
71 s0 = s1 = suma = 0;
72 for (i = 1; i <= lgMax; ++i)
73 if (s[i] == ’0’)
74 s0++;
75 else
76 s1++;
77
78 if (s1 > s0) suma++;
79
80 for (i = lgMax + 1; s[i]; ++i)
81 {
82 if (s[i-lgMax] == ’0’)
83 s0--;
84 else
85 s1--;
86
87 if (s[i] == ’0’)
88 s0++;
89 else
90 s1++;
91
92 if (s1 > s0) suma++;
93 }
94
95 ofstream fout(outFile);
96 if (optiune == 1)
97 fout << lgMax << "\n";
98 else
99 fout << suma << "\n";
CAPITOLUL 8. OJI 2015 8.1. DOMINANT 69
100 fout.close();
101
102 return 0;
103 }
35 sum--;
36
37 if (F[sum] == -1)
38 {
39 F[sum] = i;
40
41 if (F[sum - 1] != -1)
42 D[sum] = D[sum-1] + i - F[sum-1];
43 else
44 D[sum] = 0;
45
46 }
47
48 if (F[sum-1] != -1)
49 {
50 sc = D[sum-1] + i - F[sum-1];
51 if (sc > maxim)
52 {
53 maxim = sc;
54 nr = 1;
55 }
56 else
57 if (sc == maxim)
58 nr++;
59 }
60 }
61
62 if (V == 1)
63 fout<<maxim<<"\n";
64 else
65 fout<<nr<<"\n";
66
67 return 0;
68 }
39 for (i=1;S[i];i++)
40 {
41 if (S[i] == ’1’)
42 sum++;
43 else
44 sum--;
45
46 if (F[sum] == -1)
47 {
48 F[sum] = i;
49
50 if (F[sum - 1] != -1)
51 D[sum] = D[sum-1] + i - F[sum-1];
52 else
53 D[sum] = 0;
54
55 }
56
57 if (F[sum-1] != -1)
58 {
59 sc = D[sum-1] + i - F[sum-1];
60 if (sc > maxim)
61 {
62 maxim = sc;
63 nr = 1;
64 }
65 else
66 if (sc == maxim)
67 nr++;
68 }
69 }
70
71 if (V == 1)
72 fprintf(fout, "%d\n", maxim);
73 //fout<<maxim<<"\n";
74 else
75 fprintf(fout, "%d\n", nr);
76
77 //fout<<nr<<"\n";
78
79 return 0;
80 }
31 v[i] = v[i-1] + 1;
32 else
33 v[i] = v[i-1] - 1;
34
35 w[i] = make_pair(v[i], i);
36 }
37
38 sort(w, w+n+1);
39
40 j = 1;
41 for (i=2;i<=n;i++)
42 if (w[i].first != w[j].first)
43 {
44 w[++j] = w[i];
45 } else
46 if (w[i].first == w[j-1].first)
47 w[j] = w[i];
48 else
49 w[++j] = w[i];
50
51 if (w[1].second < w[0].second)
52 {
53 minim1 = w[1].second;
54 minim2 = w[0].second;
55 }
56 else
57 {
58 minim1 = w[0].second;
59 minim2 = w[1].second;
60
61 }
62
63 n = j;
64 maxim = -INF;
65 for (i=2;i<=n;i++)
66 {
67 if (w[i-1].second != minim1)
68 {
69 maxim = max(maxim, w[i].second-minim1);
70 }
71 else
72 {
73 maxim = max(maxim, w[i].second-minim2);
74 }
75
76 if (w[i].second < minim1)
77 {
78 minim2 = minim1;
79 minim1 = w[i].second;
80 }
81 else
82 if (w[i].second < minim2)
83 minim2 = w[i].second;
84 }
85
86 for (i=maxim;i<=m;i++)
87 if (v[i] - v[i-maxim] > 0)
88 nr++;
89
90 if (V == 1)
91 fout<<maxim<<"\n";
92 else
93 fout<<nr<<"\n";
94
95 return 0;
96 }
8.2 pavare
Problema 2 - pavare 100 de puncte
Ca ı̂n mai toate poveştile, Făt-Frumos a căutat o Cosânzeană şi a găsit-o, dar tatăl ei i-a cerut
să-i paveze drumul de lungime N care leagă castelele sale. Dalele cu care va pava drumul au
aceeaşi lăţime (egală cu lăţimea drumului) şi lungimi numere naturale. Fiind un ı̂mpărat cam
sâcâit, acesta doreşte ca pavarea să se facă folosind un număr minim de dale, diferenţa de lungime
ı̂ntre două dale vecine să nu fie mai mare ca 1, iar prima şi ultima dală să fie de lungime 1.
Împăratul nu se mulţumeşte să primească de la Făt-Frumos doar un număr (numărul minim de
dale necesare): el vrea şi posibilitatea de pavare cea mai mică din punct de vedere lexicografic.
Compararea lexicografică a două şiruri de numere este o extensie la numere a comparării
alfabetice a două cuvinte. Astfel, fiind date două şiruri numerice de aceeaşi lungime, A1 , A2 , ...,
Am şi B1 , B2 , ..., Bm , acestea sunt egale dacă şi numai dacă Ai Bi pentru orice i de la 1 la
m. Şirul A este mai mic lexicografic decât şirul B dacă există o valoare k astfel ı̂ncât Ak $ Bk
şi Ai Bi pentru orice i de la 1 la k 1. De exemplu, şirul 3, 5, 4, 1 este mai mare lexicografic
decât şirul 3, 5, 2, 9 pentru că prima poziţie pe care valorile diferă este poziţia 3 (4 % 2), fără a
mai conta valorile aflate după aceasta.
Cerinţe
Cunoscând lungimea drumului, determinaţi numărul minim de dale necesare pavării şi posibil-
itatea de pavare cu număr minim de dale, care este cea mai mică din punct de vedere lexicografic.
Date de intrare
Prima linie a fişierului pavare.in conţine un număr natural V . Linia a doua conţine un număr
natural N ce reprezintă lungimea drumului.
Date de ieşire
Dacă V va avea valoarea 1, ı̂n fişierul pavare.out se va scrie, pe prima linie, doar numărul
minim de dale necesare pavării.
Dacă V va avea valoarea 2, ı̂n fişierul pavare.out se va scrie, pe prima linie, un şir de numere
separate prin câte un spaţiu, ce reprezintă soluţia de pavare a drumului, folosind un număr minim
de dale, care este cea mai mică din punct de vedere lexicografic.
a V " r1, 2x
a 1 & N & 1000000000
a Pentru 30% din punctaj V 1.
Exemple
Dist(x) 1 2 4 6 9 12 16 20 25
Dale 1 11 121 1221 12321 123321 1234321 12344321 123454321
Pentru o distanta data n cautam cel mai mic x pentru care Dist(x)>=n.
Pentru a determina solutia de pavare cea mai mica din punct de vedere
lexicografic sunt 2 posibilitati:
1) Se genereaza intr-un vector sirul de dale de lungime x care are cea mai
mare lungime, iar apoi pornind de la x/2+x%2 (de la jumatatea vectorului)
in jos se scade unu din fiecare termen. Numarul de termeni din care se face
scaderea este egal cu Dist(x)-n, unde x este numarul de dale, iar n lungimea
drumului.
32
33 // calculez un numar k a.i 1,2,..k,k,k-1,...1 sa fie cel mult N
34 for( K = 1; K*(K+1) <= CN ; ++K);
35 K--;
36 NrDale += 2*K;
37 CN = CN - K*(K+1);
38
39 //incerc sa asez dale cat mai mari care sa acopere distanta ramasa
40 Dala = K+1;
41 while(CN && Dala > 0)
42 {
43 while(CN >= Dala) { CN -= Dala; NrDale++;}
44 Dala--;
45 }
46 OU<<NrDale<<’\n’;
47 }
48 else
49 {
50 //rezolvarea cerintei 2
51 for(i = 1; i < NMAX; ++i) NrApar[i] = 0;
52 CN = N;
53 NrDale = 0;
54
55 // calculez un numar k a.i 1,2,..k,k,k-1,...1 sa fie cel mult N
56 for( K = 1; K*(K+1) <= CN ; ++K);
57 K--;
58 NrDale += 2*K;
59 CN = CN - K*(K+1);
60 for( i = 1; i <= K; ++i) NrApar[i] += 2;
61
62 //incerc sa asez dale cat mai mari care sa acopere distanta ramasa
63 Dala = K+1;
64 while(CN && Dala > 0)
65 {
66 while(CN >= Dala) { CN -= Dala; NrApar[Dala]++; NrDale++;}
67 Dala--;
68 }
69
70 for ( i = 1; i<NMAX; ++i)
71 {
72 if(NrApar[i] % 2 == 1)
73 {
74 for( K = 1; K <= NrApar[i]/2 + 1; ++K) OU<<i<<’ ’;
75 NrApar[i] /= 2;
76 }
77 else
78 {
79 for( K = 1; K <= NrApar[i]/2; ++K) OU<<i<<’ ’;
80 NrApar[i] /= 2;
81 }
82 }
83
84 for ( i = NMAX - 1; i>0; --i)
85 {
86 for( K = 1; K <= NrApar[i]; ++K) OU<<i<<’ ’;
87 }
88 }
89
90 OU.close();
91 return 0;
92 }
12 {
13 int i;
14 if (v[k-1] == 1)
15 {
16 if (k-1 < minim)
17 {
18 minim = k-1;
19 for (i=1;i<=k-1;i++)
20 {
21 vminim[i] = v[i];
22 }
23 }
24 else
25 if (k-1 == minim)
26 {
27 for (i=1;i<=k-1;i++)
28 if (v[i] != vminim[i])
29 break;
30
31 if (v[i] < vminim[i])
32 {
33 for (i=1;i<=k-1;i++)
34 {
35 vminim[i] = v[i];
36 }
37 }
38 }
39 }
40 }
41 else
42 {
43 for (int i=v[k-1]-1;i<=v[k-1]+1;i++)
44 {
45 if (i == 0)
46 continue;
47
48 if (i <= s)
49 {
50 s-=i;
51 v[k] = i;
52 back(k+1);
53 s+=i;
54 }
55 }
56 }
57 }
58
59 ifstream fin ("pavare.in");
60 ofstream fout("pavare.out");
61
62 int main()
63 {
64 fin>>V;
65 fin>>n;
66 minim = INF;
67 v[1] = 1;
68 s = n-1;
69
70 back(2);
71
72 if (V == 1)
73 fout<<minim<<"\n";
74 else
75 {
76 for (i=1;i<=minim;i++)
77 fout<<vminim[i]<<" ";
78 fout<<"\n";
79 }
80 }
12
13 int main()
14 {
15 fin>>V;
16 fin>>x;
17
18 r = (long long)((1 + sqrt(4*x))/2);
19 //cout<<r;
20
21 if (x <= (r*(r-1) + r) )
22 {
23
24 if (V == 1)
25 {
26 fout<<2*r-1<<"\n";
27 return 0;
28 }
29
30 if (x == r*(r-1))
31 {
32 for (i = 1; i<=r-1; i++)
33 fout<<i<<" ";
34
35 for (i=r-1;i>=1;i--)
36 fout<<i<<" ";
37 }
38 else
39 {
40 k = x - (r*(r-1));
41 for (i=1;i<=k;i++)
42 fout<<i<<" ";
43
44 for (i=k;i<=r-1;i++)
45 fout<<i<<" ";
46
47 for (i=r-1;i>=1;i--)
48 fout<<i<<" ";
49 }
50
51 }
52 else
53 {
54 if (V == 1)
55 {
56 fout<<2*r<<"\n";
57 return 0;
58 }
59
60 k = x - (r*(r-1)) - r;
61
62 for (i=1;i<=k;i++)
63 fout<<i<<" ";
64
65 for (i=k;i<=r;i++)
66 fout<<i<<" ";
67
68 for (i=r-1;i>=1;i--)
69 fout<<i<<" ";
70 }
71
72 return 0;
73 }
OJI 2014
9.1 arrows
Problema 1 - arrows 100 de
puncte
”Arrows” este un joc care se joacă pe o tablă dreptunghiulară a cărei
suprafaţă este ı̂mpărţită ı̂n N M celule, aranjate pe N linii şi M coloane.
În fiecare celulă se află o săgeată (sus, jos, stânga sau dreapta), ca ı̂n figura
alăturată.
Când este la mutare, un jucător poate alege o poziţie de start pe care
plasează un jeton, apoi deplasează jetonul la celula ı̂nvecinată ı̂n sensul in-
dicat de săgeată. Deplasarea continuă până când jetonul părăseşte tabla
de joc, caz ı̂n care jucătorul obţine un punctaj egal cu numărul de celule
parcurse de jetonul său.
Există ı̂nsă poziţii de start denumite favorabile, pentru care jetonul nu
Figura 9.1: arrows
va părăsi niciodată tabla de joc. De exemplu, toate poziţiile din figură cu
fundal gri sunt favorabile. Jucătorul care alege o poziţie de start favorabilă obţine un punctaj egal
cu numărul de celule distincte vizitate ı̂nmulţit cu 1000.
Cerinţe
Scrieţi un program care, cunoscând configuraţia tablei de joc, rezolvă una dintre următoarele
cerinţe:
1. determină punctajul pe care ı̂l obţine un jucător care plasează jetonul său pe o poziţie de
start specificată;
2. determină numărul de celule favorabile de pe tabla de joc;
3. determină punctajul maxim pe care jucătorul ı̂l poate obţine la o mutare, alegând convenabil
poziţia de start.
Date de intrare
Fişierul de intrare arrows.in conţine pe prima linie cerinţa care trebuie să fie rezolvată (1, 2 sau
3). Pe a doua linie se află numerele naturale N M , care reprezintă numărul de linii şi respectiv de
coloane de pe tabla de joc. Pe următoarele N linii se află câte M numere din mulţimea r1, 2, 3, 4x
reprezentând săgeţile aflate ı̂n celulele de pe tabla de joc (1 semnificând săgeata la dreapta, 2
săgeata ı̂n sus, 3 săgeata la stânga şi 4 săgeata ı̂n jos). Pe ultima linie sunt scrise numerele
naturale lin col, reprezentând linia şi coloana pe care se află poziţia de start specificată. Valorile
scrise pe aceeaşi linie ı̂n fişierul de intrare sunt separate prin spaţii.
Date de ieşire
Fişierul de ieşire arrows.out va conţine o singură linie pe care va fi scris un număr natural
reprezentând răspunsul pentru cerinţa specificată pe prima linie a fişierului de intrare.
Restricţii şi precizări
a 1 & N, M & 500
a Liniile sunt numerotate de la 1 la N , iar coloanele de la 1 la M .
a Punctaj. Pentru teste valorând 20 de puncte cerinţa este 1. Pentru teste valorând 40 de
puncte cerinţa este 2. Pentru celelalte teste, valorând de asemenea 40 de puncte, cerinţa este 3.
81
CAPITOLUL 9. OJI 2014 9.1. ARROWS 82
Exemple
arrows.in arrows.out Explicaţii
1 2000 Exemplul corespunde tablei de joc din figură.
6 5 Punctajele pentru fiecare poziţie sunt:
3 1 1 4 2 1 14000 14000 14000 1
1 2 4 3 1 15000 14000 14000 14000 1
4 2 1 1 4 16000 14000 14000 14000 14000
1 2 3 3 3 15000 14000 14000 14000 14000
3 1 4 4 4 1 4000 4000 2 2000
2 2 3 4 2 2 4000 4000 1 2000
5 5 Cerinţa este 1: punctajul care se obţine plecând din poziţia de
start aflată pe linia 5 şi coloana 5 este 2000.
2 23 Cerinţa este 2: există 23 de poziţii favorabile.
6 5
3 1 1 4 2
1 2 4 3 1
4 2 1 1 4
1 2 3 3 3
3 1 4 4 4
2 2 3 4 2
5 5
3 16000 Cerinţa este 3: punctajul maxim se poate obţine plasând jetonul
6 5 ı̂n punctul de start de pe linia 3 şi coloana 1.
3 1 1 4 2
1 2 4 3 1
4 2 1 1 4
1 2 3 3 3
3 1 4 4 4
2 2 3 4 2
5 5
Cerinta 1.
int dl[5]={0,0,-1,0,1};
int dc[5]={0,1,0,-1,0};
CAPITOLUL 9. OJI 2014 9.1. ARROWS 83
dir=a[x][y];
x+=dl[dir];
y+=dc[dir];
Pentru a testa daca o anumita pozitie a mai fost deja vizitata vom utiliza
inca o matrice nitial in aceasta matrice se afla doar valoarea 0 (indicand
pozitii nevizitate).
Cerintele 2 si 3
Tabla de joc este de dimensiuni mari, prin urmare trebuie sa rezolvam eficient
cerintele b si c.
In functie de modul in care acest traseu se termina (in afara tablei de joc
sau intr-o pozitie deja vizitata) decidem daca pozitia este sau nu favorabila
(daca se termina in afara tablei de joc este nefavorabila; daca se termina
intr-o pozitie deja vizitata, pozitia de start este de acelasi tip cu pozitia
vizitata daca aceasta se afla pe un alt traseu sau (al doilea caz) este
favorabila daca pozitia vizitata se afla pe traseul curent).
56 }
57
58 void citire()
59 {int i, j, x;
60 fin>>cerinta>>n>>m;
61 assert(1<= cerinta && cerinta <=3);
62 assert(0<n && n<501);
63 assert(0<m && m<501);
64
65 for (i=1; i<=n; i++)
66 for (j=1; j<=m; j++)
67 {
68 assert(fin>>x);
69 a[i][j]=x;
70 assert(x>0 && x<5);
71 }
72
73 fin>>xs>>ys;
74 assert(0<xs && xs<=n);
75 assert(0<ys && ys<=m);
76 assert(!(fin>>i));
77 }
78
79 int goxy(int x, int y)
80 {
81 int sum=0, dir, cx=x, cy=y, s;
82
83 while (a[x][y] && !pct[x][y])
84 {pct[x][y]=1; sum++;
85 dir=a[x][y];
86 x+=dl[dir]; y+=dc[dir];
87 }
88
89 //completarea punctajelor in pct
90 if (!a[x][y] || pct[x][y]<0) //pozitie nefavorabila
91 {sum-=pct[x][y]; x=cx, y=cy; s=-sum;
92 while (pct[x][y]==1)
93 {pct[x][y]=s; s++;
94 dir=a[x][y];
95 x+=dl[dir]; y+=dc[dir];
96 }
97
98 return -sum; //pozitie nefavorabila
99 }
100
101 if (pct[x][y]==1) //pozitie favorabila de pe acelasi traseu
102 {
103 //se formeaza un circuit care contine pozitia x,y
104 //toate punctele de pe circuit se marcheaza cu acelasi punctaj
105 s=sum;
106 while (cx!=x || cy!=y)
107 {
108 pct[cx][cy]=s; s--;
109 dir=a[cx][cy];
110 cx+=dl[dir]; cy+=dc[dir];
111 }
112
113 //punctele de pe circuit le marchez cu s
114 while (pct[x][y]==1)
115 {pct[x][y]=s;
116 dir=a[x][y];
117 x+=dl[dir]; y+=dc[dir];
118 }
119 return sum;
120 }
121
122 //pozitie favorabila care conduce catre un circuit
123 s=sum+pct[x][y];
124 while (cx!=x || cy!=y)
125 {
126 pct[cx][cy]=s; s--;
127 dir=a[cx][cy];
128 cx+=dl[dir]; cy+=dc[dir];
129 }
130 return sum+pct[x][y];
131 }
CAPITOLUL 9. OJI 2014 9.1. ARROWS 86
75
76 }
77 else
78 if(ciclu[x][y]==1)
79 {//am ajuns intr-un punct ce face parte dintr-un ciclu det anterior
80 tk=tk+nr-mx;
81 val=t[x][y];
82 nr=nr-mx;
83 l=i;c=j;
84 if(nr*1000+val>Max)
85 Max=nr*1000+val;
86 while(!(l==x&&c==y)){
87 t[l][c]=nr*1000+val;ciclu[l][c]=1;
88 nr--;
89 int k=a[l][c];
90 l=l+dx[k];
91 c=c+dy[k];
92 }
93 }
94 else
95 {
96 val=t[x][y];
97 nr=nr-mx;
98 l=i;c=j;
99 if(nr+val>Max)
100 Max=nr+val;
101 while(!(l==x&&c==y)){
102 t[l][c]=nr+val;
103 nr--;
104 int k=a[l][c];
105 l=l+dx[k];
106 c=c+dy[k];
107 }
108 }
109 }
110 else
111 {
112 l=i;c=j;nr=nr-mx;
113 while(t[l][c]!=-1){
114 t[l][c]=nr--;
115 int k=a[l][c];
116 l=l+dx[k];
117 c=c+dy[k];
118
119 }
120 if(t[i][j]>Max)
121 Max=t[i][j];
122 }
123 }
124 f>>x>>y;
125 if(tip==1){
126 g<<t[x][y]<<’\n’;
127 return 0;
128 }
129 if(tip==2){
130 g<<tk<<’\n’;
131 return 0;
132 }
133 g<<Max<<’\n’;
134 return 0;
135 }
9.2 tcif
Problema 2 - tcif 100 de puncte
Avem la dispoziţie patru numere naturale N , A, B, C, precum şi trei cifre c1, c24, c3 distincte
două câte două.
CAPITOLUL 9. OJI 2014 9.2. TCIF 88
Cerinţe
Să se determine numărul natural minim, strict mai mare decât N , care are exact A cifre c1,
B cifre c2, C cifre c3 şi nu conţine alte cifre.
Date de intrare
Fişierul de intrare tcif.in conţine pe prima linie, separate prin câte un spaţiu, numerele naturale
A B C c1 c2 c3. Pe linia a doua se află numărul natural N .
Date de ieşire
Fişierul de ieşire tcif.out va conţine o singură linie pe care va fi scris cel mai mic număr natural
strict mai mare decât N care conţine exact A cifre c1, exact B cifre c2 şi exact C cifre c3 şi nu
conţine alte cifre.
Exemple
Mai intai vom considera ca avem relatiile c1 < c2 < c3 (in caz contrar
ordonam crescator cele trei cifre).
1. A+B+C este strict mai mare decat numarul cifrelor lui N. Atunci solutia
este data de numarul care are primele A cifre c1, urmatoarele B cifre c2 si
ultimele C cifre c3, cu precizarea ca daca c1=0, atunci se pune o cifra c2
ca cifra cea mai semnificativa.
solutia problemei.
100 puncte
Cazul al II-lea:
Verific daca cifra plasata pe aceasta pozitie poate fi marita (cu cea mai
mica dintre cifrele c1, c2, c3 care mai are aparitii disponibile). Daca nu
poate fi marita, mergem inapoi (adica spre cea mai semnificativa cifra) in
vector (cu atentie la contorizarea numarului de aparitii disponibile pentru
cele 3 cifre) si ma opresc la prima pozitie pe care se afla o cifra care
poate fi marita (o astfel de pozitie sigur exista pentru datele de test).
24 return 0;
25 }
26 if (cif[1] >= x && a[1] > 0) return 1;
27 if (cif[2] >= x && a[2] > 0) return 2;
28 return -1;
29 }
30
31 int main()
32 {
33 int i, j, k, nr, gata;
34 char s[Dim];
35
36 // citire
37 ifstream fin(inFile);
38 fin >> a[0] >> a[1] >> a[2] >> cif[0] >> cif[1] >> cif[2];
39 fin >> s;
40 fin.close();
41
42 // init
43 for (n = 0; s[n]; ++n)
44 t[n] = s[n] - ’0’;
45
46 for (j = 0; j < 2; ++j)
47 for (k = j + 1; k < 3; ++k)
48 if (cif[j] > cif[k])
49 {
50 nr = cif[j];
51 cif[j] = cif[k];
52 cif[k] = nr;
53 nr = a[j];
54 a[j] = a[k];
55 a[k] = nr;
56 }
57
58 // rezolvare caz 1 : n < a + b + c
59 if (n < a[0] + a[1] + a[2])
60 {
61 ofstream fout(outFile);
62 if (cif[0] == 0)
63 {
64 fout << cif[1];
65 a[1]--;
66 }
67 for (i = 0; i < 3; ++i)
68 for (j = 1; j <= a[i]; ++j)
69 fout << cif[i];
70 fout << "\n";
71 fout.close();
72 return 0;
73 }
74
75 // caz 2 : n = a + b + c
76 i = 0;
77 gata = 0;
78 while (!gata && (i < n) && ((k = AlegeCifra(t[i])) != -1))
79 {
80 sol[i] = k;
81 a[k]--;
82 if (cif[sol[i]] > t[i]) gata = 1;
83 else i++;
84 }
85 if (gata == 1) // pun restul cifrelor in sol, ordonate crescator
86 {
87 for (k = 0; k < 3; ++k)
88 for (j = 1; j <= a[k]; ++j)
89 sol[++i] = k;
90 }
91 else // ma intorc inapoi
92 {
93 i--;
94 while ((k = AlegeCifra(t[i] + 1)) == -1)
95 {
96 a[sol[i]]++; // pun cifra inapoi
97 i--;
98 }
99 a[sol[i]]++;
CAPITOLUL 9. OJI 2014 9.2. TCIF 91
100 sol[i] = k;
101 a[k]--;
102 for (k = 0; k < 3; ++k)
103 for (j = 1; j <= a[k]; ++j)
104 sol[++i] = k;
105 }
106
107 ofstream fout(outFile);
108 for (i = 0; i < n; ++i)
109 fout << cif[sol[i]];
110
111 fout << "\n";
112 fout.close();
113
114 return 0;
115 }
57 if (poz) nr[cif[poz]]--;
58 else break;
59 }
60 //i indica cea mai din dreapta pozitie care nu apare in cif
61 //caut de la i catre lg prima pozitie care poate fi marita
62 poz=potmari(cn[i]);
63 if (poz)
64 {
65 cn[i]=cif[poz]; nr[cif[poz]]--;
66 }
67 else
68 {i++;
69 while (1)
70 {
71 poz=potmari(cn[i]);
72 if (!poz) {nr[cn[i]]++; i++;}
73 else break;
74 }
75 nr[cn[i]]++; cn[i]=cif[poz]; nr[cif[poz]]--;
76 }
77
78 //completez cn de la i-1 la 0 cu cele mai mici valori posibile
79 for (j=i-1, poz=1; poz<=3; poz++)
80 while (nr[cif[poz]]) {cn[j--]=cif[poz]; nr[cif[poz]]--;}
81
82 //afisez cn
83 for (i=lg-1; i>=0; i--) fout<<cn[i];
84 fout<<’\n’;
85 fout.close();
86 return 0;
87 }
88
89
90 int apare(int x)
91 //caut cifra x in vectorul cif
92 //daca apare, returnez poz pozitia pe care apare
93 {int i;
94 for (i=1; i<=3; i++)
95 if (cif[i]==x && nr[cif[i]]) return i;
96 return 0;
97
98 }
99
100 int potmari(int x)
101 //caut in cif prima cifra >x
102 {int i;
103 for (i=1; i<=3; i++)
104 if (cif[i]>x && nr[cif[i]]) return i;
105 return 0;
106 }
OJI 2013
10.1 maxp
Problema 1 - maxp 100 de puncte
Considerăm un şir de numere a1 , a2 , ..., aN . O secvenţă nevidă ı̂n acest şir este de forma ai
ai1 ... aj , unde i & j. De exemplu, pentru N 4 şi şirul 2 3 4 3, secvenţele nevide sunt: 2, 2
3, 2 3 4, 2 3 4 3, 3, 3 4, 3 4 3, 4, 4 3, 3. Definim puterea unui element ai ca fiind numărul de
secvenţe care-l conţin pe ai şi ı̂n care ai este strict mai mare decât celelalte elemente ale fiecăreia
dintre respectivele secvenţe. Astfel ı̂n şirul 2 3 4 3 puterea elementului a1 este 1 (fiind maxim
doar ı̂n secvenţa formată din el ı̂nsuşi), a elementului a2 este 2 (a2 fiind maxim ı̂n secvenţele 2 3
şi 3), a elementului a3 este 6 (fiind maxim ı̂n secvenţele 2 3 4, 2 3 4 3, 3 4, 3 4 3, 4 şi 4 3), iar a
elementului a4 este 1.
Cerinţe
Scrieţi un program care determină puterea cea mai mare a unui element din şirul dat, precum
şi numărul de elemente din şir care au cea mai mare putere.
Date de intrare
Fişierul maxp.in conţine pe prima linie numărul natural N , iar pe a doua linie, ı̂n ordine,
numerele naturale a1 , a2 , ..., aN separate prin câte un spaţiu.
Date de ieşire
Fişierul maxp.out va conţine pe prima linie un număr natural ce reprezintă puterea cea mai
mare a unui element din şirul dat şi pe a doua linie va conţine un număr natural ce reprezintă
numărul de elemente din şir care au cea mai mare putere.
Exemple
maxp.in maxp.out Explicaţii
7 12 Elementul 5 de pe poziţia 4 este maxim ı̂n 12 secvenţe:
9345122 1 3 4 5, 3 4 5 1, 3 4 5 1 2, 3 4 5 1 2 2, 4 5, 4 5 1, 4 5 1 2,
4 5 1 2 2, 5, 5 1, 5 1 2, 5 1 2 2, deci puterea lui este 12.
Este singurul element care are această putere, celelalte
elemente având puteri mai mici.
6 3 Elementele din poziţiile 3 şi 4 sunt maxime ı̂n 3 secvenţe,
107726 2 deci puterea lor este 3. Celelalte elemente au puteri mai
mici.
95
CAPITOLUL 10. OJI 2013 10.1. MAXP 96
2 #include<cstdio>
3 #include<fstream>
4
5 #define inFile "maxp.in"
6 #define outFile "maxp.out"
7 #define dim 200001
8
9 using namespace std;
10
11 int a[dim], st[dim], dr[dim], q[dim], poz[dim], n;
12
13 int main()
14 {
15 int i, k, x, nrsol;
16 long long p, pmax;
17
18 //citire
19 freopen(inFile, "r", stdin);
20 scanf("%d", &n);
21 for (i = 1; i <= n; i++)
22 scanf("%d", a + i);
23
24 // constructie st
25 k = 0;
26 q[k] = dim + 2;
27 poz[k] = 0;
28 st[k] = 0;
29 for (i = 1; i <= n; i++)
30 {
31 x = a[i];
32 while (q[k] < x) k--;
33 st[i] = i - poz[k] - 1;
34 k++;
35 q[k] = x;
36 poz[k] = i;
37 }
38
39 // constructie dr
40 k = 0;
41 q[k] = dim + 2;
42 poz[k] = n + 1;
43 dr[k] = 0;
44 for (i = n; i >= 1; i--)
45 {
46 x = a[i];
47 while (q[k] < x) k--;
48 dr[i] = poz[k] - i - 1;
49 k++;
50 q[k] = x;
51 poz[k] = i;
52 }
53
54 // calcul
55 nrsol = 1;
56 pmax = (st[1] + 1);
57 pmax *= (dr[1] + 1);
58 for (i = 2; i <= n; i++)
59 {
60 p = (st[i] + 1);
61 p = (p * (dr[i] + 1));
62 if (p > pmax)
63 {
64 pmax = p;
65 nrsol = 1;
66 }
67 else if (p == pmax) nrsol++;
68 }
69
70 ofstream fout(outFile);
71 fout << pmax << "\n" << nrsol << "\n";
72 fout.close();
73
74 return 0;
75 }
76
77 return 0;
78 }
24 j=i-1;
25 while (a[j]<a[i])
26 j = j - st[j] - 1;
27 st[i]=i-j-1;
28 }
29
30 for (i=n;i>=1;i--)
31 {
32 j = i+1;
33 while (a[j]<a[i])
34 j = j + dr[j] + 1;
35 dr[i]=j-i-1;
36 }
37
38 for (i=1;i<=n;i++)
39 {
40 putere = st[i] + dr[i] + 1 + st[i]*dr[i];
41 if (putere > pmax)
42 pmax = putere,ct=1;
43 else
44 if (putere == pmax)
45 ct++;
46 }
47
48 ofstream gg("maxp.out");
49 gg << pmax << "\n" << ct << "\n";
50
51 f.close();
52 gg.close();
53
54 return 0;
55 }
10.2 puncte
Problema 2 - puncte 90 de
puncte
Andrei se descurcă foarte bine la geometrie şi de aceea născoceşte tot felul de jocuri pe care
le testează cu Alexandru, colegul său de bancă. Pentru a pregăti noul joc cu trei niveluri, Andrei
desenează pe o foaie de matematică reperul cartezian xOy şi mai multe puncte distincte. Fiecare
punct desenat are atât abscisa x, cât şi ordonata y, numere ı̂ntregi.
La primul nivel, Alexandru determină numărul maxim de puncte (dintre cele desenate) aflate
pe una dintre axele sistemului cartezian sau pe o dreaptă paralelă cu una dintre cele două axe.
La al doilea nivel, Alexandru consideră toate punctele desenate a căror abscisă x şi ordonată
y verifică cel puţin una dintre relaţiile x y sau x y 0 şi apoi calculează câte drepte distincte
trec prin cel puţin două dintre aceste puncte.
La al treilea nivel, Alexandru numără şi şterge punctele din 3 ı̂n 3 (primul, al 4-lea, al 7-lea
etc.), ı̂ncepând cu cel mai din stânga punct desenat şi continuând către dreapta. Dacă două sau
mai multe puncte au aceeaşi abscisă, el le numără pe acestea de jos ı̂n sus (ı̂ncepând de la punctul
cu ordonata cea mai mică). Când a ajuns cu număratul la cel mai din dreapta punct continuă cu
cel mai din stânga punct rămas.
Alexandru se opreşte cu numărarea şi ştergerea când rămâne un singur punct desenat pe foaie.
Exemplu:
CAPITOLUL 10. OJI 2013 10.2. PUNCTE 103
Cerinţe
Scrieţi un program care citeşte numărul natural nenul N , apoi cele 2 N numere ı̂ntregi ce
reprezintă coordonatele celor N puncte şi determină:
a) N RP , numărul maxim de puncte (dintre cele desenate) aflate pe una dintre axele sistemului
cartezian sau pe o dreaptă paralelă cu una dintre cele două axe;
b) N RD, numărul de drepte distincte care trec prin cel puţin două dintre punctele desenate a
căror abscisa x şi ordonată y verifică cel puţin una dintre relaţiile x y sau x y 0;
c) XP reprezentând abscisa punctului rămas pe foaie la sfârşitul celui de-al treilea nivel al
jocului.
Date de intrare
Fişierul puncte.in conţine pe prima linie numărul N de puncte, iar pe fiecare dintre
următoarele N linii, câte două numere ı̂ntregi, despărite printr-un spaiu, reprezentând, ı̂n ordine,
abscisa şi ordonata unui punct din plan.
Date de ieşire
Fişierul puncte.out va conţine pe prima linie numărul natural N RP , pe a doua linie numărul
natural N RD, iar pe a treia linie numărul ı̂ntreg ce reprezintă coordonata XP .
a)
La primul nivel este suficient sa construim doua tablouri X si Y
cu semnificatia:
b)
Pentru al II-lea nivel memoram in variabila b1 numarul de puncte aflate pe
prima bisectoare\ {O}, in variabila b2 numarul de puncte aflate pe a doua
bisectoare\ {O} si in variabila nr0 memoram aparitia punctului {O}.
c)
Pentru al III-lea nivel efectuam o stergere falsa a punctelor (prin marcare)
utilizand vectorul poz cu semnificatia:
31 for(i=1;i<=1999;i++)
32 {
33 if(X[i]>maxi)maxi=X[i];
34 if(Y[i]>maxi)maxi=Y[i];
35 }
36 g<<maxi<<’\n’;
37
38 //b) numarul de drepte
39 if(b1==0 && b2==0)nrd=0;
40 else if(b2+nr0>1 && b1+nr0>1) nrd=b1*b2+2;
41 else nrd=b1*b2+1;
42 g<<nrd<<’\n’;
43
44 //c) indicele punctului ramas nemarcat
45 i=1;
46 while(marcate<n-1)
47 {
48 marcate++;poz[i]=0;
49 do{i++;if(i>n) i=1;}while(poz[i]==0);
50 do{i++;if(i>n) i=1;}while(poz[i]==0);
51 do{i++;if(i>n) i=1;}while(poz[i]==0);
52 }
53 nrp=1;while(poz[nrp]==0)nrp++;
54
55 //c) determinare coordonate
56 i=1;
57 while(nrp>0)
58 {
59 while(X[i]==0)i++;
60 nrabs=X[i];
61 while(nrabs>0 && nrp>0)
62 {
63 nrp--;nrabs--;
64 }
65 i++;
66 }
67
68 g<<i-1001<<’\n’;
69 f.close();g.close();
70 return 0;
71 }
32 b2++;
33
34 x+=1000;
35 y+=1000;
36 X[x]++;
37 Y[y]++;
38 }
39
40 //a
41 i=2000;
42 while(i--)
43 {
44 if(X[i]>m) m=X[i];
45 if(Y[i]>m) m=Y[i];
46 }
47 g<<m<<’\n’;
48
49 //b
50 x=b1*b2;
51 if(b1>1)x++;
52 if(b2>1)x++;
53 x+=(b1==1 && z)+(b2==1 && z);
54 g<<x<<’\n’;
55
56 //c
57 for(i=1;i<2000;i++)
58 if(X[i])
59 {
60 P.h=X[i];
61 P.x=i;
62 v.push_back(P);
63 }
64
65 p=1;// distanta pana la urmatorul punct care se sterge
66 i=0;
67 while(v.size()>1)
68 {
69 if(v[i].h-p>=0)
70 {
71 x=v[i].h;
72 v[i].h=x-1-(x-p)/3;
73
74 if(v[i].h==0)
75 v.erase(v.begin()+i);
76 else
77 i++;
78
79 p=3-(x-p)%3;
80 }
81 else
82 {
83 p=p-v[i].h;
84 i++;
85 }
86
87 if(i==v.size()) i=0;
88 }
89
90 g<<v[0].x-1000<<’\n’;
91 f.close();
92 g.close();
93 return 0;
94 }
10 bool a[2010][2010];
11
12 struct punct
13 {
14 int x, y;
15 punct *leg;
16 };
17
18 punct *L;
19
20 int main()
21 {
22 int i, j, x, y, maxim, zero;
23 long long drepte;
24 punct *p, *ul;
25
26 // citire
27 ifstream fin(inFile);
28 fin >> n;
29 zero = 0;
30 for (i = 1; i <= n; i++)
31 {
32 fin >> x >> y;
33 ox[1000 + x]++;
34 oy[1000 + y]++;
35 a[1000 + y][1000 + x] = true;
36 if (x == 0 && y == 0) zero = 1;
37 else
38 {
39 if (x == y) b1++;
40 if (x == -y) b2++;
41 }
42 }
43
44 ofstream fout(outFile);
45 // punctul 1
46 maxim = 0;
47 for (i = 0; i <= 2000; i++)
48 if (maxim < ox[i]) maxim = ox[i];
49 for (i = 0; i <= 2000; i++)
50 if (maxim < oy[i]) maxim = oy[i];
51 fout << maxim << "\n";
52
53 // punctul 2
54 drepte = b1;
55 drepte *= b2;
56 if (zero == 1)
57 {
58 if (b1 > 0) drepte++;
59 if (b2 > 0) drepte++;
60 }
61 else
62 {
63 if (b1 > 1) drepte++;
64 if (b2 > 1) drepte++;
65 }
66 fout << drepte << "\n";
67
68 // punctul 3
69 L = ul = NULL;
70 for (j = 0; j <= 2000; j++)
71 for (i = 0; i <= 2000; i++)
72 if (a[i][j])
73 {
74 if (L == NULL)
75 {
76 L = new punct;
77 L->x = i - 1000;
78 L->y = j - 1000;
79 L->leg = NULL;
80 ul = L;
81 }
82 else
83 {
84 p = new punct;
85 p->x = i - 1000;
CAPITOLUL 10. OJI 2013 10.2. PUNCTE 108
86 p->y = j - 1000;
87 p->leg = NULL;
88 ul->leg = p;
89 ul = p;
90 }
91 }
92
93 ul->leg = L;
94 for (i = 1; i < n; i++)
95 {
96 ul->leg = L->leg;
97 delete L;
98 ul = ul->leg->leg;
99 L = ul->leg;
100 }
101 fout << ul->y << "\n";
102 fout.close();
103
104 return 0;
105 }
53 }
54
55 nrp=0;
56 for (i=0;i<=2000;i++)
57 {
58 if (lin[i]>nrp)nrp=lin[i];
59 if (col[i]>nrp)nrp=col[i];
60 }
61
62 bis1=bis1-oo;
63 bis2=bis2-oo;
64 nrd=bis1*bis2;
65 if (bis1+oo>1)
66 {
67 nrd++;
68 }
69 if (bis2+oo>1)
70 {
71 nrd++;
72 }
73
74 fout<<nrp<<"\n";
75 fout<<nrd<<"\n";
76 abscisa();
77 fout<<xp-1000;
78
79 fin.close();
80 fout.close();
81 return 0;
82 }
43 if (ct==0) return 2;
44 if (ct==1) return 0;
45 return 3;
46 }
47
48 if (n==5)
49 {
50 if (ct==0) return 1;
51 if (ct==1) return 3;
52 return 2;
53 }
54
55 if (n==3)
56 {
57 if (ct==0) return 2;
58 if (ct==1) return 1;
59 return 0;
60 }
61
62 return -1; // ... !!!
63 }
64
65
66 int cmp(punct a, punct b)
67 {
68 if (a.x < b.x)
69 return 1;
70 if (a.x > b.x)
71 return 0;
72 if (a.y < b.y)
73 return 1;
74 return 0;
75 }
76
77 int cmp1(punct a, punct b)
78 {
79 if (a.y < b.y)
80 return 1;
81 else
82 return 0;
83 }
84
85 int main()
86 {
87 f >> n;
88 punct buf;
89 while (f >> buf.x >> buf.y)
90 {
91 v.push_back(buf);
92 ct++;
93 }
94
95 n = v.size();
96 sort(v.begin(),v.end(),cmp1);
97
98 if (v[0].x==0 && v[0].y==0)
99 zero=1;
100 else
101 {
102 if (v[0].x == v[0].y)
103 ct1++;
104 if (v[0].x == -v[0].y)
105 ct2++;
106 }
107
108 ct=1;
109 for (i=1;i<n;i++)
110 {
111 if (v[i].y == v[i-1].y)
112 ct++;
113 else
114 ct=1;
115 if (ct > ctmax)
116 {
117 ctmax = ct;
118 val = v[i].y;
CAPITOLUL 10. OJI 2013 10.2. PUNCTE 111
119 tip = 2;
120 }
121
122 if (v[i].x==0 && v[i].y==0)
123 zero=1;
124 else
125 {
126 if (v[i].x == v[i].y)
127 ct1++;
128 if (v[i].x == -v[i].y)
129 ct2++;
130 }
131 }
132
133 sort(v.begin(),v.end(),cmp);
134
135 ct=1;
136 for (i=1;i<n;i++)
137 {
138 if (v[i].x == v[i-1].x)
139 ct++;
140 else
141 ct=1;
142 if (ct > ctmax)
143 {
144 ctmax = ct;
145 val = v[i].x;
146 tip = 1;
147 }
148 }
149
150 ofstream fout("puncte.out");
151 fout << ctmax << endl;
152 fout << rez2(ct1,ct2,zero) <<endl;
153
154 ct=0;
155
156 for (int k=0;k<30;k++)
157 {
158
159 if (v.size()<=5)
160 {
161 r = v[rez3(v.size(),ct%3)].x;
162 break;
163 }
164
165 for (i=0;i<v.size();i++,ct++)
166 {
167
168 if (ct%3!=0)
169 v2.push_back(v[i]);
170 }
171
172
173 if (v2.size()<=5)
174 {
175 r = v2[rez3(v2.size(),ct%3)].x;
176 break;
177 }
178 v = v2;
179 v2.clear();
180
181 }
182
183 fout << r << endl;
184
185 return 0;
186 }
OJI 2012
11.1 deal
Problema 1 - deal 100 de puncte
Vasilică are la grădiniţă N turnuri cu ı̂nălţimile h1 , h2 , ..., hN . Când aşază ı̂n linie nişte turnuri,
cel puţin două, astfel ı̂ncât ı̂nălţimile lor să fie ı̂n ordine crescătoare, Vasilică spune că a construit
un deal. Înălţimea dealului este egală cu ı̂nălţimea celui mai ı̂nalt turn folosit. Iată, de exemplu,
că aşezând ı̂n ordine turnurile cu ı̂nălţimile 2 4 4 7 9 a format un deal cu ı̂nălţimea 9.
Vasilică şi-ar dori să aşeze ı̂n linie cele N turnuri, formând o succesiune de dealuri astfel ı̂ncât
suma ı̂nălţimilor dealurilor formate să fie maximă.
Cerinţe
Scrieţi un program care, cunoscând ı̂nălţimile celor N turnuri, va determina suma ı̂nălţimilor
dealurilor ce se pot forma aşezând ı̂n linie cele N turnuri, maximă posibil.
Date de intrare
Fişierul de intrare deal.in conţine pe prima linie numărul natural N . Pe cea de a doua linie
se află N numere naturale separate prin spaţii, reprezentând ı̂nălţimile celor N turnuri.
Date de ieşire
Fişierul de ieşire deal.out va conţine o singură linie pe care va fi scris un număr natural
reprezentând cerinţa problemei.
Exemple
deal.in deal.out Explicaţii
7 22 O soluţie posibilă cu suma ı̂nălţimilor 22 ar fi: 2 10 2 5 2 2 7
10 2 2 2 7 5 2 S-au format trei dealuri: 2 10 (cu ı̂nălţimea 10) şi 2 5 (cu
ı̂nălţimea 5) şi 2 2 7 (cu ı̂năţimea 7).
112
CAPITOLUL 11. OJI 2012 11.1. DEAL 113
Cazul 1.
Exista un element majoritar k (element care apare de cel putin (n+1)/2 ori).
in toate cele n/2 dealuri pe care le formam acest element trebuie sa apara.
Daca acest element este egal cu maximul (k=m) atunci dealurile sunt de forma
(i,k), unde i parcurge toate celelalte inaltimi.
Daca k =/= de m, atunci formam mai intai perechile (k, i) (unde i parcurge
inaltimile mai mari decat k) si apoi formam perechile de forma (i, k), unde
i parcurge valorile mai mici decat k.
Cazul 2.
Nu exista un element majoritar.
Vom parcurge inaltimile in ordine descrescatoare.
Primele n/2 cele mai mari inaltimi le vom plasa in vectorul solutie pe
pozitii impare.
Apoi completam pozitiile pare ale vectorului solutie plasand in ordine
inaltimile ramase (mici).
Formam dealurile astfel: alegem un turn din ’’gramada’’ cea mai din dreapta
si un turn din ’’gramada’’ cea mai din stanga, pe masura ce adunam inaltimile
dealurilor astfel formate scadem din ’’gramezi’’ numarul de turnuri utilizate.
Din turnurile ramase se mai pot forma si alte dealuri tot de cate doua
turnuri, cu conditia ca printre dealurile deja formate sa nu mai fie
utilizate turnuri cu inaltimi egale cu turnurile ramase in ultima gramada.
Observatii
Exista numeroase alte strategii de formare a dealurilor.
De exemplu pentru 45-50 de puncte: alegem minimul, alegem maximul,
formam dealul (minim, maxim); construim astfel toate perehile posibile.
Pentru 60-70 de puncte adaugam la strategie precedenta analizarea cazului
elementului majoritar.
33 if (w[dr]==0) dr--;
34 else {s=s+dr;w[st]--;w[dr]--;}
35 }
36
37 pdp=(n-w[dr])/2+1-(v[dr]-w[dr]);
38
39 if (w[dr]/2<=pdp) s=s+dr*(w[dr]/2);
40 else s=s+dr*pdp;
41
42 g<<s;
43
44 f.close();
45 g.close();
46
47 return 0;
48 }
84 int j = i;
85 while ((j < n) && (sol[j] <= sol[j + 1])) ++j;
86 if (j-i+1>1) rez += sol[j];
87 i = j;
88 }
89
90 printf("%I64d\n", rez);
91
92 /*
93 for (i=0; i<n; ++i) printf("%d ", sol[i]);
94 printf("\n");
95 for (i=0; i<n; ++i) printf("%d ", a[i]);*/
96
97 return 0;
98 }
11.2 ozn
Problema 2 - ozn 100 de puncte
O invazie de N farfurii zburătoare (denumite uzual OZN) dă bătăi de cap autorităţilor. În
fiecare astfel de OZN se află extratereştri care au ca misiune distrugerea planetei noastre. Radarul
care a detectat invazia are un ecran similar cu planul XOY . Fiecare OZN este reprezentat pe
ecran printr-un segment de dreaptă.
Pentru anihilarea OZN-urilor, autorităţile dispun de K arme laser. Armele sunt poziţionate
pe sol (ilustrat pe ecranul radarului prin axa OX). Fiecare armă emite o rază laser, ilustrată
pe ecran printr-o paralelă cu axa OY . Dacă o rază laser intersectează segmentul de pe ecranul
radarului corespunzător unui OZN, raza va omorı̂ toţi extratereştrii aflaţi ı̂n OZN-ul respectiv.
Din păcate, ı̂n preajmă se află doar un militar specializat ı̂n arme laser, aşa că autorităţile
doresc să ştie exact ce armă trebuie să folosească acesta pentru a distruge cât mai mulţi ex-
tratereştri.
Cerinţe
Ajutaţi autorităţile să determine numărul de extratereştri care pot fi anihilaţi cu fiecare armă
din dotare.
Date de intrare
Fişierul de intrare ozn.in conţine pe prima linie două numere naturale separate prin spaţiu
N K reprezentând numărul de OZN-uri şi respectiv numărul de arme laser. Pe următoarele
N linii sunt descrise cele N OZN-uri, câte unul pe linie. Un OZN este descris prin 5 numere
naturale separate prin câte un spaţiu x1 y1 x2 y2 nr, reprezentând ı̂n ordine coordonatele capetelor
segmentului corespunzător x1 , y1 , x2 , y2 , iar nr - numărul de extratereştri din el. Pe ultima
linie se găsesc K numere naturale a1 a2 a3 ... aK , separate prin câte un spaţiu, reprezentând
coordonatele pe axa OX (abscisele) unde sunt amplasate armele laser.
Date de ieşire
Fişierul de ieşire ozn.out va conţine pe K linii. Pe linia i va fi scris numărul total de ex-
tratereştri care pot fi distruşi cu arma i, considerând armele numerotate ı̂n ordinea ı̂n care acestea
apar ı̂n fişierul de intrare.
Restricţii şi precizări
a 1 & N & 20000
a 1 & K & 20000
a 1 & orice coordonată din fişierul de intrare & 2000000
a 1 & nr & 100, pentru orice OZN
a x1 $ x2 , pentru orice OZN
a Pe ecranul radarului segmentele ce descriu navele se pot intersecta.
a Dacă raza laser trece prin unul dintre capetele unui OZN atunci acesta este distrus.
a Pentru 50% dintre testele de intrare 1 & N K & 10000000
CAPITOLUL 11. OJI 2012 11.2. OZN 118
Exemple
Complexitatea O(N + K)
Alte solutii
21 }
22
23 for (i = 1; i < MAXX; ++i)
24 A[i] += A[i-1];
25
26 for (i = 1; i <= K; ++i)
27 {
28 scanf("%d", &x1);
29 printf("%d\n", A[x1]);
30 }
31
32 return 0;
33 }
31 int N, K, M, i, sum, a, b, c, d, e, k;
32
33 int main()
34 {
35 FILE *f = fopen("ozn.in","r");
36 FILE *g = fopen("ozn.out","w");
37
38 fscanf(f,"%d %d",&N, &K);
39
40 for (i=1;i<=N;i++)
41 {
42 fscanf(f,"%d %d %d %d %d",&a, &b, &c, &d, &e);
43 inr.t = 1;inr.x = a;inr.v = e;inr.p = K+1;
44 V[++M] = inr;
45 inr.t = 3;inr.x = c;inr.v = e;inr.p = K+1;
46 V[++M] = inr;
47 }
48
49 for (i=1;i<=K;i++)
50 {
51 fscanf(f,"%d",&k);
52 inr.t = 2;
53 inr.x = k;
54 inr.p = i;
55 V[++M] = inr;
56 }
57
58 sort(V+1, V+M+1, cmp);
59
60 for (i=1;i<=M;i++)
61 {
62 if (V[i].t == 1)
63 sum += V[i].v;
64 else
65 if (V[i].t == 3)
66 sum -= V[i].v;
67 else
68 V[i].v = sum;
69 }
70
71 sort(V+1, V+M+1, cmp1);
72
73 for (i=1;i<=K;i++)
74 fprintf(g,"%d\n",V[i].v);
75
76 fclose(g);
77 fclose(f);
78 return 0;
79 }
OJI 2011
12.1 adunscad
Problema 1 - adunscad 100 de puncte
Considerăm un număr ı̂ntreg N şi un şir de M cifre zecimale nenule. Să se determine dacă
numărul N poate fi rezultatul unei expresii aritmetice simple (fără paranteze), formată exclusiv
din cifrele şirului citit şi din operatorii aritmetici desemnaţi pentru operaţiile de adunare şi scădere
, .
Cerinţe
Scrieţi un program care citeşte numerele N şi M de pe prima linie a fişierului de intrare şi
şirul de M cifre de pe linia următoare şi determină şi afişează expresia găsită sau valoarea 0 ı̂n
cazul ı̂n care nu există soluţie.
Date de intrare
Fişierul de intrare adunscad.in conţine pe prima linie numerele ı̂ntregi N M , separate printr-
un spaţiu, reprezentând valoarea ce trebuie obţinută la evaluarea expresiei şi numărul de cifre din
şir. Linia a doua a fişierului de intrare conţine şirul celor M cifre nenule, separate prin câte un
spaţiu.
Date de ieşire
Fişierul de ieşire adunscad.out va conţine pe prima linie expresia determinată, ı̂n cazul ı̂n
care există soluţie, sau valoarea 0 ı̂n cazul ı̂n care nu există soluţie.
Exemple
123
CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 124
n=4 n=3
0000 1111 000 111
0001 1110 001 110
0010 1101 010 101
0011 1100 011 100
0100 1011
0101 1010
0110 1001
0111 1000
se pot genera doar jumatate dintre combinatii, verificarea facandu-se insa pentru
ambele situatii.
Solutii alternative:
1. backtracking
a) iterativ - dana_ad.cpp
b) recursiv - addsubbk.c, adunsc_a.cpp, adrian3.cpp, adrian4.cpp
2. operatii pe biti - adunscad.pas, danaad2.pas, adsc_nodea.cpp
3. programare dinamica - adrian1.cpp, vi_adunscad.cpp
3. divide&impera - adrian2.cpp
4. arbori binari - nicuas.cpp
Avem : x+y = t
x-y = N
adica : 2y = t-N
Memorez intr-un tablou pentru fiecare cifra si pentru fiecare valoare obtinuta
1=valoarea a fost obtinuta prin adunare
2=valoarea a fost obtinuta prin scadere
Daca la ultima cifra s-a obtinut valoarea ceruta in enunt se poate recupera
formula folosind tabloul.
Analiza complexitatii
Solutia autorului :
adrian 3:
---------
Initial nu tin cont de ordinea cifrelor. Contorizez fiecare cifra si stabilesc
prin backtracking pentru fiecare cifra de cate ori sa folosesc semnul ’+’ si
cate ori sa folosesc ’-’ pentru ca in final sa obtin rezultatul dorit.
adrian 4:
---------
Folosesc un backtracking natural care stabileste la fiecare pozitie in
sirul de cifre ce valoare ar trebui sa obtin pana la cifra urmatoare
pentru a obtine o valoare ceruta la pozitia curenta si daca este posibil
sa obtin o astfel de valoare.
65 {
66 int i, care;
67
68 Fin = fopen(’’adunscad.in’’, ’’r’’);
69 Fout = fopen(’’adunscad.out’’, ’’w’’);
70
71 fscanf(Fin, ’’%d %d\n’’, &N, &M); //citesc N si M
72 for (i = 1; i <= M; i++) //citesc cifrele din sir
73 fscanf(Fin, ’’%d’’, &cifre[i]);
74 fclose(Fin);
75
76 for (i = 0; i < 31; i++) comb[i] = 0;
77 gasit = 0;
78 while (!gasit && !comb[1]) //cand comb[1] este 1 am terminat
79 {
80 care = OK(); //verific expresia
81 if (care) //este OK
82 {
83 afiseaza(care); //afisez
84 break; //opresc ciclul
85 }
86 i = M; //adun 1 in baza 2
87 while (comb[i]) comb[i--] = 0; //caut primul 0
88 comb[i] = 1; //il fac 1
89 }
90 if (comb[1]) fprintf(Fout, ’’0\n’’);
91
92 fclose(Fout);
93 return 0;
94 }
43
44 if(s[1]==’-’) printf("-");
45 for(i=1;i<m;i++)
46 printf("%d%c",v[i],s[i+1]);
47
48 printf("%d\n",v[m]);
49 }
50
51 return 0;
52 }
63
64 //C[y]=numar binar cu L biti
65 //daca V[y]=1 cei L biti marcheaza de la st la dr
66 //semnul utilizat de cele L cifre bit=1=>semn=’-’ bit=0=>semn=’+’
67 long *VS,*CS,*VD,*CD;//vectori analogi cu V , C
68 //pentru subsecventele stanga / dreapta
69 long L,LS,LD,i,j;
70 //L=nr total cifre
71 //LS=nr cifre in prima jumatate ( in subsecventa stanga)
72 //LD=nr cifre in a doua jumatate (in subsecventa dreapta)
73 if(st==dr)
74 {
75 V[x[st]]=1;C[x[st]]=0;
76 //daca secventa are o singura cifra atunci
77 //singura valoare generata
78 //codul este 0 adica cifra e luata cu +
79 return;
80 }
81
82 L=dr-st+1;
83 LS=(L+1)/2;VS=init();CS=init();//pregatesc apelul pe prima subsecventa
84 LD=L-LS;VD=init();CD=init();//pregatesc apelul pe a doua subsecventa
85
86 //DIVIDE apel pe cele doua subsecvente
87 //fiecare va returna valorile generate precum si codurile cu care
88 // se genereaza
89 dei(st,(st+dr)/2,VS,CS);
90 dei((st+dr)/2+1,dr,VD,CD);
91
92 //IMPERA Se parcurg toate valorile posibile care pot fi generate
93 // in stanga si in dreapta
94 //OBS pe cele doua subsecvente valoarea maxima generata este de 9 ori
95 // lungimea secventei
96 for(i=0;i<=9*LS;i++)
97 if(VS[i])//DACA VALOAREA i SE OBTINE IN STANGA
98 for(j=0;j<=9*LD;j++)
99 if(VD[j])//DACA VALOAREA j SE OBTINE IN DREAPTA
100 {
101 V[i+j]=1;
102 //VALOAREA i+j>=0 SE OBTINE PE SECVENTA
103 C[i+j]=(CD[j]*p[LS])+CS[i];
104 //CODUL SE OBTINE CONCATENAND CODURILE CU CARE
105 // S-AU OBTINUT i si j
106 if(i>=j)
107 {//DACA VALOAREA STANGA > VALOAREA DREAPTA
108 V[i-j]=1;//DIFERENTA i-j SE OBTINE PE SECVENTA
109 C[i-j]=(p[LD]-CD[j]-1)*p[LS]+CS[i];
110 //CODUL DREAPTA VA FI NEGAT DEOARECE j SE SCADE
111 //DECI TOATE SEMNELE VOR FI SCHIMBATE
112 }
113 else
114 {//ANALOG DACA VALOAREA DREAPTA > VALOAREA STANGA
115 V[j-i]=1;
116 C[j-i]=(CD[j]*p[LS])+(p[LS]-CS[i]-1);
117 //DAR VOM NEGA CODUL STANGA
118 }
119 }
120 //ELIBEREZ MEMORIA PE VECTORII TEMPORAR UTILIZAT PENTRU A GENERA VALORILE
121 //PE SUBSEVCENTE
122 delete VS;delete VD;
123 delete CS;delete CD;
124
125 }
126
127 long *init()
128 {
129 //initializeaza cu 0 un vector temporar alocat dinamic cu zona utila 0-180
130 long *VECT;
131 VECT=new long[185];
132 for(long i=0;i<=182;i++)VECT[i]=0;
133 return VECT;
134 }
12 int main()
13 {
14 freopen("adunscad.in","r",stdin);
15 freopen("adunscad.out","w",stdout);
16
17 scanf("%d%d",&c,&n);
18 for(i=1;i<=n;i++)scanf("%d",&x[i]);
19
20 sol=ver(1,c);
21
22 if(!sol){printf("0\n");return 0;}
23 if(*sol==’+’)sol++;
24 printf("%s\n",sol);
25
26 return 0;
27 }
28
29 char *ver(int p,int v)
30 {
31 char *ret;ret=0;
32 if(p==n)
33 {
34 if(v== x[n]){ret=SOL+2*n-1;*ret=’0’+x[n];ret--;*ret=’+’;}
35 if(v==-x[n]){ret=SOL+2*n-1;*ret=’0’+x[n];ret--;*ret=’-’;}
36 return ret;
37 }
38
39 if(ver(p+1,v-x[p]))
40 {ret=SOL+2*p-1;*ret=’0’+x[p];ret--;*ret=’+’;return ret;}
41
42 if(ver(p+1,v+x[p]))
43 {ret=SOL+2*p-1;*ret=’0’+x[p];ret--;*ret=’-’;return ret;}
44
45 return ret;
46 }
38 fprintf(Fout, "-");
39 }
40 fprintf(Fout, "%d\n", cifre[M]);
41 }
42 }
43
44 int OK(void)
45 {
46 int i, Sum = 0, Sum_ = 0;
47
48 for (i = 1; i <= M; i++)
49 if (comb[i])
50 Sum -= cifre[i]; //1 este ’-’, 0 este ’+’
51 else
52 Sum += cifre[i];
53 for (i = 1; i <= M; i++) //invers
54 if (comb[i]) //1 este ’+’, 0 este ’-’
55 Sum_ += cifre[i];
56 else
57 Sum_ -= cifre[i];
58
59 if (Sum == N && Sum_ == N) return 3; // daca sumele sunt egale caz = 3,
60 // deci indiferent cum afisez
61 if (Sum == N) return 1; // 1 este ’-’
62 if (Sum_ == N) return 2; // 1 este ’+’
63
64 return 0; //combinatia NU este buna
65 }
66
67 void back(long k)
68 {
69 int care;
70 if (k == M + 1) //am o combinatie de +/- (0/1)
71 {
72 care = OK(); //verific daca furnizeaza valoarea N si in ce mod
73 if (care) //DA, e buna
74 {
75 afiseaza(care); //afiseaza
76 fclose(Fout); //inchide
77 exit(0); //opreste
78 } //NU, nu e buna - nu fa nimic
79 }
80 else //nu am inca o combinatie de +/- (0/1)
81 {
82 comb[k] = 0; back(k+1); //pune pe pozitia curenta 0 (+) si treci
83 //mai departe
84 comb[k] = 1; back(k+1); //la revenire pune 1 (-) si treci mai departe
85 }
86 }
87
88 int main(void)
89 {
90 unsigned long i;
91 Fin = fopen("adunscad.in", "r");
92 Fout = fopen("adunscad.out", "w");
93
94 fscanf(Fin, "%d %d\n", &N, &M);
95 for (i = 1; i <= M; i++)
96 fscanf(Fin, "%d", &cifre[i]);
97 back(2); //cand pozitia 1 din vectorul solutie comb devine 1 am terminat
98 if (!gasit) //nu am gasit nici o combinatie
99 fprintf(Fout, "0\n"); //scrie 0
100 fclose(Fout);
101 return 0;
102 }
8
9 int N, M, gasit;
10 int cifre[31], comb[31];
11
12 void afiseaza(void)
13 {
14 int i;
15 gasit = 1;
16 if (comb[1]) fprintf(Fout, "-");
17 for (i = 1; i < M; i++)
18 {
19 fprintf(Fout, "%d", cifre[i]);
20 if (comb[i+1])
21 fprintf(Fout, "-");
22 else
23 fprintf(Fout, "+");
24 }
25 fprintf(Fout, "%d\n", cifre[M]);
26 }
27
28 int OK(void)
29 {
30 int i, Sum = 0;
31
32 for (i = 1; i <= M; i++)
33 if (comb[i])
34 Sum -= cifre[i];
35 else
36 Sum += cifre[i];
37 return (Sum == N);
38 }
39
40 int main(void)
41 {
42 int i;
43
44 Fin = fopen("adunscad.in", "r");
45 Fout = fopen("adunscad.out", "w");
46
47 fscanf(Fin, "%d %d\n", &N, &M); //citesc N si M
48 for (i = 1; i <= M; i++) //citesc cifrele din sir
49 fscanf(Fin, "%d", &cifre[i]);
50 fclose(Fin);
51
52 gasit = 0;
53 while (!gasit && !comb[0])
54 {
55 if (OK())
56 {
57 afiseaza();
58 break;
59 }
60 i = M;
61 while (comb[i]) comb[i--] = 0;
62 comb[i] = 1;
63 }
64
65 if (comb[0]) fprintf(Fout, "0\n");
66
67 fclose(Fout);
68 return 0;
69 }
11 {
12 int i,s=0;
13 unsigned long b=1<<(m-1);
14
15 for (i=1; i<=m; ++i,b>>=1)
16 {
17 if (x & b)
18 s-=a[i];
19 else
20 s+=a[i];
21 }
22
23 return (s==n);
24 }
25
26 void afiseaza(unsigned long x)
27 {
28 unsigned long b=1<<(m-1);
29 int i;
30 for (i=1;i<=m;++i,b>>=1)
31 {
32 if (x & b)
33 printf("-");
34 else
35 if (i!=1)
36 printf("+");
37
38 printf("%d", a[i]);
39 }
40
41 printf("\n");
42 }
43
44 int main()
45 {
46 freopen("adunscad.in","r",stdin);
47 freopen("adunscad.out","w",stdout);
48 scanf("%d %d", &n, &m);
49
50 for (i=1;i<=m;++i)
51 {
52 scanf("%d", &a[i]);
53 fin=(fin<<1)+1;
54 }
55
56 for (i=0;i<=fin;++i)
57 if (solutie(i))
58 {
59 afiseaza(i);
60 return 0;
61 }
62
63 printf("0\n");
64 return 0;
65 }
18 for(i=1;i<=M;i++)
19 if(s[i]==1)
20 E=E+x[i];
21 else
22 E=E-x[i];
23
24 if(E==N)
25 {
26 if(s[1]==2) g<<"-";
27 g<<x[1];
28
29 for(i=2;i<=M;i++)
30 {
31 if(s[i]==1)
32 g<<"+";
33 else
34 g<<"-";
35
36 g<<x[i];
37 }
38
39 g<<’\n’;
40 g.close();
41 exit(0);
42 }
43 }
44
45 void gen(int i)
46 {
47 if(i<=M)
48 {
49 s[i]=1;
50 gen(i+1);
51 s[i]=2;
52 gen(i+1);
53 }
54 else
55 {
56 testare_afisare();
57 }
58 }
59
60 int main()
61 {
62 int i;
63
64 f>>N>>M;
65 for(i=1;i<=M;i++)
66 f>>x[i];
67
68 gen(1);
69
70 g<<0<<’\n’;
71 g.close();
72 return 0;
73 }
22 g<<0<<’\n’;
23 else
24 {
25 y=(t-n)/2;
26 a[0]=1;
27 int d=0;
28
29 for(i=1;i<=m &&!a[y];i++)
30 for(k=d;k>=0&&!a[y];k--)
31 if(a[k] &&k+c[i]<=y && !a[k+c[i]])
32 {
33 a[k+c[i]]=i;
34 if(k+c[i]>d) d=k+c[i];
35 }
36
37 if(!a[y])
38 g<<0<<’\n’;
39 else
40 {
41 while(y)
42 {
43 b[a[y]]=1;
44 y-=c[a[y]];
45 }
46
47 if(b[1]) g<<’-’;
48
49 g<<c[1];
50 for(i=2;i<=m;i++)
51 {
52 if(b[i])
53 g<<’-’;
54 else
55 g<<’+’;
56
57 g<<c[i];
58 }
59
60 g<<’\n’;
61 }
62 }
63
64 f.close();
65 g.close();
66 return 0;
67 }
12.2 comp
Problema 2 - comp 100 de puncte
Locuitorii planetei Eudora folosesc o reprezentare mai ciudată a numerelor naturale, astfel că
orice număr natural va fi scris notând câte mii, sute, zeci, respectiv unităţi conţine acesta. De
exemplu, numărul 3207 se poate reprezenta ı̂n mai multe moduri echivalente: 3m2s7u (3 mii 2
sute şi 7 unităţi), 32s0z7u (32 sute 0 zeci şi 7 unităţi), 32s7u, 3207u, etc.
Pentru a compara două numere naturale, eudorienii folosesc semnele $ şi %, acestea având
semnificaţia cunoscută şi pe Terra, iar pentru a calcula suma a două numere naturale utilizează
semnul .
Pentru a testa abilităţile pământenilor ı̂n privinţa lucrului cu numere naturale, eudorienii au
trimis pe Terra un fişier text ce conţine N linii, fiecare linie fiind o comparaţie de forma:
expresie1 % expresie2
sau
expresie1 $ expresie2
Observaţi că o comparaţie este constituită din două expresii separate prin semnul $ sau prin
semnul %.
CAPITOLUL 12. OJI 2011 12.2. COMP 139
O expresie este compusă dintr-un număr natural sau dintr-o sumă de două sau mai multe
numere naturale, toate scrise ı̂n forma eudoriană. Fişierul nu conţine caractere spaţiu.
Cerinţe
Scrieţi un program care determină câte dintre comparaţiile date utilizează semnul $, precum
şi valoarea de adevăr a fiecărei comparaţii dintre cele N date (afişând 0 dacă acea comparaţie e
falsă, respectiv 1 dacă acea comparaţie e adevărată).
Date de intrare
Fişierul comp.in conţine pe prima linie numărul natural nenul N , reprezentând numărul de
comparaţii, iar pe fiecare dintre următoarele N linii câte un şir de caractere corespunzător unei
comparaţii.
Date de ieşire
a 0 $ N & 1000
a Numerele din fişier nu depăşesc ı̂n valoare numărul eudorian 1000m1000s1000z1000u.
a Lungimea fiecărei linii din fişier este cel mult 250.
a Punctaj. Dacă numărul de comparaţii care utilizează semnul $ este corect se va acorda 20%
din punctajul pe test. Punctajul integral se acordă pentru rezolvarea corectă a ambelor cerinţe.
Exemple
71 close (f);
72 assign (f,’comp.out’); rewrite (f);
73 writeln (f, k);
74
75 for i := 1 to n do
76 writeln(f, ord(sol[i]),’ ’);
77 close (f);
78 End.
19 x[i]=comp();
20 }
21 printf("%d\n",cnt);
22 for(int i=1;i<=n;i++)printf("%d\n",x[i]);
23 return 0;
24 }
25
26 int comp()
27 {
28 //evalueaza intreaga expresie
29 //cursorul p este la inceputul expresiei
30 int left,right;
31 char op;
32
33 //evalueaza membrul I
34 left=member();
35
36 //cursorul p ajunge pe semnul de inegalitate
37 op=*p;
38
39 //daca semnul este < se contorizeaza
40 if(op==’<’)cnt++;
41
42 //avanseaza cursorul la inceputul membrului II
43 p++;
44
45 //evalueaza membrul II
46 right=member();
47
48 // decide daca inegalitatea este corecta
49 // si returneaza 1/0 pentru adevarat/fals
50 if(op==’<’&&(left<right))return 1;
51 if(op==’>’&&(left>right))return 1;
52 return 0;
53 }
54
55 int member()
56 {
57 //evalueaza un membru de inegalitate
58 //cursorul p este pozitionat la inceputul membrului
59
60 //initializeaza valoarea membrului cu valoarea termen din membru
61 //la final cursorul va fi in spatele termenului
62
63 int ret=number();
64 while(*p==’+’)
65 {
66 //cat timp intalnim semnul + membrul mai contine termeni
67
68 //avanseaza cursorul pe urmatorul termen
69 p++;
70
71 //evalueaza termenul si il adauga la rezultat
72 ret+=number();
73 }
74
75 //returneaza rezultatul membrului curent
76 return ret;
77 }
78
79 int number()
80 {
81 //evalueaza un numar eudorian
82 //cursorul este pozitionat la inceputul numarului
83 //la final el va fi in spatele numarului
84
85 //initializeaza valoarea cu valoarea primei cifre eudoriene
86 //la final cursorul va fi in spatele cifrei
87 int ret=digit();
88
89 //cat timp cursorul ajunge pe caracter cifra
90 //numarul mai contine cifre eudoriene
91 //deci se adauga cifra la valoarea numarului
92
93 while(isdigit(*p))
94 ret+=digit();
CAPITOLUL 12. OJI 2011 12.2. COMP 144
95
96 //returneaza valoare numar
97 return ret;
98 }
99
100 int digit()
101 {
102 //evalueaza o cifra eudoriana
103 //se parseaza numarul pana intalnim u,z,s sau m
104
105 int ret=0;
106 while(isdigit(*p)){ret*=10;ret+=*p-’0’;p++;};
107
108 // in functie de codul cifrei eudoriene
109 // se multiplica rezultatul cu 1,10,100 sau 1000
110 // si returneaza valoarea
111
112 if(*p==’u’){p++;return ret;}
113 if(*p==’z’){p++;return 10*ret;}
114 if(*p==’s’){p++;return 100*ret;}
115 p++;
116 return 1000*ret;
117 }
50 while (p)
51 {x=numar(p);
52 sum+=x;
53 p=strtok(NULL, "+");}
54 return sum;
55 }
56
57
58 long int numar (char * s)
59 {
60 long int sum=0;
61 char *p;
62 p=strchr(s,’m’);
63 if (p)
64 {*p=0;
65 sum+=1000*atol(s);
66 s=p+1;}
67 p=strchr(s,’s’);
68 if (p)
69 {*p=0;
70 sum+=100*atol(s);
71 s=p+1;}
72 p=strchr(s,’z’);
73 if (p)
74 {*p=0;
75 sum+=10*atol(s);
76 s=p+1;}
77 p=strchr(s,’u’);
78 if (p)
79 {*p=0;
80 sum+=atol(s);
81 }
82 return sum;
83 }
39
40 p=strchr(s,’<’);
41 if (!p) p=strchr(s,’>’);
42 strcpy(dr,p+1);
43 sm=*p;
44
45 *p=’\0’;
46 p=strtok(s,"+");
47 while(p)
48 {
49 xs+=numar(p);
50 p=strtok(NULL,"+");
51 }
52
53 p=strtok(dr,"+");
54 while(p)
55 {
56 xd+=numar(p);
57 p=strtok(NULL,"+");
58 }
59
60 if (sm==’>’) return xs>xd;
61 if (sm==’<’) return xs<xd;
62
63 return -1; // ... !!!
64 }
65
66 int main()
67 {
68 f>>n;f.get();
69 for (i=1;i<=n;++i)
70 {
71 f.getline(e,256);
72 g<<eval(e)<<’\n’;
73 }
74 return 0;
75 }
75 m2+=s2/10; s2=s2%10;
76
77 if(c==’<’ && (m1<m2 || m1==m2&& s1<s2 || m1==m2&&s1==s2&& z1<z2 ||
78 m1==m2&&s1==s2&&z1==z2&&u1<u2))
79 g<<"1\n";
80 else
81 if(c==’>’ && (m1>m2 || m1==m2&& s1>s2 || m1==m2&&s1==s2&& z1>z2 ||
82 m1==m2&&s1==s2&&z1==z2&&u1>u2))
83 g<<"1\n";
84 else
85 g<<"0\n";
86 }
87
88 f.close();
89 g.close();
90 return 0;
91 }
OJI 2010
13.1 cladiri
Problema 1 - cladiri 100 de puncte
Institutul de Fizică a Pământului studiază
efectele unui potenţial cutremur folosind simulări
computerizate. Harta plană a clădirilor de pe un
teritoriu oarecare este reprezentată folosind coor-
donatele GPS ı̂n plan, longitudine şi latitudine,
faţă de un reper considerat de coordonate 0, 0,
ca ı̂n figura alăturată.
Fiecare dintre clădirile aflate pe hartă, au două
coordonate GPS, (Longitudine, Latitudine) şi un
Grad de rezistenţă la cutremure.
Un cutremur se poate produce ı̂n orice punct
de coordonate de pe hartă, numit centrul seismu-
lui şi are o anumită intensitate. Unda de şoc se
propagă sub forma unor pătrate concentrice cu Figura 13.1: cladiri
centrul seismului, numite nivele (nivelul 0 reprezintă centrul seismului, nivelul 1 primul pătrat
concentric, nivelul 2 al doilea pătrat concentric şi aşa mai departe). Intensitatea slăbeşte la fiecare
pătrat concentric cu centrul seismului cu câte o unitate. Clădirile sunt afectate de cutremur doar
dacă gradul lor de rezistenţă la cutremur este mai mic sau egal cu intensitatea cutremurului ı̂n
poziţia clădirii.
Cerinţe
Scrieţi un program care să citească coordonatele centrului seismului şi intensitatea sa ı̂n acel
punct, precum şi coordonatele clădirilor şi gradul lor de rezistenţă la cutremur, şi apoi să deter-
mine:
a) numărul N total de clădiri afectate;
b) numărul M maxim de clădiri afectate pe un nivel;
c) numerele nivelelor cu M clădiri afectate, ı̂n ordinea crescătoare a numerelor acestor nivele.
Date de intrare
Date de ieşire
149
CAPITOLUL 13. OJI 2010 13.1. CLADIRI 150
a pe a treia linie se vor scrie numerele nivelelor cu M clădiri afectate, ı̂n ordinea crescătoare
a numerelor acestor nivele.
Exemple
cladiri.in cladiri.out Explicaţii
12 7 11 8 Numărul N total al clădirilor afectate este 8.
10 9 2 3 Numărul M maxim de clădiri afectate pe acelaşi nivel este
10 7 3 24 3 şi este atins pe nivelele 2 şi 4.
13 5 1
8 11 4
876
15 4 3
15 9 10
13 10 1
16 8 4
3 3 3 0 Intensitatea cutremurului este 3 şi nu poate afecta cele
1 3 5 0 3 clădiri, deci avem N=0 clădiri afectate, iar maximul
2 4 7 clădirilor afectate pe un nivel este, evident, M=0.
3 2 9
max(cx-x,cy-y),
8
9 long x,y,ic,nivel[10001],nivmax=0;
10
11 // acesta functie testeaza daca o cladire rezista cutremurului
12 void test(long xf,long yf,long grf)
13 {
14 long mx,my,niv;
15 if (xf-x>=0) mx = xf-x;
16 else mx = x-xf;
17 if (yf-y>=0) my = yf-y;
18 else my = y-yf;
19 if (mx>=my) niv=mx;
20 else niv=my;
21 if (niv>nivmax) nivmax=niv;
22 if (grf<=ic-niv) nivel[niv]++;
23 }
24
25 // acesta functie determina numarul total de cladiri distruse
26 long total(vector v,long n)
27 {
28 long s=0,i;
29 for(i=0;i<=n;i++)
30 s+=v[i];
31 return s;
32 }
33
34 // acesta functie determina nivelul pe care se gasesc cele mai multe cladiri
35 // distruse
36 long maxim(vector v,long n)
37 {
38 long max=v[0],i;
39 for(i=1;i<=n;i++)
40 if (v[i]>max) max=v[i];
41 return max;
42 }
43
44 int main()
45 {
46 long xc,yc,gr,i,m,c=0;
47 ifstream f("cladiri.in");
48 ofstream g("cladiri.out");
49
50 // citesc datele cutremurului
51 f>>x>>y>>ic;
52
53 // citesc succesiv datele cladirilor
54 while (f>>xc>>yc>>gr)
55 {
56 test(xc,yc,gr);
57 c++;
58 }
59
60 g<<total(nivel,nivmax)<<"\n";
61
62 m=maxim(nivel,nivmax);g<<m<<"\n";
63
64 // aflu nivelele cu cele mai multe cladiri distruse
65 if (m)
66 for(i=0;i<=nivmax;i++)
67 if (nivel[i]==m) g<<i<<" ";
68
69 g<<"\n";
70
71 f.close();
72 g.close();
73 return 0;
74 }
13.2 secvente
Problema 2 - secvente 100 de
puncte
Mariei ı̂i plac numerele prime şi puterile numerelor prime. Pornind de la un număr prim p, ea
construieşte noi numere, fiecare număr construit fiind un produs de forma p (y " N, y j 0) sau
y
Cerinţe
Scrieţi un program care să citească mai multe seturi de date, fiecare set fiind format din
numerele n, p, k, cu semnificaţiile din enunţ, şi şirul cu n elemente a1 , a2 , a3 , ..., an , numerele
Mariei. Programul va determina pentru fiecare set de date numărul secvenţelor ce conţin exact k
numere p-prime, precum şi poziţiile de ı̂nceput şi de final ale acestor secvenţe ı̂n şirul din set.
CAPITOLUL 13. OJI 2010 13.2. SECVENTE 153
Date de intrare
Date de ieşire
Exemple
La citire se verifica pentru fiecare numar daca este p-prim. Mai intai se imparte
numarul in mod repetat la p atat timp cat este posibil, iar apoi verifica daca
valoarea ramasa este numar prim.
a. Se pot determina toate numerele prime pana la n folosind ciurul lui Eratostene
complexitate O(nlgn)
sau
b. se pot memora numerele prime intr-un vector de constante (ramane insa O(lgp n)
puterii lui p pentru fiecare numar). Cautarea in vectorul de numere prime se rezolv
folosind cautarea binara.
9 var f,g:text;
10 t,n,p,k,m,i,x,d:integer; {n-atatea numere de pe foaie}
11 {p-baza componentei putere a numarului p-prim}
12 {k-cate numere prime vor fi in secventele cerute}
13 a:array[1..15000] of integer; {a- contine pozitiile numerelor p-prime}
14 prime: array[1..MAX] of boolean;
15 {prim[i]=true/false ne va spune daca i este numar prim}
16
17 procedure numereprime; {ciurul lui Eratostene}
18 var i,j:integer; {pentru generarea numerelor prime}
19 rad:real;
20 begin
21 rad:=sqrt(MAX);
22 for i:=1 to MAX do {toate numerele le consideram prime la inceput}
23 prime[i]:=true;
24 i:=1; {plec de la inceputul vectorului}
25 while i<=rad do {se lucreaza doar pana la radical din n}
26 begin
27 repeat
28 inc(i)
29 until prime[i]; {caut urmatorul numar prim la rand}
30 j:=i*i; {primul numar care nu mai este prim este patratul lui}
31 while j<=MAX do {apoi plecand de la el}
32 begin
33 prime[j]:=false; {il marchez ca fiind neprim (e multiplu de i)}
34 j:=j+i {si marchez din i in i}
35 end;
36 end;
37 End;
38
39 function pprim(x:integer):boolean;
40 begin
41 if x=1 then pprim:=false {numarul 1 nu e pprim}
42 else
43 begin
44 while x mod p=0 do {simplific cu p}
45 x := x div p;
46 pprim:= prime[x]; {si verific daca e prim}
47 {- de data asta se accepta si 1}
48 end;
49 end;
50
51 begin
52 assign(f,fin);
53 reset(f);
54 assign(g,fout);
55 rewrite(g);
56 numereprime; {generez tabelul numerelor prime cu ciurul lui Eratostene}
57 readln(f,D);
58 for t:=1 to D do
59 begin
60 readln(f,n,p,k);
61 m:=0;
62 for i:=1 to n do
63 begin
64 readln(f,x);
65 if pprim(x) then
66 begin
67 inc(m);
68 a[m]:=i;
69 {am memorat pozitia de aparitie a unui numar p-prim}
70 end;
71 end;
72 if m-k+1<=0 then
73 writeln(g,0)
74 else
75 writeln(g,m-k+1);
76 {din m numere prime pot crea m-k+1 secvente de lungime k}
77
78 for i:=1 to m-k+1 do
79 writeln(g,a[i],’ ’,a[i+k-1]); {astea sunt extremitatile secventelor}
80 end;
81 close(f);
82 close(g);
83 end.
CAPITOLUL 13. OJI 2010 13.2. SECVENTE 156
75 else
76 writeln(g,m-k+1);
77 {din m numere prime pot crea m-k+1 secvente de lungime k}
78
79 for i:=1 to m-k+1 do
80 writeln(g,a[i],’ ’,a[i+k-1]); {astea sunt extremitatile secventelor}
81 end;
82 close(f);
83 close(g);
84 end.
158
Capitolul 14
ONI 2022
14.1 proeminenta
Problema 1 - Proeminenta 100 de puncte
Cea mai cunoscută măsură a unui vârf de munte este altitudinea sa. Vârfuri secundare ale
unui munte ı̂nalt pot avea altitudini considerabile, dar ı̂n general acestea nu sunt atât de relevante.
Din acest motiv, geografii au introdus o nouă măsură pentru un vârf: proeminenţa.
Un profil este o reprezentare a altitudinilor ı̂n puncte succesive, ı̂ntre care ne putem deplasa
la stânga sau dreapta.
În desen avem profilul munţilor Făgăraş, pe care exemplificăm următoarele definiţii:
1. Un vârf este un punct (sau o succesiune de puncte consecutive cu aceeaşi altitudine), pentru
care altitudinile punctelor alăturate ı̂n stânga, respectiv ı̂n dreapta sunt strict mai mici decât
altitudinea vârfului. De exemplu, Negoiu este un vârf (altitudinea sa este 2535m, punctul de
pe profil situat ı̂n stânga sa are altitudinea 2306m, iar punctul de pe profil situat ı̂n dreapta
sa are altitudinea 2030m). Un alt exemplu de vârf este Urlea, fiind reprezentat pe profil
prin două puncte consecutive cu altitudinea 2473m, având ı̂n stânga un punct cu altitudinea
2160m, iar ı̂n dreapta un punct cu altitudinea 1511m.
2. Cea mai adâncă vale dintre două vârfuri este altitudinea minimă a unui punct care se află
ı̂ntre cele două vârfuri. De exemplu, ı̂n desen ı̂ntre Negoiu şi Moldoveanu cea mai adâncă
vale este la 2030m.
3. Proeminenţa unui vârf este diferenţa minimă dintre altitudinea lui şi cea mai adâncă
vale dintre el şi un vârf strict mai ı̂nalt decât el. Dacă nu există un vârf strict mai ı̂nalt,
proeminenţa este altitudinea vârfului. De exemplu, pentru a determina proeminenţa vârfului
Gălăşescu Mare putem considera vârfurile strict mai ı̂nalte (ı̂n ordine de la stânga la dreapta:
Negoiu, Moldoveanu şi Urlea). Dacă plecăm de pe Negoiu, diferenţa dintre cea mai adâncă
vale şi Gălăşescu Mare este 2470-2030=440m. Dacă plecăm de pe Moldoveanu, diferenţa
159
CAPITOLUL 14. ONI 2022 14.1. PROEMINENTA 160
dintre cea mai adâncă vale şi Gălăşescu Mare este 2470-2213=257m. Dacă plecăm de
pe Urlea, diferenţa dintre cea mai adâncă vale şi Gălăşescu Mare este 2470-2160=310m.
Diferenţa minimă este 257m, deci proeminenţa vârfului Gălăşescu Mare este dată de vârful
Moldoveanu.
Cerinţe
Scrieţi un program care, cunoscând configuraţia unui profil, rezolvă următoarele două cerinte:
1. determină numărul de vârfuri existente pe profilul respectiv;
2. determină proeminenţa fiecărui vârf de pe profil.
Date de intrare
Date de ieşire
3 & N & 10
6
a1 $ a2 şi aN 1 % aN
# Punctaj Restricţii
1 10 Pentru teste valorând 10 de puncte: C 1.
2 30 Pentru teste valorând 30 de puncte: C 2 şi N & 1000.
3 20 Pentru teste valorând 20 de puncte: C 2 şi 1000 $ N ,
iar altitudinile ai sunt distincte două câte două.
Exemple:
proeminenta.in proeminenta.out
1 7
19
1387 1890 2306 2535 2030 2391 2079
2544 2213 2470 2305 2433 2160 2473
2473 1511 1735 1621 1367
2 505 312 2544 257 128 313 224
19
1387 1890 2306 2535 2030 2391 2079
2544 2213 2470 2305 2433 2160 2473
2473 1511 1735 1621 1367
Explicaţii:
Profilul din fişierele de intrare este cel reprezentat ı̂n desen. Cele 7 vârfuri sunt denumite pe desen.
Deşi altitudinile apar ı̂n tabel pe mai multe linii, ele vor fi scrise pe o singură linie ı̂n fişierul de
intrare.
CAPITOLUL 14. ONI 2022 14.1. PROEMINENTA 161
Propusă de: Lector dr. Paul Diac, Facultatea de Informatică, Universitatea ”Alexandru Ioan Cuza”
Iaşi
Pentru ambele cerinţe trebuie eliminate altitudinile consecutive egale. Acest lucru se poate
face prin citirea altitudinilor ı̂ntr-o variabilă temporară x şi adăugarea ı̂n vector doar a valorilor
diferite de ultima adăugată:
if ( x != a [n-1] ) { a [n++] = x ; }
Pentru cerinţa C 1:
Se parcurg altitudinile 0 $ i $ n 1 şi se numără vârfurile (pe pozi c tia i este un vârf dacă
ai 1 $ ai % ai 1.
Pentru cerinţa C 2:
3
Soluţie O N . O primă soluţie pentru a determina proeminenţa unui vârf de pe poziţia
i este să aplicăm definiţia ı̂n mod direct. Parcurgem orice alt vârf j şi le procesăm pe cele cu
aj % ai.
Pentru a determina cea mai adâncă vale dintre i şi j, se parcurg poziţiile k dintre min i, j şi
max i, j şi se reţine cea mică valoare ak . Proeminenţa vârfului i va fi diferenţa minimă dintre
ai şi valorile ak obţinute astfel. Soluţia se ı̂ncadrează ı̂n timp pe testele cu N & 1 000.
2
Soluţie O N . După ce fixăm un vârf i pentru care calculăm proeminenţa, putem parcurge
vârfurile j ı̂ncepând de la pozitia i prima dată ı̂n stânga, apoi ı̂n dreapta. Prin aceste parcurgeri
putem determina vârfuri aj % ai concomitent cu a determina cea mai adâncă vale dintre ele.
Reţinem acestă altitudine minimă ı̂ntr-o variabilă pe care o folosim şi pentru următoarele poziţii
j.
Optimizare: Analizând mai atent definiţia proeminenţei, deducem că pe un profil
proeminenţa unui vârf i va fi dată de primul vârf mai mare decât ai situat ı̂n stânga sa as % ai
(s $ i) sau de primul vârf mai mare decât ai situat ı̂n dreapta sa ad % ai (d % i).
Justificarea constă ı̂n faptul că, dacă ar exista alte vârfuri ak % ai cu k $ s sau k % d până
la care văile ar fi şi mai adânci, ele nu vor fi relevante, deoarece atunci când se alege minimul dintre
diferenţa ai şi cea mai adâncă vale, ele dau o diferenţă mai mare, care nu modifică minimul.
2
Soluţiile de complexitate O N se ı̂ncadrează ı̂n timp pe un număr semnificativ mai mare de
teste, ı̂n funcţie de optimizările efectuate.
Soluţie O N . Pentru a proiecta o soluţie de complexitate liniară determinăm mai ı̂ntâi
”proeminenţa la stânga”, parcurgând vârfurile de la stânga către dreapta. Procedăm apoi similar
pentru a determina ”proeminenţa la dreapta”, parcurgând vârfurile de la dreapta cs̆tângaşi la final
afişăm minimul dintre cele două valori obţinute pentru proeminenţă.
Pentru a calcula proeminenţa la stânga, parcurgem vârfurile ı̂n ordinea crescătoare a indicilor
i. Pentru orice vârf i doar cel mai apropiat vârf j din stânga lui i (j $ i), strict mai ı̂nalt decât
vârful i este relevant, prin urmare toate vârfurile dintre acestea j $ k $ i care au altitudini mai
mici ak $ ai, pot fi ignorate mai departe.
Este deci suficient să reţinem o stivă cu vârfuri montane ı̂n ordinea strict descrescătoare
a ı̂nălţimilor. Pentru fiecare dintre aceste vârfuri, vom reţineşi cea mai adâncă vale situată ı̂n
dreapta vârfului, până la următorul vârf adăugat ı̂n stivă, vale pe care o determinăm ”din mers”.
Rezumând, parcurgem profilul de la stânga la dreapta şi identificăm vârfurile montane (fie ai
altitudinea vârfului montan curent).
Cât timp ı̂n stivă există elemente şi ai ' altitudinea vârfului montan plasat la vârful stivei,
extragem din stivă un element (evident, doar elementul situat la vârful stivei poate fi extras).
Atunci când extragem un element din stivă, actualizăm, dacă este cazul, şi ı̂nălţimea celei mai
adânci văi până la vârful montan curent.
Dacă stiva a devenit vidă, inserăm vârful montan curent ı̂n stivă şi iniţializăm ı̂nălţimea celei
mai adânci văi până la momentul curent cu ai.
Dacă stiva este nevidă, atunci ai $ altitudinea vârfului plasat la vârful stivei. În acest caz:
CAPITOLUL 14. ONI 2022 14.1. PROEMINENTA 162
(1) pentru vârful montan situat ı̂n vârful stivei memorăm cea mai mică altitudine a unei văi
identificate până la vârful montan curent;
(2) determinăm proeminenţa la stânga a vârfului montan curent ca fiind diferenţa dintre a[i] si
altitudinea minimă a unei văi până la vârful montan curent;
61 prom[d][i] = a[i] ;
62 while( top >= 0 && a[peak[top]] <= a[i] )
63 { // elimim varfurile mai
64 // mici din stiva pe rand
65 popTop() ;
66 }
67
68 if( top >= 0 && a[peak[top]] > a[i] )
69 {
70 prom[d][i] = a[i] - valley[top] ;
71 // am gasit primul varf mai mare din stanga
72 }
73 peak[++top] = i ;
74 valley[top] = a[i] ; // adaugam noul varf
75 }
76
77 if( top >= 0 && valley[top] > a[i] )
78 valley[top] = a [i];
79 }
80
81 for( int i = 0; i < N/2; i++)
82 { // oglindim altitudinile
83 long aux = a[i];
84 a[i] = a[N-1-i];
85 a[N-1-i] = aux ;
86 }
87
88 top = -1;
89 }
90
91 for( int i=1; i < N-1; i++)
92 if( a[i-1] < a[i] && a[i] > a[i+1] )
93 {
94 if( prom[0][i] > prom[1][N-1-i ] )
95 { // retinem maximul in prom[0][i]
96 prom[0][i] = prom[1][N-1-i] ;
97 }
98 }
99
100 for( int i=1; i < N-1; i++)
101 if( prom[0][i] > 0 )
102 printf( "% l d ", prom[0][i] ) ; } // si il afisam
103
104 printf( "\n" );
105 fclose( stdout );
106 return 0 ;
107 }
aaaaaaaaaaa
bbbbbbbbbbbb
ccccccccccccc
14.2 rgb
Problema 2 - RGB 100 de puncte
Ionuţ, tânăr programator, se lansează pe piaţa producătorilor de jocuri pe calculator. Jocul
pe care l-a proiectat se numeşte RGB. În joc există N personaje extraterestre. Fiindcă Ionuţ nu
este de acord cu teoria omuleţilor verzi, personajele lui sunt de 3 culori:
R extratereştri de culoare roşie;
G extratereştri de culoare verde;
B extratereştri de culoare albastră.
CAPITOLUL 14. ONI 2022 14.2. RGB 164
Fiecare extraterestru are o anumită putere, exprimată printr-un număr natural impar, puterile
oricăror doi extratereştri fiind diferite.
Pe parcursul jocului fiecare extraterestru va lupta cu fiecare dintre ceilalţi extratereştri.
Rezultatul unei lupte ı̂ntre doi extratereştri depinde de puterea acestora, dar şi de culoarea lor.
Într-o luptă dintre doi extratereştri de aceeaşi culoare, va câştiga cel cu puterea cea mai mare.
Într-o luptă ı̂ntre doi extratereştri de culori diferite, puterile lor se modifică după cum urmează,
iar după modificare lupta o va câştiga extraterestrul cu puterea mai mare:
Puterea unui extraterestru roşu este dublată dacă adversarul este un extraterestru verde.
Puterea unui extraterestru verde este dublată dacă adversarul este un extraterestru albastru.
Puterea unui extraterestru albastru este dublată dacă adversarul este un extraterestru roşu.
După fiecare luptă, puterile extratereştrilor revin la valorile iniţiale, ı̂n caz că s-au modificat.
Cerinţe
Scrieţi un program care, cunoscând culorile şi puterile extratereştrilor, rezolvă următoarele
două cerinte:
1. determină puterea extraterestrului care câştigă cele mai multe lupte; dacă există mai mulţi
astfel de extratereştri, se va afişa puterea minimă;
2. determină pentru fiecare extraterestru numărul de lupte câştigate de acesta.
Date de intrare
Fişierul de intrare rgb.in va conţine pe prima linie numerele naturale C R G şi B, unde C
este cerinţa care trebuie să fie rezolvată (1 sau 2), R reprezintă numărul de extratereştri roşii, G
numărul de extratereştri verzi, iar B numărul de extratereştri albaştri.
Pe cea de a doua linie se află R numere naturale impare ı̂n ordine strict crescătoare,
reprezentând puterile celor R extratereştri roşii.
Pe cea de a treia linie se află G numere naturale impare ı̂n ordine strict crescătoare,
reprezentând puterile celor G extratereştri verzi.
Pe cea de a patra linie se află B numere naturale impare ı̂n ordine strict crescătoare,
reprezentând puterile celor B extratereştri albaştri.
Valorile scrise pe aceeaşi linie sunt separate prin câte un spaţiu.
Date de ieşire
Pentru C 1, fişierul de ieşire rgb.out va conţine o singură linie pe care va fi scrisă puterea
extraterestrului care câştigă cele mai multe lupte; dacă există mai mulţi extratereştri care câştigă
un număr maxim de lupte, se va afişa puterea minimă.
Pentru C 2, fişierul de ieşire rgb.out va conţine trei linii. Pe prima linie se va scrie numărul
de lupte câştigate de fiecare extraterestru roşu. Pe a doua linie, se va scrie numărul de lupte
câştigate de fiecare extraterestru verde. Pe a treia linie, se va scrie numărul de lupte câştigate de
fiecare extraterestru albastru.
Pentru fiecare culoare, valorile vor fi afişate considerând ordinea extratereştrilor din fişierul de
intrare.
Valorile scrise pe aceeaşi linie vor fi separate prin câte un spaţiu.
N R+G+B
1 & R, G, B & N 2
1 & puterea oricărui extraterestru &2 N 1
CAPITOLUL 14. ONI 2022 14.2. RGB 165
# Punctaj Restricţii
1 21 C 1, iar 1 & N 500 000
2 18 C 2, iar 1 & N & 1 000
3 25 C 2, iar 1 000 $ N & 100 000
4 36 C 2, iar 100 000 $ N & 500 000
Exemple:
rgb.in rgb.out
1122 7
3
17
59
2122 1
3 04
17 23
59
Explicaţii:
Pentru primul exemplu, C 1, deci se va rezolva prima cerinţă. Există un extraterestru roşu,
care are puterea 3, doi extratereştri verzi, având puterile 1, respectiv 7 şi doi extratereştri albaştri
cu puterile 5, respectiv 9.
Extraterestrul cu puterea 7 este singurul care va câştiga cele mai multe lupte (ı̂n cazul acesta,
chiar toate):
când luptă cu extraterestrul verde cu puterea 1 câştigă, pentru că are puterea mai mare;
când luptă cu extraterestrul roşu cu puterea 3, acesta ı̂şi va dubla puterea (va avea puterea
6), dar va fi insuficient pentru a câştiga lupta;
când luptă contra extraterestrului albastru cu puterea 9, va avea puterea dublată (14), prin
urmare va câştiga şi această luptă.
Extraterestrul cu puterea 3 poate câştiga doar o luptă (contra extraterestrului cu puterea 1).
Extraterestrul cu puterea 1 nu poate câştiga nicio luptă.
Extraterestrul cu puterea 7 poate câştiga toate luptele (vezi explicaţia de la exemplul prece-
dent).
Extraterestrul cu puterea 5 poate câştiga două lupte (contra extratereştrilor cu puterile 1 şi 3).
Extraterestrul cu puterea 9 poate câştiga 3 lupte (contra extratereştrilor cu puterile 1, 3 şi 5).
Pentru a calcula numărul de lupte câştigate de un extraterestru, este necesar să calculăm doar
câte lupte va câştiga luptând cu extratereştrii de celelalte două culori (el fiind cel mai puternic
de culoarea sa va câştiga toate luptele cu extratereştrii având aceeaşi culoare cu el). O atenţie
specială trebuie acordată cazului ı̂n care există mai mulţi extratereştri care câştigă acelaşi număr
maxim de lupte (se va afişa puterea cea mai mică).
2
Cerinţa 2 - Soluţie O N . Pentru C 2, simulăm toate luptele dintre extratereştri şi
reţinem câte lupte câştigă fiecare.
Cerinţa 2 - Soluţie O N log2 N . Fie X şi Y doi extratereştri de culori diferite. Observăm
că, dacă X va câştiga lupta contra lui Y , atunci X va câştiga luptele contra tuturor extratereştrilor
de aceeaşi culoare ca Y , ı̂nsă cu putere mai mică. Prin urmare, pentru fiecare extraterestru, putem
căuta binar care este cel mai puternic extraterestru de fiecare culoare diferită de a sa pe care ı̂l
va ı̂nvinge. O astfel de soluţie poate obţine mai multe puncte ı̂n funcţie de cât de eficientă este
implementarea.
Cerinţa 2-Soluţie O N . Putem optimiza soluţia anterioară. Fie Z următorul extraterestru
de aceeaşi culoare ca X (cu putere mai mare). Atunci, Z va câştiga din start toate luptele pe
care X le va câştiga. Deci, ce trebuie verificat este dacă mai câştigă şi alte lupte ı̂n plus. Să
presupunem că A este cel mai puternic extraterestru de o culoare diferită pe care X ı̂l ı̂nvinge.
Atunci, Z ı̂i va ı̂nvinge pe toţi până la A inclusivşi vom verifica dacă va mai ı̂nvinge şi pe
alţii de aceeaşi culoare, căutând secvenţial, ı̂ncepând după A. Să presupunem că cel mai puternic
extraterestru pe care ı̂l va ı̂nvinge va fi B. Atunci, pentru următorul extraterestru de aceeaşi
culoare ca X şi Z, căutarea va ı̂ncepe după B, şi aşa mai departe. Analog pentru cealaltă culoare
diferită. Tehnica de rezolvare mai este cunoscută drept ”Two pointers”.
De precizat este că citirea poate fi parsată
(datele citite ca şir de caractere şi apoi transformate
ı̂n numere). Nu este ı̂nsă necesară pentru a obţine 100 de puncte.
14.3 subsir
Problema 3 - Subsir 100 de puncte
Fie S S1 , S2 , ..., SN un şir format din N numere naturale mai mici decât 10 şi x un număr
natural cu p cifre, reprezentat ı̂n baza 10 prin x x1 x2 ...xp . Observaţi că poziţiile din şirul S
sunt numerotate de la stânga la dreapta de la 1 la N , iar cifrele numărului x sunt numerotate de
la stânga la dreapta de la 1 la p (cifra p fiind cifra unităţilor).
Vom spune că x apare ca subşir ı̂n şirul S dacă există poziţiile 1 & j1 $ j2 $ ... $ jp & N
astfel ı̂ncât să avem Sji xi pentru orice 1 & i & p.
De exemplu, pentru S 2, 3, 7, 9, 27 apare ca subşir ı̂n S, dar 93 nu apare ca subşir ı̂n S.
Numim prefix de lungime j al lui S secvenţa S1 , S2 , ..., Sj (secvenţa care ı̂ncepe la poziţia
1 şi se termină la poziţia j, 1 & j & N ).
Cerinţe
Date de intrare
CAPITOLUL 14. ONI 2022 14.3. SUBSIR 167
Fişierul de intrare subsir.in conţine pe prima linie un număr natural C care reprezintă cerinţa
care urmează a fi rezolvată (1 sau 2).
Pe a doua linie se află numărul natural N .
Pe a treia linie se află N numere naturale mai mici decât 10, S1 S2 ...SN , reprezentând elementele
şirului S.
Pe linia a patra se află un număr natural N r care reprezintă numărul de perechi.
Pe următoarele N r linii se află câte o pereche de numere naturale. Numerele scrise pe aceeaşi
linie ı̂n fişierul de intrare sunt separate prin câte un spaţiu.
Date de ieşire
Fişierul de ieşire subsir.out va conţine N r linii, pe cea de a i-a linie fiind scris rezultatul
pentru cea de a i-a pereche dintre cele N r perechi din fişierul de intrare, conform cerinţei C.
1 & N & 10
4
1 & N r & 10
4
0 & x $ 10
7
1&j&N
0 & a & b $ 107
# Punctaj Restricţii
1 30 Pentru teste valorând 30 de puncte cerinţa este 1.
2 70 Pentru teste valorând 70 de puncte cerinţa este 2.
Exemple:
subsir.in subsir.out
1 1
10 0
5610325231 0
5 1
14 0
12
153 6
153 9
72 8
2 11
10 15
5610325231 0
3
0 20
40 105
81 99
Explicaţii:
Pentru primul exemplu, cerinţa este 1, deci trebuie să verificăm pentru fiecare dintre cele
Nr 5 perechi x j specificate dacă x apare ca subşir ı̂n prefixul de lungime j al lui S.
Prima pereche este 1 4. Numărul 1 apare ca subşir ı̂n prefixul 5 6 1 0, deci se afişează 1.
A doua pereche este 1 2. Numărul 1 nu apare ca subşir ı̂n prefixul 5 6, deci se afişează 0.
A treia pereche este 153 6. Numărul 153 nu apare ı̂n prefixul 5 6 1 0 3 2, deci se va afişa 0.
A patra pereche este 153 9. De data aceasta numărul 153 apare ca subşir ı̂n prefixul 5 6 1 0 3
2 5 2 3, deci se afişează 1.
CAPITOLUL 14. ONI 2022 14.3. SUBSIR 168
A cincea pereche este 72 8. Numărul 72 nu apare ca subşir ı̂n niciun prefix al lui S, deci nici
ı̂n cel de lungime 8 şi se va afişa 0.
Pentru al doilea exemplu cerinţa este 2, deci trebuie să determinăm pentru fiecare dintre cele
Nr 3 perechi a b specificate câte numere naturale din intervalul [a, b] apar ca subşir ı̂n S.
Pentru intervalul [0,20] există 11 numere, acestea fiind 0, 1, 2, 3, 5, 6, 10, 11, 12, 13, 15.
Pentru intervalul [40, 105] există 15 numere, acestea fiind 50, 51, 52, 53, 55, 56, 60, 61, 62, 63,
65, 101, 102, 103, 105.
Pentru intervalul [81,99] răspunsul este 0, deoarece nu există niciun număr ı̂n acest interval
care să fie subşir al lui S.
Propusă de: prof. Ionel-Vasile Piţ-Rada, Colegiul Naţional ”Traian”, Drobeta-Turnu Severin
Cerinţa 1 Soluţie ”naivă” O N N r. Pentru fiecare pereche x, j , se determină şirul
cifrelor lui x şi apoi, ı̂ncepând cu cifra cea mai semnificativă (cea din stânga) şi continuând spre
cifra cea mai puţin semnificativă (ultima, cea din dreapta), se caută succesiv prima apariţie a cifrei
respective şi când se găseşte această poziţie se continuă căutarea de acolo pentru cifra următoare.
Dacă se depăşeşte poziţia j sau una dintre cifre nu se mai găseşte, atunci se opreşte căutarea.
Cerinţa 2 Soluţie ”naivă” O N alf a. Am notat cu alf a suma lungimilor intervalelor
de interogare, care este cel mult egală cu Nr 10 000 000. Pentru fiecare interval a, b şi pentru
fiecare număr x din interval se aplică ideea de la cerinţa 1, cu limita de căutare j N .
Optimizări. Pentru obţinerea unei performanţe mai bune este util să facem următoarele
precalculări:
(1) Pentru fiecare poziţie 1 & i & N şi pentru fiecare cifră zecimală 0 & cif & 9, determinăm
poz icif cea mai mică (prima) poziţie dinşirul S pe care apare cifra cif , după poziţia
i sau N 1 ı̂n caz că cifra cif nu mai apare ı̂n S după poziţia i.
Pentru a verifica dacă x apare ca subşir ı̂n prefixul de lungime j al lui S, parcurgem cifrele
lui x de la stânga la dreapta şi construim subşirul preluând poziţiile din poz (dacă cifra
curentă din x este cif , iar poziţia pe care apare precedenta cifra din x este i, atunci poziţia
ı̂n subşir a cifrei cif va fi poz icif . Astfel, numărul de paşi efectuaţi pentru verificarea
unei valori x este cel mult egal cu numărul de cifre din x.
Asemănător, se poate rezolva cerinţa 2, verificând succesiv fiecare valoare din fiecare interval
a, b.
(2) O a doua precalculare foarte utilă este aceea prin care pentru fiecare număr x din intervalul
7
0, 10 se determină minpoz x cea mai mică poziţie din S cu proprietatea că numărul
x apare ca subşir ı̂n prefixul de lungime minpoz x al lui S, respectiv minpoz x N 1
ı̂n cazul ı̂n care x nu apare ca subşir ı̂n S.
Iniţializarea se face prin minpoz x poz 1x, pentru 0 & x & 9, apoi, ı̂n ordine
crescătoare, pentru fiecare număr x cu 10 & x & 10 , vom calcula
7
După aceste precalculări, la cerinţa 1, pentru fiecare pereche x, j verificarea are complexitatea
O 1, deci complexitatea totală (pentru cele N r perechi) este O N r.
Analog, la cerinţa 2 pentru fiecare interval a, b numărarea se va realiza tot ı̂n O 1, deci
complexitatea totală (pentru cele N r intervale) va fi O N r.
Problema admite multiple alte abordări, care obţin diferite punctaje.
CAPITOLUL 14. ONI 2022 14.3. SUBSIR 169
ONI 2021
15.1 Bile
Problema 1 - Bile 100 de puncte
Presupunem că avem două cutii notate A şi B. Cutia A conţine N bile numerotate cu numerele
naturale distincte: 0, 1, 2, ..., N 1. Cutia B este goală.
Spunem că o bilă dintr-o cutie este bila specială a acestei cutii dacă numărul X cu care este
numerotată această bilă este egal cu media aritmetică a numerelor celorlalte bile din cutie.
La un moment dat, cineva mută bila cu numărul K din cutia A ı̂n cutia B.
Vi se cere să alegeţi alte K bile, din cutia A, pe care să le mutaţi ı̂n cutia B astfel ı̂ncât cutia
B să conţină K 1 bile, iar bila cu numărul K să fie bila specială a cutiei B.
Cerinţe
1. dacă, ı̂nainte să fie mutate bile din cutia A ı̂n cutia B, există o bilă specială ı̂n cutia A; ı̂n caz
afirmativ, programul determină numărul X cu care este numerotată această bilă specială;
2. cel mai mic (ı̂n sens lexicografic) şir strict crescător al numerelor celor K bile care pot fi
mutate din cutia A ı̂n cutia B astfel ı̂ncât bila cu numărul K să fie bila specială a cutiei B;
3. cel mai mare (ı̂n sens lexicografic) şir strict crescător al numerelor celor K bile care pot fi
mutate din cutia A ı̂n cutia B astfel ı̂ncât bila cu numărul K să fie bila specială a cutiei B.
Date de intrare
Fişierul de intrare bile.in conţine pe prima linie trei numere naturale C, N şi K, separate prin
câte un spaţiu. C reprezintă cerinţa care trebuie rezolvată (1, 2 sau 3), iar N şi K au semnificaţia
din enunţ.
Date de ieşire
dacă C 1, pe prima linie, numărul natural X reprezentând numărul bilei speciale din cutia
A sau valoarea -1 dacă cutia A nu conţine o astfel de bilă (reprezentând răspunsul la cerinţa
1);
dacă C 2, pe prima linie, un şir strict crescător de K numere naturale, separate prin câte
un spaţiu (reprezentând răspunsul la cerinţa 2);
dacă C 3, pe prima linie, un şir strict crescător de K numere naturale, separate prin câte
un spaţiu (reprezentând răspunsul la cerinţa 3).
Şirul y1 , y2 , ..., yK este mai mic ı̂n sens lexicograficc decât şirul z1 , z2 , ..., zK dacă există un
indice p, 1 & p & K, astfel ı̂ncât: y1 z1 , y2 z2 , ..., yp1 zp1 şi yp $ zp
Pentru cerinţa 1 se acordă 20p, iar pentru fiecare dintre cerinţele 2 şi 3 se acordă câte 40p.
Exemple:
2 K 2 K 1
0, 1, 2, 3, . . . , K 2, K
ÍÒÒ Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò ÒÑ Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ï 2
cele mai mici K-1
numere naturale distincte
K 2 K 1
aDacă K
2
2
% N 1, notăm soluţia acestei cerinţe cu s.
Iniţializăm si i pentru i 0, 1, ..., K 1 şi notăm cu S suma valorilor iniţiale din s:
S s0 s1 ... sK 1 K K 1©2.
CAPITOLUL 15. ONI 2021 15.1. BILE 172
2
Pentru ca media aritmetică a şirului s să fie K, trebuie să distribuim diferenţa dif K S la
elementele din şir. Întrucât dorim să obţinem cea mai mică soluţie din punct de vedere lexicografic,
vom utiliza această diferenţă pentru a maximiza ultimele elemente ale lui s.
Şirul s trebuie să conţină numere distincte mai mici sau egale cu N 1. Astfel, cea mai mare
valoare posibilă pentru sK 1 este N 1, cea mai mare valoare posibilă pentru sK 2 este
N 2, ..., iar cea mai mare valoare posibilă pentru s0 este N K. Deci, diferenţa dintre valoarea
actuală a oricărui element din s şi valoarea maximă a acelui element este N K.
Întrucât putem distribui doar diferenţa dif , vom adăuga N K la ultimele g dif © N K
elemente din şir. Rămâne să adăugăm restul rest dif % N K . Încercăm să adăugăm restul
la elementul sK g 1. Dacă sK g 1 rest j K, adăugăm restul astfel:
Complexitatea soluţiei: O K .
Cerinţa 3 (40 puncte)
Notăm soluţia acestei cerinţe cu s. Întrucât s trebuie să fie cea mai mare soluţie din punct de
vedere lexicografic, ı̂ncercăm să maximizăm primele elemente din s. Putem construi s astfel:
K par
Fie M K ©2. Cea mai mare valoare posibilă pentru s0 astfel ı̂ncât media valorilor din
şir să fie K este K 2. Alegem valorile celorlalţi termeni din şir astfel ı̂ncât:
– suma termenilor situaţi pe poziţii simetrice faţă de mijlocul şirului să fie egală cu 2K:
s[0] + s[K-1] = 2K; s[1] + s[K-2] = 2K; ..., s[M-1] + s[M] = 2K.
K impar
Fie M K ©2. Cea mai mare valoare posibilă pentru s0 astfel ı̂ncât media valorilor din
şir să fie K este [K/2]. Alegem valorile celorlalţi termeni din şir astfel ı̂ncât:
– s0 sK 1 2K 1;
– sM K 1;
– suma termenilor situaţi pe poziţii simetrice faţă de mijlocul şirului să fie egală cu 2K:
s[1] + s[K-2] = 2K, s[2] + s[K-3] = 2K, ..., [M-1] + s[M+1] = 2K;
Complexitatea soluţiei: O K .
Observaţii
CAPITOLUL 15. ONI 2021 15.2. SECVENTE 173
Nu este necesar să se utilizeze un tablou unidimensional pentru a memora termenii lui s.
2
O soluţie care generează toate modalităţile de a scrie pe K ca o suma de K numere cu
proprietatea cerută va obţine un punctaj parţial.
15.2 Secvente
Problema 2 - Secvente 100 de puncte
Se consideră un şir cu N elemente numere ı̂ntregi. Definim următoarele noţiuni:
Cerinţe
Scrieţi un program care să citească numărul natural N şi apoi şirul de N elemente. Programul
determină:
Date de intrare
Fişierul de intrare secvente.in conţine pe prima linie două numere naturale C şi N , separate
printr-un singur spaţiu, C reprezentând cerinţa care trebuie rezolvată (1, 2 sau 3). A doua linie a
fişierului conţine cele N elemente numere ı̂ntregi, separate prin câte un spaţiu.
Date de ieşire
numărul ı̂ntreg negativ X este divizibil cu numărul natural nenul N dacă restul ı̂mpărţirii
modulului lui X la N este 0 (de exemplu, X 30 este divizibil cu N 6, iar X 45 nu
este divizibil cu N 6).
Pentru fiecare dintre cerinţele 1 şi 2 se acordă 30p, iar pentru cerinţa 3 se acordă 40p.
Exemple:
şir situate ı̂ntre p1 (exclusiv) şi p2 (inclusiv) formează o N -secvenţa de lungime p2 p1 care este
maximală relativ la valoarea restului respectiv. Căutăm apoi maximul dintre aceste lungimi.
Cerinţa 3 (40 puncte)
În timp ce se construiesc cei doi vectori (vectorul sumelor parţiale şi vectorul frecvenţelor
de apariţie) se păstrează pentru fiecare rest posibil poziťia unde ı̂ncepe o potenţială secvenţă de
sumă maximă. Pentru fiecare element din şirul dat se calculează suma maximă a secvenţei care
se termină ı̂n el şi se compară cu o variabilă ı̂n care memorăm suma maximă (iniţializată cu 0
deoarece se garantează că există cel puţin o N -secvenţă de sumă pozitivă).
Pentru fiecare element v k din şir (k 1, 2, ..., N ) vom proceda astfel:
se calculează suma parţială S k S k 1 v k ;
se calculează restul r S k %N ;
dacă r $ 0 atunci r r N;
Restr Restr 1;
dacă v k %N 0 atunci Smax max Smax, v k ;
dacă S k %N 0 atunci Smax max Smax, S k ;
dacă Restr 1 (adică, r apare pentru prima dată) atunci SumaRestr sk
altfel
– dacă S k SumaRestr $ 0 (adică suma N -secvenţei curente este negativă), atunci
SumaRestr S k ;
– Smax max Smax, S k SumaRestk
La final, se va afişa Smax.
15.3 Valoare
Problema 3 - Valoare 100 de puncte
Valoarea unei litere este dată de numărul ei de ordine ı̂n alfabet (A are valoarea 1, B are
valoarea 2, C are valoarea 3, etc.).
Un text special este format din una sau mai multe litere mari ale alfabetului englez şi nu conţine
alte tipuri de caractere.
Valoarea unui text special o definim ca fiind egală cu suma valorilor literelor care ı̂l compun.
De exemplu, textul special ABAC are valoarea 7 (7=1+2+1+3).
Un text special poate fi transcris ı̂ntr-o formă restrânsă prin utilizarea perechilor de
paranteze şi a numerelor naturale. De exemplu, transcrierea ı̂n forma restrânsă a textului
ABACABACEECDCDE este textul ABAC 2E2 CD2E deoarece secvenţa ABAC apare de
două ori consecutiv, litera E are două apariţii consecutive şi la fel secvenţa de litere CD.
Transcrierea ı̂n formă restrânsă a unui text special se realizează prin ı̂nlocuirea fiecărei secvenţe
care are apariţii consecutive ı̂n text cu secvenţa scrisă ı̂ntre o pereche de paranteze rotunde urmată
apoi de numărul de repetiţii. De exemplu, textul special ABABAB se transcrie ı̂n forma restrânsă
ı̂n AB 3.
Dacă, după transcriere, textul ı̂n formă restrânsă conţine secvenţe care au apariţii con-
secutive ı̂n text atunci şi acesta se va transcrie ı̂n formă restrânsă. De exemplu, textul
special ABABCABABC se poate transcrie ı̂ntâi ı̂n forma restrânsă ı̂n ABABC 2 sau ı̂n
AB 2C AB 2C iar apoi ı̂n AB 2C 2.
Dacă textul special nu conţine nicio secvenţă cu apariţii consecutive ı̂n text, atunci forma
restrânsă a textului este identică cu forma iniţială. De exemplu, textul special ABAC are forma
restrânsă ABAC care este identică cu acest text special.
CAPITOLUL 15. ONI 2021 15.3. VALOARE 176
Cerinţe
Scrieţi un program care citeşte un şir de caractere S reprezentând forma restrânsă a unui text
special, şi apoi determină:
Date de intrare
pe prima linie, un număr natural C (1, 2 sau 3) reprezentând cerinţa care trebuie rezolvată;
pe a doua linie, şirul de caractere S reprezentând forma restrânsă a unui text special.
Date de ieşire
dacă C 1, pe prima linie, un număr natural reprezentând numărul literelor distincte care
apar ı̂n textul special (răspunsul la cerinţa 1);
dacă C 2, pe prima linie, un număr natural reprezentând suma numerelor care apar ı̂n
forma restrânsă a textului special (răspunsul la cerinţa 2);
dacă C 3, pe prima linie, un număr natural reprezentând valoarea textului special dat ı̂n
forma restrânsă (răspunsul la cerinţa 3).
Şirul de caractere S ce reprezintă forma restrânsă a unui text special poate avea cel mult
1000 de caractere (litere mari ale alfabetului englez, perechi de paranteze rotunde, cifre).
Numerele naturale care apar ı̂n forma restrânsă a unui text special sunt nenule şi aparţin
intervalului ı̂nchis 2, 9999.
Valoarea unui text special este un număr natural format din cel mult 12 cifre.
Pentru cerinţa 1 se acordă 10p, pentru cerinţa 2 se acordă 20p, iar pentru cerinţa 3 se acordă
70p.
Pentru teste ı̂n valoare de 10p, forma restrânsă a textului special este identică cu textul
special şi cerinţa este 3.
Exemple:
3 43 Se rezolvă cerinţa 3.
(ABAC)2E2(CD)2E Forma restrânsă (ABAC)2E2(CD)2E reprezintă transcrierea
textului special ABACABACEECDCDE. În textul special
apar 4 de A, 2 de B, 4 de C, 2 de D şi 3 de E. Astfel, valoarea
textului special este egală cu 43 (=4*1+2*2+4*3+2*4+3*5).
folosim un vector caracteristic pentru a identifica literele distincte care apar ı̂n textul special.
Complexitate O n, unde n este lungimea textului
se parcurge şirul pentru a identifica secvenţele formate doar din cifre, fiecare secvenţă se
transformă ı̂n valoare numerică şi se adună la suma generală
Complexitate O n, unde n este lungimea textului
folosim o stivă (simulată cu un vector) ı̂n care vom adăuga pe rând elemente după cum
urmează:
– dacă ı̂n text urmează o paranteză deschisă adăugăm pe stivă un element care să
marcheze acest lucru, de exemplu o valoare negativă (pentru a şti că aici ı̂ncepe repetiţia
unei secvenţe)
– dacă ı̂n text urmează o literă atunci adăugăm pe stivă valoarea ei
– dacă urmează o paranteză ı̂nchisă atunci sumăm şi eliminăm elementele de pe stivă,
pânla prima paranteză deschisă pe care o ı̂ntâlnim, şi ı̂nlocuim paranteza cu valoarea
sumei calculate (pentru a păstra pe stivă valoarea pe care o are textul cuprins ı̂ntre
paranteze)
– dacă ı̂ntâlnim un număr atunci valoarea din vârful stivei se ı̂nmulţeşte cu numărul şi
este ı̂nlocuită de acest produs (valoarea din vârful stivei reprezintă valoarea secvenţei
de text care trebuie repetat de număr ori)
179
Capitolul 17
ONI 2019
Cerinţe
Date de intrare
Fişierul de intrare criptografie.in conţine pe prima linie cerinţa C (care poate fi 1 sau 2),
pe linia a doua numărul natural k, cu semnificaţia de mai sus, precum şi un număr natural n,
separate printr-un spaţiu. Pe a treia linie se află un text format din n litere mici ale alfabetului
englez (neseparate prin spaţii).
Date de ieşire
a o secvenţă este formată dintr-o succesiune de litere aflate pe poziţii consecutive ı̂ntr-un şir.
a 0 $ n & 105
a 0$k&n
a Pentru teste ı̂n valoare de 67 de puncte C 1, iar pentru 33 de puncte C 2
a Pentru teste ı̂n valoare de 17 puncte se garantează C 1, k 1 şi n & 100
a Pentru teste ı̂n valoare de alte 17 puncte se garantează C 1 şi n & 1000
a Pentru cerinţa 2 se garantează că valoarea lui k este 1.
Exemple
180
CAPITOLUL 17. ONI 2019 17.1. CRIPTOGRAFIE - ONI 2019 181
Pentru numararea secventelor o prima abordare este cea bruta, anume sa fixam
pozitia de inceput si sa iteram la dreapta contorizand numarul de aparitii
ale fiecarei litere pana ce apare una care are k+1 aparitii sau pana la
finalul sirului. Aceasta abordare are timp de rulare de ordinul N*N.
23 p = 1;
24 strcpy(smaxim, "zz");
25 for (i=1;i<=n;i++)
26 {
27 ch = s[i]-’a’;
28 f[ ch ] ++;
29 while ( f[ch] > k )
30 {
31 f[ s[p]-’a’ ]--;
32 p++;
33 }
34 if (c == 1)
35 sol += i-p+1;
36 else
37 {
38 if (i-p+1 > maxim)
39 {
40 maxim = i-p+1;
41 strncpy(smaxim, s+p, maxim);
42 }
43 else
44 if (i-p+1 == maxim)
45 if (strncmp(smaxim, s+p, maxim) > 0)
46 strncpy(smaxim, s+p, maxim);
47 }
48 }
49 if (c == 1)
50 fout<<sol;
51 else
52 {
53 smaxim[maxim] = 0;
54 fout<<smaxim;
55 }
56 return 0;
57 }
Cerinţe
Date de intrare
Fişierul drept.in conţine pe prima linie, separate prin spaţiu, un număr natural C şi un număr
natural T . Următoarele 2 T linii vor descrie testele, câte două linii pentru fiecare test. Pe prima
linie corespunzătoare unui test se află un număr natural n, iar pe a doua linie un şir de n numere
ı̂ntregi, separate prin câte un spaţiu.
Date de ieşire
CAPITOLUL 17. ONI 2019 17.2. DREPT - ONI 2019 183
a 1 & T & 10
a 4 & n & 100
a Pentru teste ı̂n valoare de 45 de puncte C 1, pentru restul de 55 de puncte C 2
a Pentru teste ı̂n valoare de 30 de puncte C 1 şi numerele ce descriu figura sunt ı̂ntregi
nenule aparţinând intervalului 100, 100
a Pentru celelalte 15 puncte şi C 1 numerele ce descriu figura sunt ı̂ntregi nenule aparţinând
intervalului 109, 109
a Pentru teste ı̂n valoare de 33 de puncte se garantează că avem C 2 şi intersecţia dintre
orice orizontală cu poligonul este fie vidă, fie formată dintr-un singur segment.
a Pentu testele ı̂n care valoarea lui C este 2, numerele din şirul ce descrie poligonul sunt ı̂ntregi
nenule din intervalul 100, 100 şi se garantează că aceste şiruri reprezintă poligoane drepte.
a Într-un poligon drept laturile nu au puncte comune, exceptând capetele laturilor adiacente.
a Prima şi ultima latură ale unui poligon drept sunt perpendiculare.
Exemple
drept.in drept.out
12 10
8
5 3 -3 -1 2 -1 -4 -1
8
-2 1 3 1 -4 -3 2 1
22 91
8
5 3 -3 -1 2 -1 -4 -1
4
1 1 -1 -1
Explicaţii:
37
38 int main ()
39 {
40 ifstream fin ("drept.in");
41 ofstream fout("drept.out");
42
43 fin>>C>>t;
44
45 for (;t--;)
46 {
47 fin>>n;
48 xmin = xmax = ymin = ymax = 0;
49 v[0].x = 0;
50 v[0].y = 0;
51
52 for (i=1;i<=n;i++)
53 {
54 fin>>d;
55 if (i%2 == 1)
56 {
57 v[i].x = v[i-1].x + d;
58 v[i].y = v[i-1].y;
59 }
60 else
61 {
62 v[i].x = v[i-1].x;
63 v[i].y = v[i-1].y + d;
64 }
65
66 xmin = min(xmin, v[i].x);
67 xmax = max(xmax, v[i].x);
68 ymin = min(ymin, v[i].y);
69 ymax = max(ymax, v[i].y);
70 }
71
72 poligon = 1;
73 if (v[n].x != 0 || v[n].y != 0)
74 poligon = 0;
75
76 for (i=0;i<n-1;i++)
77 {
78 for (j=i+2;j<n;j++)
79 {
80 if (i == 0 && j == n-1)
81 continue;
82
83 if (i%2 == j%2)
84 {
85 /// paralele
86 if (punctPeSegment(i, j))
87 poligon = 0;
88 if (punctPeSegment(i+1, j))
89 poligon = 0;
90 if (punctPeSegment(j, i))
91 poligon = 0;
92 if (punctPeSegment(j+1, i))
93 poligon = 0;
94 }
95 else
96 if (i%2 == 0)
97 if (intersectieSegmente(v[i], v[i+1], v[j], v[j+1]))
98 poligon = 0;
99 else
100 if (intersectieSegmente(v[j], v[j+1], v[i], v[i+1]))
101 poligon = 0;
102 }
103 }
104
105 if (C!=1)
106 {
107 aria = 0;
108 for (i=xmin+1;i<=xmax;i++)
109 for (j=ymin+1;j<=ymax;j++)
110 {
111 jos = 0;
112 stanga = 0;
CAPITOLUL 17. ONI 2019 17.3. LUMINI - ONI 2019 186
Cerinţe
Se dau date despre fulgere, ı̂n ordinea ı̂n care acestea acţionează. Se cere ca la finalul furtunii
să se indice care este starea anumitor faruri, aflate la coordonate precizate de pe insulă.
Date de intrare
Prima linie a fişierului lumini.in conţine un numărul natural L, cu semnificaţia de mai sus.
Pe linia a doua se află un număr natural F , reprezentând numărul de fulgere.
CAPITOLUL 17. ONI 2019 17.3. LUMINI - ONI 2019 187
Pe următoarele F linii se află date despre câte un fulger, ı̂n ordinea ı̂n care acestea apar.
Primul număr de pe fiecare linie este tipul de fulger (1 sau 2 sau 3). Dacă acest număr este 1
sau 2, se mai află pe această linie un spaţiu şi ı̂ncă un număr. Acesta reprezintă linia pe care
loveşte fulgerul (dacă linia din fişier are la ı̂nceput 1), respectiv coloana pe care loveşte fulgerul
(dacă linia respectivă din fişier are la ı̂nceput 2). Dacă este vorba despre fulger de tip 3, pe acea
linie se mai află două numere, reprezentând linia şi coloana elementului de pe insulă unde loveşte
fulgerul, separate prin spaţiu.
Linia următoare conţine un număr Q. Pe următoarele Q linii se află câte două numere (separate
prin spaţiu) reprezentând linia şi coloana unui far de pe insulă pentru care se doreşte aflarea stării
după furtună.
Date de ieşire
Fişierul de ieşire lumini.out conţine pe o linie, separate prin câte un spaţiu, Q numere,
reprezentând rezultatele interogărilor ı̂n ordinea ı̂n care acestea apar ı̂n fişierul de intrare. Dacă
farul corespunzător este aprins se va scrie valoarea 1, iar dacă este stins se va scrie valoarea 0.
Restricţii şi precizări
a 2 & L & 2000000000
a 1 & F & 1000
a Se garantează că la precizarea fulgerelor linia ı̂ncepe cu 1, 2 sau 3, iar celelalte valori de pe
liniile respective sunt cuprinse ı̂ntre 1 şi L inclusiv.
a 1 & Q & 100000
a Se garantează că pentru fiecare far a cărei stare trebuie aflată linia şi coloana sunt cuprinse
ı̂ntre 1 şi L inclusiv.
a Pentru 24 de puncte, L & 100, F & 100 şi apar doar fulgere de tipul 1;
a Pentru alte 18 puncte, L & 100, F & 100;
a Pentru alte 12 puncte, L & 10000 şi apar doar fulgere de tipul 1;
a Pentru alte 8 puncte, L & 10000;
a Pentru alte 13 puncte, L & 1000000;
a Pentru restul de 25 de puncte nu sunt restricţii suplimentare.
Exemple
Solutia 1
Solutia 2
Observam ca in loc sa parcurgem toata zona din matrice care este afectata
de un fulger, putem nota doar informatii despre liniile, coloanele si
diagonalele afectate.
Solutia 3
Solutia 4
2
3 using namespace std;
4
5 ifstream f("lumini.in");
6 ofstream g("lumini.out");
7
8 #define L 1010
9
10 struct fulger
11 {
12 long long poz;
13 int fr;
14 } lin[L], col[L], diag[2*L];
15
16 int nLin, nCol, nDiag, n;
17
18 int cauta(fulger a[], int n, long long v)
19 {
20 int s=1, d=n, m;
21 while(s<=d)
22 {
23 m=(s+d)/2;
24 if(a[m].poz==v)return m;
25 else if(v<a[m].poz)d=m-1;
26 else s=m+1;
27 }
28 return s;
29 }
30
31 void insereaza(fulger a[], int &n, long long v)
32 {
33 if(n==0)
34 {
35 a[++n].poz=v;
36 a[n].fr=1;
37 return;
38 }
39
40 int pozitie=cauta(a,n,v), i;
41
42 if(pozitie<=n && a[pozitie].poz==v)a[pozitie].fr++;
43 else
44 {
45 i=n;
46 while(i>0 && a[i].poz>v) a[i+1]=a[i--];
47 a[i+1].poz=v;
48 a[i+1].fr=1;
49 n++;
50 }
51 }
52
53 int main()
54 {
55 f>>n;
56 int x, y, z, i, Q;
57 f>>Q;
58 for(i=1; i<=Q; i++)
59 {
60 f>>x;
61 if(x==1)f>>y,
62 insereaza(lin, nLin, y);
63 else
64 if(x==2)
65 f>>y, insereaza(col, nCol, y);
66 else
67 f>>y>>z, insereaza(diag, nDiag, 1LL*y+1LL*z-1);
68 }
69
70 lin[nLin+1].poz = col[nCol+1].poz = diag[nDiag+1].poz=n+1;
71
72 for(i=2; i<=nLin+1; i++) lin[i].fr+=lin[i-1].fr;
73 for(i=1; i<=nCol+1; i++) col[i].fr+=col[i-1].fr;
74 for(i=1; i<=nDiag+1; i++) diag[i].fr+=diag[i-1].fr;
75
76 f>>Q;
77 for(i=1; i<=Q; i++)
CAPITOLUL 17. ONI 2019 17.3. LUMINI - ONI 2019 190
78 {
79 f>>x>>y;
80 int far=1-(x%2+y%2)%2;
81
82 int p=cauta(lin,nLin, x);
83 if(lin[p].poz>x) p--;
84 if(lin[p].fr%2) far=!far;
85
86 p=cauta(col,nCol, y);
87 if(col[p].poz>y) p--;
88 if(col[p].fr%2) far=!far;
89
90 p=cauta(diag,nDiag, 1LL*x+1LL*y-1);
91 if(diag[p].poz>1LL*x+1LL*y-1) p--;
92 if(diag[p].fr%2) far=!far;
93 g<<far<<’ ’;
94 }
95
96 return 0;
97 }
ONI 2018
18.1 gene
Problema 1 - gene 100 de puncte
Gigel este curios să afle ı̂n ce zonă a ţării au trăit cei mai mulţi dintre strămoşii săi. El reuşeşte
să adune informaţii despre structura genetică a persoanelor din diferite părţi ale ţării şi speră că,
prin compararea cu propria structură genetică, să identifice o zonă pătratică ı̂n care au trăit cei
mai mulţi dintre strămoşii săi.
Structura genetică a unei persoane este reprezentată sub forma unei secvenţe cu cel mult 20 de
caractere (litere mici ale alfabetului englez). O persoană poate fi considerată strămoş a lui Gigel
dacă gradul de similaritate dintre secvenţa corespunzătoare persoanei respective şi cea a lui Gigel
este mai mare strict decât un număr K, cunoscut.
Gradul de similaritate dintre două secvenţe este reprezentat de numărul de caractere comune
celor două secvenţe. De exemplu pentru secvenţele abcdabd şi acbdaad gradul de similaritate este
6 (2 caractere a, 2 caractere d, 1 caracter b, 1 caracter c).
Gigel reprezintă harta ţării sub forma unui tablou bidimensional cu N linii şi M coloane ı̂n
care fiecare element reprezintă structura genetică a unei persoane din zona respectivă.
Cerinţe
Date de intrare
Fişierul de intrare gene.in conţine pe prima linie numărul natural C, care nu poate avea decât
valorile 1 sau 2 şi indică numărul cerinţei care va fi rezolvată.
Pe a doua linie numerele naturale N , M şi K, separate prin câte un spaţiu, cu semnificaţia de
mai sus.
Pe a treia linie se află structura genetică a lui Gigel, iar pe următoarele N M linii câte o
secvenţă de maximum 20 de litere mici ale alfabetului englez, care reprezintă structurile genetice
ale persoanelor din ţară, ı̂n ordinea parcurgerii hărţii pe linii.
Date de ieşire
191
CAPITOLUL 18. ONI 2018 18.1. GENE 192
Exemple
gene.in gene.out Explicaţii
1 1 1 aaacctg Secvenţa corespunzătoare lui Gigel este: acgt
333 3 2 ccgtabd Pentru secvenţele din hartă gradele de similaritate sunt:
acgt aaacctg - grad 4
aaacctg abrcg - grad 3
abrcg saasdfs - grad 1
saasdfs ecctg - grad 3
ecctg abnctt - grad 3
abnctt agtatggt - grad 3
agtatggt aaa - grad 1
aaa ccgtabd - grad 4
ccgtabd bbbb - grad 0
bbbb deci maximul este 4 şi apare pentru două dintre secvenţe
2 232 Pentru harta dată, gradele de similaritate cu secvenţa acgt
442 sunt, ı̂n ordine: 2 3 1 4 2 1 3 4 1 2 4 4 3 3 0 1.
acgt Deci reprezentarea hărţii ı̂n formă bidimensională este:
aec 2314
ctg 21 34
abvf 12 44
acgtaaa 3301
bbbbttg Cea mai mare zonă pătratică, ı̂n care toate gradele sunt mai
saa mari strict decât 2 are latura 2 şi ı̂ncepe la linia 2, coloana 3.
acgec
actgt
abvf
nctt
cagtatggt
acgtaaa
bbabttg
saatg
sdfs
fhgj
Pentru determinarea gradului de similaritate dintre două secvenţe putem număra, folosind doi
vectori de frecvenţă, numărul de apariţii a fiecărui caracter ı̂n fiecare din cele două şiruri.
Gradul de similaritate ı̂l obţinem ı̂nsumând numărul minim de apariţii pentru fiecare caracter.
CAPITOLUL 18. ONI 2018 18.2. JOCXZERO 193
Pentru determinarea zonei pătratice de dimensiune maximă ı̂n care toate valorile gradelor de
similaritate depăşesc pragul k, construim o matrice a cu n linii şi m coloane ı̂n care fiecare element
are valoarea 1 dacă gradul de similaritate a persoanei din zona respectivă depăşeşte pragul k sau
0 ı̂n caz contrar.
Determinăm apoi, ı̂n O n m, pentru fiecare poziţie i, j latura maximă a pătratului din
care face parte elementul şi respectă restricţia ca toate elemetele corespunzătoare din pătrat să
fie mai mari egale 1, ı̂n modul următor: dacă elementul curent este 1 atunci am putea extinde
pătratul anterior determinat, deci ai, j min ai 1, j 1, ai 1, j , ai, j 1 1, altfel
latura va rămne 0.
Latura maximă a unui pătrat va fi valoarea maximă din matricea a.
18.2 jocxzero
Problema 2 - jocxzero 100 de puncte
Pe o foaie dintr-un caiet de matematică de dimensiune N M
X X X X 0
(N numărul de linii şi M numărul de coloane) sunt completate toate
pătrăţelele cu X sau 0. Pentru un număr natural K dat, numim şir 0 X X X 0
corect, o secvenţă de K elemente consecutive pe linie, coloană sau di- 0 0 X 0 0
agonale care au aceeaşi valoare (X sau 0). Două pătrăţele de pe foaie
sunt vecine pe aceeaşi diagonală dacă au un singur colţ comun. 0 0 0 X X
Exemplu din figura alăturată, pentru care N 4, M 5, K 3 conţine 6 şiruri corecte de X
şi 5 şiruri corecte de 0.
Cerinţe
1. Se dau numerele naturale N , M şi K şi o foaie de matematică plină cu X şi 0. Determinaţi
câte şiruri corecte de X şi câte şiruri corecte de 0 se găsesc pe foaia dată.
2. Se dau Q ı̂ntrebări de forma A B, ı̂n care A este caracterul X sau 0 şi B este un număr
natural. Determinaţi ı̂n câte moduri putem tăia foaia de matematică vertical pentru a obţine ı̂n
subtabloul din partea stângă exact B şiruri corecte de A. Foia se poate tăia ı̂n M 1 variante:
după prima coloană, a doua coloană, după a treia coloană, ş.a.m.d, până după penultima coloană.
Date de intrare
Fişierul de intrare jocxzero.in conţine pe prima linie un număr natural P reprezentând cerinţa
din problemă care trebuie rezolvată.
Dacă P 1 atunci pe a doua linie se găsesc ı̂n ordine numerele naturale N , M şi K, separate
prin câte un spaţiu, apoi pe următoarele N linii câte M caractere de X sau 0 reprezentând foaia
dată.
Dacă P 2 atunci pe a doua linie se găsesc ı̂n ordine numerele naturale N , M şi K, separate
prin câte un spaţiu, apoi pe următoarele N linii câte M caractere de X sau 0 reprezentând foaia
dată.
Pe linia N 3 se găseşte numărul natural Q. Pe următoarele Q linii se găsesc câte un caracter
A şi un număr natural B despărţite prin un spaţiu.
Date de ieşire
Dacă P 1 atunci fişierul de ieşire jocxzero.in conţine pe o singură linie două numere naturale
separate printr-un spaţiu, reprezentând, ı̂n ordine, numărul de şiruri corecte de X şi numărul de
şiruri corecte de 0.
Dacă P 2 atunci fişierul de ieşire jocxzero.out conţine pe Q linii, câte un număr natural
reprezentând răspunsul la ı̂ntrebarea corespunzătoare din fişierul de intrare.
Exemple
jocxzero.in jocxzero.out Explicaţii
1 65 Pe prima linie sunt 2 şiruri corecte de X, pe a doua un şir
453 corect de X, pe diagonală avem 2 şiruri corecte de X şi unul
XXXX0 pe verticală.
0XXX0 Pe ultima linie avem un şir corect de 0, pe prima coloana avem
00X00 un şir corect de 0, pe ultima coloană avem un şir corect de 0,
000XX pe diagonală mai avem 2 şiruri corecte de 0.
2 2 Putem tăia vertical după prima coloană, după a doua, după
453 0 a treia şi după a patra coloană. Dacă tăiem după prima şi a
XXXX0 doua obţinem un singur şir corect de 0.
0XXX0 Indiferent pe unde tăiem nu putem avea un subtablou cu un
00X00 singur şir corect de X.
000X0
2
01
X1
prof. Dan Octavian Dumitraşcu - Colegiul Naţional Dinicu Golescu, Câmpulung, Argeş
18.3 laser
Problema 3 - laser 100 de puncte
Considerăm N segmente ı̂n plan identificate prin coordonatele extremităţilor lor. Toate seg-
mentele sunt ı̂nchise, adică fiecare conţine şi cele două puncte considerate extremităţile sale.
Presupunem că ı̂n punctul O 0, 0 care este originea sistemul de axe ortogonale XOY , se află un
laser care poate transmite câte un fascicul de lumină ı̂n orice punct cu ordonata pozitivă (' 0).
Fasciculul poate fi reprezentat ı̂n plan, ca o semidreaptă cu extremitatea ı̂n originea axelor. Totuşi,
dacă fasciculul de lumină ı̂ntâlneşte un segment, acesta ı̂l obturează, adică ı̂l ı̂mpiedică să treacă
mai departe de acesta. Considerăm că fiecare segment are asociat un număr care reprezintă un
cost pentru desenarea lui ı̂n plan.
Cerinţe
Determinaţi costul total minim al segmentelor care pot fi alese pentru a obtura orice fascicul
de lumină care ar pleca din origine către un punct cu ordonata pozitivă.
Date de intrare
Date de ieşire
Fişierul de ieşire laser.out va conţine un număr reprezentând costul minim determinat sau
1 dacă nu există soluţie.
Exemple
laser.in laser.out Explicaţii
4 4 S-au ales segmentele de cost total minim
23502 [(-5, 0), (-2,4)], [(-4, 4), (2,3)] şi [(2, 3), (5,0)].
2 3 -4 4 1 Segmentele [(-5, 0), (-2,4)] şi [(-14, 1), (6,0)]
-2 4 -5 0 1 obturează orice fascicul dar are cost total mai mare
6 0 -14 1 8
4 3 S-au ales segmentele
-1 3 1 3 1 [(-2, 0), (-1,1)];
-2 0 -1 1 1 [(-1, 1), (1,1)] ;
20111 [(1, 1), (2,0)].
1 1 -1 1 1
3 -1 Nu există segmente care să respecte cerinţele.
-1 3 1 3 1
-2 0 -1 1 4
20115
CAPITOLUL 18. ONI 2018 18.3. LASER 196
Considerăm două puncte A şi B ı̂n planul XOY , care reprezintă capetele unui segment.
Atunci vom reţine segmentul AB cu ajutorul pantelor dreptelor AO şi BO şi costul segmentului
(pantaunghi1, pantaunghi2, cost). ı̂n cadrul unui segment, capetele vor fi ordonate antitrigono-
metric (ı̂n sensul acelor de ceasornic), adică pantaunghi1 % pantaunghi2.
Toate cele N segmente vor fi reţinute ı̂ntr-un vector A. Toate segmentele se vor ordona
descrescător după pantaunghi1.
Pentru determinarea costului minim, ne aflăm ı̂n faţa problemei de a selecta submulţimea de
cost minim de segmente din cele N cu proprietatea că obturează orice fascicul de lumina (către
un punct cu ordonata pozitivă). Vom folosi vectorul auxiliar dp, ı̂n care vom reţine, pentru fiecare
segment i:
dpi - costul minim al unei subşir care ı̂l are ca utim element pe segmentul i şi care obturează
orice fascicul de lumină cu panta unghiul format cu axa OX, mai mare sau egal decât pantaunghi2.
Printre două segmente P , Q (pantaunghi1P % pantaunghi1Q ) nu trece niciun fascicul de
lumină dacă şi numai dacă pantaunghi2P pantaunghi1Q
Pentru a determina dpi, vom traversa toate segmentele j (1 & j & i1) a.ı̂. printre segmentele
i şi j nu trece niciun fascicul de lumină şi vom retine min dpj . Dacă nu există vom avea
dpi inf.
ONI 2017
19.1 boats
Problema 1 - boats 100 de puncte
Pe o foaie de matematică cu N pătrăţele orizontale (pe aceeaşi linie) şi M pătrăţele verticale
(pe aceeaşi coloană), Alex a pictat nave.
Definim o navă linie (L) ca un set de pătrăţele umbrite, consecutive pe un rând al foii de
matematică.
Definim o navă coloană (C) ca un set de pătrăţele umbrite consecutive pe o coloană a foii de
matematică.
Dimensiunea unei nave este egală cu numărul de pătrăţele din care este formată. O navă
formată dintr un singur pătrăţel nu este nici linie, nici coloană. Navele pot avea diferite dimensiuni.
Două nave diferite nu se ating pe laturi sau colţuri, nu se suprapun şi nu au pătrăţele comune. Pe
foaia de matematică sunt pictate doar nave de cele 3 tipuri: navă linie (L), navă coloană (C) sau
navă pătrăţel.
Cerinţe
Cunoscându-se M , N şi pictura lui Alex, scrieţi un program care să determine:
1. Numărul de nave formate doar dintr-un singur pătrăţel;
2. Numărul de nave linie şi numărul de nave coloană, precum şi dimensiunile acestora.
Date de intrare
Fişierul de intrare boats.in conţine pe prima linie un număr natural P reprezentând cerinţa
care trebuie să fie rezolvată 1 sau 2 ). Pe cea de a doua linie fişierul conţine două numere ı̂ntregi,
separate printr-un spaţiu, reprezentând valorile M şi N din enunţ. Pe următoarele M linii se află
câte N valori egale cu 0 sau 1, separate prin câte un spaţiu, 0 dacă pătrăţelul nu face parte dintr-o
navă, 1 ı̂n cazul ı̂n care pătrăţelul este o parte a unei nave).
Date de ieşire
Dacă cerinţa este P 1, atunci pe prima linie a fişierului boats.out va fi scris un număr
natural reprezentând numărul de nave formate dintr un singur pătrăţel.
Dacă cerinţa este P 2, aunci ı̂n fişierul boats.out vor fi scrise pe câte o linie, separate prin
câte un spaţiu, trei valori: caracterul L urmat de numerele d şi c, ı̂n ordine crescătoare după
valoarea d, unde d reprezintă lungimea navei (numărul de pătrăţele) iar c numărul de nave linie
de lungime d. Apoi, pe fiecare dintre liniile următoare vor fi scrise, separate prin câte un spaţiu,
caracterul C urmat de două numere: d şi c, ı̂n ordine crescătoare după d, unde d reprezintă
lungimea navei (numărul de pătrăţele) şi c numărul de nave coloană de lungime d.
197
CAPITOLUL 19. ONI 2017 19.1. BOATS 198
Exemple:
boats.in boats.out Explicaţii
1 Se rezolvă doar cerinţa 1.
Există o singură navă formatată dintr-
un singur pătrăţel
Se reţin lungimile navelor de tip linie, respectiv de tip coloană ı̂n 2 vectori de poziţie
pentru a contoriza numărul de nave de o anumită lungime, astfel: pe poziţia i a tabloului
lungime orizontal vom avea numărul de nave linie de lungime i, iar pe poziţia j a tabloului
lungime vertical numărul de nave coloană de lungime j.
Parcurgând matricea determinăm secvenţele de 1 (adică părţi ale navelor). Iniţial parcurgem
din punctul curent către EST pe aceeaşi linie (pentru determinarea navelor linie), apoi, din punctul
curent către SUD (pentru determinarea navelor coloană). Dacă găsim o parte a unei nave marcăm
poziţia curentă din matricea iniţială cu 0, semn că am adăugat valoarea 1 la contorul pentru
lungime şi pentru a evita posibilitatea de a o repeta.
Dacă nu se găsesc valori egale cu 1 ı̂n alte direcţii la est sau sud, ı̂nseamnă că nava este alcătuită
dintr-un singur pătrăţel.
3
4 #define FIN "boats.in"
5 #define FOU "boats.out"
6 #define Mmax 1002
7 #define Nmax 1002
8
9 short Old[Nmax] ={0}, New[Nmax] ={0}, Nxt[Nmax]={0};
10 short Oriz[Nmax]={0}, Vert[Nmax]={0};
11 int M, N, P;
12
13 using namespace std;
14
15 void Vcopy(short A[], short B[], int n)
16 {
17 int i;
18 for(i=1; i<=n; ++i) A[i] = B[i];
19 }
20
21 int main()
22 {
23 ifstream fin(FIN);
24 ofstream fou(FOU);
25 int i, j, NrUnu, Dim;
26 bool Este;
27 //citesc datele
28 fin >> P >> M >> N;
29 if( P == 1)
30 {
31 NrUnu = 0;
32 for(j=1; j <= N; ++j) fin >> Old[j];
33 for(j=1; j <= N; ++j) fin >> New[j];
34
35 //calculez pentru prima linie
36 for(j=1; j <= N; ++j)
37 if(Old[j] == 1 && Old[j-1] + Old[j+1] + New[j-1] + New[j] + New[j+1] == 0)
38 NrUnu++;
39
40 for(i=3; i <= M; ++i)
41 {
42 for(j=1; j <= N; ++j) fin >> Nxt[j];
43 for(j=1; j <= N; ++j)
44 if(New[j] == 1 &&
45 New[j-1] + New[j+1] + Old[j-1] + Old[j] + Old[j+1] +
46 Nxt[j-1] + Nxt[j] + Nxt[j+1] == 0)
47 NrUnu++;
48 Vcopy(Old, New, N);
49 Vcopy(New, Nxt, N);
50 }
51
52 //calculez pentru ultima linie
53 for(j=1; j <= N; ++j)
54 if(New[j] == 1 &&
55 New[j-1] + New[j+1] + Old[j-1] + Old[j] + Old[j+1] == 0)
56 NrUnu++;
57 //scriu rezultatul
58 fou << NrUnu << ’\n’;
59 }
60 else
61 {
62 for(j=1; j <= N; ++j) fin >> Old[j];
63
64 //pentru prima linie
65 Este = false;
66 for(j=2; j <= N; ++j)
67 if(Old[j] && Old[j-1])
68 if( ! Este) Este = true, Dim = 1, Old[j-1] = 0;
69 else Dim++, Old[j-1] = 0;
70 else
71 if( Este) Oriz[ Dim+1 ]++, Este = false, Old[j-1] = 0;
72
73 if( Este) Oriz[ Dim+1 ]++, Old[N] = 0;
74
75 for(i=2; i <= M; ++i)
76 {
77 for(j=1; j <= N; ++j) fin >> Nxt[j];
78 //caut nave orizontale
CAPITOLUL 19. ONI 2017 19.1. BOATS 200
79 Este = false;
80 for(j=2; j <= N; ++j)
81 if(Nxt[j] && Nxt[j-1])
82 if( ! Este) Este = true, Dim = 1, New[j-1] = Nxt[j-1] = 0;
83 else Dim++, New[j-1] = Nxt[j-1] = 0;
84 else
85 if( Este) Oriz[ Dim+1 ]++, Este = false, New[j-1] = Nxt[j-1] = 0;
86 else New[j] = 0;
87 if( Este) Oriz[ Dim+1 ]++, New[N] = Nxt[N] = 0;
88
89 //caut nave verticale sau orizontale de dim. 1
90 for(j=1; j <= N; ++j)
91 if(Nxt[j] == 0)
92 {
93 if(Old[j])
94 if(Old[j] == 1) Oriz[1]++, New[j] = 0;
95 else Vert[ Old[j] ]++, New[j] = 0;
96 }
97 else
98 New[j] = Old[j] + 1;
99 Vcopy(Old, New, N);
100 }
101
102 //pentru ultima linie
103 for(j=1; j <= N; ++j)
104 {
105 if(Old[j])
106 if(Old[j] == 1) Oriz[1]++;
107 else Vert[ Old[j] ]++;
108 }
109
110 //scriu datele de iesire
111 for(i=2; i <= Nmax - 2; ++i)
112 if(Oriz[i]) fou<<’L’<<’ ’<<i<<’ ’<<Oriz[i]<<’\n’;
113 for(i=2; i <= Nmax - 2; ++i)
114 if(Vert[i]) fou<<’C’<<’ ’<<i<<’ ’<<Vert[i]<<’\n’;
115 }
116
117 fin.close();
118 fou.close();
119 return 0;
120 }
31 }
32
33 void sol()
34 {
35 int i,j,nr;
36 for(i=1;i<=n;i++)
37 for(j=1;j<=m;j++)
38 if(a[i][j])
39 { nr=1;
40 if(a[i+1][j]==0)
41 { j++;
42 while(a[i][j]==1)
43 {a[i][j]=0; j++;nr++;}
44 oriz[nr]++;j--;
45 }
46 else
47 { int k=i+1;
48 while(a[k][j]==1)
49 {a[k][j]=0; k++;nr++;}
50 vert[nr]++; }
51 ///
52 ///cout<<nr<<endl;
53 ;
54 }
55 if(p==1)g<<oriz[1];
56 else{
57 for(i=2;i<=m;i++)
58 if(oriz[i])g<<"L "<<i<<" "<<oriz[i]<<endl;
59 for(i=2;i<=n;i++)
60 if(vert[i])g<<"C "<<i<<" "<<vert[i]<<endl;
61 }
62 }
63
64 int main()
65 {
66 citire();
67 sol();
68 ///scrie();
69 return 0;
70 }
33 while(v[k][j]==1) {
34 v[k][j]=0;
35 k--;
36 lungime++;
37 }
38 v[k][j]=0;
39 lungime_vertical[lungime].numar++;
40 }
41 else if(v[i+1][j]==1) {
42 v[i][j]=0;
43 int lungime=1,k;
44 if(v[i+1][j]==1) {
45 k=i+1;
46 while(v[k][j]==1) {
47 v[k][j]=0;
48 k++;
49 lungime++;
50 }
51 v[i+1][j]=0;
52 lungime_vertical[lungime].numar++;
53 }
54 }
55 else if(v[i][j+1]==1) {//aceeasi linie,stanga
56 v[i][j]=0;
57 int lungime=1,k;
58 if(v[i][j+1]==1) {
59 k=j+1;
60 while(v[i][k]==1) {
61 v[i][k]=0;
62 k++;
63 lungime++;
64 }
65 v[i][k]=0;
66 lungime_orizontal[lungime].numar++;
67 }
68 }
69 else if(v[i][j-1]==1) {//aceeasi linie,dreapta
70 v[i][j]=0;
71 int lungime=1,k;
72 if(v[i][j-1]==1) {
73 k=j-1;
74 while(v[i][k]==1) {
75 v[i][k]==0;
76 k--;
77 lungime++;
78 }
79 v[i][j-1]=0;
80 lungime_orizontal[lungime].numar++;
81 }
82 }
83 else singur_patratel++;
84 }
85 }
86 }
87
88 void afisare() {
89 if(p==1) g<<singur_patratel<<endl;
90 else if(p==2)
91 {for(int i=2;i<=1001;i++)
92 if(lungime_orizontal[i].numar!=0)
93 g<<"L "<<i<<" "<<lungime_orizontal[i].numar<<endl;
94
95 for(int j=2;j<=1001;j++)
96 if(lungime_vertical[j].numar!=0)
97 g<<"C "<<j<<" "<<lungime_vertical[j].numar<<endl;
98 }
99 }
100
101 int main()
102 {
103 citire();
104 determinare_nave();
105 afisare();
106 return 0;
107 }
CAPITOLUL 19. ONI 2017 19.2. DOILAN 203
19.2 doilan
Problema 2 - doilan 100 de puncte
Fie n un număr natural nenul.
Se construieşte mulţimea M a tuturor numerelor formate din exact n cifre, numere formate
doar cu cifrele 1 şi 2.
Cerinţe
Scrieţi un program care citeşte numărul natural n şi apoi determină cel mai mic număr natural
n
x din mulţimea M cu proprietatea că x este divizibil cu 2 .
Date de intrare
Date de ieşire
Fişierul de ieşire doilan.out va conţine pe prima linie un număr natural format din n cifre,
n
doar cifre 1 şi 2, reprezentând cel mai mic număr x din mulţimea M , divizibil cu 2 .
Exemple
doilan.in doilan.out Explicaţii
3 112 Cel mai mic număr de trei cifre, format doar cu cifrele 1 şi 2,
3
divizibil cu 2 , este x 112.
Astfel, acest număr se va scrie pe prima linie a fişierului de
ieşire doilan.out
19.3 z
Problema 3 - z 100 de puncte
Magazinul de jocuri a lansat cea mai recentă versiune a jocului Z, pentru
a-i ajuta pe elevii din clasa a VIII-a să ı̂nţeleagă mai bine modul de identifi-
care a coordonatelor unui punct din plan, ı̂ntr-un sistem de axe ortogonale.
Numim semn Z ı̂n planul xOy figura obţinută cu ajutorul a 4 puncte
distincte A xA , yA , B xB , yB , C xC , yC , D xD , yD , unite ca ı̂n fig.1, ı̂n
care xA xC , xB xD , yA yB , yC yD .
CAPITOLUL 19. ONI 2017 19.3. Z 205
Cerinţe
Cunoscându-se n (numărul de puncte afişate succesiv pe ecran) şi coordonatele celor n puncte
din plan, să se scrie un program care determină:
1. Numărul de treceri prin originea sistemului de coordonate.
2. Numărul maxim al semnelor Z distincte, formate cu puncte marcate.
Date de intrare
Fişierul de intrare z.in conţine pe prima linie un număr natural P reprezentând cerinţa care
trebuie să fie rezolvată (1 sau 2).
Pe cea de a doua linie se află numărul natural n, reprezentând numărul punctelor afişate
succesiv pe ecran.
Pe următoarele n linii se află câte două numere ı̂ntregi, x şi y, separate printr-un spaţiu,
reprezentând coordonatele unui punct x, y din plan, ı̂n ordinea apariţiei pe ecran.
Date de ieşire
Dacă cerinţa este P 1, atunci pe prima linie a fişierului z.out va fi scris un număr natural
reprezentând numărul de treceri prin originea sistemului de coordonate.
Dacă cerinţa este P 2, atunci fişierul de ieşire z.out va conţine numărul maxim al semnelor
Z distincte, formate cu puncte marcate.
1) Pentru determinarea numărului de treceri prin originea sistemului xOy, se verifică pentru
fiecare segment trasat, care nu are capete ı̂n origine, dacă acesta conţine originea sistemului de
coordonate.
2) Pentru a determina numărul semnelor Z, reţinem, pentru fiecare ordonată Y : mulţimea
punctelor marcate, distincte, cu ordonata Y şi intervalele orizontale x1, Y , x2, Y .
Se calculează reuninea intervalelor orizontale, pentru fiecare ordonată Y .
Se memorează segmentele oblice SO care ar putea forma un semn Z şi se realizează reuniunea
segmentelor oblice.
Pentru fiecare segment oblic astfel determinat, se identifică mulţimea perechilor de puncte
marcate care se află pe acest segment şi se verifică, pentru fiecare diagonală determinată de
punctele perechii, dacă determină un semn Z.
75 S[y][0].st=k;
76 }
77 }
78
79 void adaugaSegmentOblic(short xs,short ys,short xj,short yj)
80 {
81 short i,j=0;
82 for(short i=1;i<=nro;++i)
83 if(( xj>=SO[i].xj && xj<=SO[i].xs || xj<=SO[i].xj && SO[i].xj<=xs)
84 && peAceeasiDreapta(SO[i].xj,SO[i].yj, xj,yj, SO[i].xs,SO[i].ys)
85 && peAceeasiDreapta(SO[i].xj,SO[i].yj, xs,ys, SO[i].xs,SO[i].ys))
86 {
87 xj=min(xj,SO[i].xj);
88 yj=min(yj,SO[i].yj);
89 xs=max(xs,SO[i].xs);
90 ys=max(ys,SO[i].ys);
91 }
92 else
93 SO[++j]=SO[i];
94 ++j;
95 SO[j].xj=xj;
96 SO[j].yj=yj;
97 SO[j].xs=xs;
98 SO[j].ys=ys;
99 nro=j;
100 }
101
102 int uniteOrizontal(short x1, short x2, short y)
103 {
104 if(!P[y][x1]) return 0;
105 if(!P[y][x2]) return 0;
106
107 int i=1;
108 while(i<=S[y][0].st &&S[y][i].dr<x1)
109 i++;
110 if(i<=S[y][0].st && x2 <= S[y][i].dr)
111 return 1;
112
113 return 0;
114 }
115
116 int ZZ(short i)
117 {
118 short nrp=0,y,x,z=0,j;
119 for(y=SO[i].ys;y>=SO[i].yj;--y)
120 if(S[y][0].st &&
121 (y-SO[i].yj)*(SO[i].xs-SO[i].xj)%(SO[i].ys-SO[i].yj)==0)
122 {
123 x=(y-SO[i].yj)*(SO[i].xs-SO[i].xj)/(SO[i].ys-SO[i].yj)+SO[i].xj;
124 if(P[y][x])
125 {
126 ++nrp;
127 p[nrp].x=x;
128 p[nrp].y=y;
129 }
130 }
131
132 for(i=1;i<nrp;++i)
133 for(j=i+1;j<=nrp;++j)
134 if(uniteOrizontal(p[j].x,p[i].x,p[i].y) &&
135 uniteOrizontal(p[j].x, p[i].x, p[j].y))
136 ++z;
137 return z;
138 }
139
140 void numaraZ()
141 {
142 short i;
143 unsigned long long nrz=0;
144 for(i=1;i<=nro;++i)
145 nrz+=ZZ(i);
146 fout<<nrz<<’\n’;
147 }
148
149 int main()
150 {
CAPITOLUL 19. ONI 2017 19.3. Z 209
151 fin>>k>>n>>x>>y;
152 if(k==1)
153 { xa=x;ya=y;z=0;
154 for(i=2;i<=n;++i)
155 {
156 fin>>x>>y;
157 if(xa*x<0 && ya*y<0 && xa*(y-ya)==ya*(x-xa) ||
158 xa*x<0 && ya==0 && y==0 || ya*y<0 && xa==0 && x==0)
159 ++z;
160
161 xa=x;ya=y;
162 }
163 fout<<z<<’\n’;
164 }
165 else
166 {
167 x+=1000;
168 y+=1000;
169 ymax=ymin=y;
170 P[y][x]=true;
171 xa=x;ya=y;
172
173 for(i=2;i<=n;++i)
174 {
175 fin>>x>>y;
176 x+=1000;
177 y+=1000;
178
179 if(y>ymax)
180 ymax=y;
181 else
182 if(y<ymin)
183 ymin=y;
184
185 P[y][x]=true;
186 if(y==ya && x!=xa)
187 adaugaSegmentOrizontal(y,xa,x);
188 else
189 if(xa>x && ya>y)
190 adaugaSegmentOblic(xa,ya,x,y);
191 else
192 if(x>xa && y>ya)
193 adaugaSegmentOblic( x,y,xa,ya);
194 xa=x;ya=y;
195 }
196
197 reuniuneIntervale();
198 numaraZ();
199 }
200
201 fin.close();
202 fout.close();
203
204 return 0;
205 }
ONI 2016
20.1 cercetasi
Problema 1 - cercetasi 100 de puncte
Un grup de N cercetaşi, numerotaţi de la 1 la N , se află ı̂n tabără la munte. Pentru ei,
organizatorii au pregătit N scaune, de asemenea numerotate de la 1 la N , aşezate ı̂n cerc, astfel
ı̂ncât fiecare cercetaş să aibă locul său (locul cercetaşului i este pe scaunul i, 1 & i & N ).
Pentru desfăşurarea următoarei activităţi, organizatorii au decis ca M dintre cercetaşi să prez-
inte diferite exerciţii. Numărul M este egal cu cea mai mare putere a lui 2 cu proprietatea că
numărul N de cercetaşi aflaţi ı̂n tabără se poate scrie ca sumă de M numere consecutive ı̂n
mulţimea numerelor impare. Cei M cercetaşi care vor prezenta sunt cei numerotaţi cu numerele
impare consecutive a căror sumă este N . De exemplu, dacă N 8, atunci M este 2, iar exerciţiile
vor fi prezentate de cercetaşii numerotaţi cu 3, respectiv cu 5.
Din joacă, micii cercetaşi s-au aşezat pe scaune la ı̂ntâmplare. Organizatorii au nevoie pentru a
desfăşura activitatea ca cel puţin cei M cercetaşi care vor prezenta exerciţiile să se afle pe locurile
lor. Pentru aceasta, o parte dintre cercetaşi trebuie să-şi schimbe locul şi organizatorii invită
micii cercetaşi să participe la jocul numit ”Mutare”. Acest joc se desfăşoară astfel: unul dintre
cercetaşii care nu se află pe locul lor se ridică şi merge ı̂n interiorul cercului. Cercetaşul numerotat
cu numărul scaunului rămas liber ı̂şi va ocupa locul, iar locul ocupat de el anterior rămâne astfel
liber. Jocul continuă până când scaunul cercetaşului aflat ı̂n interiorul cercului se eliberează şi el
se aşază pe locul său.
Cerinţe
Fiind dat numărul N , precum şi ordinea ı̂n care s-au aşezat cercetaşii pe scaunele numerotate
de la 1 la N , scrieţi un program care să determine:
a numărul M de cercetaşi care vor prezenta exerciţii ı̂n cadrul activităţii;
a numerele de identificare ale celor M cercetaşi care vor prezenta exerciţiile, ı̂n ordine strict
crescătoare;
a numărul minim de cercetaşi care ı̂şi vor schimba locul, astfel ı̂ncât toţi cei M cercetaşi care
vor prezenta exerciţiile să se afle pe locurile lor.
Date de intrare
Fişierul de intrare cercetasi.in conţine pe prima linie numărul natural N cu semnificaţia din
enunţ. Pe a doua linie, se află N numere naturale distincte din mulţimea r1, 2, ..., N x, separate
prin spaţii, reprezentând ordinea ı̂n care s-au aşezat cei N cercetaşi pe scaunele numerotate de la
1 la N .
Date de ieşire
Fişierul de ieşire cercetasi.out va conţine 3 linii. Pe prima linie se va scrie un singur număr
natural repre-zentând numărul M de cercetaşi care vor prezenta exerciţiile. Pe a doua linie se vor
scrie M numere naturale, ı̂n ordine strict crescătoare, separate prin câte un spaţiu, reprezentând
cercetaşii care vor prezenta exerciţiile. Pe a treia linie se va scrie un număr natural, reprezentând
numărul minim de cercetaşi care ı̂şi vor schimba locul.
210
CAPITOLUL 20. ONI 2016 20.1. CERCETASI 211
Exemple
cercetasi.in cercetasi.out Explicaţii
8 2 Dacă N 8, atunci M este 2, iar exerciţiile vor fi prezentate
23415867 35 de cercetaşii numerotaţi cu 3, respectiv cu 5.
4 Cercetaşul cu numărul 3 nu se află pe locul său şi va trece ı̂n
interiorul cercului, astfel scaunul cu numărul 2 rămâne liber.
Cercetaşul cu numărul 2 ı̂şi ocupă locul şi rămâne liber scaunul
cu numărul 1.
Cercetaşul cu numărul 1 ı̂şi ocupă locul şi rămâne liber scaunul
cu numărul 4.
Cercetaşul cu numărul 4 ı̂şi ocupă locul şi rămâne liber scaunul
cu numărul 3 şi astfel cercetaşul aflat ı̂n interiorul cercului se
poate aşeza pe locul său. In cadrul acestui joc ”Mutare” şi-au
schimbat locul 4 cercetaşi. Cum cercetaşul cu numărul 5 se
află deja pe locul său, numărul de cercetaşi care ı̂şi schimbă
locul rămâne 4.
1 #include <fstream>
2
3 using namespace std;
4
5 #define Nmax 10002
6
7 ifstream fin ("cercetasi.in");
8 ofstream fout ("cercetasi.out");
9
10 int n,ok[Nmax][3];
11
12 int mutare(int poz)
13 {
14 int k=0,aux;
15 do
16 {
17 k++;
18 aux=ok[poz][2];
19 ok[poz][2]=poz;
20 ok[poz][1]=1;
21 poz=aux;
22 } while (ok[poz][2]!=poz);
23
24 return k;
25 }
26
27 void calcul(int n, int &k, int &x)
28 {
29 k=1;
30 x=n;
31 if(n%4==0)
32 {
33 while(n%(k*2)==0 && n/(k*2)%2==0 && n/(k*2)-(k*2-1)>0)
34 k=k*2;
35 x=n/k-(k-1);
36 }
37 }
38
39 int main()
40 {
41 int m,i,nr=0,x,cx;
42 fin>>n;
43
44 calcul(n,m,cx);
45 x=cx;
46 fout<<m<<"\n";
47
48 for(i=1;i<=m;i++)
49 {
50 fout<<x<<" ";
51 ok[x][0]=1;
52 x=x+2;
53 }
54 fout<<"\n";
55
56 for(i=1;i<=n;i++)
57 {
58 fin>>x;
59 if(x==i)
60 ok[x][1]=1;
61 if(ok[x][0]==1&&x==i)
62 nr++;
63 ok[x][2]=i;
64 }
65
66 if(nr==m)
67 fout<<"0\n";
68 else
69 {
70 nr=0;
71 for(i=1;i<=m;i++)
72 {
73 if(ok[cx][0]==1 && ok[cx][1]==0)
74 nr=nr+mutare(cx);
75
CAPITOLUL 20. ONI 2016 20.2. FARMA 214
76 cx=cx+2;
77 }
78
79 fout<<nr<<"\n";
80 }
81
82 return 0;
83 }
20.2 farma
Problema 2 - farma 100 de puncte
Noile reguli din sistemul sanitar cer ca medicii să nu prescrie pe reţete un anumit medicament,
ci să menţioneze substanţa activă. Reţeta este formată din n prescripţii, câte una pentru fiecare
substanţă activă prescrisă.
Farmacista de la care cumpăr medicamentele mi-a făcut o listă ı̂n care pentru fiecare substanţă
activă de pe reţetă sunt trecute medicamentele care conţin substanţa activă respectivă, precum
şi preţul pastilelor prescrise din medicamentul respectiv, sub forma următoare:
substanţa activă: medicament1 preţ1 , medicament2 preţ2 , ..., medicamentk preţk
Din păcate, ı̂ntre anumite medicamente există incompatibilităţi şi ca urmare ele nu pot fi
administrate simultan, deoarece ar produce reacţii adverse. De aceea, farmacista mea mi-a dat
şi o listă de incompatibilităţi, ı̂n listă fiind specificate perechi de medicamente incompatibile, sub
forma:
medicament1 /medicament2
Când cumpăr reţeta, eu trebuie să iau câte un medicament pentru fiecare substanţă activă
prescrisă de medic şi să am grijă să nu cumpăr medicamente care sunt incompatibile. Desigur, voi
cumpăra pastilele prescrise pentru tratamentul complet.
Cerinţe
Cunoscând lista pe care mi-a dat-o farmacista, precum şi incompatibilităţile dintre medica-
mente, scrieţi un program care să determine:
1. câte medicamente am la dispoziţie pentru fiecare substanţă activă;
2. suma minimă pe care trebuie să o cheltui pentru a cumpăra reţeta.
Date de intrare
Fişierul de intrare farma.in conţine pe prima linie numărul natural c, reprezentând cerinţa
pe care trebuie să o rezolvăm (1 sau 2).
Pe a doua linie se află numărul natural n, reprezentând numărul de substanţe active prescrise
de medic.
Pe următoarele n linii se află lista pe care mi-a dat-o farmacista, pe fiecare linie fiind specificată
o substanţă activă, urmată de medicamentele care conţin această substanţă şi preţurile lor, sub
forma precizată ı̂n enunţ.
Pe următoarea linie se află un număr natural m, reprezentând numărul de perechi de medica-
mente existente ı̂n lista de incompatibilităţi.
Pe următoarele m linii sunt scrise perechile de medicamente incompatibile, câte o pereche pe
o linie, sub forma precizată ı̂n enunţ.
Date de ieşire
Dacă cerinţa este 1, fişierul de ieşire farma.out va conţine n linii, pe linia i (1 & i & n) fiind
scris numărul de medicamente disponibile pentru substanţa activă descrisă pe linia i 1 ı̂n fişierul
de intrare.
Dacă cerinţa este 2, fişierul de ieşire farma.out va conţine o singură linie pe care va fi scris un
număr natural reprezentând suma minimă pe care trebuie să o plătesc pentru a cumpăra reţeta,
ı̂n condiţiile descrise ı̂n enunţ.
CAPITOLUL 20. ONI 2016 20.2. FARMA 215
a 0 $ n $ 10
a 0 & m & 1400
a Denumirile substanţelor active şi ale medicamentelor sunt şiruri de maximum 30 de litere
mici ale alfabetului englez. Un medicament poate apărea ı̂n lista unei singure substanţe active.
a Preţurile pastilelor sunt numere naturale nenule strict mai mici decât 1000.
a Pentru fiecare substanţă activă există cel mult 9 medicamente care să conţină substanţa
activă respectivă.
a În fiecare pereche din lista de incompatibilităţi se află medicamente care conţin substanţe
active diferite.
a În lista de medicamente corespunzătoare fiecărei substanţe active pot exista oricâte spaţii,
dar lungimea oricărei linii nu depăşeşte 700 de caractere.
a Pentru datele de test există ı̂ntotdeauna soluţie.
a Pentru teste valorând 10% din punctaj cerinţa este 1.
Exemple
farma.in farma.out Explicaţii
1 4 Există 4 medicamente pen-
3 3 tru prima substanţă activă, 3
metformin:siofor 10,glibomet 30,bidiab 60,gliformin 10 3 medicamente pentru cea de a
ibuprofen:nurofen 24,advil 35, ibusinus 9 doua şi tot 3 medicamente pen-
diclofenac : diclac 28 , voltaren 50, cambia 102 tru cea de a treia.
0
2 67 Plătim suma minimă 67 dacă
3 vom cumpăra glibomet (preţ
metformin:siofor 10,glibomet 30,bidiab 60,gliformin 10 30), ibusinus (preţ 9) diclac
ibuprofen:nurofen 24,advil 35, ibusinus 9 (preţ 28).
diclofenac : diclac 28 , voltaren 50, cambia 102 Observaţi că oricare două
5 medicamente cumpărate sunt
siofor/diclac compatibile.
gliformin/diclac
ibusinus/siofor
ibusinus/voltaren
bidiab/diclac
r1, 2, ..., lg 1x r1, 2, ..., lg 2x ... r1, 2, ..., lg nx
Nu toate elementele produsului cartezian ne interesează, ci doar acelea pentru care medicamentele
corespunzătoare sunt compatibile două câte două. Iar din toate soluţiile posibile, ne interesează
cea pentru care suma elementelor este minimă.
Problema poate fi abordată ı̂n moduri diferite:
CAPITOLUL 20. ONI 2016 20.2. FARMA 216
1. Generăm toate elementele produsului cartezian printr-un algoritm de tip succesor. Pentru
fiecare element generat, verificăm dacă medicamentele sunt compatibile două câte două. În caz
afirmativ, comparăm suma necesară pentru soluţia curentă cu suma minimă şi o reţinem dacă este
cazul.
Această abordare obţine 65 de puncte (sursa f arma gen)
2. Pentru a optimiza generarea observăm că pe poziţia i ı̂n soluţie nu are rost să selectăm un
medicament care este incompatibil cu cel puţin unul deja selectate pe poziţiile 1, 2, ..., i 1.
Putem genera soluţiile ţinând cont de această observaţie utilizând o stivă, implementată ca un
vector sol cu n elemente.
La pasul curent i, trebuie să selectăm un element care poate fi pus ı̂n soluţie pe poziţia i.
Dacă i % n, soluţia este completă, comparăm suma necesară pentru cumpărarea ei cu suma
minimă şi o reţin dacă este cazul.
Dacă i & n, atunci incrementăm soli (plasăm un nou element pe poziţia i ı̂n soluţie).
Apar următoarele cazuri posibile:
soli % lg i (ı̂n acest caz am epuizat valorile posibile pentru poziţia i, cobor pe stivă revenind
la poziţia precedentă (i–).
soli & lg i, iar valoarea soli curentă este compatibilă cu toate valorile sol1, sol2, ...,
soli 1, atunci urcăm un nivel pe stivă (i++) şi iniţializăm soli cu 0.
soli & lg i, dar valoarea soli curentă nu este compatibilă cu valorile sol1, sol2, ...,
soli 1; ı̂n acest caz rămân pe stivă la acelaşi nivel i, urmând la pasul următor să incrementăm
valoarea curentă.
Această generare este implementată ı̂n f arma ok slow şi obţine 75 de puncte
3. Putem optimiza generarea ı̂n continuare făcând următoarea observaţie. Dacă suma necesară
cumpărării medicamentelor ı̂n soluţia parţial generată depăşeşte deja suma minimă curentă, aban-
donăm generarea cu valoarea curentă şi revenim la pasul precedent, pentru că e clar că această
soluţie nu va fi convenabilă.
Pentru a obţine mai rapid o sumă mai mică, vom sorta crescător după preţ medicamentele din
lista fiecărei substanţe active.
Această soluţie obţine 100 de puncte (f arma ok).
O implementare recursivă pentru această idee este concisă şi eficientă (f arma rec) şi obţine
deasemenea 100 de puncte, dar depăşeşte nivelul clasei a VIIII-a.
Sunt posibile şi abordări Greedy care obţin punctaje parţiale. De exemplu dacă nu ţinem
cont de incompatibilităţi şi pentru fiecare substanţă activă selectăm medicamentul cel mai ieftin
obţinem 10 puncte pentru cerinţa 2.
Dacă abordăm Greedy ţinând cont de incompatibilităţi (adică mă opresc la prima soluţie care
ı̂ndeplineşte condiţia de compatibilitate, evident, după sortarea medicamentelor crescător după
preţ) se obţin 20 de puncte la cerinţa 2.
În toate soluţiile prezentate este necesară verificarea rapidă a compatibilităţii medica-
mentelor selectate ı̂n soluţie. Pentru această propunem următoarea codificare a medicamentelor.
Substanţele active sunt numerotate de la 1 la n (n $ 10), iar medicamentele din lista substanţei
active i de la 1 la lg i (lg i $ 10). Identificăm ı̂n mod unic un medicament printr-un număr de
două cifre format din numărul substanţei active şi numărul de ordine al medicamentului ı̂n lista
de medicamente a substanţei active.
Pentru a reţine relaţiile de incompatibilitate ı̂ntre medicamente utilizăm o matrice d100100 .
dc1c2 dc2c1 1, dacă medicamentele care au numerele de identificare c1 şi c2 sunt
incompatibile, respectiv 0 ı̂n caz contrar.
Atenţie! Codificarea medicamentelor se va face după sortarea acestora după preţ, pentru că la
sortare medicamentele ı̂şi schimbă poziţiile ı̂n lista de medicamente.
3 #include <cstring>
4
5 #define LGMAX 1004
6 #define INF 1000000000
7
8 using namespace std;
9
10 ifstream fin("farma.in");
11 ofstream fout("farma.out");
12
13 struct med
14 {
15 char nume[31];
16 int pret;
17 };
18
19 int suma, smin=INF;
20 int sol[10];
21 int solmin[10];
22 med a[10][10];
23 int n, cerinta;
24 int lg[10];
25 char s[LGMAX];
26 bool d[100][100];
27
28 void citire();
29 void sortare();
30 void generare();
31 int verif(int vf);
32 int nr(char *s);
33 int cauta(char * s);
34
35 int main()
36 { int i;
37 citire();
38 if (cerinta==1)
39 for (i=1; i<=n; i++) fout<<lg[i]<<’\n’;
40 else
41 {
42 generare();
43 fout<<smin<<’\n’;
44 //for (i=1; i<=n; i++)
45 // fout<<a[i][solmin[i]].nume<<’ ’<<a[i][solmin[i]].pret<<’\n’;
46 }
47 fout.close();
48 return 0;
49 }
50
51 void citire()
52 {char c, *p;
53 int i, poz1, poz2, m;
54 fin>>cerinta>>n;
55 fin.get(c);
56 for (i=1; i<=n; i++)
57 {
58 fin.getline(s,LGMAX);
59 p=strtok(s,":");
60 do
61 {
62 p=strtok(NULL,", ");
63 if (!p) break;
64 strcpy(a[i][++lg[i]].nume,p);
65 p=strtok(NULL,", ");
66 a[i][lg[i]].pret=nr(p);
67 }
68 while (1);
69 }
70 sortare();
71 fin>>m;
72 fin.get(c);
73 for (i=1; i<=m; i++)
74 {fin.getline(s,LGMAX);
75 p=strchr(s,’/’);
76 *p=NULL;
77 poz1=cauta(s);
78 poz2=cauta(p+1);
CAPITOLUL 20. ONI 2016 20.2. FARMA 218
79 d[poz1][poz2]=d[poz2][poz1]=1;
80 }
81 }
82
83 void sortare()
84 //sortez medicamentele de pe fiecare linie crescator dupa pret
85 {int i, j, sch;
86 med aux;
87 for (i=1; i<=n; i++)
88 {
89 do
90 {
91 sch=0;
92 for (j=1; j<lg[i]; j++)
93 if (a[i][j].pret>a[i][j+1].pret)
94 {
95 aux=a[i][j]; a[i][j]=a[i][j+1]; a[i][j+1]=aux;
96 sch=1;
97 }
98 }
99 while (sch);
100 }
101 }
102
103 void generare()
104 {int vf=1, i;
105 sol[1]=0;
106 while (vf>0)
107 {
108 if (vf>n) //solutie completa
109 { if (suma<smin)
110 {smin=suma;
111 for (i=1; i<=n; i++) solmin[i]=sol[i];
112 }
113 vf--;
114 }
115 else
116 {sol[vf]++; //incrementez pozitia curenta
117 if (sol[vf]>lg[vf])
118 {suma=suma-a[vf][sol[vf]-1].pret; sol[vf--]=0;}
119 else
120 if (sol[vf]>1)
121 {suma=suma-a[vf][sol[vf]-1].pret+a[vf][sol[vf]].pret;
122 if (suma>=smin)
123 {suma-= a[vf][sol[vf]].pret; sol[vf--]=0; }
124 else
125 if (verif(vf)) sol[++vf]=0;
126 }
127 else
128 {suma+=a[vf][1].pret;
129 if (suma>=smin)
130 {suma-= a[vf][1].pret; sol[vf--]=0; }
131 else
132 if (verif(vf)) sol[++vf]=0;
133 }
134 }
135 }
136 }
137
138 int verif(int vf)
139 { int i, pvf=vf*10+sol[vf];
140 for (i=1; i<vf; i++)
141 if (d[pvf][i*10+sol[i]]) return 0;
142 return 1;
143 }
144
145 int nr(char *s)
146 {int i, rez;
147 for (rez=i=0; s[i]; i++)
148 rez=rez*10+s[i]-’0’;
149 return rez;
150 }
151
152 int cauta(char * s)
153 {int i, j;
154 for (i=1; i<=n; i++)
CAPITOLUL 20. ONI 2016 20.2. FARMA 219
69
70 if(k==n)
71 {
72 if(sum<sumMin)
73 {
74 sumMin=sum;
75 }
76 }
77 else
78 if(sum<sumMin)
79 calc(k+1);
80
81 sum-=pret[k][i];
82 }
83 }
84
85 int main()
86 {
87 fin>>C>>n;
88 char s[701],*p,*q;
89 int i, nr, j;
90
91 for(i=1;i<=n;i++)
92 {
93 fin.get();
94 fin.get(s,7001);
95
96 trim(s);
97 p=strtok(s,":");
98 strcpy(activ[i][0],p);
99 p=strtok(NULL,",");
100
101 while(p)
102 {
103 for(nr=0,j=0;p[j];j++)
104 if(p[j]>=’0’ && p[j]<=’9’)
105 nr=nr*10+p[j]-’0’,p[j]=0;
106 pret[i][0]++;
107 pret[i][pret[i][0]]=nr;
108 strcpy(activ[i][pret[i][0]], p);
109 p=strtok(NULL,",");
110 }
111 }
112
113 for(i=1;i<=n;i++)
114 ordoneaza(i);
115
116 fin>>m;
117 fin.get();
118 for(i=1;i<=m;i++)
119 {
120 fin.getline(s,701);
121 trim(s);
122 p=strtok(s,"/");
123 q=strtok(NULL,"/");
124 int l=cauta(p), c=cauta(q);
125 incomp[l][c]=incomp[c][l]=1;
126 }
127
128 if(C==1)
129 for(j=1;j<=n;j++)
130 fout<<pret[j][0]<<’\n’;
131 else
132 {
133 calc(1);
134 fout<<sumMin<<’\n’;
135 }
136
137 fin.close();
138 fout.close();
139 return 0;
140 }
1 //Lucia Miron
2 #include <fstream>
3 #include<cstring>
4
5 using namespace std;
6
7 ifstream fin("farma.in");
8 ofstream fout("farma.out");
9
10 int a[10][10], b[100][100],sp,smin,x[10],n,m,cer,i;
11 char med[10][1001];
12
13 void determina(char s[1001], int&l,int &c)
14 {
15 int i, pas,nr;
16 char ss[1001],*p;
17 for(i=1;i<=n;i++)
18 {
19 strcpy(ss,med[i]);
20 if(strstr(ss,s))
21 {
22 nr=0;pas=1;
23 p=strtok(ss," ,");
24 while(p)
25 {
26 if(pas==1)nr++;
27 if(strcmp(p,s)==0)
28 {
29 l=i,c=nr;return;
30 }
31 pas=3-pas;
32 p=strtok(NULL," ,");
33 }
34 }
35 }
36 }
37
38 void citire()
39 {
40 int i,k,j,pas,l,cc,l1,c1;
41 char c;
42 char s[1001],*p,s1[1001];
43
44 fin>>cer>>n;
45 fin.get();
46
47 for(i=1;i<=n;i++)
48 {
49 fin.getline(s,1001);
50 p=strchr(s,’:’);
51 strcpy(s,p+1);
52 strcpy(med[i],s);
53 p=strtok(s," ,");pas=1;j=0;
54 while(p)
55 {
56 if(pas==2)
57 {
58 j++;a[i][j]=0;
59 for(k=0;p[k];k++)
60 a[i][j]=a[i][j]*10+(p[k]-’0’);
61 }
62 pas=3-pas;
63 p=strtok(NULL," ,");
64 }
65 a[i][0]=j;
66 }
67
68 if(cer==2)
69 {
70 fin>>m;
71 for(i=1;i<=m;i++)
72 {
73 fin>>s;
74 p=strchr(s,’/’);
75 s1[0]=’\0’;
76 strncat(s1,s,p-s);
CAPITOLUL 20. ONI 2016 20.3. STELE 222
77 strcpy(s,p+1);
78 determina(s,l,cc);
79 determina(s1,l1,c1);
80 b[l*10+cc][l1*10+c1]=b[l1*10+c1][l*10+cc]=1;
81 }
82 }
83
84 }
85
86 int valid(int k)
87 {
88 int i;
89 for(i=1;i<k;i++)
90 if(b[k*10+x[k]][i*10+x[i]]==1)
91 return 0;
92 return 1;
93 }
94
95 void bkt(int k)
96 {
97 int i,j;
98 if(k>n)
99 {
100 if(sp<smin)smin=sp;
101 }
102 else
103 for(i=1;i<=a[k][0];i++)
104 {
105 x[k]=i;
106 sp=sp+a[k][i];
107 if(valid(k)&&sp<smin)
108 bkt(k+1);
109 sp=sp-a[k][i];
110 }
111 }
112
113 int main()
114 {
115 citire();
116
117 if(cer==1)
118 {
119 for(i=1;i<=n;i++) fout<<a[i][0]<<’\n’;
120 }
121 else
122 {
123 smin=100000;
124 bkt(1);
125 fout<<smin;
126 }
127
128 return 0;
129 }
20.3 stele
Problema 3 - stele 100 de puncte
Pasionată de astronomie, Teodora doreşte să ţină evidenţa numărului de stele din galaxii.
Pentru a face lucrurile mai interesante, ea codifică aceste numere ı̂ntr-un sistem propriu, trans-
formându-le ı̂ntr-o ı̂nşiruire de litere şi cifre după algoritmul următor:
26
- notează fiecare putere a lui 2, strict mai mică decât 2 , cu o literă a alfabetului, astfel:
- reprezintă fiecare număr ca un şir de cifre şi litere obţinut din scrierea acelui număr ca sumă
CAPITOLUL 20. ONI 2016 20.3. STELE 223
de puteri ale lui 2; dacă o putere este folosită de mai multe ori ı̂n descompunerea numărului atunci
ea va fi precedată ı̂n şir de numărul de utilizări.
Un număr poate fi reprezentat astfel ı̂n mai multe moduri. De exemplu, pentru numărul 100
printre variantele de reprezentare avem:
2 5 6
100 = cfg = 2 2 2 = 4+32+64 = 100
0 1 2 3 4 5
100 = 2ab2cde2f = 2 2 2 2 2 2 2 2 2 = 2*1+2+2*4+8+16+2*32 = 100
1 2 6
100 = 16bcg = 16 2 2 2 = 16*2+4+64 = 100
Cerinţe
Date de intrare
Fişierul de intrare stele.in conţine pe prima linie un număr natural c, reprezentând cerinţa
care trebuie rezolvată (1 sau 2).
Dacă cerinţa este 1, pe a doua linie se află un număr natural s, ce reprezintă numărul care
trebuie codificat.
Dacă cerinţa este 2, pe a doua linie se află un număr natural g reprezentând numărul de galaxii,
iar pe următoarele g linii câte un şir de caractere reprezentând numărul de stele dintr-o galaxie,
codificat folosind algoritmul descris mai sus.
Date de ieşire
Fişierul de ieşire stele.out va conţine o singură linie pe care va fi scris un şir de litere mici
distincte, ordonate alfabetic, reprezentând scrierea codificată a numărului s (dacă cerinţa este 1)
sau un număr natural ı̂n scriere zecimală ce reprezintă numărul total de stele din cele g galaxii
(dacă cerinţa este 2).
1&s&2 1
26
a
a 1 & g & 1000
a Reprezentările codificate din fişierul de intrare pot avea maximum 420 caractere.
a Numărul care poate apărea ı̂n faţa unei litere poate avea maximum 15 cifre.
a Pentru teste valorând 30% din punctaj cerinţa este 1.
a Pentru teste corespunzătoare cerinţei 2 valorând 20% din punctaj valoarea obţinută nu
18
depăşeşte 10 .
Exemple
stele.in stele.out Explicaţii
1 cfg Cerinţa este 1. Reprezentarea numărului 100 care respectă
100 cerinţa este:
2 5 6
cfg=2 2 2 =4+32+64=100
2 6320 Cerinţa este 2 şi avem 5 numere:
5 2a7g = 450
2a7g 17b5d14g = 970
17b5d14g 100a2000b = 4100
100a2000b 7e15f = 592
7e15f 2d6f = 208
2d6f Suma lor este: 450 + 970 + 4100 + 592 + 208 = 6320
Cerinţa 1 - 30 de puncte
Varianta 1 - generăm partiţiile numărului s ca sumă de puteri distincte ale lui 2 (10 puncte)
Varianta 2 - având ı̂n vedere că orice număr se poate scrie ca suma de puteri distincte ale lui 2,
şi s $ 2 , vom determina direct scrierea numărului. Pentru aceasta, putem lucra ı̂n două moduri:
26
I. cât timp s % 0 determinăm cea mai mare putere a lui 2 mai mică sau egală cu s, o reţinem
şi o scădem din s; la final afişăm codificarea puterilor reţinute ı̂n ordine inversă.
II. Folosim scrierea ı̂n baza 2 a numărului s, afişând acele puteri ale lui 2 corespunzători biţilor
egali cu 1. (30 de puncte)
Cerinţa 2 - 70 de puncte
Varianta 1 - folosind un vector de numărare calculăm numărul de utilizări ale fiecărei litere
ı̂n scrierea celor g numere şi determinăm suma numerelor. Este necesară utilizarea operaţiilor
cu numere mari, deoarece rezultatul poate depăşi 18 cifre. Tot punctaj maxim se obţine dacă
transformăm fiecare număr ı̂n baza 10 şi adunăm numerele obţinute, folosind operaţii cu numere
mari. (70 de puncte)
Varianta 2 - transformăm fiecare numar ı̂n scriere zecimală şi determinăm suma lor, fără a
utiliza operaţii cu numere mari. (20 de puncte)
42 fout.close();
43 return 0;
44 }
45
46 void decodifica()
47 {int i;
48 long long int nr;
49 rezultat[0]=0; lgr=1;
50 for (i=0; sir[i]; i++)
51 {if (sir[i]>=’a’ && sir[i]<=’z’) nr=1;
52 else
53 for (nr=0; sir[i]>=’0’ && sir[i]<=’9’; i++) nr=nr*10+sir[i]-’0’;
54 produs (nr, p[sir[i]-’a’], prod, lgprod);
55 suma(rezultat,lgr,prod,lgprod,rezultat,lgr);
56 }
57 }
58
59 void suma(int a[], int lga, int b[], int lgb, int s[], int &lgs)
60 {int i, t, val;
61 if (lga<lgb) {for (i=lga; i<lgb; i++) a[i]=0; lgs=lgb;}
62 else {for (i=lgb; i<lgs; i++) b[i]=0; lgs=lga;}
63 for (i=t=0; i<lgs; i++)
64 {
65 val=a[i]+b[i]+t;
66 s[i]=val%10;
67 t=val/10;
68 }
69 if (t) s[lgs++]=t;
70 }
71
72 void produs (long long int a, long long int b, int c[], int& lgc)
73 {
74 int sa[100], lga, i;
75 long long int t, val;
76 if (!a || !b) {lgc=1; c[0]=0; return;}
77 for (lga=0; a; lga++) {sa[lga]=a%10; a/=10;}
78 for (t=i=0; i<lga; i++)
79 {
80 val=sa[i]*b+t;
81 c[i]=val%10;
82 t=val/10;
83 }
84 lgc=lga;
85 while (t) {c[lgc++]=t%10; t/=10;}
86 }
28 i=0;j--;
29 while(i<j)
30 x=s[i], s[i]=s[j], s[j]=x, i++, j--;
31 }
32
33 void numarare(char s[])
34 {
35 int i=0;
36 long long n=0;
37
38 if(s[0]>=’a’ && s[0]<’z’)
39 nr[s[i]-’a’]+=1, i=1;
40
41 for(;s[i];i++)
42 if(s[i]>=’0’ && s[i]<=’9’)
43 n=n*10+s[i]-’0’;
44 else
45 if(s[i-1]>=’a’ && s[i-1]<=’z’)
46 nr[s[i]-’a’]+=1;
47 else
48 nr[s[i]-’a’]+=n, n=0;
49 }
50
51 void prod(long long nr, long long v, int rez[])
52 {
53 int i;
54 long long t=0;
55
56 rez[0]=0;
57 while(nr)
58 rez[++rez[0]]=nr%10, nr/=10;
59
60 for(i=1; i<=rez[0] || t; i++)
61 t+=rez[i]*v, rez[i]=t%10, t/=10;
62
63 rez[0]=i-1;
64 }
65
66 void add(int s[], int rez[])
67 {
68 int i;
69 long long t=0;
70
71 for(i=1;i<=s[0] || i<=rez[0] || t; i++, t/=10)
72 t+=s[i]+rez[i], s[i]=t%10;
73
74 s[0]=i-1;
75 }
76
77 void suma()
78 {
79 int s[101]={0}, rez[101], i, j;
80
81 s[0]=1;
82 s[1]=0;
83 for(i=0;i<26;i++)
84 if(nr[i])
85 {
86 for(j=0;j<100;j++) rez[j]=0;
87 prod(nr[i], v[i], rez);
88 add(s, rez);
89 }
90
91 for(i=s[0];i>=1;i--)
92 fout<<s[i];
93 fout<<’\n’;
94 }
95
96 int main()
97 {
98 int n, C, i, p=1;
99 char s[500];
100
101 for(i=0;i<26;i++)
102 v[i]=p,p*=2;
103
CAPITOLUL 20. ONI 2016 20.3. STELE 227
104 fin>>C>>n;
105 if(C==1)
106 cerinta1(n, s), fout<<s<<’\n’;
107 else
108 {
109 for(i=1;i<=n;i++)
110 fin>>s, numarare(s);
111 suma();
112 }
113
114 fin.close();
115 fout.close();
116 return 0;
117 }
ONI 2015
21.1 magic
Problema 1 - magic 100 de puncte
Pentru obţinerea Pietrei Filosofale, un alchimist a preparat un elixir folosind un creuzet de
capacitate C, ı̂n care a turnat picături de metal topit, ı̂ntr-o ordine bine stabilită, ı̂n N etape.
Numărul de picături turnate ı̂ntr-o etapă este cuprins ı̂ntre 0 şi C 1, iar procesul ı̂ncepe când ı̂n
creuzet s-a turnat prima picătură (ı̂n prima etapă numărul de picături turnate este nenul).
Picăturile se adună ı̂n creuzet una câte una şi, de fiecare dată când acesta se umple complet,
alchimistul rosteşte o formulă magică, provocând transformarea ı̂ntregului conţinut ı̂ntr-o singură
picătură, apoi continuă procesul.
O reţetă de obţinere a elixirului se exprimă printr-un şir de N numere, reprezentând numărul
de picături turnate ı̂n cele N etape.
De exemplu, aplicând reţeta 5 6 1 0, cu un creuzet de capacitate C 7, ı̂n cele N 4 etape
procesul este:
- etapa 1: se toarnă 5 picături;
- etapa a 2-a: se toarnă 6 picături, astfel: după primele 2 picături se umple creuzetul (5+2=7)
şi deci se rosteşte formula magică, ı̂n creuzet rămânând o picătură; se continuă cu celelalte 4
picături; la finalul etapei ı̂n creuzet sunt 5 picături (1+4=5);
- etapa a 3-a: se toarnă o picătură; la finalul etapei ı̂n creuzet sunt 6 picături (5+1=6);
- etapa a 4-a: se toarnă 0 picături; după ultima etapă creuzetul conţine 6 picături (6+0=6).
O reţetă care corespunde Pietrei Filosofale trebuie să conducă, la finalul aplicării ei, la obţinerea
unei singure picături, chintesenţa metalelor amestecate. Bineı̂nţeles, sunt mai multe astfel de
reţete.
Fiind un tip responsabil, alchimistul a lăsat posterităţii un set de tratate, care cuprind toate
aceste reţete. El a scris pe fiecare pagină câte o reţetă, astfel ı̂ncât niciuna să nu se repete ı̂n
cadrul ı̂ntregii lucrări. Pe vremea aceea erau meşteri pricepuţi, care fabricau tratate de dimensiuni
corespunzătoare, ı̂ncât fiecare pagină să poată cuprinde o reţetă ca a noastră, oricât de lungă ar
fi ea. Fiecare tratat are P pagini şi doar după ce completează toate cele P pagini ale unui tratat,
alchimistul ı̂ncepe un nou tratat.
Cerinţe
Date de intrare
Fişierul magic.in conţine pe prima linie, ı̂n această ordine, numerele naturale C, N , P , sepa-
rate prin câte un spaţiu şi având semnificaţia din enunţ.
Date de ieşire
Fişierul magic.out conţine un număr natural reprezentând numărul de reţete publicate ı̂n
ultimul tratat.
228
CAPITOLUL 21. ONI 2015 21.1. MAGIC 229
Exemple
magic.in magic.out Explicaţii
423 1 Creuzetul are capacitatea C 4, sunt N 2 etape de aplicare
a fiecărei reţete, tratatele au câte P 3 pagini. Reţetele apli-
cate ı̂n două etape, ı̂n urma fiecărui astfel de proces rămânând
ı̂n creuzet câte o singură picătură, sunt:
1 0; 1 3; 3 1; 2 2.
Pentru acestea sunt necesare două tratate, primul conţinând
trei reţete, iar al doilea (ultimul) o singură reţetă.
Procedeul de determinare a numărului de picături rămase ı̂n creuzet pentru o reţetă are ca
rezultat cifra de control a unui număr de N cifre, scris ı̂n baza de numeraţie C (număr reprezen-
tat de reţeta respectivă, ı̂n care numărul de picături din fiecare etapă reprezintă câte o cifră a
numărului).
Dacă se scriu toate numerele de N cifre ı̂n baza de numeraţie C ı̂n ordine
crescătoare/lexicografică, cifrele de control ale acestora vor forma un şir de secvenţe de numere
consecutive, fiecare secvenţă fiind de forma 1, 2, ... C 1, unde termenii unei secvenţe sunt cifrele
de control determinate (C 1 numere consecutive).
N
Numărul total de valori de maximum N cifre, scrise ı̂n baza de numeraţie C, este C .
N 1
Numărul total de valori de maximum N 1 cifre, scrise ı̂n baza de numeraţie C, este C .
Numărul total de valori de N cifre, scrise ı̂n baza de numeraţie C, este
N N 1 N 1
C C C C 1.
N 1
C C 1 N 1
Astfel, numărul total de secvenţe de numere consecutive este C 1
C .
Întrucât cifra 1 apare o singură dată ı̂n fiecare astfel de secvenţă, ı̂nseamnă că numărul total de
N 1 N 1
reţete care respectă cerinţa este chiar C . Numărul cerut este (C modulo P ), dacă această
valoare este nenulă, sau P , ı̂n caz contrar (ultimul tratat este complet, deci el va conţine chiar P
reţete).
Pentru obţinerea punctajului maxim, algoritmul de determinare a numărului cerut are o com-
plexitate logaritmică.
15 fin>>C>>N>>P;
16 //P = 1000;
17 // D[i][j] = cate siruri de lungime maxim i se termina in j
18
19 for (j=1;j<C;j++)
20 {
21 D[0][j] = 1;
22 S[0][j] = (S[0][j-1] + D[0][j]) % P;
23 }
24
25 c = 1;
26 for (i=2;i<=N;i++)
27 {
28 for (j=1;j<C;j++)
29 {
30 D[c][j] = D[1-c][j];
31 if (j == 1)
32 {
33 D[c][j] = D[c][j] + S[1-c][C-1];
34 D[c][j] %= P;
35 }
36 else
37 {
38 D[c][j] = D[c][j] + S[1-c][C-1];
39 D[c][j] %= P;
40 }
41
42 S[c][j] = (S[c][j-1] + D[c][j]) % P;
43 }
44 c = 1 - c;
45 }
46
47 if (D[1-c][1] == 0)
48 fout<<P<<"\n";
49 else
50 fout<<D[1-c][1]<<"\n";
51
52 return 0;
53 }
34 mag=mag+reteta[i];
35 return mag;
36 }
37
38 int main()
39 {
40 long i;
41 long long nr;
42 in>>C>>N>>P;
43
44 reteta[0]=1;
45 nr=0;
46 do
47 {
48 if(calculeazaMagic()==1)
49 nr=(nr+1)%P;
50 } while(increment()==0);
51
52 if(nr==0)
53 nr=P;
54
55 out<<nr;
56 return 0;
57 }
2 #include <fstream>
3
4 using namespace std;
5
6 ifstream in("magic.in");
7 ofstream out("magic.out");
8
9 long sol[100000];
10 long ultima[100000];
11 long C,N,P;
12 long long nr;
13
14
15 void bkt(long k, long mag)
16 {
17 long transport;
18 for(long i=0;i<=C-1;i++)
19 {
20 sol[k]=i;
21 transport=i+mag;
22 if(transport==C)
23 transport=1;
24 else
25 if(transport>C)
26 transport=1+transport-C;
27
28 if(k==N-1)
29 {
30 if(transport==1)
31 nr=(nr+1)%P;
32 }
33 else bkt(k+1, transport);
34 }
35 }
36
37 int main()
38 {
39 long i;
40 in>>C>>N>>P;
41
42 nr=0;
43 for(i=1;i<C;i++)
44 {
45 sol[0]=i;
46 bkt(1,i);
47 }
48
49 if(nr==0)
50 nr=P;
51
52 out<<nr;
53 return 0;
54 }
20 while(N!=0)
21 {
22 if(N%2==1)
23 {
24 nr=(nr*C)%P;
25 if(nr==0)
26 nr=P;
27 }
28 C=(C*C)%P;
29 if(C==0)
30 C=P;
31 N/=2;
32 }
33
34 out<<nr;
35 return 0;
36 }
21.2 restaurare
Problema 2 - restaurare 100 de puncte
După descoperirea ruinelor unei cetăţi medievale, arheologii au hotărât restaurarea acesteia,
ı̂ncepând cu zidul principal. Acesta este format din N piloni, fiecare cu lăţimea de 1 metru, aşezaţi
unul lângă altul (lipiţi). Se cunoaşte ı̂nălţimea, ı̂n metri, a fiecărui pilon dar, din păcate, nu toţi
mai sunt acum la acelaşi nivel.
Pentru restaurarea zidului, arheologii dispun de cărămizi care au lăţimea de câte 1 metru
şi lungimi variabile, exprimate ı̂n metri. Sunt disponibile oricâte cărămizi, de oricare lungime.
Considerăm că toate cărămizile disponibile şi toţi pilonii care alcătuiesc zidul au aceeaşi grosime,
de 1 metru.
Restaurarea constă ı̂n două etape:
- ı̂n prima etapă, toţi pilonii cu ı̂nălţimea mai mare sau egală cu H se retează, aducându-se
astfel la ı̂nălţimea H, ceilalţi, mai scunzi, păstrându-şi ı̂nălţimea iniţială;
- ı̂n a doua etapă se aduc toţi pilonii la aceeaşi ı̂nălţime, umplându-se golurile dintre ei cu
cărămizi, astfel ı̂ncât zidul să devină compact; din motive neı̂nţelese, arheologii vor aşeza cărămizile
”culcate”, fiecare dintre acestea ocupând, eventual, spaţiul aflat deasupra mai multor piloni.
CAPITOLUL 21. ONI 2015 21.2. RESTAURARE 234
Cerinţe
Pentru fiecare dintre cele Q valori alese pentru ı̂nălţimea H, se cere să se determine numărul
minim de cărămizi necesare restaurării zidului, independent, pornind de la ı̂nălţimile iniţiale ale
pilonilor.
Date de intrare
Date de ieşire
Fişierul restaurare.out conţine Q numere, câte unul pe linie, reprezentând numărul minim
de cărămizi necesare restaurării pentru fiecare dintre ı̂nălţimile H, ı̂n ordinea ı̂n care acestea apar
ı̂n fişierul de intrare.
Exemple
restaurare.in restaurare.out Explicaţii
5 0 Forma iniţială Pentru H=1 toţi pilonii au aceeaşi ı̂nălţime,
43242 4 a zidului deci nu mai este necesară nicio cărămidă,
3 2 pentru H=4, sunt necesare 4 cărămizi, zidul
143 având, după restaurare structura din fig. a,
iar pentru H=3, sunt necesare 2 cărămizi,
zidul având, după restaurare structura din fig.
b.
Considerăm că umplem golurile din zid de sus ı̂n jos. Aşadar, calculăm valoarea maximă
(notată M ax) dintre ı̂nălţimile pilonilor.
Pentru a obţine datele cerute este necesară sortarea ı̂nălţimilor iniţiale ale pilonilor.
La implementare, considerăm că pornim cu K M ax 1 şi secv 1. ı̂n paralel, ı̂ntr-un vector
U vom seta la 0 poziţiile ce nu vor mai face parte din secvenţe goale (iniţial toate valorile din U
sunt 1, reprezentând secvenţa iniţială, aflată deasupra zidului). Observăm că o valoare a lui U
odată setată la 0 va rămâne cu această valoare până la finalul algoritmului.
Analizând o ı̂nălţime K (la care ajungem parcurgı̂nd ı̂nălţimile ı̂n ordine descrescătoare),
trebuie să cunoaştem câte secvenţe ”goale” avem la acea ı̂nălţime (secv). ı̂n funcţie de ı̂nălţimea
pilonului curent (considerat pe o poziţie notată p), vom actualiza valoarea variabilei secv astfel:
dacă U p 1 0 şi U p 1 0, secv scade cu 1 (dispare secvenţa curentă),
iar dacă U p 1 1 şi U p 1 1, secv creşte cu 1 (spargem o secvenţă ı̂n două).
Evident, U p va deveni 0. Astfel, când trecem de la o ı̂nălţime la următoarea ştim câte
secvenţe goale sunt la acea ı̂nălţime precum şi diferenţa de ı̂nălţime şi astfel calculăm numărul
necesar de cărămizi.
Procesând ı̂n ordine descrescătoare ı̂nălţimile la care se retează pilonii, vom putea calcula
valoarea cerută pentru fiecare dintre ele odată cu aplicarea algoritmului prezentat mai sus.
Algoritmul descris anterior are complexitate ı̂n timp liniară.
44 x[ f[v[i] ] ]= v[i];
45 y[ f[v[i] ] ]= i;
46 f[ v[i] ]--;
47 }
48
49 for (i=1;i<=n;i++)
50 u[i] = 1;
51 secv = 1;
52
53 last = 0;
54 for (i=1;i<=n;i++)
55 {
56 if (x[i] != x[i-1])
57 {
58 sol += secv * (x[i]-last);
59 //
60 A[++d] = make_pair( maxim-x[i]+2, secv * (x[i]-last) );
61 //
62 last = x[i];
63
64 }
65 p = y[i];
66
67 u[p] = 0;
68 if (u[p-1] == 1 && u[p+1] == 1)
69 secv++;
70 if (u[p-1] == 0 && u[p+1] == 0)
71 secv--;
72 }
73
74 p = d;
75 for (i=1;i<=maxim;i++)
76 {
77 if (A[p].first == i)
78 {
79 crt = A[p].second / (A[p-1].first - A[p].first);
80 p--;
81 }
82 S[i] = S[i-1] + crt;
83 }
84
85 fin>>m;
86 for (i=1;i<=m;i++)
87 {
88 fin>>h;
89 fout<<S[h]<<"\n";
90 }
91 /*
92 for (i=1;i<=maxim;i++)
93 cout<<i<<" "<<S[i]<<"\n";
94
95 cout<<"\n";
96 for (i=1;i<=d;i++)
97 cout<<A[i].first<<" "<<A[i].second<<"\n";
98 */
99 // fout<<sol-1<<"\n";
100
101 return 0;
102 }
90 fclose(fout);
91 return 0;
92 }
2 #include <fstream>
3
4 using namespace std;
5
6 int n, maxim, crt, ant,i, v[100010], h, m;
7
8 long long nr;
9
10 int main()
11 {
12 ifstream in ("restaurare.in");
13 ofstream out("restaurare.out");
14
15 in>>n;
16 for (i=1;i<=n;i++)
17 in>>v[i];
18
19 in>>m;
20 for (;m--;)
21 {
22 in>>h;
23 ant = v[1];
24 if (ant > h)
25 ant = h;
26 maxim=ant;
27 nr = 0;
28 for (i=2;i<=n;i++)
29 {
30 crt = v[i];
31 if (crt > h)
32 crt = h;
33
34 if (crt<ant)
35 nr=nr+ant-crt;
36 else
37 if(crt>maxim)
38 {
39 nr=nr+crt-maxim;
40 maxim=crt;
41 }
42 ant=crt;
43 }
44 out<<nr<<"\n";
45 }
46 return 0;
47 }
21.3 sort2dist
Problema 3 - sort2dist 100 de puncte
Jocul pe care ı̂l joaca Robo atunci când se plictiseşte este un joc inteligent pentru roboţei.
Pe ecranul tabletei lui roboţeşti, sunt N căsuţe de formă pătrată, cu latura egală cu 1. Căsuţele
sunt aşezate pe un rând, una lângă alta, fiind etichetate, ı̂n această ordine, cu numere de la 1 la
N . Fiecare căsuţă conţine câte un număr natural, identificatorul câte unuia dintre prietenii săi,
roboţei, ca şi el. Identificatorii se pot repeta.
Robo poate interschimba conţinutul a două căsuţe, numai dacă distanţa
dintre centrele acestora pe orizontală este egală cu distanţa dintre braţele
sale; distanţa, pe orizontală, dintre centrele a două căsuţe etichetate cu i,
respectiv cu j, este j i (1 & i $ j & N ).
El ı̂şi poate fixa ı̂n orice moment distanţa dintre braţe la 1 sau ı̂şi poate dubla distanţa curentă
dintre braţe, de oricâte ori este necesar, fără a depăşi valoarea N 1. Astfel, distanţa dintre braţele
sale poate fi 1, apoi, prin dublare, 2, apoi, prin dublare 4, apoi, prin dublare 8 etc. La ı̂nceputul
jocului, distanţa dintre braţele lui Robo este 1. De fiecare dată când consideră convenabilă distanţa
dintre braţe, realizează o interschimbare.
CAPITOLUL 21. ONI 2015 21.3. SORT2DIST 240
Cerinţe
Se cere ca Robo să aşeze identificatorii ı̂n căsuţe ı̂n ordine crescătoare, prin maximum 12 500
interschimbări de tipul celei precizate mai sus.
Date de intrare
Fişierul sort2dist.in conţine:
- pe prima linie numărul natural N , cu semnificaţia din enunţ;
- pe următoarele N linii, N numere, reprezentând, ı̂n această ordine, identificatorii conţinuţi
ı̂n căsuţele tabletei (identificatorul de pe linia i este conţinut de căsuţa i 1).
Date de ieşire
Fişierul sort2dist.out conţine:
- pe prima linie un număr natural M , reprezentând numărul de interschimbări realizate de
Robo (nu neapărat numărul minim de interschimbări necesare);
- pe fiecare dintre următoarele M linii (doar dacă M este nenul), câte două numere naturale,
separate prin câte un spaţiu, reprezentând etichetele căsuţelor al căror conţinut s-a interschimbat,
ı̂n ordinea realizării acestor interschimbări.
Restricţii şi precizări
a 1 & N & 1000;
a identificatorii sunt numere naturale de maximum 30 de cifre;
a pentru 25% din punctaj, fişierele de test conţin numere cu maximum 18 cifre;
a pentru 25% din punctaj, N & 100.
Exemple
16
17 int main()
18 {
19 fin>>n;
20 for (i=1;i<=n;i++)
21 fin>>v[i];
22
23 do
24 {
25 ok = 1;
26 for (i=1;i<n;i++)
27 if (v[i] > v[i+1])
28 {
29 aux = v[i];
30 v[i] = v[i+1];
31 v[i+1] = aux;
32 sol.push_back( make_pair(i, i+1) );
33 ok = 0;
34 }
35
36 } while (!ok);
37
38 fout<<sol.size()<<"\n";
39 for (i=0;i<sol.size(); i++)
40 fout<<sol[i].first<<" "<<sol[i].second<<"\n";
41
42 return 0;
43 }
45 {
46 fin>>v[i];
47 }
48
49 for (i=n;i>=2;i--)
50 {
51 //m = v[1];
52 strcpy(m, v[1]);
53
54 p = 1;
55 for (j=2;j<=i;j++)
56 if (myStrcmp(v[j], m) > 0)
57 {
58 //if (v[j] > m) {
59 //m = v[j];
60 strcpy(m, v[j]);
61 p = j;
62 }
63
64 if (p != i)
65 {
66 d = i-p;
67
68 putere = 1;
69 last = p;
70 while (d!=0)
71 {
72 if (d%2 == 1)
73 {
74 schimba(last+putere, last);
75 last += putere;
76 }
77 d = d/2;
78 putere = putere*2;
79 }
80 }
81 }
82
83 fout<<sol.size()<<"\n";
84 for (i=0;i<sol.size();i++)
85 {
86 fout<<sol[i].first<<" "<<sol[i].second<<"\n";
87 }
88
89 return 0;
90 }
27 return 1;
28 }
29
30 void schimba(int x, int y)
31 {
32 sol.push_back( make_pair(x, y) );
33 int aux = v[x];
34 v[x] = v[y];
35 v[y] = aux;
36 }
37
38 int main()
39 {
40
41 fin>>n;
42 for (i=1;i<=n;i++)
43 {
44 fin>>s[i];
45 strcpy(w[i], s[i]);
46
47 // v[i] = i;
48 }
49
50 for (i=1;i<n;i++)
51 for (j=i+1;j<=n;j++)
52 {
53 if (myStrcmp(w[i], w[j]) > 0)
54 {
55 strcpy(aux, w[i]);
56 strcpy(w[i], w[j]);
57 strcpy(w[j], aux);
58 }
59 }
60
61 k = 1;
62 for (i=2;i<=n;i++)
63 {
64 if (myStrcmp(w[i], w[k]) != 0)
65 {
66 k++;
67 strcpy(w[k], w[i]);
68 }
69 }
70
71 for (i=1;i<=n;i++)
72 {
73 st = 1;
74 dr = k;
75 while (st <= dr)
76 {
77 mid = (st + dr)/2;
78 if (myStrcmp(s[i], w[mid]) == 0)
79 {
80 v[i] = mid;
81 break;
82 }
83
84 if (myStrcmp(s[i], w[mid]) < 0)
85 dr = mid - 1;
86 else
87 st = mid + 1;
88 }
89 }
90
91 for (i=n;i>=2;i--)
92 {
93 m = v[1];
94 p = 1;
95 for (j=2;j<=i;j++)
96 if (v[j] > m)
97 {
98 m = v[j];
99 p = j;
100 }
101
102 if (p != i)
CAPITOLUL 21. ONI 2015 21.3. SORT2DIST 246
103 {
104 d = i-p;
105
106 putere = 1;
107 last = p;
108 while (d!=0)
109 {
110 if (d%2 == 1)
111 {
112 schimba(last+putere, last);
113 last += putere;
114 }
115 d = d/2;
116 putere = putere*2;
117 }
118 }
119 }
120
121 fout<<sol.size()<<"\n";
122 for (i=0;i<sol.size();i++)
123 {
124 fout<<sol[i].first<<" "<<sol[i].second<<"\n";
125 }
126
127 return 0;
128 }
47 // v[i+1] = aux;
48 sol.push_back( make_pair(i, i+1) );
49 ok = 0;
50 }
51
52 } while (!ok);
53
54 fout<<sol.size()<<"\n";
55 for (i=0;i<sol.size(); i++)
56 fout<<sol[i].first<<" "<<sol[i].second<<"\n";
57
58 return 0;
59 }
60 }
61
62 fout<<sol.size()<<"\n";
63 for (i=0;i<sol.size();i++)
64 {
65 fout<<sol[i].first<<" "<<sol[i].second<<"\n";
66 }
67
68 return 0;
69 }
63 for (i=0;i<sol.size();i++)
64 {
65 fout<<sol[i].first<<" "<<sol[i].second<<"\n";
66 }
67
68 return 0;
69 }
66 }
67
68 return 0;
69 }
ONI 2014
22.1 cifre
Problema 1 - cifre 100 de puncte
Maia tocmai a ı̂nvăţat la şcoală să facă adunări cu numere naturale având mai multe cifre.
Pentru că ı̂i place foarte mult matematica s-a apucat să scrie pe o foaie multe numere naturale,
cu una sau mai multe cifre, şi a ı̂nceput să le adune.
După o vreme s-a cam plictisit şi s-a gândit să afle cea mai mare sumă ce s-ar putea obţine
dacă s-ar schimba ı̂ntre ele cifrele numerelor de pe foaie. Are ı̂nsă o singură dorinţă: după ce
schimbă cifrele ı̂ntre ele să rămână acelaşi număr de numere cu o cifră, acelaşi număr de numere
cu două cifre şi aşa mai departe.
Cerinţe
Date de intrare
Fişierul de intrare cifre.in conţine pe prima linie un număr natural n reprezentând numărul
de numere scrise de Maia pe foaie. Următoarele n linii conţin cele n numere naturale scrise iniţial
pe foaie, câte un număr pe fiecare linie.
Date de ieşire
Fişierul de ieşire cifre.out va conţine pe prima linie un număr natural S reprezentând suma
maximă obţinută. Pe următoarele n linii vor fi scrise n numere naturale, câte un număr pe o
linie, reprezentând un şir de numere pentru care se obţine suma maximă, respectând restricţiile
din enunţ.
Exemple
252
CAPITOLUL 22. ONI 2014 22.1. CIFRE 253
Se citesc pe rând numerele, dar nu le vom memora, ci vom construi următoarele tablouri:
nr[k] = numărul de cifre k care apar ı̂n toate numerele citite;
t[k] = numărul de numere având k cifre
(Atenţie să număraţi şi numerele egale cu 0!)
De exemplu pentru şirul 3120 400 1000 50 1 7 30 60 vom avea:
nr[0]=9 nr[1]=3 nr[2]=1 nr[3]=2 nr[4]=nr[5]=nr[6]=nr[7]=1
t[1]=2 t[2]=3 t[3]=1 t[4]=2
Pentru rezolvarea problemei se foloseste o strategie Greedy, ı̂ncercând să distribuim cifrele de
0, apoi cifrele 1 etc ı̂ncepând cu cifra unităţilor, apoi a zecilor etc. Pentru aceasta putem folosi
două variante:
Varianta 1. (cifre1.cpp) - 80 puncte
Construim efectiv numerele folosind un vector a cu 100 000 de componente. Considerăm cifrele
unui număr numerotate de la dreapta la stânga ı̂ncepând cu 0. Notăm cu m numarul maxim de
cifre din numerele date.
x tm
Vom memora ı̂n a0, a1, ..., ax numerele de m cifre. Completăm cifra de ordin m 1 a
acestor numere cu cele mai mari cifre disponibile (folosind vectorului nr)
Numerele de m 1 cifre sunt memorate ı̂n ax 1, ax 2, ..., ax nrm 1
Vom completa acum numerele cifra de ordin m 2 a numerelor a0, ..., ax nrm 1
ş.a.m.d.
Completarea cifrelor de un anumit rang se va face de la dreapta la stânga, pentru a evita să
punem un 0 pe cea mai semnificativă poziţie a unui număr.
Varianta 2.
Vom construi pe rând fiecare număr ı̂ncepând cu cele având cele mai multe cifre. Pentru a
face acest lucru, mai construim două tablouri suplimentare:
a[i][j][k] = numărul de numere de j cifre care conţin pe poziţia i cifra k
b[i][j] = numărul de numere cu j cifre care nu au ı̂ncă completată cifra de pe poziţia i
Pentru exemplul de mai sus obţinem:
a[0][1][0]=2
a[0][2][0]=3
a[0][3][0]=1
a[0][4][0]=2
a[1][2][1]=3
a[1][3][0]=1
a[1][4][2]=1
a[1][4][3]=2
CAPITOLUL 22. ONI 2014 22.1. CIFRE 254
int nrc[10];
//nrc[i]=numarul de cifre i folosite in scrierea celor n numere
int v[NMAX][LGMAX];
//pe linia i in matricea v retin cifrele numarului i
int lg[NMAX];
//lg[i]=lungimea celui de al i-lea numar construit.
ı̂n v numerele vor fi memorate ı̂n ordinea crescătoare a numărului de cifre.
Pasul 1
Citesc numerele succesiv şi contorizez ı̂n vectorul nrc numărul de cifre folosite, respectiv ı̂n
vectorul nr numărul de numere pentru fiecare lungime posibilă.
Pasul 2.
Construiesc un vector auxiliar s
int s[LGMAX];
s[1]=1; for (i=2; i<LGMAX; i++) s[i]=s[i-1]+nr[i-1];
Numerele de i cifre sunt plasate ı̂n v de la si la si nri 1
Pasul 3
Plasez zerourile necesare ı̂n numere de o cifră (acestea sunt singurele numere care pot să ı̂nceapă
cu cifra 0):
for (i=1; i<=nr[1] && nrc[0]; i++) {nrc[0]--; lg[i]=1;}
Plasez celelalte zerouri \ˆin mod echilibrat, p\ˆan\u a le epuizez
for (i=1; i<LGMAX-1; i++) //plasez zerouri pe pozitia i
{if (!nrc[0]) break;
for (j=s[i+1]; j<=n && nrc[0]; j++) lg[j]++, nrc[0]--;
}
CAPITOLUL 22. ONI 2014 22.1. CIFRE 255
53 if (b<m)
54 m=b;
55 a=a-m;
56 b=b-m;
57 return m;
58 }
59
60 void distrib()
61 {
62 int i,j,k,p,q,mx=0;
63
64 for (i=0;i<=m;i++)
65 for (j=i+1;j<=m;j++)
66 b[i][j]=t[j];
67
68 // 0 la cifra unitatilor
69 k=0;
70 for (j=1;j<=m && nr[0]>0;j++)
71 a[0][j][0]=minim(b[0][j],nr[0]);
72
73 // 0 la cifra de pe pozitia i (i=1, cifra zecilor, i=2 cifra sutelor ect)
74 // ATENTIE! numerele de q cifre nu pot avea pe pozitia q-1 cifra 0
75 for (i=1;i<=m && nr[0]>0;i++)
76 for (j=i+2;j<=m;j++)
77 a[i][j][0]=minim(b[i][j],nr[0]);
78
79 // distribuim celelalte cifre (cifrele diferite de 0)
80 for (k=1;k<=9;k++) // cifra
81 for (i=0;i<m && nr[k]>0;i++) // pozitia
82 for (j=i+1;j<=m && nr[k]>0;j++) // nr de cifre
83 {
84 q=minim(b[i][j],nr[k]);
85 if (t>0)
86 {
87 a[i][j][k]=q;
88 sum[i]+=q*k; // cifra k apare de q
89 // ori pe pozitia i
90 // => k*q pe pozitia i in suma
91 p=i; // verificam transportul
92 while (sum[p]>9)
93 {
94 q=sum[p]/10;
95 sum[p]=sum[p]%10;
96 p++;
97 sum[p]+=q;
98 if (p>mx) // mx numarul de cifre din suma
99 mx=p;
100 }
101 }
102 }
103 for (i=mx;i>=0;i--)
104 fout<<sum[i];
105 fout<<"\n";
106 }
107
108 void constr()
109 {
110 int i,j,c,nr,p=0;
111 for (j=m;j>=1;j--) // numarul de cifre
112 while (t[j]>0) // numarul de numere cu j cifre
113 {
114 nr=0;
115 for (i=j-1;i>=0;i--)
116 {
117 c=9;
118 while (a[i][j][c]==0)
119 c--;
120 nr=nr*10+c;
121 a[i][j][c]--;
122 }
123 fout<<nr<<’\n’;
124 p=1;
125 t[j]--;
126 }
127 }
128
CAPITOLUL 22. ONI 2014 22.1. CIFRE 257
66 return 0;
67 }
71 else
72 if(f[cif]>0)
73 {
74 a[i]=a[i]+p[i]*cif;
75 p[i]*=10;
76 f[cif]--;
77 sol=1;
78 }
79 }
80 /// printf("i=%lld %lld %lld\n",i,a[i],u[i]);
81 }
82 }
83
84
85
86 s=0;
87 for(i=1;i<=n;i++)
88 s+=a[i];
89 printf("%lld\n",s);
90
91 for(i=1;i<=n;i++)
92 printf("%lld\n",a[i]);
93
94 /// for(i=1;i<=n;i++) printf("%lld ",u[i]);
95
96 return 0;
97 }
46 if(t<n)
47 t++;
48 else
49 t=1;
50 }
51
52 for(i=1;i<=9;i++)
53 while(cif[i]>0)
54 {
55 if(a[t]<nr[t])
56 {
57 x[t]=p[t]*i+x[t];
58 p[t]*=10;
59 cif[i]--;
60 a[t]++;
61 }
62
63 if(t<n)
64 t++;
65 else
66 t=1;
67 }
68
69 long long s=0;
70 for(i=1;i<=n;i++)
71 s=s+x[i];
72
73 g<<s<<’\n’;
74 for(i=1;i<=n;i++)
75 g<<x[i]<<"\n";
76
77 return 0;
78 }
22.2 solitar
Problema 2 - solitar 100 de puncte
Se consideră un joc de cărţi cu un număr nelimitat de coloane. Iniţial,
pe prima coloană există, ı̂ntr-o ordine oarecare, N cărţi cu numere distincte
din mulţimea r1, 2, ..., N x, următoarele coloane fiind vide (fără cărţi). Numim
secvenţă de la sfârşitul coloanei ultima sau ultimele două sau ultimele trei etc.
cărţi din coloană care au scrise pe ele numere consecutive ı̂n ordine crescătoare,
considerate de jos ı̂n sus.
De exemplu, ı̂n figurile 1 şi 2 sunt reprezentate două astfel de coloane cu câte
6 cărţi având numere ı̂ntre 1 şi 6. În figura 1, secvenţa de la sfârşitul coloanei este
formată doar din cartea 1. În figura 2, secvenţa de la sfârşitul coloanei este formată din cărţile 3,
4 şi 5. Se observă că ı̂n coloana din figura 1 mai există o secvenţă formată din cărţile 2, 3 şi 4,
dar aceasta nu este la sfârşitul coloanei.
Operaţiile permise ale jocului sunt:
A. mutarea secvenţei de cărţi de la sfârşitul unei coloane pe o coloană nouă, dacă acea coloană
este vidă (nu conţine nicio carte);
B. mutarea secvenţei de cărţi de la sfârşitul unei coloane la sfârşitul altei coloane cu cărţi, doar
dacă secvenţa mutată formează o secvenţă de numere consecutive cu cele de pe cartea sau cărţile
aflate la sfârşitul coloanei respective.
Se doreşte ca, printr-un număr minim de operaţii permise, să se obţină pe una dintre coloane
toate numerele de la 1 la N , ı̂n ordine crescătoare, considerate de jos ı̂n sus.
De exemplu, de la configuraţia iniţială din figura 2 se va obţine, printr-o operaţie A, configuraţia
1 de mai jos. Apoi, printr-o operaţie B, se obţine configuraţia 2, printr-o nouă operaţie B se obţine
configuraţia 3, apoi se mută secvenţa 2, 3, 4, 5, 6 pe o coloană vidă (operaţia A), apoi se mută
secvenţa 1 peste secvenţa 2, 3, 4, 5, 6 (operaţia B) şi se obţine, pe coloana a doua, configuraţia
finală cerută.
CAPITOLUL 22. ONI 2014 22.2. SOLITAR 261
Cerinţe
Cunoscând valoarea lui N , precum şi valorile cărţilor de pe prima coloană, să se determine
numărul minim de operaţii prin care se poate obţine secvenţa 1, 2, ..., N pe una dintre coloane.
Date de intrare
Fişierul solitar.in conţine pe prima linie numărul natural N şi pe linia următoare N numere
naturale distincte din mulţimea r1, 2, ..., N x, separate prin câte un spaţiu, date ı̂n ordinea de pe
coloană, de sus ı̂n jos.
Date de ieşire
Exemple
solitar.in solitar.out Explicaţii
6 5 Cele 5 mutări sunt descrise ı̂n enunţul problemei
162543
22.3 tdrept
Problema 3 - tdrept 100 de puncte
Se consideră N puncte de coordonate ı̂ntregi ı̂n sistemul de coordonate cartezian.
Cerinţe
Scrieţi un program care determină numărul de triunghiuri dreptunghice având vârfurile plasate
ı̂n 3 dintre punctele date şi catetele respectiv paralele cu axele de coordonate.
Date de intrare
CAPITOLUL 22. ONI 2014 22.3. TDREPT 264
Fişierul de intrare tdrept.in conţine pe prima linie numărul natural N , care reprezintă
numărul de puncte.
Pe următoarele N linii se află câte două numere naturale x y, separate prin spaţiu, reprezentând
coordonatele carteziene ale celor N puncte (abscisa şi ordonata).
Date de ieşire
Fişierul de ieşire tdrept.out va conţine o singură linie pe care va fi scris un număr natural
reprezentând numărul de triunghiuri dreptunghice care respectă condiţiile din enunţ. Deoarece
numărul de soluţii poate fi foarte mare, rezultatul va fi afişat modulo 666013 (adică restul ı̂mpărţirii
rezultatului la 666013).
Exemple
tdrept.in tdrept.out Explicaţii
8 5
11 Triunghiurile
14 dreptunghice
10 8 formate
41 sunt:
91
55 (1,1) (1,4) (4,1)
74 (1,1) (9,1) (1,4)
75 (5,5) (7,4) (7,5)
(1,4) (7,4) (7,5)
(1,1) (1,4) (7,4)
3
Soluţie O N - 15 puncte
Construim toate tripletele de N puncte distincte şi verificăm dacă cele 3 puncte reprezintă
vârfurile unui triunghi dreptunghic cu laturile respectiv paralele cu axele.
2
Soluţie O N logN - 38 puncte
Sortez punctele crescător după abscisă. Dacă sunt mai multe puncte cu aceeaşi abscisă, vor fi
sortate crescător după ordonată.
Aleg toate perechile de puncte şi consider că segmentul format de punctele respective este
ipotenuza unui triunghi dreptunghic. Pentru a verifica dacă acest lucru se ı̂ntâmplă, caut binar ı̂n
vectorul sortat de puncte dacă există cele două puncte care ar putea fi al treilea vârf al triunghiului
dreptunghic respectiv.
Soluţie O N logN - 65 puncte
Sortăm punctele după abscisă, iar ı̂n alt vector sortăm punctele după ordonată.
Parcurgem abscisele şi numărăm perechile de puncte cu aceeaşi abscisă. Pentru fiecare dintre
acestea, căutăm binar ordonatele lor ı̂n al doilea vector, contorizând triunghiurile dreptunghice
care se formează.
Soluţie O N - 100 puncte
Pentru fiecare abscisă contorizez numărul de puncte având abscisa respectivă
CAPITOLUL 22. ONI 2014 22.3. TDREPT 265
58 }
59 if (dr<n+1 && a.x==p[dr].x && a.y==p[dr].y) return 1;
60 return 0;
61 }
18
19 int n, nr;
20
21 void citire();
22 int verif (int, int, int);
23
24 int main()
25 {int i, j, k;
26 citire();
27 for (i=1; i<n-1; i++)
28 for (j=i+1; j<n; j++)
29 for (k=j+1; k<=n; k++)
30 nr=(nr+verif(i,j,k))%MOD;
31 fout<<nr<<’\n’;
32 fout.close();
33 return 0;
34 }
35
36 void citire()
37 {int i;
38 fin>>n;
39 for (i=1; i<=n; i++)
40 fin>>p[i].x>>p[i].y;
41 }
42
43 int verif(int i, int j, int k)
44 {
45 if (p[i].x==p[j].x && (p[j].y==p[k].y || p[i].y==p[k].y))
46 return 1;
47 if (p[i].x==p[k].x && (p[j].y==p[k].y || p[i].y==p[j].y))
48 return 1;
49 if (p[k].x==p[j].x && (p[j].y==p[i].y || p[i].y==p[k].y))
50 return 1;
51 return 0;
52 }
38
39 sort (px+1, px+n+1,cmpx);
40 sort (py+1, py+n+1,cmpy);
41
42 poz=cautbin(px[1].y);
43 for (nr1=0; px[1].y==py[poz].y; nr1++, poz++);
44
45 lg=1; v[lg]=nr1-1;
46 for (i=2; i<=n+1; i++)
47 {
48 poz=cautbin(px[i].y);
49 for (nr1=0; px[i].y==py[poz].y; nr1++, poz++);
50 if (px[i].x==px[i-1].x)
51 v[++lg]=nr1-1;
52 else
53 {
54 for (j=1; j<lg; j++)
55 for (k=j+1; k<=lg; k++)
56 nr=(nr+v[j]+v[k])%MOD;
57 lg=1; v[lg]=nr1-1;
58 }
59 }
60
61 fout<<nr<<’\n’;
62 fout.close();
63 return 0;
64 }
65
66 void citire()
67 {int i;
68 fin>>n;
69 for (i=1; i<=n; i++)
70 {fin>>px[i].x>>px[i].y;
71 py[i]=px[i];}
72 }
73
74 int cautbin(int a)
75 {int st=0, dr=n+1, mij;
76 while (dr-st>1)
77 {
78 mij=(st+dr)/2;
79 if (py[mij].y<a) st=mij;
80 else dr=mij;
81 }
82 if (dr<n+1 && a==py[dr].y) return dr;
83 return 0;
84 }
26 for(i=1;i<=n;i++)
27 {
28 sol+=1ll*(x[v[i].x]-1)*(y[v[i].y]-1)%666013 ;
29 if(sol>=666013)
30 sol-=666013;
31 }
32
33 g<<sol;
34 return 0;
35 }
63 }
64
65 g<<sol;
66 return 0;
67 }
ONI 2013
23.1 amestec
Problema 1 - amestec 100 de puncte
Se consideră un amestec de două substanţe, ale căror molecule se notează cu 0, respectiv 1,
reprezentat ca o matrice pătratică cu n linii şi n coloane.
În vederea separării celor două substanţe, asupra amestecului se aplică succesiv o serie de k
forţe magnetice, caracterizate de următoarele trei mărimi:
a durata aplicării forţei, notată cu di (1 & i & k) exprimată ı̂n secunde;
a poziţia aplicării forţei, notată cu pi {’N’, ’S’, ’E’, ’V’}, 1 & i & k, ce reprezintă unul dintre
cele patru puncte cardinale (Nord, Sud, Est, Vest);
a tipul moleculelor (0 sau 1) asupra cărora acţionează forţa, notat cu mi , 1 & i & k.
Cerinţe
Să se scrie un program care determină matricea amestecului obţinut după aplicarea forţelor
magnetice.
Spre exemplu, dacă n 3, matricea moleculelor este cea de mai jos şi se aplică k 2 forţe,
caracterizate prin 1N 1, 2E0, amestecul va trece prin următoarele etape:
Date de intrare
Fişierul de intrare amestec.in conţine pe prima linie două numerele naturale, n şi k, separate
printr-un spaţiu, cu semnificaţia de mai sus. Pe fiecare din următoarele n linii se găseşte câte un
şir de n caractere 0 sau 1. Pe fiecare dintre următoarele k linii se găsesc câte 3 valori, după cum
urmează: un număr natural di , un caracter pi (’N’, ’S’, ’E’, ’V’) şi un număr natural mi , 1 & i & k,
având semnificaţia de mai sus, neseparate prin spaţiu.
Date de ieşire
Fişierul de ieşire amestec.out va conţine matricea amestecului final. Pe fiecare din cele n linii
ale fişierului de ieşire se va scrie câte un şir de n caractere 0 sau 1, neseparate prin spaţiu.
271
CAPITOLUL 23. ONI 2013 23.2. EOLIENE 272
Exemple
amestec.in amestec.out Explicaţii
32 111 Matricea moleculelor are 3 linii şi 3 coloane.
011 110 Se aplică un număr de k 2 forţe, prima cu durata de 1
101 100 secundă, spre nord, şi care atrage moleculele de tip 1, şi a
110 doua cu durata de 2 secunde, spre est, ce atrage moleculele de
1N1 tip 0. După aplicarea celor 2 forţe, moleculele se vor reaşeza
2E0 conform matricei alăturate.
23.2 eoliene
Problema 2 - eoliene 100 de puncte
Primarul oraşului Oradea intenţionează să instaleze N turbine eoliene cu câte
trei pale (imaginea alăturată) pentru a produce ecologic, cu costuri minime, energia
electrică necesară locuitorilor oraşului. Conform planului primarului, cele N turbine
eoliene (numerotate cu 1, 2, 3, ..., N ) vor fi montate ı̂n linie dreaptă, paralel cu
şoseaua care leagă Oradea de Băile Felix, la distanţe nu neapărat egale unele de
altele. Prima turbină se va instala la distanţa D1 faţă de Oradea, a doua la distanţa
D2 faţă de Oradea, ..., a N -a turbină la distanţă DN faţă de Oradea. Palele turbinelor
sunt poziţionate, ı̂n acelaşi plan, paralel cu şoseaua. Sub acţiunea vântului, palele
turbinelor se rotesc ı̂n jurul nacelei (imaginile următoare), vitezele de rotaţie putând fi diferite de
la o turbină la alta.
Primarul a achiziţionat turbinele şi a angajat echipa inginerului Eol pentru a le construi
fundaţiile şi pentru a le instala. După construirea fundaţiilor, ı̂nainte de instalare, inginerul
Eol a studiat turbinele şi a constatat că:
a turbina 1 are cele trei pale identice de lungime L1 , turbina 2 are cele trei pale identice de
lungime L2 , ..., turbina N are cele trei pale identice de lungime LN iar lungimile L1 , L2 , ..., LN nu
sunt toate egale, o parte dintre turbine având palele cu lungimi diferite faţă de celelalte turbine;
a pilonii celor N turbine sunt identici;
CAPITOLUL 23. ONI 2013 23.2. EOLIENE 273
a dacă vor instala turbinele conform planului, atunci pot fi turbine care ı̂şi pot lovi palele ı̂n
timpul rotirii şi astfel se vor strica.
În concluzie, inginerul Eol va trebui să determine numărul minim M de turbine care pot
fi eliminate din planul primarului, astfel ı̂ncât oricare două turbine dintre cele rămase să nu-şi
lovească palele ı̂n timpul funcţionării (palele a două turbine se lovesc dacă se ating chiar şi ı̂ntr-un
punct), orice valori ar avea vitezele lor de rotaţie.
Cerinţe
Scrieţi un program care să citească numerele naturale N , D1 , D2 , ..., DN , L1 , L2 , ..., LN (cu
semnificaţia din enunţ) şi să determine numărul minim M de turbine ce pot fi eliminate din
planul primarului astfel ı̂ncât oricare două turbine alăturate din cele rămase să nu-şi lovească
palele ı̂n timpul funcţionării.
Date de intrare
Fişierul de intrare eoliene.in conţine pe prima linie numărul natural N . A doua linie conţine
cele N numere naturale D1 , D2 , ..., DN separate prin câte un spaţiu. A treia linie conţine cele N
numere naturale L1 , L2 , ..., LN , separate prin câte un spaţiu, cu semnificaţia din enunţ.
Date de ieşire
Exemple
eoliene.in eoliene.out
7 27 9 28 37 3 54 50 3
1554522
Explicaţii:
Sunt N 7 turbine. ı̂n planul primarului ele figurează astfel:
turbine 1 2 3 4 5 6 7
distanţe D1 27 D2 9 D3 28 D4 37 D5 3 D6 54 D7 50
lungime pale L1 1 L2 5 L3 5 L4 4 L5 5 L6 2 L7 2
Palele perechilor de turbine (2,5), (1,3), (3,4) şi (6,7) se vor lovi.Astfel, se vor elimina minimum
M 3 turbine (turbinele 2, 3 şi 6 sau 2,3 şi 7 sau 5,3 şi 6 sau 5,3 şi 7).
23.3 sudoku1
Problema 3 - sudoku1 100 de puncte
Numim tablou Sudoku o matrice cu n n elemente ce conţine doar cifrele 1, 2 şi 3 astfel ı̂ncât
ı̂n fiecare pătrat format din 2 2 elemente alăturate să existe toate cele 3 cifre şi oricare două
elemente alăturate pe linie sau pe coloană să fie distincte.
Fiecărui tablou Sudoku i se asociază un număr obţinut prin scrierea
cifrelor ı̂n ordine, ı̂ncepând cu prima linie.
De exemplu, tabloul Sudoku din imaginea alăturată are asociat numărul:
2132132132213211321321321.
Se defineşte şirul S n ca fiind un şir ordonat, format din toate tablourile
Sudoku cu n n elemente, {s1 , s2 , s3 , ...} . Pentru orice pereche si , sj
din S n cu i $ j, numărul asociat tabloului Sudoku si este strict mai mic
decât numărul asociat tabloului Sudoku sj .
Pentru n 2, şirul S 2 conţine, ı̂n ordine, tablourile Sudoku:
Cerinţe
Date de intrare
Fişierul sudoku.in conţine pe prima sa linie două numere naturale n şi k separate prin câte
un spaţiu.
Date de ieşire
Exemple
sudoku1.in sudoku1.out Explicaţii
26 12 şirul S(2) conţine 12 tablouri Sudoku, pe poziţia 6 ı̂n şir
21 aflându-se tabloul:
32 21
32
ONI 2012
24.1 alune
Problema 1 - alune 100 de puncte
Chip şi Dale s-au plictisit de jocurile de până acum şi au hotărât că este
timpul să ı̂mbine culesul alunelor cu un joc care să le stimuleze inteligenţa.
Chip propune: ”eu pun alunele culese de mine ı̂ntr-un şir de C scorburi, iar
tu pui alunele culese de tine ı̂ntr-un alt şir, de D scorburi”.
Dale a ascultat, a fost de acord şi a propus ca jocul să continue astfel:
”dacă la ı̂mpărţirea numărului de alune din prima scorbură a şirului meu
la numărul de alune din fiecare scorbură a şirului tău se obţine acelaşi rest,
atunci consider că scorbura mea este umplută corect şi scriu pe hârtie cifra 1, altfel o consider
umplută incorect şi scriu cifra 0. Verific apoi, aplicând aceeaşi regulă, dacă a doua scorbură din
şirul meu este umplută corect, adică dacă la ı̂mpărţirea numărului de alune din aceasta la numărul
de alune din fiecare scorbură din şirul tău, se obţine acelaşi rest. Notez pe hârtie, ı̂n continuare,
rezultatul verificării (0 sau 1). ı̂ncheiem jocul atunci când terminăm de verificat, după această
regulă, toate cele D scorburi ale mele.”
Cerinţe
Scrieţi un program care citeşte din fişierul alune.in numerele naturale nenule C şi D şi numărul
de alune din fiecare scorbură din şirul lui Chip, respectiv al lui Dale. Programul determină şirul
de cifre notat de Dale pe hârtie.
Date de intrare
Fişierul alune.in conţine pe prima linie cele două numere naturale, C şi D, pe a doua linie
C numere naturale, reprezentând numărul de alune din fiecare scorbură a lui Chip, iar pe a treia
linie D numere naturale, reprezentând numărul de alune din fiecare scorbură a lui Dale. Toate
numerele situate pe aceeaşi linie a fişierului sunt separate prin câte un spaţiu.
Date de ieşire
Fişierul alune.out conţine o singură linie pe care se află şirul determinat. Cifrele din acest şir
nu sunt separate prin spaţii.
Exemple
276
CAPITOLUL 24. ONI 2012 24.1. ALUNE 277
Se calculează cel mai mic multiplu comun al celor C numere de pe a doua linie a fişierului
de intrare, notat cu M . Dau acelaşi rest la ı̂mpărţirea la toate numerele de pe această linie
doar acele numere de pe linia a treia care sunt de forma M k r, pentru care r este cuprins
ı̂ntre 0 şi M in 1 (unde M in este minimul valorilor de pe a doua linie a fişierului de intrare).
Trebuie acordată atenţie detaliilor de implementare (de exemplu se observă că nu este necesară
calcularea celui mai mic multiplu comun dacă acesta depăşeşte valorile reprezentabile pe 64 de
biţi). Complexitatea ı̂n timp unei soluţii pe această idee este O C log V M AX D şi permite
obţinerea a 100 de puncte.
38 {
39 fscanf(f,"%d",&X);
40 m = minim(m, X);
41 if (depaseste)
42 continue;
43 mc = mc / cmmdc(mc, X) * X;
44 if (mc > MAX)
45 {
46 depaseste = 1;
47 }
48 }
49
50 for (;Q;--Q)
51 {
52 ok = 1;
53 fscanf(f,"%d",&q);
54 if (depaseste)
55 {
56 if (q >= m)
57 {
58 ok = 0;
59 }
60 }
61 else
62 {
63 if (q % mc >= m)
64 {
65 ok = 0;
66 }
67 }
68 fprintf(g,"%d",ok);
69 }
70
71 fprintf(g,"\n");
72 fclose(g);
73 fclose(f);
74 return 0;
75 }
35 g<<ok;
36 }
37 }
38
39 g<<’\n’;
40 f.close();
41 g.close();
42 return 0;
43 }
11 int N, Q, a, i, j, ok, r, s;
12
13
14 int main()
15 {
16 FILE *f = fopen("alune.in","r");
17 FILE *g = fopen("alune.out","w");
18
19 fscanf(f,"%d %d",&N, &Q);
20 for (i=1;i<=N;i++)
21 fscanf(f,"%d",&A[i]);
22
23 for (i=1;i<=Q;i++)
24 fscanf(f,"%d",&B[i]);
25
26 sort(A+1, A+N+1);
27
28 a = 1;
29 for (i=2;i<=N;i++)
30 if(A[i] != A[a])
31 {
32 A[++a] = A[i];
33 }
34
35 for (i=1;i<=Q;i++)
36 {
37 ok = 1;
38 r = B[i]%A[1];
39 for (j=2;j<=a;j++)
40 if(B[i]%A[j] != r)
41 {
42 ok = 0;
43 break;
44 }
45 S[s++] = (ok == 1 ? ’1’ : ’0’);
46 }
47
48 S[s] = 0;
49 fprintf(g,"%s\n",S);
50 fclose(f);
51 return 0;
52 }
31
32 int main()
33 {
34 FILE *f = fopen("alune.in","r");
35 FILE *g = fopen("alune.out","w");
36
37 fscanf(f,"%d %d",&N, &Q);
38 for (i=1;i<=N;i++)
39 fscanf(f,"%d",&A[i]);
40 for (i=1;i<=Q;i++)
41 {
42 fscanf(f,"%d",&B[i].q);
43 B[i].p = i;
44 }
45
46 sort(A+1, A+N+1);
47
48 a = 1;
49 for (i=2;i<=N;i++)
50 if(A[i] != A[a])
51 {
52 A[++a] = A[i];
53 }
54
55 sort(B+1, B+Q+1, cmp1);
56
57
58 for (i=1;i<=Q;i++)
59 {
60 if (i!=1 && B[i].q == B[i-1].q)
61 {
62 B[i].v = B[i-1].v;
63 continue;
64 }
65 ok = 1;
66 r = B[i].q%A[1];
67 for (j=2;j<=a;j++)
68 if(B[i].q%A[j] != r)
69 {
70 ok = 0;
71 break;
72 }
73 B[i].v = ok;
74 }
75
76 sort(B+1, B+Q+1, cmp2);
77
78 for (i=1;i<=Q;i++)
79 {
80 S[s++] = (B[i].v == 1 ? ’1’ : ’0’);
81 }
82
83 S[s] = 0;
84 fprintf(g,"%s\n", S);
85
86 fclose(g);
87 fclose(f);
88 return 0;
89 }
14
15 int main()
16 {
17 FILE *f = fopen("alune.in","r");
18 FILE *g = fopen("alune.out","w");
19
20 fscanf(f,"%d %d\n",&N, &Q);
21
22 fgets(buff, 1000000, f);
23 p = buff;
24
25 for (i=1;i<N;i++)
26 {
27 u = strchr(p, ’ ’);
28 A[i] = atoi(p);
29 p = u+1;
30 }
31 A[N] = atoi(p);
32
33 fgets(buff, 1000000, f);
34 p = buff;
35
36 for (i=1;i<Q;i++)
37 {
38 u = strchr(p, ’ ’);
39 B[i] = atoi(p);
40 p = u+1;
41 }
42 B[Q] = atoi(p);
43
44 for (i=1;i<=Q;i++)
45 fscanf(f,"%d",&B[i]);
46
47 sort(A+1, A+N+1);
48
49 a = 1;
50 for (i=2;i<=N;i++)
51 if(A[i] != A[a])
52 {
53 A[++a] = A[i];
54 }
55
56 for (i=1;i<=Q;i++)
57 {
58 ok = 1;
59 r = B[i]%A[1];
60 for (j=2;j<=a;j++)
61 if(B[i]%A[j] != r)
62 {
63 ok = 0;
64 break;
65 }
66 S[s++] = (ok == 1 ? ’1’ : ’0’);
67 }
68
69 S[s] = 0;
70 fprintf(g,"%s\n",S);
71 fclose(f);
72 return 0;
73 }
13 {
14 r = a % b;
15 a = b;
16 b = r;
17 }
18 return a;
19 }
20
21 int main()
22 {
23 freopen("alune.in","r",stdin);
24 freopen("alune.out","w",stdout);
25
26 scanf("%d %d",&N,&Q);
27 cmmmc = 1LL;
28 minim = INF;
29 for (i=1; i<=N; ++i)
30 {
31 scanf("%d",&x);
32 if (x < minim)
33 minim = x;
34 cmmmc = 1LL * x * cmmmc / cmmdc(x, cmmmc);
35 if (cmmmc > INF)
36 cmmmc = INF;
37 }
38
39 for (i=1; i<=Q; ++i)
40 {
41 scanf("%d",&x);
42 if (x % cmmmc < minim)
43 printf("1");
44 else
45 printf("0");
46 }
47
48 printf("\n");
49 return 0;
50 }
35 ofstream g("alune.out");
36
37 // fscanf(f,"%d %d",&N, &Q);
38 f >> N >> Q;
39
40 // fscanf(f,"%d",&X);
41 f>>X;
42
43 mc = m = X;
44 depaseste = 0;
45 for (i = 2; i<=N; i++)
46 {
47 // fscanf(f,"%d",&X);
48 f>>X;
49 m = minim(m, X);
50 if (depaseste)
51 continue;
52 mc = mc / cmmdc(mc, X) * X;
53 if (mc > MAX)
54 {
55 depaseste = 1;
56 }
57 }
58
59 for (;Q;--Q)
60 {
61 ok = 1;
62 // fscanf(f,"%d",&q);
63 f>>q;
64 if (depaseste)
65 {
66 if (q >= m)
67 {
68 ok = 0;
69 }
70 }
71 else
72 {
73 if (q % mc >= m)
74 {
75 ok = 0;
76 }
77 }
78 // fprintf(g,"%d",ok);
79 g<<ok;
80 }
81
82 // fprintf(g,"\n");
83 g<<"\n";
84 g.close();
85 f.close();
86 // fclose(g);
87 // fclose(f);
88 return 0;
89 }
24.2 cuburi
Problema 2 - cuburi 100 de puncte
Ionuţ a ı̂nvăţat la şcoală să lucreze cu numere mari. El are la dispoziţie un şir de N numere
naturale nenule. Din fiecare număr el şterge exact trei cifre, fără să schimbe ordinea cifrelor
rămase, astfel ı̂ncât să obţină cel mai mic număr natural nenul posibil.
De exemplu, din numărul 20731049 se
obţine numărul 20049, iar din numărul 13004
se obţine numărul 10. Înlocuind fiecare număr
citit cu numărul obţinut prin operaţia de mai
CAPITOLUL 24. ONI 2012 24.2. CUBURI 285
Cerinţe
Scrieţi un program care citeşte numerele naturale nenule N şi K, apoi cele N numere naturale
ce fac parte din şirul iniţial, şi determină:
a) Numărul de piramide complete construite de Ionuţ.
b) Numerele scrise pe cuburile din primele K piramide.
Date de intrare
Fişierul cuburi.in are două linii: prima linie conţine două numere naturale, N şi K, iar a
doua linie conţine N numere naturale. Pe fiecare linie a fişierului numerele sunt separate prin câte
un spaţiu.
Date de ieşire
Fişierul cuburi.out are două linii: prima linie conţine numărul de piramide complete care
au fost construite, iar a doua linie conţine toate numerele scrise pe cuburile ce formează primele
K piramide. Numerele sunt scrise separate prin câte un spaţiu, ı̂n ordinea apariţiei lor ı̂n şirul
nou obţinut.
Exemple
cuburi.in cuburi.out Explicaţii
31 2 2 Primele 6 numere se găsesc pe cubul ce
18250 9280 18250 953805 20800 10 2 10 305 20 formează prima piramidă, următoarele
6040065 24208 4405 8794 1720 4005 20 4 4 1 86 24 de numere sunt scrise, ı̂n această or-
98886 96400 45544 8560056 40 44 5005 30 40 dine, pe feţele cuburilor ce alcătuiesc a
36055 60400 80200 11560 36475 20 10 34 22 20 40 doua piramidă.
26992 68320 69400 20296 72640 20 20 30 50 20 40 Observaţie: ı̂n acest tabel, datorită
34048 57700 66520 47440 91232 12 20 spaţiului insuficient, numerele nu apar
26080 90280 scrise pe exact două linii, ca ı̂n fişierele
de intrare/ieşire.
Pentru determinarea numerelor obţinute prin modificare, se poate proceda ı̂n mai multe mod-
uri.
1. Presupunem că au rămas q cifre de şters, iar ultima cifră adăugată stă pe poziţia last ı̂n
vectorul ce reţine cifrele iniţiale ale numărului. Următoarea cifră adăugată la numărul nou, se va
afla pe una din pozitiile din intervalul last 1...last q 1. Se caută cifra minimă situată pe aceste
poziţii, iar ı̂n caz de egalitate se alege prima. Notam poziţia acestei cifre cu minim. Toate cifrele
de la last 1 la minim 1 vor fi şterse, cu alte cuvinte, rămân de şters q minim last 1.
Noua ultimă poziţie devine minim, deci la pasul următor last minim.
Se tratează separat cazul primei cifre care se alege ca cifră minimă nenulă din primele 4 ale
numarului.
2. Folosim o stivă ı̂n care introducem pe rând cifrele numărului ı̂n ordine, de la stânga la
dreapta. Cât timp cifra curentă este mai mică decât cea din vârful stivei, se scoate o cifră din
stivă. Apoi, cifra curentă se plasează ı̂n vârful stivei. Odată ce s-au scos 3 cifre din stivă, cifrele
numărului care au rămas neanalizate se vor pune ı̂n stivă. Numerele rămase ı̂n stivă reprezintă
soluţia. Trebuie acordată atenţie detaliilor de implementare ı̂n vederea evitării soluţiilor ce ı̂ncep
cu 0.
3. Construim toate numerele ce se pot obţine prin eliminarea ı̂n toate modurile a 3 cifre.
Acest lucru ı̂l putem realiza fie cu un algoritm gen backtracking (de generare a combinărilor de
3 elemente dintr-o mulţime cu C elemente - unde C reprezintă şirul cifrelor numărului), fie prin
fixarea lor pe poziţii ı̂n toate modurile posibile prin 3 foruri.
Pentru a determina câte piramide complete a construit se observă că numărul cuburilor din
fiecare piramidă determină un şir format din primele pătrate perfecte ı̂ncepând cu 1, sau se poate
utiliza formula an an1 2 n 1 plecând de la a1 1.
Pentru ultima cerinţă se vor fi afişa primele k k 1 2k 1 numere obţinute după modificări.
32 {
33 Min=v[in];imin=in;
34 for(i=in;i<=sf;i++)
35 if(v[i]<Min)
36 {
37 Min=v[i];
38 imin=i;
39 }
40 nr=nr*10+Min;
41 in=imin+1;sf++;
42 }
43 return nr;
44 }
45
46 int main()
47 {
48 int x,nrc,n,nrpir=0,i,k,j=1,nr=0,s=0;
49 f>>n>>k;
50
51 for(i=1;i<=k;i++)
52 nr=nr+i*i*6;
53
54 for(i=1;i<=n;i++)
55 {
56 f>>x;
57 nrc=(int)log10(x)+1;
58 if(i%6==0 && (sqrt(i/6)==(int)sqrt(i/6)))
59 {
60 s=s+i;
61 if(s<=n)
62 nrpir++;
63 }
64
65 if(i<=nr)
66 w[j++]=nrmin(x,nrc);
67 }
68
69 g<<nrpir<<’\n’;
70 for(i=1;i<j;i++)
71 g<<w[i]<<’ ’;
72 g<<’\n’;
73
74 f.close();
75 g.close();
76 return 0;
77 }
27 }
28
29 int nr=Min;
30 in=imin+1;
31 sf=5;
32 while(sf<=nrc)
33 {
34 Min=v[in];imin=in;
35 for(i=in;i<=sf;i++)
36 if(v[i]<Min)
37 {
38 Min=v[i];
39 imin=i;
40 }
41 nr=nr*10+Min;
42 in=imin+1;sf++;
43 }
44 return nr;
45 }
46
47
48 int main()
49 {
50 freopen("cuburi.in","r",stdin);
51 freopen("cuburi.out","w",stdout);
52
53 int x,nrc,n,nrpir=0,i,k,j=1,nr=0,s=0;
54
55 scanf("%d %d",&n, &k);
56 // f>>n>>k;
57 for(i=1;i<=k;i++)
58 nr=nr+i*i*6;
59
60 for(i=1;i<=n;i++)
61 {
62 // f>>x;
63 scanf("%d",&x);
64 nrc=(int)log10(x)+1;
65
66 if(i%6==0 && (sqrt(i/6)==(int)sqrt(i/6)))
67 {
68 s=s+i;
69 if(s<=n)
70 nrpir++;
71 }
72 if(i<=nr) w[j++]=nrmin(x,nrc);
73 }
74
75 printf("%d\n",nrpir);
76 // g<<nrpir<<’\n’;
77 for(i=1;i<j;i++)
78 printf("%d ",w[i]);
79 // g<<w[i]<<’ ’;
80 // g<<’\n’;
81 printf("\n");
82
83 return 0;
84 }
91 for (p=c;p>=1;p--)
92 if (p!=i && p!=j && p!=k)
93 nr = nr * 10 + C[p];
94 if (nr < mn)
95 mn = nr;
96 }
97 }
98 return mn;
99 }
64
65 N/=6;
66 i = 1;
67 while (i*i <= N)
68 {
69 N -= i*i;
70 ++i;
71 }
72
73 printf("%d\n", i-1);
74
75 int fete = K * (K+1) * (2*K+1);
76
77 for (i=1; i< fete; ++i)
78 printf("%d ", A[i]);
79 printf("%d\n", A[fete]);
80
81 return 0;
82 }
54 {
55 freopen("cuburi.in","r",stdin);
56 freopen("cuburi.out","w",stdout);
57
58 scanf("%d %d\n",&N,&K);
59 for (i=1; i<=N; ++i)
60 {
61 scanf("%d", &x);
62 A[i] = g(x);
63 }
64
65 N/=6;
66 i = 1;
67 while (i*i <= N)
68 {
69 N -= i*i;
70 ++i;
71 }
72
73 printf("%d\n", i-1);
74
75 int fete = K * (K+1) * (2*K+1);
76
77 for (i=1; i< fete; ++i)
78 printf("%d ", A[i]);
79 printf("%d ", A[fete]);
80 printf("10\n");
81 return 0;
82 }
44 }
45 }
46
47 int main()
48 {
49 long z[100000]={0},w,u,k,x,y;
50 long n,m,i,j,nrp,nrc,p,v;
51
52 FILE *f,*g;
53 f=fopen("cuburi.in","r");
54 g=fopen("cuburi.out","w");
55
56 j=u=0;
57 fscanf(f,"%d %d",&n,&k);
58 m=k*(k+1)*(2*k+1);
59 for(i=1;i<=m;i++)
60 {
61 fscanf(f,"%ld",&x);
62 transf(x,y);
63 z[i]=y;
64 }
65
66 nrp=0;
67 v=0;
68 p=0;
69 do
70 {
71 p++;
72 w=6*p*p;
73 if(v+w<=n)
74 {
75 nrp++;
76 v=v+w;
77 }
78 else
79 break;
80 } while(v<=n);
81
82 fprintf(g,"%d\n",nrp);
83
84 for(i=1;i<m;i++)
85 fprintf(g,"%ld ",z[i]);
86
87 fprintf(g,"%ld\n",z[m]);
88
89 return 0;
90 }
26 {
27 fscanf(fin,"%d",&V[i]);
28
29 X = V[i];
30 c = 0;
31 while (X)
32 {
33 C[++c] = X%10;
34 X/=10;
35 }
36 memcpy(D, C, sizeof(C));
37 d = c;
38
39 mi = INF;
40 for (k = 0; k<=3; k++)
41 {
42 if (k == 3)
43 mi = minim(mi, f(C, c, 3));
44 else
45 if (k == 2)
46 mi = minim(mi, f(C, c-1, 2));
47 else
48 if (k == 1)
49 mi = minim(mi, f(C, c-2, 1));
50 else
51 mi = minim(mi, f(C, c-3, 0));
52
53 memcpy(C, D, sizeof(C));
54 c = d;
55 }
56
57 W[i] = mi;
58 }
59 fclose(fin);
60
61 M = N;
62 sum = 0;
63 for (B = 1;;B+=2)
64 {
65 cc += B*6;
66 sum += cc;
67 if (sum <= M)
68 {
69 if (K == B/2 + 1)
70 sm = sum;
71 cub++;
72 }
73 else
74 {
75 sum -= cc;
76 break;
77 }
78 }
79
80 fprintf(fout,"%d\n",cub);
81 for (i=1;i<=sm;i++)
82 fprintf(fout,"%d ",W[i]);
83
84 return 0;
85 }
86
87 int f(int *C, int c, int k)
88 {
89 int v = c; int i;
90 for (i=c-1;i>=1;i--)
91 {
92 while (k>0 && C[i]<C[v] && v<=c)
93 {
94 if (C[i] == 0 && v == c)
95 break;
96 v++;
97 k--;
98 }
99 C[--v] = C[i];
100 }
101
CAPITOLUL 24. ONI 2012 24.3. OPTIM 295
24.3 optim
Problema 3 - optim 100 de puncte
Gigel primea de la mama lui, ca temă, o foaie pe care era scris un şir
de N numere ı̂ntregi. Singurul calcul pe care ştia să ı̂l facă până acum era
suma tuturor numerelor. Pentru aceasta el plasa N 1 semne de adunare,
+, ı̂ntre numerele aflate pe poziţii consecutive ı̂n şir şi calcula astfel suma
acestor numere. ı̂ntre timp a crescut şi a ı̂nvăţat şi operaţia de ı̂nmulţire
pentru care foloseşte semnul *. Din şirul celor N 1 semne de adunare, ı̂i
trece prin minte să ı̂nlocuiască K semne + cu K semne *.
Îşi dă seama că tema se complică, deoarece ı̂nmulţirile trebuie efectuate ı̂naintea adunărilor,
dar nu se dă bătut şi duce calculul până la capăt.
Cerinţe
Scrieţi un program care să determine valoarea minimă pe care o poate obţine şi valoarea
maximă pe care o poate obţine după ı̂nlocuirea menţionată.
Date de intrare
Fişierul de intrare optim.in conţine pe prima linie numerele naturale N şi K, separate printr-
un spaţiu, reprezentând numărul de numere ı̂ntregi din şir, respectiv numărul de operaţii de
ı̂nmulţire ce vor fi efectuate. Pe cea de a doua linie se află N numere ı̂ntregi separate prin câte un
spaţiu, x1 x2 ...xN , reprezentând numerele din şir.
Date de ieşire
Fişierul de ieşire optim.out va conţine pe o singură linie, separate printr-un spaţiu, ı̂n ordine
crescătoare, cele două valori cerute.
Restricţii şi precizări
2 & N & 30;
a
0 & K & 9; K $ N ;
a
a 8 & xi & 8, 1 & i & N ;
a Dacă fişierul de ieşire conţine exact două numere, dar doar unul este corect, se obţine 40%
din punctajul acordat testului respectiv.
Exemple
optim.in optim.out Explicaţii
6 3 2 0 3 -1 7 -31 86 2 * 0 + 3 * (-1) + 7 * (-4) = -31
-4 2 + 0 + 3 * (-1) * 7 * (-4) = 86
Timp maxim de executare/test: 1.0 secunde
Memorie: total 2 MB din care pentru stivă 1 MB
Dimensiune maximă a sursei: 5 KB
CAPITOLUL 24. ONI 2012 24.3. OPTIM 296
14 {
15 if (lv == N)
16 {
17 suma += aux;
18 if (suma > maxim) maxim = suma;
19 if (suma < minim) minim = suma;
20 return;
21 }
22 if (N - lv - 1 >= k)
23 back(lv+1, k, A[lv+2], suma + aux);
24 if (k > 0)
25 back(lv+1, k-1, aux * A[lv+2], suma);
26 }
27
28 int main()
29 {
30 freopen("optim.in","r",stdin);
31 freopen("optim.out", "w",stdout);
32
33 scanf("%d %d",&N,&K);
34 for (i=1; i<=N; ++i)
35 scanf("%d",&A[i]);
36
37 --N;
38 maxim = -2000000000;
39 minim = 2000000000;
40
41 back(0, K, A[1], 0);
42
43 printf("%d %d\n", minim, maxim);
44 return 0;
45 }
41 z = x[i - t -1][j - t] + p;
42 v = d[i - t -1][j - t] + p;
43 if (z < Min)
44 Min = z;
45 if (v > Max)
46 Max = v;
47 }
48 }
49
50 x[i][j] = Min;
51 d[i][j] = Max;
52 }
53 }
54
55 printf("%d %d\n",x[n][k],d[n][k]);
56 return 0;
57 }
56
57 fscanf(f,"%d %d",&N, &K);
58 for (i=1;i<=N;i++)
59 {
60 fscanf(f,"%d",&V[i]);
61 }
62 fclose(f);
63
64 back(1);
65
66 fprintf(g,"%d %d\n",Min, Max);
67 fclose(g);
68 return 0;
69 }
59
60 for (int i=0;i<=1;i++)
61 {
62 X[k] = i;
63 if (nr + X[k] <= K)
64 {
65 nr += X[k];
66
67 back(k+1);
68
69 nr -= X[k];
70 }
71 }
72 }
73
74 int main()
75 {
76 FILE *f = fopen("optim.in","r");
77 FILE *g = fopen("optim.out","w");
78
79 fscanf(f,"%d %d",&N, &K);
80 for (i = 1; i<=N; i++)
81 {
82 fscanf(f,"%d",&V[i]);
83 }
84 fclose(f);
85
86 back(1);
87
88 fprintf(g,"%d %d\n",Min, Max);
89 fclose(g);
90 return 0;
91 }
40 return;
41 }
42 for (int i = X[k-1]+1;i<N;i++) {
43 X[k] = i;
44 back(k+1);
45 }
46 }
47 */
48
49 int main()
50 {
51 FILE *f = fopen("optim.in","r");
52 FILE *g = fopen("optim.out","w");
53
54 fscanf(f,"%d %d",&N, &K);
55 for (i=1;i<=N;i++)
56 {
57 fscanf(f,"%d",&V[i]);
58 }
59
60 k = 1;
61 X[k] = 0;
62 while (k)
63 {
64 if (X[k] < N-1)
65 {
66 X[k] ++;
67 if (k == K)
68 sol();
69 else
70 {
71 k++;
72 X[k] = X[k-1];
73 }
74 }
75 else
76 k--;
77 }
78
79 fclose(f);
80 // back(1);
81 fprintf(g,"%d %d\n",Min, Max);
82 fclose(g);
83 return 0;
84 }
28 {
29 if (k == K+1)
30 {
31 sol();
32 return;
33 }
34
35 for (int i = X[k-1]+1;i<N;i++)
36 {
37 X[k] = i;
38
39 if (k == 1)
40 {
41 s = 1;
42 S[s].lung = 1;
43 S[s].start = i;
44 }
45 else
46 if (X[k] == X[k-1]+1)
47 {
48 S[s].lung++;
49 }
50 else
51 {
52 Rez += PP[ S[s].start ][ S[s].lung + 1];
53 Rez -= SP[ S[s].start + S[s].lung] - SP[ S[s].start - 1 ];
54
55 S[++s].lung = 1;
56 S[s].start = i;
57 }
58
59 back(k+1);
60
61 if (k == 1)
62 {
63 s = 1;
64 S[s].lung = 1;
65 S[s].start = i;
66 }
67 else
68 if (X[k] == X[k-1]+1)
69 {
70 S[s].lung--;
71 }
72 else
73 {
74 s--;
75 Rez -= PP[ S[s].start ][ S[s].lung + 1];
76 Rez += SP[S[s].start+S[s].lung] - SP[S[s].start-1];
77 }
78 }
79 }
80
81 int main()
82 {
83 FILE *f = fopen("optim.in","r");
84 FILE *g = fopen("optim.out","w");
85
86 fscanf(f,"%d %d",&N, &K);
87 for (i=1;i<=N;i++)
88 {
89 fscanf(f,"%d",&V[i]);
90 SP[i] = SP[i-1] + V[i];
91 }
92
93 Rez = SP[N];
94
95 //PP[i][j] = produsul numerelor din
96 // secventa care incepe pe pozitia i si are lungimea j
97 for (i=1;i<=N;i++)
98 {
99 PP[i][1] = V[i];
100 for (j=2;j<=K+1;j++)
101 {
102 if (i+j-1 > N)
103 break;
CAPITOLUL 24. ONI 2012 24.3. OPTIM 303
60 S=S+v1[i];
61 if(S>Smax)
62 Smax=S;
63 if(S<Smin)
64 Smin=S;
65 }
66
67 i=n-1;
68 while(semn[i]==1)
69 {
70 semn[i]=0;
71 i--;
72 }
73
74 semn[i]=1;
75 nrs=0;
76 for(i=1;i<=k+1;i++)
77 nrs=nrs+semn[i];
78 } while(nrs<=k);
79
80 g<<Smin<<’ ’<<Smax<<’\n’;
81 }
82
83 f.close();
84 g.close();
85 return 0;
86 }
46 int main()
47 {
48 int i;
49 ifstream f("optim.in");
50 ofstream g("optim.out");
51
52 f>>n>>k;
53 for(i=0;i<n;i++)
54 f>>a[i];
55
56 val=a[0];
57 for(i=1;i<=k;i++)
58 val=val*a[i];
59
60 for(i=k+1;i<n;i++)
61 val=val+a[i];
62
63 valmin=valmax=val;
64
65 comb(1);
66
67 g<<valmin<<’ ’<<valmax<<’\n’;
68 }
ONI 2011
25.1 butoane
Problema 1 - butoane 100 de puncte
Echipa SG1 se află ı̂n faţa unei noi provocări. Un dispozitiv antic are un sistem foarte ciudat
prin care poate fi pus ı̂n funcţiune. Dispozitivul are n butoane numerotate de la stânga la dreapta
de la 1 la n. Pe fiecare buton se găseşte un număr natural. Suma tuturor numerelor de pe butoane
este divizibilă cu n.
S-a constatat că la atingerea butoanelor din capete (butonul 1 şi butonul n) numărul scris pe
acestea scade cu o unitate, iar numărul de pe butonul vecin creşte cu o unitate. Dacă se atinge
unul dintre celelalte butoane (cele numerotate cu 2, 3, ..., n 1) numărul corespunzător scade cu
două unităţi, iar cele corespunzătoare vecinilor cresc cu câte o unitate. Dispozitivul va fi pus ı̂n
funcţiune dacă toate cele n numere devin egale.
Ajuţati echipa SG1 să pună dispozitivul ı̂n funcţiune folosind un număr minim de atingeri ale
butoanelor.
Cerinţe
Cunoscându-se n, numărul de butoane, precum şi cele n numere naturale scrise iniţial pe
butoane să se stabilească de câte ori trebuie atins fiecare buton astfel ı̂ncât dispozitivul să fie
pornit astfel ı̂ncât numărul total de atingeri să fie minim.
Date de intrare
Fişierul de intrare butoane.in conţine pe prima linie numărul natural n, reprezentând numărul
de butoane. Pe cea de-a doua linie se află n numere naturale, separate prin câte un spaţiu,
reprezentând ı̂n ordine valorile ı̂nscrise iniţial pe cele n butoane.
Date de ieşire
Fişierul de ieşire butoane.out va conţine n linii. Pe linia i (1 & i & n) se va afişa un număr
natural reprezentând numărul de atingeri ale butonului i.
Restricţii şi precizări
a 3 & n & 1000
a Numerele ı̂nscrise iniţial pe cele n butoane sunt numere naturale mai mici sau egale cu 100.
Suma celor n numere este divizibilă cu n.
a Numărul de atingeri ale oricărui buton va fi & 2 000 000 000 (două miliarde).
a Punctaj. Dacă programul afişează o soluţie care determină deschiderea dispozitivului cu
număr minim de atingeri, obţine integral punctajul pentru testul respectiv. Dacă numărul de
atingeri nu este minim, dar soluţia afişată determină deschiderea dispozitivului, se obţine 30% din
punctaj.
Exemple
butoane.in butoane.out Explicaţii
3 0
10 11 12 1
2
306
CAPITOLUL 25. ONI 2011 25.1. BUTOANE 307
OBS. Mutările DOW N şi U P pot fi aplicate simultan de un numar de ori folosind ”SMENUL
LUI MARS” de modificare, ı̂n timp 1, a sumei unei secvenţe, cu o valoare constantă.
Pasul 3. Deoarece este posibil ca soluţia să nu fie optimă vom determina numărul minim de
apăsări obţinute ı̂n pasul 2. Scăzând această valoare din soluţia eventual neoptimă se va obţine
soluţia optimă.
(1) A-a+b=M
(2) B + a - 2b + c = M
(3) C + b - 2c + d = M
(4) D+c-d=M
Se observă că pentru orice alegere iniţială a valorii lui a rezulta sucesiv prin metoda ı̂nlocuirii:
În plus, printr-o analiză atentă a sistemului se observă că diferenţele a b, b c, c d pot fi
bine determinate şi valorile b, c, d sunt obţinute din a modificat cu constante unic determinate
din datele de intrare. Ideea e analogă pentru oricâte butoane.
Algoritm
Notez x[ ] = şirul celor n numere din fişierul de intrare şi sol[ ] şirul soluţie.
Se alege sol[1] = 0
Se calculează
sol[2] = M + sol[1] - x[1]
sol[k] = M - sol[k-2] + 2 sol[k-1] - x[k-1] pentru k &3
Deoarece este posibil ca prin alegerea lui sol[1] = 0 să obţinem sol[k] ¡ 0 (imposibil, deoarece
numărul de atingeri nu poate fi negativ) va trebui să translatăm soluţia prin scăderea valorii
V = min sol[k]
25.2 macheta
Problema 2 - macheta 100 de puncte
Cristi, participant la ONIGIM 2011, este pasionat de machete. El a realizat la scară macheta
campusului ı̂n care se desfăşoară olimpiada. ı̂n macheta lui sunt modelate N clădiri, numerotate
de la 1 la N , sub forma unor paralelipipede dreptunghice.
Privind macheta de sus, evident, toate clădirile sunt vizibile. Mai mult, asociind un sistem
de coordonate cartezian, cu originea ı̂n colţul stânga-jos al vederii de sus a machetei, axa OX
pe latura sudică (cea de jos) cu sensul către Est, iar axa OY pe latura vestică (cea din stânga)
cu sensul către Nord, observăm că vederea de sus a fiecărei clădiri este un dreptunghi cu laturile
respectiv paralele cu axele. Prin urmare, vederea de sus a unei clădiri poate fi specificată prin 4
valori x y Lx Ly cu semnificaţia: x abscisa, respectiv y ordonata colţului stânga-jos al vederii de
sus a clădirii; Lx lungimea laturilor paralele cu OX, respectiv Ly lungimea laturilor paralele cu
OY .
După ce a analizat macheta privind-o de sus, identificând astfel toate clădirile, Cristi priveşte
macheta perpendicular dinspre laterala sudică (adică priveşte perpendicular pe laterala machetei
pe care se află axa OX). Privind astfel macheta nu mai sunt vizibile toate cele N clădiri.
Cerinţe
Scrieţi un program care, cunoscând vederea de sus a machetei şi ı̂nălţimile clădirilor, să deter-
mine ce clădiri sunt vizibile privind macheta dinspre laterala sudică.
Date de intrare
Date de ieşire
CAPITOLUL 25. ONI 2011 25.2. MACHETA 309
Fişierul de ieşire macheta.out va conţine o singură linie pe care se vor scrie ı̂n ordine
crescătoare numerele clădirilor vizibile privind macheta dinspre laterala sudică.
Exemple
macheta.in macheta.out Explicaţii
5 1235
1 6 9 1 8
9 2 1 3 10
1 1 7 1 8
1 3 3 1 6
5 3 3 1 9
Prima dată normalizez coordonatele existente astfel ı̂ncât atât pe direcţia orizontală (pe
direcţia OX) cât şi pe verticală (pe ı̂nălţime) coordonatele să fie ı̂nlocuite cu valori consecutive
ı̂ncepând cu valoarea 1.
Ideea prezintă valoarea ca ı̂n acest moment fără să fi modificat problema ı̂n esenţă fiecare zona
pătrată cu latura de o unitate de lungime poate fi reprezentată ı̂ntr-o matrice de dimnesiuni 200
pe orizontală şi 100 pe verticală.
Aceste zone pătrate de latură 1 corespund ı̂n reprezentarea reală unor zone create printr-un
caroiaj creat prin toate laturile orizontale şi verticale ale clădirilor aşa cum se văd ele dinspre sud.
În aceste condiţii şi cu modificarea la scară a desenului problema revine la inscriptionarea
clădirilor ı̂n matrice luate din spate spre ı̂n faţă (pe direcţia Oy) - adică o clădire stă mai ı̂n spate
ı̂n orizontul vizual dacă are y spate ymax mai mare.
Este necesară deci şi o sortare a clădirilor ı̂n ordinea descrescătoare a valorilor Y max Y LY .
Un exemplu de normalizare: să spunem că luate de la stânga la dreapta pe Ox extremităţile
clădirilor (nu contează dacă sunt cele din stânga sau cele din sau dreapta) iau următoarele valori
x:
2 12 23 112 1002 1331 <- coordonatele reale (cel mult 200, cate doua de cladire)
| | | | | | REZULTA
V V V V V V
1 2 3 4 5 6 <- coordonatele normalizate (coordonate cu valori
intre 1 si 200)
Similar se pot normaliza şi ı̂nălţimile. Rezultând ı̂nălţimi reduse ı̂ntre 1 şi 100.
25.3 sport
Problema 3 - sport 100 de puncte
Profesorul nostru de sport este bun prieten cu profesorul de matematică. Din acest motiv
la ora de sport inventează tot felul de probleme şi apoi ı̂i cere profesorului de matematică să le
rezolve.
Azi la ora de sport participă N elevi, care poartă tricouri cu numerele 1, 2, ..., N . La ı̂nceputul
orei, cei N elevi se aşează ı̂n rând ı̂n ordinea p1 p2 ... pN (adică elevul cu tricoul p1 se
aşează pe poziţia 1 ı̂n rând, elevul cu tricoul p2 stă pe poziţia 2, etc., poziţiile ı̂n rând fiind
numerotate de la 1 la N de la stânga la dreapta). Profesorul de sport spune aşa: ”La comanda
mea schimbaţi locurile astfel: pe poziţia i să se aşeze elevul care acum stă pe poziţia ppi
(pentru fiecare 1iN )”.
De exemplu, dacă N=6 şi iniţial elevii s-au aşezat astfel: 3 1 4 2 6 5
După prima comandă: 4 3 2 1 5 6
Observaţi că pe poziţia 1 se află elevul 3 iar pe poziţia 3 se află elevul 4. După prima comandă
pe poziţia 1 va ajunge elevul pp1 p3 4. Pe poziţia 2 se află elevul 1, iar pe poziţia 1 se
află elevul 3. După prima comandă pe poziţia 2 va ajunge elevul pp2 p1 3 ... După a
doua comandă: 2 4 1 3 6 5
Observaţi că ı̂n configuraţia obţinută după prima comandă pe poziţia 1 stătea elevul 4 deci
după ı̂ncă o comandă va ajunge pe poziţia 1 elevul p4, adică elevul 2. Pe poziţia 2 stătea elevul
3, deci după ı̂ncă o comandă va ajunge pe poziţia 2 elevul p3, adică elevul 4 etc.
După a treia comandă se obţine configuraţia 1 2 3 4 5 6
Iar după a patra comandă se revine la configuraţia iniţială.
CAPITOLUL 25. ONI 2011 25.3. SPORT 311
Profesorul de sport ı̂l ı̂ntreabă pe profesorul de matematică: care este numărul minim de
comenzi pe care trebuie să le dau astfel ı̂ncât elevii să revină ı̂n configuraţia iniţială? şi care ar
fi cea mai mică configuraţie iniţială (considerând ordinea lexicografică) pentru care este necesar
acelaşi număr minim de comenzi pentru a reveni la configuraţia iniţială.
Cerinţe
Scrieţi un program care să ı̂l ajute pe profesorul de matematică să răspundă la cele două
ı̂ntrebări ale profesorului de sport.
Date de intrare
Fişierul de intrare sport.in conţine pe prima linie un număr natural N reprezentând numărul
de elevi. Pe cea de a doua linie se află N valori distincte cuprinse ı̂ntre 1 şi N reprezentând
configuraţia iniţială a elevilor.
Date de ieşire
Fişierul de ieşire sport.out va conţine două linii. Pe prima linie va fi scris un număr natural
reprezentând numărul minim de comenzi ce trebuie date astfel ı̂ncât elevii să revină la configuraţia
iniţială. Pe cea de a doua linie vor fi scrise N valori distincte cuprinse ı̂ntre 1 şi N reprezentând cea
mai mică configuraţie iniţială (considerând ordinea lexicografică) pentru care este necesar acelaşi
număr minim de comenzi pentru a reveni la configuraţia iniţială.
Exemple
sport.in sport.out Explicaţii
6 4
314265 124563
Deoarece:
cmmmc a, b a b©cmmdc a, b.
Vom alege lungimile ciclurilor ı̂n permutarea pe care o construim astfel:
a a a
n 1 1 ... 1 p1 1 p2 2 ... pmm
Odată determinate lungimile ciclurilor permutării, pentru a obţine prima permutare de grad
maxim ı̂n ordine lexicografică vom construi permutarea astfel:
1. Considerăm că lg1 & lg2 & ... & lgk
2. Pentru ca permutarea să fie minimă din punct de vedere lexicografic, ciclul 1 va conţine
elementele 1, 2, ..., lg1 , pe care le vom plasa ı̂n permutare ı̂n ordinea:
2 3... lg1 1
Dacă lg1 1, atunci p1 1.
Ciclul al doilea va conţine elementele lg1 1, ..., lg1 lg2 , pe care le plasăm ı̂n permutare ı̂n
ordinea:
lg1 2, lg1 3, ..., lg1 lg2 , lg1 1.
Dacă lg2 1, atunci p2 2.
etc.
ONI 2010
26.1 fractie
Problema 1 - fractie 100 de puncte
Gigel a ı̂nvăţat să lucreze cu fracţii zecimale neperiodice, periodice simple, respectiv periodice
mixte şi să transforme o fracţie zecimală ı̂n fracţie ordinară. El ştie că există fracţii zecimale şi
fracţii ordinare echivalente.
Gigel are de transformat o fracţie zecimală ı̂n fracţie ordinară scriind numitorul fracţiei ı̂n una
din următoarele două forme:
1. o cifră 1 care poate fi urmată sau nu de 0-uri;
2. una sau mai multe cifre de 9 urmate eventual de 0-uri.
Pot exista mai multe soluţii, din care o va alege pe cea cu număr minim de cifre la numitor.
Pentru fiecare din cele două exemple, oricare ar fi fracţia zecimală dată, Gigel - elev silitor -
va alege fracţia ordinară echivalentă ı̂ngroşată.
Cerinţe
Scrieţi un program care citeşte o fracţie zecimală strict pozitivă şi afişează numărătorul şi
numitorul unei fracţii ordinare echivalente, având numitorul ı̂n una din formele 1 sau 2 şi număr
minim de cifre.
Date de intrare
Date de ieşire
a partea ı̂ntreagă a unei fracţii zecimale este formată din cel puţin o cifră;
a şirul citit poate conţine cel mult 77 cifre şi reprezintă o fracţie zecimală corectă;
a pentru numărător corect se acordă 40% din punctajul testului, iar pentru numitor corect
60%.
313
CAPITOLUL 26. ONI 2010 26.1. FRACTIE 314
Exemple
fractie.in fractie.out Explicaţii
0,3(754754) 3751
9990 3751
0, 3 754754 0, 3 754
9990
6,230000 623
100 623
6, 230000 6, 23
100
Problema se compune din mai multe cazuri elementare, pe care le putem combina astfel:
1. Data de intrare este număr ı̂ntreg dacă nu conţine virgula zecimală, sau dacă după virgula
zecimală sunt numai 0-uri.
a ı̂n acest caz numărătorul este data de intrare, iar numitorul este 1.
2. Dacă data de intrare conţine cifre nenule după virgula zecimală, dar nu are paranteze, atunci
fracţia este neperiodică.
a se elimină 0-urile nesemnificative de la partea fractionară;
a numărătorul este data de intrare din care eliminăm virgula zecimală, iar numitorul este
format din 1 urmat de atâtea 0-uri câte cifre au fost după virgula zecimală
3. Dacă avem paranteze urmărim, dacă este cazul, reducerea părţii fracţionare:
a 0,(1212121212)=0,(12);
a 0,123(523)=0,1(235).
16
17 strcpy(p,p+1);
18
19 y=x;
20 while(*p!=’(’)y++,p++;
21
22 strcpy(p,p+1);
23
24 z=y;
25 while(*p!=’)’)z++,p++;
26 strcpy(p,p+1);
27 }
28
29 void elim(char s[])
30 {char *p,*q,*r,c;
31 int x, i;
32
33 p=strchr(s,’(’)-1;
34 q=strchr(s,’)’)-1;
35
36 while(*p==*q){c=*p;*p=*(p+1);*(p+1)=c;strcpy(q, q+1);p--;q--;}
37
38 p=strchr(s,’(’)+1;
39 q=strchr(s,’)’);
40 x=q-p;
41
42 for(i=1;i<=x/2;i++)
43 if (x%i==0)
44 {r=p+i;
45 while(!strncmp(p,r,i)) {r+=i;}
46 if(*r==’)’) {strcpy(p+i,r);i=x;}
47 }
48 }
49
50 int main()
51 {char s[81],*p,*q,*r,a[81];
52 int x,y,z,i;
53
54 f>>s;
55 if(!(p=strchr(s,’,’)))
56 {g<<s<<’\\n’<<1<<’\\n’;g.close();f.close();return 0;}
57
58 while(s[strlen(s)-1]==’0’) s[strlen(s)-1]=0;
59
60 if(!(q=strchr(s,’(’)))
61 {if(s[strlen(s)-1]==’,’)
62 {s[strlen(s)-1]=0;g<<s<<’\\n’<<1<<’\\n’;}
63 else
64 {strcpy(p,p+1);
65 r=s;while(*r==’0’)r++;
66 g<<r<<’\\n’<<1;
67 while(*p){g<<0;p++;}
68 g<<’\\n’;}
69
70 g.close();f.close();return 0;
71 }
72 else {elim(s);
73 p=strchr(s,’,’);q=strchr(s,’(’);
74 if(p+1==q\&\&s[0]==’0’)
75 {s[strlen(s)-1]=’\\0’;
76 g<<s+3<<’\\n’;
77 p+=2;
78 while(*p){g<<9;p++;}
79 g<<’\\n’;
80 }
81 else{norm(s,x,y,z);
82 strncpy(a,s,y);a[y]=0;
83 p=s+z-1;q=a+y-1;i=0;
84 while(q>a)
85 {if(*p<*q+i){*p=*p+’0’+10-*q-i;i=1;}
86 else {*p=*p+’0’-*q-i;i=0;}
87 p--;q--;
88 }
89 if(*p<*q+i){*p=*p+’0’+10-*q-i;i=1;}
90 else {*p=*p+’0’-*q-i;i=0;}
91 p--;
CAPITOLUL 26. ONI 2010 26.2. NEURONI 316
92 if(i)
93 {while(*p==’0’)*p=’9’,p--;
94 (*p)--;
95 }
96 i=0;while(s[i]==’0’)i++;
97 g<<s+i<<’\\n’;
98 z-=y;
99 for(i=0;i<z;i++)g<<9;
100 y-=x;
101 for(i=0;i<y;i++)g<<0;
102 g<<’\\n’;
103 }
104 }
105
106 f.close();
107 g.close();
108 return 0;
109 }
26.2 neuroni
Problema 2 - neuroni 100 de puncte
Cercetătorii neurologi au identificat ı̂n retina umană o zonă de neu-
roni bipolari, ce au exact două ramificaţii, aranjaţi ı̂ntr-o structură
piramidală. Structura de neuroni este dispusă pe n niveluri astfel ı̂ncât
pe un nivel k există k neuroni (k 1, 2, ..., n). S-a constatat că un neu-
ron din această structură poate transmite impulsurile nervoase numai
către cei doi neuroni, corespunzători celor două ramificaţii, aşezaţi pe
nivelul următor.
ı̂n momentul receptării primului impuls de către un neuron din reţea, acesta transmite mai
departe impulsul astfel: dacă se află pe un nivel par, către neuronul din stânga, iar dacă se află
pe un nivel impar către neuronul din dreapta, de pe nivelul următor.
Transmiterea impulsurilor ı̂ntre neuroni funcţionează alternativ. Astfel, după ce un impuls a
fost transmis către neuronul aflat pe ramificaţia din stânga, următorul impuls va fi transmis către
neuronul aflat pe ramificaţia din dreapta şi invers.
Neuronii de pe ultimul nivel al structurii, numiţi şi neuroni receptori, primesc impulsurile din
această reţea. Toate impulsurile provin de la neuronul aflat pe nivelul 1.
Cerinţe
Cunoscând numărul n de niveluri pe care sunt dispuşi neuronii şi numărul m de impulsuri ce
sunt transmise ı̂n reţea, scrieţi un program care să determine numărul de impulsuri receptate de
fiecare neuron de pe nivelul n.
Date de intrare
Prima linie a fişierului de intrare neuroni.in conţine cele două numere naturale n şi m separate
printr-un spaţiu, având semnificaţia de mai sus.
Date de ieşire
Exemple
neuroni.in neuroni.out Explicaţii
35 131 Traseul celor 5 impulsuri şi numărul de impulsuri recepţionat
pe nivelul 3 va fi următorul:
a 1 : dreapta - stânga (0,1,0)
a 2 : stânga - stânga (1,1,0)
a 3 : dreapta - dreapta (1,1,1)
a 4 : stânga - dreapta (1,2,1)
a 5: dreapta - stânga (1,3,1)
Dificultatea problemei constă mai ales ı̂n găsirea unui model matematic şi a unei structuri de
date care să permită implementarea problemei.
Astfel o primă abordare se poate face cu ajutorul unui vector care are n n 1©2 elemente
şi care ı̂n prima parte a sa (n n 1©2 elemente) să reţină sensul de transmitere a impulsurilor
(0 pentru stânga şi 1 pentru dreapta).
Ultima parte a vectorului (de la n n 1©2 1 până la n n 1©2) reţine numărul de impulsuri
ce ajung pe ultimul nivel.
Utilizarea acestui vector se face după ce ı̂n prealabil am notat nodurile din structura de neuroni
cu 1, 2, 3, ..., n n 1©2 de sus ı̂n jos şi de la stânga la dreapta.
Pentru parcurgerea structurii se va folosi şi un vector ı̂n care să reţinem nivelul pe care se
găseşte fiecare neuron.
A doua abordare se poate face cu ajutorul unei matrice cu n n linii şi coloane şi din care să
utilizăm doar zona de sub diagonala principală. În acest caz nu mai este nevoie de utilizarea unui
vector de nivele iar matricea va reţine tot 0 sau 1 conform sensului de deplasare.
Prima structura permite utilizarea mai eficientă a memoriei ı̂n timp ce a doua structură este
mai rapidă dar utilizează ineficient memoria.
25 i=1;j=1;
26 while(i<=n-1)
27 if (mat[i][j]==0) {mat[i][j]=1;i++;}
28 else {mat[i][j]=0;i++;j++;}
29
30 mat[n][j]++;
31 }
32
33 for(i=1;i<=n;i++)
34 g<<mat[n][i]<<" ";
35
36 g<<"\n";
37
38 f.close();
39 g.close();
40 return 0;
41 }
26.3 raze
Problema 3 - raze 100 de puncte
Harta digitală a câmpului de luptă este memorată ı̂ntr-un tablou bidimensional cu N linii,
M coloane şi elemente din mulţimea {0,1}. Valoarea 0 reprezintă o poziţie liberă, iar valoarea 1
reprezintă o poziţie ocupată de un obstacol. În fiecare element aflat pe conturul tabloului, adică
pe prima linie, prima coloana, ultima linie şi ultima coloană, se află obiective inamice. Pe conturul
tabloului se găsesc numai elemente nule.
În interiorul tabloului (elementele care nu se află pe contur), ı̂ntr-o poziţie liberă, trebuie plasat
un soldat. Scopul său este să anihileze cât mai multe obiective inamice. Din păcate, el deţine
o armă laser cu care poate executa doar un singur atac. La lansarea atacului, se trimit 4 raze,
câte una ı̂n fiecare dintre cele 4 direcţii diagonale. O rază poate merge până la ı̂ntâlnirea unui
obstacol (ı̂n acest caz se opreşte şi nu va avea nici un efect) sau până ajunge pe contur (ı̂n acest
caz distruge obiectivul inamic respectiv).
Cerinţe
Scrieţi un program care determină numărul maxim de obiective inamice, notat cu K, ce pot
fi distruse ı̂n urma unui atac, precum şi numărul poziţiilor ı̂n care putem plasa soldatul pentru a
distruge K obiective inamice.
Date de intrare
Fişierul text raze.in are următoarea structură:
a Pe prima linie se găseşte numărul natural T, reprezentând numărul seturilor de date de
intrare.
a Pentru fiecare set de date de intrare:
` Pe prima linie a setului se află numerele naturale N şi M, separate printr-un spaţiu,
reprezentând numărul liniilor, respectiv numărul coloanelor tabloului;
` Pe următoarele N linii ale setului de date se află câte M numere naturale din mulţimea
0,1, separate prin câte un spaţiu, reprezentând forma digitală a hărţii câmpului de
luptă.
Date de ieşire
Fişierul text raze.out va conţine T linii, corespunzătoare celor T seturi de date de intrare. Pe
fiecare linie se vor tipări două numere naturale K şi P , separate printr-un spaţiu, reprezentând
numărul maxim de obiective inamice distruse ı̂n atac, respectiv numărul poziţiilor din care se pot
distruge K obiective inamice.
Restricţii şi precizări
a 1 & T & 80
a 3 & N, M & 135
a Se garantează că există cel puţin un obiectiv inamic ce poate fi anihilat pentru fiecare set de
date de intrare.
Exemple
raze.in raze.out Explicaţii
2 41 În fişier se găsesc 2 seturi de date de intrare.
46 32 În primul set de date se pot anihila maximum 4 obiective inamice,
00 0000 poziţionı̂nd soldatul ı̂n linia 2 şi coloana 2.
0 01110 În al doilea set de date se pot anihila maximum 3 obiective inam-
00 0000 ice, poziţionând soldatul ı̂n elementul de pe linia 3 şi coloana 2
000 000 sau ı̂n elementul din linia 3 şi coloana 6.
47
0000000
011111
0
0 0000 0
0
00 00 00
0
CAPITOLUL 26. ONI 2010 26.3. RAZE 320
34 for (d=0;d<=3;d++)
35 for (ii = i, jj = j; ii>=0 && ii<=N && jj>=0 &&
36 jj<=M && A[ii][jj] == 0; ii+=DI[d],jj+=DJ[d])
37 B[ii][jj]++;
38 i = N;
39 for (d=0;d<=3;d++)
40 for (ii = i, jj = j; ii>=0 && ii<=N && jj>=0 &&
41 jj<=M && A[ii][jj] == 0; ii+=DI[d],jj+=DJ[d])
42 B[ii][jj]++;
43 }
44
45 for (i=2;i<N;i++)
46 { //coloanele
47 j = 1;
48 for (d=0;d<=3;d++)
49 for (ii = i, jj = j; ii>=0 && ii<=N && jj>=0 &&
50 jj<=M && A[ii][jj] == 0; ii+=DI[d],jj+=DJ[d])
51 B[ii][jj]++;
52 j = M;
53 for (d=0;d<=3;d++)
54 for (ii = i, jj = j; ii>=0 && ii<=N && jj>=0 &&
55 jj<=M && A[ii][jj] == 0; ii+=DI[d],jj+=DJ[d])
56 B[ii][jj]++;
57 }
58
59 max = 0;
60 nMax = 0;
61 for (i=2;i<N;i++)
62 for (j=2;j<M;j++)
63 if (B[i][j] > max)
64 {
65 max = B[i][j];
66 nMax = 1;
67 }
68 else
69 if (B[i][j] == max)
70 nMax++;
71
72 fprintf(g,"%d %u\n",max,nMax);
73 }
74
75 fclose(g);
76 fclose(f);
77 return 0;
78 }
28 {
29 fprintf(g,"%u %u\n",4,(M-2)*(N-2));
30 continue;
31 }
32
33 max = 0;
34 for (i=2;i<N;i++)
35 for (j=2;j<M;j++)
36 {
37 k = 0;
38 if (A[i][j] == 0)
39 {
40 for (ii = i+1, jj = j+1;
41 A[ii][jj] == 0 && ii!=N && jj!=M;
42 ii++,jj++);
43
44 if (A[ii][jj] == 0)
45 k++;
46
47 for (ii = i-1, jj = j-1;
48 A[ii][jj] == 0 && ii!=0 && jj!=0;
49 ii--,jj--);
50
51 if (A[ii][jj] == 0)
52 k++;
53
54 for (ii = i-1, jj = j+1;
55 A[ii][jj] == 0 && ii!=0 && jj!=M;
56 ii--,jj++);
57
58 if (A[ii][jj] == 0)
59 k++;
60
61 for (ii = i+1, jj = j-1;
62 A[ii][jj] == 0 && ii!=N && jj!=0;
63 ii++,jj--);
64
65 if (A[ii][jj] == 0)
66 k++;
67 }
68 /*
69 for (d = 0; d<=3; d++) {
70 for (ii = i, jj = j;
71 ii!=0 && ii!=N && jj!=0 && jj!=M && A[ii][jj] == 0;
72 ii+=DI[d],jj+=DJ[d]);
73 if (A[ii][jj] == 0)
74 k++;
75 }
76 */
77 if (k>max)
78 {
79 max = k;
80 nMax = 1;
81 }
82 else
83 if (k==max)
84 nMax++;
85 }
86
87 fprintf(g,"%d %u\n",max,nMax);
88 }
89
90 fclose(f);
91 fclose(g);
92 return 0;
93 }
”Instalare” C++
Instalarea este foarte uşoară (este de tipul ”next -> next -> ... -> next”) iar pe in-
ternet sunt multe site-uri de ajutor. De exemplu:
https://www.pbinfo.ro/?pagina=intrebari-afisare&id=26
https://www.youtube.com/watch?v=CLkWRvAwLO8
https://infoas.ro/lectie/112/tutorial-instalare-codeblocks-usor-introducere-in-in
https://www.competentedigitale.ro/c/oji2019/Kit_OJI_2017.rar
324
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 325
A.1.1 Code::Blocks
Pentru versiuni mai noi, de Code::Blocks şi compilatoare, se poate accesa site-ul
http://www.codeblocks.org/downloads/binaries
de unde se poate descărca, de exemplu, codeblocks-20.03mingw-setup.exe.
Versiuni mai vechi, dar foarte bune, se pot descărca de la adresa
26
Code::Blocks este un IDE (integrated development environment) pentru C/C++, un fel de Notepad ... mai
sofisticat, cu multe butoane care lansează ı̂n execuţie diverse programe, de exeplu g++.exe
27
g++.exe este compilatorul de C++, programul care va verifica dacă instrucţiunile noastre sunt ok sau nu ...
şi care ne va supăra mereu cu erorile pe care ni le arată ... ... Este “o mică artă” să ı̂nţelegem mesajele de
eroare pe care le vedem pe ecran şi să fim ı̂n stare să “depanăm” programele noastre!
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 326
http://www.codeblocks.org/downloads/source/5
Mai precis:
https://sourceforge.net/projects/codeblocks/files/Binaries/20.03/
http://sourceforge.net/projects/codeblocks/files/Binaries/17.12
http://sourceforge.net/projects/codeblocks/files/Binaries/16.01
De preferat este să avem cel puţin două partiţii: C:¯, D:¯, ... şi să “lăsăm ı̂n pace” partiţia C:¯
pentru sistemul de operare şi programele instalate!
În figura de mai sus se poate observa că pe D:¯ există un folder de lucru pentru programele ı̂n
C++, folder care se numeşte Programe C++. Aici vom salva toate programele C++ pe care
le vom scrie, eventual organizate pe mai multe subfoldere.
Click-dreapta cu mouse-ul şi selectăm “New -¿ Text document” ca ı̂n figura următoare.
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 327
Dacă vom executa două click-uri pe numele fşierului p01.cpp sau un click pentru a marca
fişierul şi apoi un ¡Enter¿, se va declanşa Code::Blocks cu p01.cpp ı̂n fereastra de editare şi, ce
este şi mai important, cu Programe C++ ca folder curent de lucru pentru Code::Blocks.
Adică, aici vor apărea toate fişiere generate de Code::Blocks pentru p01.cpp.
Figura A.9: Pregătit pentru a scrie cod de program C++ ı̂n Code::Blocks
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 329
Dacă avem fişierele p01.cpp, ..., p05.cpp, ı̂n folderul nostru de lucru, şi facem dublu-click pe
fiecare ... vor apărea toate ...
A.2 winlibs
A.2.1 GCC şi MinGW-w64 pentru Windows
Se descarcă de la
http://winlibs.com/#download-release
unul dintre fişierele:
winlibs-x86_64-posix-seh-gcc-10.2.0-llvm-11.0.0-mingw-w64-8.0.0-r
3.7z dimensiune fişier = 148 MB
winlibs-x86_64-posix-seh-gcc-10.2.0-llvm-11.0.0-mingw-w64-8.0.0-r
3.zip dimensiune fişier = 324 MB
winlibs-x86_64-posix-seh-gcc-10.2.0-mingw-w64-8.0.0-r3.7z dimensiune
fişier = 52.1 MB
winlibs-x86_64-posix-seh-gcc-10.2.0-mingw-w64-8.0.0-r3.zip dimensi-
une fişier = 141 MB
A.2.2 PATH
Trebuie pusă ı̂n PATH calea pentru D:\mingw64\bin\. În “Type here to search” scrieţi: path
şi apoi click pe “Edit the system environment variables”, ca ı̂n figura următoare:
APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 335
Se selecteaza “Path” şi click pe “Edit”. Apare fereastra “Edit Environment Variables”
C:¯path
C:¯gcc –version (Atentie! sunt 2 caractere - consecutive)
APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 337
Dacă totul este OK atunci se trece la instalarea IDE-ului preferat (Integrated Development
28
Environment ), de exemplu Code::Blocks 20.03 (sau Eclipse, Visual Studio Code, Dev C++,
NetBeans, şi altele).
A.2.3 CodeBlocks
CodeBlocks se poate descărca de la http://www.codeblocks.org/downloads/26. Sunt
mai multe variante dar eu am descărcat numai codeblocks-20.03-setup.exe care are 35.7 MB.
La instalare am lăsat totul implicit, deci ... Next, Next, ..., Next până la sfârşit!
La prima lansare ı̂n execuţie este necesară setarea locaţiei compilatorului de C++ (gcc-ul pe
care tocmai l-am instalat!).
28
https://en.wikipedia.org/wiki/Integrated_development_environment
https://ro.wikipedia.org/wiki/Mediu_de_dezvoltare
APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 338
Exponenţiere rapidă
În figura B.1 este considerat numărul nr 234 care ı̂n baza 2 se scrie sub forma 11101010 (celula
C10).
Pe R3 (rândul 3) este arătat ce se ı̂ntâmplă cu nr atunci când se fac ı̂mpărţiri succesive prin
baza 10: se ”şterge” ulima cifră (care este egală cu nr%10 adică ultima cifră este de fapt restul
ı̂mpărţirii lui nr la baza 10).
Pe R10 (rândul 10) este arătat ce se ı̂ntâmplă cu nr atunci când se fac ı̂mpărţiri succesive prin
baza 2: se ”şterge” ulima cifră (care este egală cu nr%2 adică ultima cifră este de fapt restul
ı̂mpărţirii lui nr la baza 2, la fel cum se ı̂ntâmplă ı̂n baza 10).
Observaţia 1. Cifrele se obţin ı̂n ordine inversă. Prima cifră obţinută ca rest este de fapt ultima
cifră din număr (vezi R4 şi R11).
Pe R6 şi R16 sunt precizate puterile bazei.
Valoarea numărului nr este suma produselor dintre cifre şi puterile corespunzătoare:
ı̂n baza 10: 4*1 + 3*10 + 2*100 = 234 (R3 şi R6)
ı̂n baza 2: 0*1 + 1*2 + 0*4 + 1*8 + 0*16 + 1*32 + 1*64 + 1*128 = 234 (R13 şi R16)
347
APPENDIX B. EXPONENŢIERE RAPIDĂ B.2. NOTAŢII, RELAŢII ŞI FORMULE 348
2 2 2 4 2 8 2 16
a0 a a1 a0 a a2 a1 a a3 a2 a a4 a3 a
2 32 2 64 2 128
a5 a4 a a6 a5 a a7 a6 a
Dacă notăm ek ”exponentul (puterea)” la care apare ak ı̂n produs, atunci putem observa
uşor că şirul e0 , e1 , e2 , e3 , ... se obţine prin ı̂mpărţiri succesive ale lui n prin 2 şi preluı̂nd restul
rezultatului. Pentru a folmaliza acest lucru vom considera şirul n0 , n1 , n2 , n3 , ... definit astfel:
n0 n,
w
nk nk1 ©2 (câtul ı̂mpărţirii!) k '1
Folosind aceste notaţii, putem scrie:
~
n0 n;
a0 a;
e n0 %2 " r0, 1x;
0
1 a0 , dacă e0 1;
p0
e
a00 sau, altfel scris: p0 w
1, dacă e0 0;
(B.2.1)
nk nk1 ©2 k ' 1; nk1 j 0 desigur ! ... dar şi nk1 j 1
ak1 , k ' 1;
2
ak
ek nk %2 " r0, 1x, k ' 0;
pk1 ak , k ' 0, dacă ek 1; ak ak modifică produsul pk1
1
p w
k k ' 0, dacă ek 0; ak 1 nu modifică produsul pk1
0
pk1 ,
B.4 Codul
Codul pentru relaţiile (B.2.1) devine:
Observaţia 2. În acest cod actualizarea lui p se face după actualizările pentru a şi n.
Observaţia 3. În codul următor actualizarea lui p se face ı̂naintea actualizărilor pentru a şi n,
corespunzător relaţiilor (B.4.2).
APPENDIX B. EXPONENŢIERE RAPIDĂ B.4. CODUL 350
~
iniţializări:
p1 1;
n1 n;
a1 a;
calcul pentru k ' 0:
e
k nk1 %2 " r0, 1x; k ' 0 (B.4.2)
pk1 ak1 , k ' 0, dacă ek 1; ak1
1
ak1 modifică produsul pk1
k w
p
k ' 0, dacă ek 0; ak1
0
pk1 , 1 nu modifică produsul pk1
actualizări k ' 0:
a ak1 , k ' 0;
2
k
nk nk1 ©2 k ' 0; şi nk1 j 0 desigur !
APPENDIX B. EXPONENŢIERE RAPIDĂ B.4. CODUL 351
Observaţia 4. Instrucţiunile care sunt ”ı̂n plus” se pot elimina. Codul următor arată acest lucru:
Observaţia 5. Produsul poate deveni foarte mare şi din cauza asta se cere rezultatul modulo un
număr prim. Codul următor arată acest lucru:
14 a = (a * a)%MOD;
15 n = n / 2;
16 nropr=nropr+6; // n%2, p*a, a*a, (a * a)%MOD si n/2
17 }
18 return p;
19 }
20
21 int main()
22 {
23 int a=1234;
24 int n=1e+9; // 10ˆ9
25 int rezn; // rezultat exponentiere naiva
26
27 rezn=exponentiere_rapida(a,n);
28
29 cout<<"rezr = "<<rezn<<" nropr = "<<nropr<<"\n";
30
31 return 0;
32 }
33 /*
34 rezr = 376 nropr = 180
35
36 Process returned 0 (0x0) execution time : 0.021 s
37 Press any key to continue.
38 */
Numărul de operaţii:
cu metoda naivă acest număr este 2 000 000 000
cu metoda rapidă este 180.
Iar ca număr de operaţii ... una este să faci 2 miliarde de operaţii şi alta este să faci
numai 180 de operaţii de acelaşi tip (de fapt sunt numai 30 de paşi dar la fiecare pas se fac 5
sau 6 operaţii aritmetice)!
29
şi nu mai trebuie ”atâtea formule matematice”!
29
Este o glumă!
Index
355
INDEX INDEX 356
[1] Aho, A., Hopcroft, J., Ullman, J.D.; Data strutures and algorithms, Addison Wesley, 1983
[2] Andreica M.I.; Elemente de algoritmică - probleme şi soluţii, Cibernetica MC, 2011
[3] Andonie R., Gârbacea I.; Algoritmi fundamentali, o perspectivă C++, Ed. Libris, 1995
[4] Atanasiu, A.; Concursuri de informatică. Editura Petrion, 1995
[5] Bell D., Perr M.; Java for Students, Second Edition, Prentice Hall, 1999
[21] Knuth, D.E.; The art of computer programming, vol. 4A: Combinatorial algorithms, Part 1,
Addison Wesley, 2011.
[22] Lambert,K. A., Osborne,M.; Java. A Framework for Programming and Problem Solving,
PWS Publishing, 1999
[23] Laaksonen A.; Guide to competitive programming, Springer, 2017
[24] Livovschi, L.; Georgescu H.; Analiza şi sinteza algoritmilor. Ed. Enciclopedică, Bucureşti,
1986.
[25] Niemeyer, P., Peck J.; Exploring Java, O’Reilly, 1997.
357
BIBLIOGRAFIE BIBLIOGRAFIE 358
[26] Odăgescu, I., Smeureanu, I., Ştefănescu, I.; Programarea avansată a calculatoarelor personale,
Ed. Militară, Bucureşti 1993
[27] Odăgescu, I.; Metode şi tehnici de programare, Ed. Computer Lobris Agora, Cluj, 1998
[28] Popescu Anastasiu, D.; Puncte de articulaţie şi punţi ı̂n grafuri, Gazeta de Informatică nr.
5/1993
[29] Răbâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire
/2007/Info/Lista_probleme_2000-2007.pdf
[30] Răbâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire
/2007/Info/Rezolvari_C09.pdf
360
What’s the next?
ORNL’s Frontier First to Break the Exaflop Ceiling
Over time
the following steps
will lead you to the value
you seek for yourself
now!