Sunteți pe pagina 1din 315

PROBLEME DE INFORMATIC€

date la olimpiade

în

2020
2019 2018 2017 2016 2015
2014 2013 2012 2011 2010

la clasa a 8-a

... draft (ciorn ) ...


*** Nobody is perfect ***

Ph.D. Adrian R bâea

9 decembrie 2020
Dedicaµie

I would like to dedicate this book ...

1
to myself ...
in a time when ...
I will not be able ...
2
to be.

That is because ...

3
When I Die Nobody Will Remember Me

1
I Dedicate This Book to Myself  By Carol Lynne
2
To be, or not to be ..., Hamlet, Act III, Scene I, William Shakespeare, 1564-1616
3
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 ecare 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!.
4
Sunt convins c  este important s  studiem cu atenµie cât mai multe probleme rezolvate! Cred
cred c  sunt utile ³i primele versiuni în care sunt prezentate chiar ³i numai enunµurile ³i indicaµiile
"ociale" de rezolvare. Acestea se g sesc în multe locuri; aici încerc s  le pun pe toate la un loc !

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
5 6
dicil ! :-) 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
7
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  posibil ...
a³a c , de aceast  dat , anii sunt în ordine ... descresc toare! :-)

Codurile surs  sunt cele ociale (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
disponibil ³i, oricum, calculatoarele folosite la olimpiade înainte de 2010 erau ceva mai 'slabe' ³i
8
... restricµiile de memorie, din enunµurile problemelor, par 'ciudate' acum!). “i indc  a venit
vorba despre calculatoare mai  slabe sau mai  puternice: laptopul meu¯t u este puµin mai  slab
decât cel mai puternic calculator din lume în 1985 dar ³i ... un pic mai  puternic decât cel mai

9
puternic calculator din lume în 1983. (armaµia este valabil  acum, în 2020).

4
Se poate observa din Coduri surs  c  orice problem  are numeroase soluµii, atât ca algoritmi de rezolvare
cât ³i ca stil de programare! Studiind aceste coduri ... avem ce înv µa ... de³i uneori pare c  'se trage cu tunul' ...
5
IOI2019 ³i IOI2020 au a permis utilizarea limbajelor de programare C++ ³i Java
6
IOI2015 a permis utilizarea limbajelor de programare C++, Java, Pascal, Python ³i Rubi (...)
7
8
Vezi cele 5 secunde pentru Timp maxim de executare/test din problema avârcolaci - ONI2014 clasa a 11-a
Când eram eu elev/student un calculator obi³nuit executa în jur de 1.000.000 de operaµii pe secund , acum
execut  1.000.000.000 de operaµii pe secund , iar mai târziu ... cine stie ce va mai ?!
9
https://en.wikipedia.org/wiki/List_of_fastest_computers

iii
În perioada 2017-2020 cele mai puternice calculatoare din lume au fost: în noiembrie 2017 în
China, în noiembrie 2019 în SUA ³i ... în iunie 2020 în Japonia (Fugaku: 415 petaops, adic 
15 10
415 ˜ 10 operaµii pe secund , adic  ... 415 milioane de ... miliarde de ... operaµii pe secund ).

O mic  observaµie: în 2017 a fost prima ediµie a olimpiadei EJOI11 în Bulgaria ³i ... tot în
12
Bulgaria a fost ³i prima ediµie a olimpiadei IOI în 1989.
Dar ... prima ediµie a olimpiadei IMO (International Mathematical Olympiad) a fost în
13
România în 1959. Tot în România s-au µinut ediµiile din anii 1960, 1969, 1978, 1999 ³i 2018.

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!). Totu³i, vor  prezentate ³i Rezolv ri
detaliate ale problemelor de liceu (pe care le-am considerat în mod subiectiv!) utile.

Î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) nu au nevoie de ajutorul meu! Se descurc  singuri! La
Indicaµii de rezolvare voi prezenta numai ... numele algoritmilor clasici folosiµi în rezolvare.

ALGORITMI utili la olimpiadele de informatic  , separat pentru gimnaziu ³i liceu, sper s 


e de folos, a³a cum cred c  sunt [1] - [28], [34] - [47], [57] - [82], ... ³i multe alte c rµi ³i site-uri!.

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).
Nu sunt necesare preciz ri suplimentare!

“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 ... !!!

"I'm only responsible for what I say,


14
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:

http://adrianrabaea.scienceontheweb.net/
https://www.scribd.com/user/243528817/Adrian-Rabaea
https://drive.google.com/drive/folders/1hC5PZuslCdS95sl37SW46H-qy59GRDGZ

Adrese utile (programe ³colare):

http://www.ise.ro/wp-content/uploads/2017/01/Informatica-si-TIC.pdf
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

Bistriµa, Ph.D. Adrian R bâea


9 decembrie 2020

10
https://www.top500.org/lists/top500/
11
https://ejoi.org/about/
12
https://stats.ioinformatics.org/olympiads/
13
https://en.wikipedia.org/wiki/International_Mathematical_Olympiad
14
https://www.etsy.com/listing/604809336/john-wayne-quotes-i-am-only-responsible
"Acknowledgements"
15
"I want to thank God most of all because without God I wouldn't be able to do any of this."

Bistriµa, 9 decembrie 2020

Adrian R.

15
I.d.k.: "I don't know who the author is."

v
Despre autor16
nume: R bâea Aurel-Adrian, 18.03.1953 - ...
adresa: Str. Valea Ghinzii nr. 21 B, Bistriµa, România
telefon: +40 728 18 03 53 +40 363 10 25 10
email: adrian1803@gmail.com

Lector universitar - Universitatea Tehnic  din Cluj Napoca - Centrul


Universitar Nord din Baia Mare, Facultatea de “tiinµe, Str. Victoriei,
nr. 76, Baia Mare, România, (pensionat: 01.10.2018)
http://www.stiinte.utcluj.ro/
Discipline predate (1992-2018):
Algoritmi ³i structuri de date, Algoritmi în teoria opµiunilor nanciare, Bazele matematice
ale calculatoarelor, Bazele tehnologiei informaµiei, Birotic , Capitole speciale de inteligenµ 
articial , Capitole speciale de teoria algoritmilor, Calcul paralel, Informatic  economic ,
Instruire asistat  de calculator, Limbaje de programare; Programare orientat  pe obiecte,
Programare procedural , Structuri de date,

Studii doctorale în informatic  economic  - Diplom  de doctor (1997-2002):


Institutµia: Academia de Studii Economice, Bucure³ti;
17
Titlul tezei: Algoritmi paraleli ³i aplicaµii pe ma³ini virtual paralele
18
Conduc tor ³tiinµic: Prof. dr. ing. Gheorghe Dodescu
Teme studiate: utilizarea algoritmilor paraleli în teoria opµiunilor nanciare

Studii de specializare în informatic  - Certicat anul V - 'master' (1978-1979):


Instituµia: Facultatea de matematic  ³i informatic , Bucure³ti;
Titlul tezei: Probleme la limit  pentru procese cu cre³teri independente ³i aplicaµii în teoria
a³tept rii
19
Conduc tor ³tiinµic: Prof. dr. Constantin Tudor

Studii universitare de licenµ  în informatic  - Diplom  de licenµ  (1974-1978):


Instituµia: Facultatea de matematic  ³i informatic , Bucure³ti;
Titlul tezei: Metode de comparaµie multipl  în analiza dispersional 
20
Conduc tor ³tiinµic: Prof. dr. Ion V duva

Locuri de munc : (1979-2018):


- (2018-2009) Universitatea Tehnic  din Cluj-Napoca, Centrul Universitar Nord din Baia-
Mare, Facultatea de “tiinµe, Departamentul de Matematic -Informatic 
- (2009-1992) Universitatea "Ovidius" din Constanµa, Facultatea de Matematic  ³i Informa-
tic , Departamentul de Informatic 
- (1992-1979) Centrul de Informatic  ³i organizare CINOR, Bucure³ti

https://scholar.google.com/citations?user=-sSE_1wAAAAJ&hl=en
https://www.scopus.com/authid/detail.uri?origin=resultslist&authorId=56122389200&zone=
http://www.facebook.com/adrian.rabaea

16
https://stiinte.utcluj.ro/files/cv/CV%20Rabaea_Adrian.pdf
17
http://opac.biblioteca.ase.ro/opac/bibliographic_view/149021
18
http://www.ionivan.ro/2015-PERSONALITATI/Dodescu.htm
19
https://sites.google.com/site/ciprianatudor/Home/professor-constantin-tudor
20
https://ro.wikipedia.org/wiki/Ion_V%C4%83duva

vi
Cuprins

Prefaµ  iii
Cuprins vii
Lista gurilor xiii
Lista tabelelor xiv
Lista programelor xv

I OJI - Olimpiada judeµean  de informatic  1


1 OJI 2020 2
1.1 datorii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2 triunghi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2 OJI 2019 14
2.1 cate3cifre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2 paralele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

3 OJI 2018 23
3.1 Cruce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.2 pal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

4 OJI 2017 32
4.1 tablou . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.2 triunghiuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

vii
4.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

5 OJI 2016 40
5.1 arma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.2 ks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

6 OJI 2015 51
6.1 dominant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
6.2 pavare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
6.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
6.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
6.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

7 OJI 2014 66
7.1 arrows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
7.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
7.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
7.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
7.2 tcif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
7.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
7.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
7.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

8 OJI 2013 79
8.1 maxp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
8.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
8.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
8.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
8.2 puncte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
8.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
8.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
8.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

9 OJI 2012 96
9.1 deal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
9.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
9.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
9.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
9.2 ozn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
9.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
9.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
9.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

10 OJI 2011 107


10.1 adunscad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
10.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
10.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
10.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
10.2 comp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
10.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
10.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
10.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

11 OJI 2010 133


11.1 cladiri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
11.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
11.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
11.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
11.2 secvente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
11.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
11.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
11.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141

II ONI - Olimpiada naµional  de informatic  142


12 ONI 2020 143
12.1 *** . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
12.1.1 *Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
12.1.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
12.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
12.2 *** . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
12.2.1 *Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
12.2.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
12.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
12.3 *** . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
12.3.1 *Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
12.3.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
12.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145

13 ONI 2019 146


13.1 criptograe - ONI 2019 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
13.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
13.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
13.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
13.2 drept - ONI 2019 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
13.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
13.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
13.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
13.3 lumini - ONI 2019 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
13.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
13.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
13.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

14 ONI 2018 157


14.1 gene . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
14.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
14.1.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
14.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
14.2 jocxzero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
14.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
14.2.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
14.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
14.3 laser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
14.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
14.3.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
14.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
15 ONI 2017 163
15.1 boats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
15.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
15.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
15.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
15.2 doilan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
15.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
15.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
15.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
15.3 z . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
15.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
15.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
15.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175

16 ONI 2016 176


16.1 cercetasi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
16.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
16.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
16.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
16.2 farma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
16.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
16.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
16.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
16.3 stele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
16.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
16.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
16.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193

17 ONI 2015 194


17.1 magic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
17.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
17.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
17.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
17.2 restaurare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
17.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
17.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
17.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
17.3 sort2dist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
17.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
17.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
17.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217

18 ONI 2014 218


18.1 cifre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
18.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
18.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
18.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
18.2 solitar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
18.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
18.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
18.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
18.3 tdrept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
18.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
18.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
18.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
19 ONI 2013 237
19.1 amestec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
19.1.1 *Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
19.1.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
19.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
19.2 eoliene . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
19.2.1 *Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
19.2.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
19.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
19.3 sudoku1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
19.3.1 *Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
19.3.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
19.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241

20 ONI 2012 242


20.1 alune . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
20.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
20.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
20.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
20.2 cuburi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
20.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
20.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
20.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
20.3 optim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
20.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
20.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
20.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271

21 ONI 2011 272


21.1 butoane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
21.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
21.1.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
21.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
21.2 macheta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
21.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
21.2.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
21.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
21.3 sport . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
21.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
21.3.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
21.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278

22 ONI 2010 279


22.1 fractie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
22.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
22.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
22.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
22.2 neuroni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
22.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
22.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
22.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
22.3 raze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
22.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
22.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
22.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288

Anexa A Un pic de matematic ! 290


A.1 ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
A.1.1 Prezentare general  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
A.1.2 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
Anexa B Un pic de programare! 291
B.1 ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
B.1.1 Prezentare general  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
B.1.2 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291

Glosar 292
Bibliograe 294
Lista autorilor 297
Lista gurilor
4.1 triunghiuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

7.1 arrows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

8.1 Puncte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
8.2 Puncte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

9.1 Puncte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

11.1 cladiri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

13.1 Drept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

xiii
Lista tabelelor
2.1 cate3cifre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

3.2 pal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

17.1 sort2dist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206

xiv
Lista programelor
1.1.1 datorii_ab.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.2 datorii_ec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.1 triunghi_ab.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2.2 triunghi_ec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.1.1 cate3cifre.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2.1 paralele1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.2.2 paralele2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2.3 paralele3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.1.1 cruce_dl.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.1.2 cruce_RC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.1.3 cruceEm.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.2.1 pal_dl.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.2.2 pal_RC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.1.1 tabCS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.1.2 tablou_CM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.2.1 tri_On.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.2.2 triCS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.1.1 arma.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.1.2 arma_ema.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
5.1.3 arma_ema_no_rec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
5.2.1 ks_dan.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.2.2 ks_ema_n.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
6.1.1 domDP_citire_elem_cu_elem.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.1.2 domDPliniar.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.1.3 domMN_n.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
6.1.4 domMN_n_ccc.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
6.1.5 domMN_nlogn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
6.2.1 pavareAB.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
6.2.2 pavareBackMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
6.2.3 pavareCT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
6.2.4 pavaredan.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
6.2.5 pavareDP.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
6.2.6 pavareMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
7.1.1 arrows_100_ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
7.1.2 arrows_100_soa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
7.2.1 tcif_100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
7.2.2 tcif_ema100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
7.2.3 Tcif_GCC_100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
8.1.1 maxpCS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
8.1.2 maxpDP_cstdio.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
8.1.3 maxpDP_fstream.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
8.1.4 maxpPRI.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
8.1.5 maxpPRV.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
8.1.6 maxpSC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
8.2.1 puncteAI.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
8.2.2 puncteCS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
8.2.3 puncteDP.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
8.2.4 punctePRI.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
8.2.5 puncteSC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

xv
9.1.1 deal_100_ch.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
9.1.2 deal_100_ct.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
9.1.3 DEAL_100_em.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
9.2.1 ozn_stream100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
9.2.2 ozn100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
9.2.3 oznarbint100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
9.2.4 oznEvenimente100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
10.1.1 ADUNSCAD.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
10.1.2 adrian1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
10.1.3 adrian2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
10.1.4 adrian3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
10.1.5 adrian4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
10.1.6 ADDSUBBK.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
10.1.7 ADUNSCADM.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
10.1.8 ADSC_nodea.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
10.1.9 ADUNSC_A.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
10.1.10 dana_ad.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
10.1.11 vi_adunscad.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
10.2.1 comp.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
10.2.2 alin.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
10.2.3 comp_r.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
10.2.4 em.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
10.2.5 nodea.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
10.2.6 p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
10.2.7 vi_comp.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
11.1.1 sol2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
11.1.2 sol3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
11.2.1 BERATO.PAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
11.2.2 BPREPRO.PAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
13.1.1 criptograe.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
13.2.1 drept.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
13.3.1 lumini.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
15.1.1 boatsAB.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
15.1.2 boatsCM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
15.1.3 boatsLC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
15.2.1 doilan.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
15.3.1 z.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
16.1.1 cercetasi_ema.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
16.1.2 cercetasi2_RBalasa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
16.2.1 farma_ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
16.2.2 farma_raluca.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
16.2.3 lucia_farma.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
16.3.1 stele_ema_100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
16.3.2 stele_raluca.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
17.1.1 magicN2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
17.1.2 sursaGenerarePas1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
17.1.3 sursaGenerarePasC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
17.1.4 sursaNumarareBKT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
17.1.5 sursaPutereLogN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
17.1.6 sursaPutereN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
17.2.1 restaurare.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
17.2.2 restaurare_stdio.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
17.2.3 restaurareBrut1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
17.2.4 restaurareLT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
17.3.1 2dist_alin.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
17.3.2 sort2_distBrute1FNM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
17.3.3 sort2_distBSFBM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
17.3.4 sort2dist.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
17.3.5 sort2dist1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
17.3.6 sort2distBS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
17.3.7 sort2distFNM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
17.3.8 sort2distFNMDouble.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
17.3.9 sort2distFNMLongDouble.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
17.3.10 sort2gv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
18.1.1 cifre_carmen_ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
18.1.2 cifre_ema.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
18.1.3 cifre_GCC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
18.1.4 cifre_soa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
18.2.1 solitar_cp.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
18.2.2 solitar_ema.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
18.3.1 tdrept_ema_n2logn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
18.3.2 tdrept_ema_ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
18.3.3 tdrept_eman3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
18.3.4 tdrept_emanlogn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
18.3.5 tdrept_n_soa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
18.3.6 tdrept_n2logn_soa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
20.1.1 alune.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
20.1.2 alune_DT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
20.1.3 aluneBrute.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
20.1.4 aluneBrute1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
20.1.5 aluneBrute2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
20.1.6 aluneBruteParse.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
20.1.7 alunedanal.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
20.1.8 aluneS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
20.2.1 cubmin.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
20.2.2 cubminC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
20.2.3 cuburiBackMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
20.2.4 cuburiDL.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
20.2.5 cuburiDL20.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
20.2.6 cuburi-iv4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
20.2.7 cuburiStackMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
20.3.1 optim_back100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
20.3.2 optim_dinamica100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
20.3.3 optim0MN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
20.3.4 optim1MN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
20.3.5 optim2MN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
20.3.6 optim3MN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
20.3.7 optimDT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
20.3.8 optim-iv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
22.1.1 FLORE7.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
22.2.1 neuroni1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
22.2.2 neuroni2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
22.3.1 RAZE2.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
22.3.2 RAZE3.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
Partea I

OJI - Olimpiada judeµean  de


informatic 

1
Capitolul 1

OJI 2020

1.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 nanciare. De exemplu, o rm  X poate avea datorii c tre o rm  Y pe care
nu le poate pl ti, deoarece o alt  rm  Z are datorii c tre rma X pe care nu le-a pl tit, ³.a.m.d.
Exist  o list  cu toate datoriile rmelor sub forma urm toare:

X %YS
cu semnicaµia rma X datoreaz  rmei Y suma S .
Este posibil ca X s  aib  mai multe datorii la rma 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 rmelor, scrieµi un program care s  rezolve urm toarele cerinµe:

1. determin  num rul de rme distincte care apar în aceast  list ;

2. realizeaz  o situaµie nanciar  a rmelor distincte din aceast  list , scrise în ordine lexico-
grac ; pentru ecare rm  se vor determina dou  valori SD SP, unde SD reprezint  suma
total  a datoriilor pe care r-ma le are c tre alte rme, iar SP este totalul sumelor pe care
rma trebuie s  le primeasc  de la alte rme.

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  e rezolvat  (1 sau 2). Pe a doua linie se a  un num r natural D care reprezint 
num rul de înregistr ri existente în lista datoriilor rmelor. Pe urm toarele D linii sunt descrise
datoriile rmelor, în forma specicat  în enunµ, câte o datorie pe o linie.

Date de ie³ire
Fi³ierul de ie³iredatorii.out va conµine r spunsul la cerinµa C specicat  în ³ierul de intrare.
Dac  C 1 ³ierul va conµine un num r natural, reprezentând num rul de rme distincte care
apar în lista menµionat .
Dac  C 2 ³ierul va conµine pentru ecare dintre rmele distincte din lista menµionat  câte
un singur triplet de forma X SD SP , unde X este numele rmei, iar SD ³i SP au semnicaµia
din enunµ pentru rma X; tripletele vor  scrise astfel încât numele rmelor s  apar  în ordine
lexicograc , ecare triplet pe câte o linie a ³ierului, iar X , SD ³i SP vor  separate prin câte
un singur spaµiu.

Restricµii ³i preciz ri
a Exist  în total cel mult 6 000 de rme distincte în lista menµionat  de datorii.

2
CAPITOLUL 1. OJI 2020 3

a Numele unei rme 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
rmelor; nu exist  alte restricµii referitoare la numele rmelor.

a Dou  rme distincte au nume distincte. O rm  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 rme sunt numere naturale nenule & 106 .
a Dac  X ³i Y sunt numele a dou  rme 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 lexicograc
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 rmelor nu conµin spaµii. 10 puncte se acord  din ociu.

Exemple:

datorii.in datorii.out Explicaµii


1 5
4
Vasile Inc > Anatolia 100
ana > Anatolia 10
ana > Vasilescu Inc 5
Popa25 PF > Anatolia 30
2 Anatolia 0 140
5 Popa25 PF 80 0
Vasile Inc > Anatolia 100 Vasile Inc 100 0
ana > Anatolia 10 Vasilescu Inc 0 5
ana > Vasilescu Inc 5 ana 15 50
Popa25 PF > Anatolia 30
Popa25 PF > ana 50

Timp maxim de executare/test: 0.5 secunde


Memorie: total 16 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 15 KB

1.1.1 Indicaµii de rezolvare


prof. Emanuela Cerchez, Colegiul Naµional Emil Racoviµ  Ia³i

Vom citi datoriile rmelor succesiv, linie cu linie, într-un ³ir de caractere s.
Suntem interesaµi doar de rmele distincte, ca urmare vom reµine evidenµa rmelor distincte
într-un vector F.
O rm  va  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 rmei c tre alte rme, respectiv
suma total  pe care rma trebuie s  o primeasc  de la alte rme).
Vom prelucra ³irul s, care reprezint  o datorie, în scopul de a extrage numele celor dou  rme
(X ³i Y) ³i suma datorat  S.
Deoarece numele rmelor 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 rmei X, cel lalt numele
rmei Y ), ³tiind c  cele dou  nume sunt separate prin >.
CAPITOLUL 1. OJI 2020 4

Vom c uta numele rmei X F . Dac  acesta exist  deja în vectorul F , doar ad ug m
în vectorul
suma S la câmpul SD X . Dac  nu exist  o rm  cu numele X , o ad ug m în
al rmei cu numele
vectorul F , iniµializând SD cu S ³i SP cu 0.
în mod similar proced m cu rma Y : o c ut m în vectorul F ; dac  exist  deja ad ug m suma
S la câmpul SP al rmei cu numele Y ; dac  nu exist  o rm  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 rme 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 rmelor nu conµin spaµii ³i deci etapa
de prelucrare a ³irului s este eliminat , citind separat X , Y , S.
Pentru un algoritm ecient, va  necesar s  facem o c utare binar  în vectorul F. Pentru ca
acest lucru s  e posibil, ar trebui ca, la ecare pas, vectorul F s  e deja sortat lexiicograc dup 
numele rmelor.
Pentru aceasta, vom face o sortare prin inserare. Mai exact, de ecare dat  când ad ug m o
nou  rm  î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.

1.1.2 Cod surs 

Listing 1.1.1: datorii_ab.cpp

1 ///Alin Burta 100 puncte


2 #include <fstream>
3 #include <cstring>
4 #include <cstdlib>
5 #include <cctype>
6
7 #define MaxF 3001
8
9 using namespace std;
10
11 ifstream citeste("datorii.in");
12 ofstream scrie("datorii.out");
13
14 struct firma
15 {
16 char nume[21];
17 long long SP, SD;
18 };
19
20 int C, D;
21 firma v[62][MaxF];
22 int nrFirme = 0;
23 int nrF[62] = {0};
24
25 int apartine(char*);
26
27 int main()
28 {
29 char linie[500], numeFirma1[21], numeFirma2[21];
30 int i, j;
31 int suma;
32 int poz;
33 firma tmp;
34 int lista;
35
36 //citesc datele de intrare
37 citeste >> C >> D;
38 citeste.get();
39
40 for(i = 1; i <= D; ++i)
41 {
42 citeste.get(linie,500);
43 citeste.get();
44 //extrageDate(linie, numeFirma1, numeFirma2, suma);
45
46 //extrag datele
47 char *cuv,ii;
48 int z = 1;
CAPITOLUL 1. OJI 2020 5

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 1. OJI 2020 6

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 }

Listing 1.1.2: datorii_ec.cpp

1 //Em. Cerchez 100 puncte


2 #include <fstream>
3 #include <cstring>
4 #define LGMAX 21
5 #define LMAX 100
6 #define NMAX 6002
7
8 using namespace std;
9
10 ifstream fin("datorii.in");
11 ofstream fout("datorii.out");
12
13 struct firma
14 {
15 char nume[LGMAX];
16 long long int SD, SP;
17 } ;
18
19 int n, D, cerinta, lg;
20 firma F[NMAX];
21 char s[LMAX];
22 int cauta(char * s);
23
24 int main()
CAPITOLUL 1. OJI 2020 7

25 {int i, j, nr, p10, poz1, poz2;


26 char *p, c;
27 fin>>cerinta>>D;fin.get(c);
28
29 for (i=0; i<D; i++)
30 {
31 fin.getline(s,LMAX);
32 lg=strlen(s);
33 nr=0; p10=1;
34 for (j=lg-1; s[j]>=’0’ && s[j]<=’9’; j--)
35 {
36 nr=nr+p10*(s[j]-’0’);
37 p10*=10;
38 }
39 s[j]=0;
40 p=strchr(s,’>’);
41 *(p-1)=0;
42 poz1=cauta(s); F[poz1].SD+=nr;
43 poz2=cauta(p+2); F[poz2].SP+=nr;
44 }
45
46 if (cerinta==1)
47 fout<<n<<’\n’;
48 else
49 for (i=1; i<=n; i++)
50 fout<<F[i].nume<<’ ’<<F[i].SD<<’ ’<<F[i].SP<<’\n’;
51
52 fout.close();
53 return 0;
54 }
55
56
57 int cauta(char * s)
58 {int st=0, dr=n+1, mij, i;
59 while (dr-st>1)
60 {
61 mij=(st+dr)/2;
62 if (strcmp(F[mij].nume,s)<0) st=mij;
63 else dr=mij;
64 }
65
66 if (dr<=n && strcmp(F[dr].nume,s)==0) return dr;
67 for (i=n; i>=dr; i--) F[i+1]=F[i];
68 n++;
69 F[dr].SD=F[dr].SP=0;
70 strcpy(F[dr].nume,s);
71 return dr;
72 }

1.1.3 *Rezolvare detaliat 

1.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 ¶, denit  astfel:

1. Pentru k % 0, zona este compus  din k linii:


a pe prima linie a zonei se a  elementele Alincol, Alincol  1, ..., Alincol 
k  1;
a pe a doua linie a zonei se a  elementele Alin  1col, Alin  1col  1, ...,
Alin  1col  k  2;
a pe a treia linie a zonei se a  elementele Alin  2col, Alin  2col  1, ...,
Alin  2col  k  3;
CAPITOLUL 1. OJI 2020 8

a ...

a pe ultima linie a zonei se a  elementul Alin  k  1col.

2. Pentru k $ 0, zona este compus  din ¶k¶ k linii:

a pe prima linie a zonei se a  elementul Alin  ¶k ¶  1col;


a pe a doua linie a zonei se a  elementele Alin  ¶k ¶  2col  1, Alin  ¶k ¶  2col;
a ...

a pe ultima linie a zonei se a  elementele Alincol  ¶k ¶  1, Alincol  ¶k ¶  2, ...,
Alincol.

Suma elementelor ce compun o zon  triunghiular  se nume³te suma zonei.

Cerinµe
Scrieµi un program care, cunoscând tabloul A ³i Q zone triunghiulare, determin  cea mai mare
dintre sumele zonelor.

Date de intrare
Fi³ierul de intrare triunghi.in conµine pe prima linie num rul natural n, cu semnicaµia din
enunµ.
Pe urm toarele n linii se g sesc câte n valori naturale, reprezentând elementele tabloului A.
Pe linia n2 se a  num rul natural Q, reprezentând num rul zonelor triunghiulare.
Pe urm toarele Q linii se g sesc tripletele de valori lin col k , care reprezint  cele Q zone, în
forma descris  în enunµ. Valorile aate pe aceea³i linie a ³ierului sunt separate prin câte un
spaµiu.

Date de ie³ire
Fi³ierul de ie³ire triunghi.out va conµine o singur  linie pe care va  scris un num r natural
reprezentând suma maxim  cerut .

Restricµii ³i preciz ri
a 3&n& 1000; 1&Q& 100000; 2 & ¶k ¶ & n
a Valorile din tablou sunt numere naturale din intervalul 1, 100.

a Liniile ³i coloanele tabloului A sunt numerotate de la 1 la n (liniile de sus în jos, iar coloanele
de la stânga la dreapta).

a ¶k ¶ reprezint  modulul num rului k (k , pentru k ' 0, respectiv k, pentru k $ 0).


a Se garanteaz  c  orice zon  triunghiular  dintre cele Q este complet inclus  în tabloul A.

Exemple:

triunghi.in triunghi.out Explicaµii


6 59
5 8 10 4 9 4 Zona triunghiular  de sum  ma-
2 10 10 2 4 8 xim  (59) este reprezentat  de tri-
8 10 3 4 6 6 pletul (4 4 -4) ³i conµine valorile
4 6 9 7 1 9 evienµiate:
6 7 2 2 10 6 (59= 4+
10 4 6 1 10 4 10+2+
3 10+3+4+
4 1 3 4+6+9+7).
4 4 -4
6 5 -2
CAPITOLUL 1. OJI 2020 9

Timp maxim de executare/test: 0.7 secunde


Memorie: total 32 MB din care pentru stiv  16 MB
Dimensiune maxim  a sursei: 15 KB

1.2.1 Indicaµii de rezolvare


prof. Alin Burµa, Colegiul Naµional "B. P. Ha³deu" Buz u

Soluµia 1. (30 de puncte)


Este o abordare brute-force în care, pentru ecare întrebare Q, vom calcula suma elementelor
din triunghiul determinat de tripletul lin col k , parcurgând liniile acestuia în funcµie de tipul
triunghiului (k % 0 sau k $ 0). La ecare zon  vom reactualiza, dac  e cazul, suma maxim .
Complexitatea algoritmului este O Q ˜ k ˜ k  n ˜ n deoarece, pentru ecare zon , parcurgem
toate cele k ˜ k  1 2 elemente ale triunghiului respectiv, la care se adaug  parcurgerea
tabloului la citirea elementelor.
Aceast  soluµie obµine 30 de puncte.

Soluµia 2. (70 de puncte)


Soluµia aceasta reprezint  o îmbun t µire a soluµiei precedente prin reducerea num rului ope-
raµiilor necesare calcul rii sumei elementelor dintr-un triunghi.
Vom precalcula un tablou bidimensional Slin, cu urm toarea semnicaµie:
Slinij  = suma primelor j i
elemente de pe linia
Pe baza tabloului Slin putem aa în timp constant suma elementelor unei linii din triunghiul
determinat de tripletul lin col k .
De exemplu, presupunând k % 0, suma elementelor din triunghi se reduce la:
SumT riunghi = (Slinlincol  k  1 – Slinlincol  1) + (Slinlin  1col  k  2
– Slinlincol  1) + ... + (Slinlin  k  1col – Slinlin  k1col  1)
Pentru cazul k $ 0 se procedeaz  similar.
Complexitatea algoritmului scade la O Q ˜ k  n ˜ n

Soluµia 3. (90 de puncte)


Pornim de la ideea de a calcula suma elementelor din triunghiul determinat de tripletul lin
col k (k % 0) în timp constant. Vom deni trei tablouri bidimensionale de ordin n: sum, slin ³i
sdiag , având semnicaµia:
sumij  = suma elementelor din zona dreptunghiular  având colµul din stânga-sus de coor-
donate 1, 1 ³i colµul din dreapta-jos de coordonate i, j 
slinij  = suma primelor j elemente de pe linia i;
Sdiag ij  va  suma elementelor dintr-o zon  determinat  astfel:
Dac  ipotenuza triunghiului se g se³te de-a lungul unei diagonale aate deasupra diagonalei
principale, zona va  de forma unui trapez dreptunghic determinat de punctele 1, 1, i, 1, i, j ,
1, i  j  1 – vezi gura 1.

Figura 1

Dac  ipotenuza triunghiului se g se³te de-a lungul unei diagonale aate sub diagonala princi-
pal , zona va  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 gura 2).
CAPITOLUL 1. OJI 2020 10

Figura 2

Valorile tabloului Sdiag se calculeaz  folosind tablourile sum ³i slin:

for(i = 1; i <= N; ++i)


diag[1][i] = slin[1][i];
for(i = 2; i <= N; ++i)
{
for(j = 1; j < N; ++j)
diag[i][j] = diag[i-1][j+1] + slin[i][j];
diag[i][N] = sum[i][N];
}

Pe baza tablourilor precalculate, aarea sumei valorilor din triunghiul determinat de tripletul
i j k, cu k % 0, se realizeaz , în general, cu formula:
Suma sdiag i  k  1j sdiag i  1j  k sumi  k  1j  1  sumi  1j  1

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 gura 3).

Figura 3

Formula de calcul sufer  unele modic 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 cal-
culeaz  ca ind 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.

1.2.2 Cod surs 


CAPITOLUL 1. OJI 2020 11

Listing 1.2.1: triunghi_ab.cpp

1 //Alin Burta 100 puncte


2 #include <fstream>
3 #define Nmax 1002
4 #define FIN "triunghi.in"
5 #define FOU "triunghi.out"
6
7 using namespace std;
8
9 ifstream citeste(FIN);
10 ofstream scrie(FOU);
11
12 int A[Nmax][Nmax]={0}, sum[Nmax][Nmax]={0},
13 slin[Nmax][Nmax]={0}, diag[Nmax][Nmax]={0};
14
15 int N;
16 int nQ;
17 int lin, col, k;
18 int linMax, colMax, kMax;
19 int SumCrt;
20 int SumMax;
21
22 int aflaSuma(int, int, int);
23 void afis(int A[Nmax][Nmax], int N);
24
25 int main()
26 {
27 int i,j;
28
29 //citire date de intrare
30 citeste >> N;
31 for(i = 1; i <= N; ++i)
32 for(j = 1; j <= N; ++j)
33 citeste >> A[i][j];
34
35 //precalculez matricea de sume parÈŻiale
36 sum[1][1] = A[1][1];
37 for(i = 2; i <= N; ++i)
38 sum[1][i] = sum[1][i-1] + A[1][i],
39 sum[i][1] = sum[i-1][1] + A[i][1];
40
41 for(i = 2; i <= N; ++i)
42 for(j = 2; j <= N; ++j)
43 sum[i][j] = sum[i][j-1] + sum[i-1][j] - sum[i-1][j-1] + A[i][j];
44
45 //precalculez matricea sumelor pe linii
46 for(i = 1; i <= N; ++i)
47 slin[i][1] = A[i][1];
48 for(i = 1; i <= N; ++i)
49 for(j = 2; j <= N; ++j)
50 slin[i][j] = slin[i][j-1] + A[i][j];
51
52 //precalculez matricea de sume partiale pe diagonale
53 for(i = 1; i <= N; ++i)
54 diag[1][i] = slin[1][i];
55 for(i = 2; i <= N; ++i)
56 {
57 for(j = 1; j < N; ++j)
58 diag[i][j] = diag[i-1][j+1] + slin[i][j];
59 diag[i][N] = sum[i][N];
60 }
61 /*
62 afis(A, N);
63 afis(sum, N);
64 afis(diag, N);
65 */
66 SumMax = -1;
67 citeste >> nQ;
68 for( i = 1; i <= nQ; ++i)
69 {
70 citeste >> lin >> col >> k;
71 if(k > 0)
72 SumCrt = aflaSuma(lin, col, k);
73 else
74 {
CAPITOLUL 1. OJI 2020 12

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 }

Listing 1.2.2: triunghi_ec.cpp

1 //Em. Cerchez 100 puncte


2 #include <fstream>
3 #define NMAX 1002
4
5 using namespace std;
6
7 ifstream fin("triunghi.in");
8 ofstream fout("triunghi.out");
9
10 int n, Q;
11 int A[NMAX][NMAX];
12 int sl[NMAX][NMAX];//sl[i][j]=suma elementele de pe linia i de la 1 la j
13 int sd[NMAX][NMAX];//sd[i][j]=suma elem din dreptunghiul cu colutl 1,1
14 // si coltul i,j
15 int st[NMAX][NMAX];//st[i][j] este suma elementelor din trapezul cu
16 // baza mare pe linia 1
17 //si baza mica pe linia i de la 1 la j
CAPITOLUL 1. OJI 2020 13

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 }

1.2.3 *Rezolvare detaliat 


Capitolul 2

OJI 2019

2.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
³ierului de intrare se a  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 ³ierului de ie³ire au aceea³i structur :
pe ecare 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 a³a în ordine cresc toare.

Restricµii ³i preciz ri
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 ecare test exist  soluµie.

Exemple

14
CAPITOLUL 2. OJI 2019 15

cate3cifre.in cate3cifre.out Explicaµii


1 931 Num rul determinat este 931
1000 Num rul determinat se scrie în baza 11 ca 777 11
Acela³i num r se scrie în baza 30 ca 111 30
2 11 7
1000 30 1
1 26733 Num rul determinat este 26733
30000 Num rul determinat se scrie în baza 37 ca 19 19 19 37
Acela³i num r se scrie în baza 163 ca111 163
2 37 19
30000 163 1

Tabela 2.1: cate3cifre

Timp maxim de executare/test: 0.5 secunde


Memorie: total 20 MB din care pentru stiv  20 MB
Dimensiune maxim  a sursei: 10 KB
Sursa: cate3cifre.cpp, cate3cifre.c sau cate3cifre.pas va  salvat  în folderul care are drept
nume ID-ul t u.
Toate subiectele sunt obligatorii. Timpul de lucru efectiv alocat probei este de 3 ore.
Punctajul maxim cumulat este de 200 de puncte, dintre care 20 de puncte sunt acordate din
ociu.

2.1.1 Indicaµii de rezolvare


Propunator, prof. Marinel \c Serban

Descriere a unei/unor solutii posibile

Solutia 1

Se verifica, descrescator, fiecare numar x mai mic sau egal cu n pana la


determinarea primului cu proprietatea ceruta. Un numar de 3 cifre identice c
se scrie in baza b sub forma:

ccc(b) = c * b * b + c * b + c = c * (b * b + b + 1) = x(10)

Pentru un numar x, se va verifica fiecare baza b de la 2 pana cand


b * b + b + 1 > x.

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).

Ambele solutii descrise mai sus obtin punctaj maxim.

Grad de dificultate: 3
CAPITOLUL 2. OJI 2019 16

2.1.2 Cod surs 

Listing 2.1.1: cate3cifre.cpp

1 #include <fstream>
2
3 using namespace std;
4 int C[4], B[4];
5 int n, m, i, st, dr, mid, j, nr, baza, cifra, k, t;
6
7 ifstream fin ("cate3cifre.in");
8 ofstream fout("cate3cifre.out");
9
10 int main ()
11 {
12 fin>>t>>n;
13
14 for (i=n;i>=2;i--)
15 {
16 nr = 0;
17 for (baza=2;1+baza+baza*baza<=i;baza++)
18 {
19 if (i%(1+baza+baza*baza) != 0)
20 continue;
21
22 cifra = i/(1+baza+baza*baza);
23 if (cifra < baza)
24 {
25 nr++;
26 C[nr] = cifra;
27 B[nr] = baza;
28 if (nr == 3)
29 break;
30 }
31 }
32
33 if (nr == 2)
34 {
35 if (t == 1)
36 fout<<i;
37 else
38 fout<<B[1]<<" "<<C[1]<<"\n"<<B[2]<<" "<<C[2]<<"\n";
39
40 return 0;
41 }
42 }
43 }

2.1.3 *Rezolvare detaliat 

2.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 aate una lâng  alta, toate pe aceea³i linie, sau toate pe aceea³i coloan  a
matricei.

Cerinµe
Se cere determinarea num rului de perechi de segmente:
1. aate pe linii distincte ale matricei;
2. aate pe coloane distincte ale matricei;

Date de intrare
CAPITOLUL 2. OJI 2019 17

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 a  elementele matricei, o linie a matricei pe o linie a ³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 .
Restricµii ³i preciz ri
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
paralele.in paralele.out Explicaµii
1 5 6 11 Prima valoare din ³ierul de intrare ind 1, ne intereseaz 
0 1 1 1 0 0 segmente formate pe linii. Pe prima linie este o secvenµ  de
1 0 0 0 0 0 valori 1 format  din trei elemente. Ea produce trei segmente:
0 0 0 1 0 0 cel cu primele dou  valori de 1, cel cu ultimele dou  valori
1 1 0 1 1 0 de 1 ³i cel cu toate cele trei valori de 1. Pe linia a doua
0 1 1 0 0 0 nu se g se³te niciun segment, neind cel puµin dou  valori 1
al turate. Pe linia a treia nu se g se³te niciun segment, pe
linia a patra sunt dou  segmente iar pe linia a cincea este
un singur segment. Num rul cerut se obµine astfel: ecare
dintre cele trei segmente de pe prima linie este paralel cu
ecare dintre segmentele de pe a patra ³i de pe a cincea linie
iar segmentele de pe linia a patra sunt paralele cu segmentul
de pe ultima linie. Pentru exemplul prezentat, dac  am 
avut T 2 rezultatul calculat ar  trebuit s  e 1 (segmentul
de pe coloana a doua este paralel cu segmentul de pe coloana
a patra).

Timp maxim de executare/test: 0.6 secunde


Memorie: total 20 MB din care pentru stiv  20 MB
Dimensiune maxim  a sursei: 10 KB
Sursa: paralele.cpp, paralele.c sau paralele.pas va  salvat  în folderul care are drept nume
ID-ul t u.
Toate subiectele sunt obligatorii. Timpul de lucru efectiv alocat probei este de 3 ore.
Punctajul maxim cumulat este de 200 de puncte, dintre care 20 de puncte sunt acordate din
ociu.

2.2.1 Indicaµii de rezolvare


Propunator, prof. Marius Nicoli

Descriere a unei/unor solutii posibile

Varianta de 78 de puncte presupune sa stocam intr-un vector F numarul de secvente


de lungime cel putin 2 de pe fiecare linie. Solutia este suma produselor de forma
F[i] * F[j] cu i diferit de j (am notat cu F[i] numarul de secvente de pe linia i).

Timpul de calcul va fi de ordin n*m (de la calculul valorilor din F âĂŞ observam ca
CAPITOLUL 2. OJI 2019 18

numarul de elemente de pe aceeasi linie a matricei se face cu o singura parcurgere


a liniei) + n*n in etapa a doua.

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 pen
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.

Problema admite si alte solutii de complexitati diverse, permitand obtinerea de


punctaje partiale.

Grad de dificultate: 3

2.2.2 Cod surs 

Listing 2.2.1: paralele1.cpp

1 //Raluca Costineanu
2 #include <bits/stdc++.h>
3
4 using namespace std;
5
6 #define nMax 50010
7 #define mMax 1010
8
9 ifstream f("paralele.in");
10 ofstream g("paralele.out");
11
12 int n, m, t;
13 long long lin[nMax], col[mMax], lgCol[nMax];
14 bool a[mMax];
15
16 long long peLinii()
17 {
18 int i, j, lg;
19 long long total=0;
20
21 for(i=1;i<=n;i++)
22 {
23 for(j=1;j<=m;j++)
24 f>>a[j];
25
26 lg=0;
27 for(j=1;j<=m+1;j++)
28 if(a[j]==1)
29 lg++;
30 else
31 lin[i]+=1LL*lg*(lg-1)/2, lg=0;
32
33 total+=lin[i];
34 }
35
36 total=total*(total-1)/2;
37 for(i=1;i<=n;i++)
38 total-=1LL*lin[i]*(lin[i]-1)/2;
39
40 return total;
41 }
42 long long peColoane()
43 {
CAPITOLUL 2. OJI 2019 19

44 long long total=0;


45 int i, j;
46
47 for(i=1;i<=n;i++)
48 {
49 for(j=1;j<=m;j++)
50 f>>a[j];
51
52 for(j=1;j<=m+1;j++)
53 if(a[j]==1)
54 lgCol[j]++;
55 else
56 col[j]+=1LL*lgCol[j]*(lgCol[j]-1)/2, lgCol[j]=0;
57 }
58
59 for(j=1;j<=m;j++)
60 {
61 if(lgCol[j])
62 col[j]+=1LL*lgCol[j]*(lgCol[j]-1)/2;
63
64 total+=col[j];
65 }
66
67 total=total*(total-1)/2;
68 for(j=1;j<=m;j++)
69 total-=1LL*col[j]*(col[j]-1)/2;
70
71 return total;
72 }
73
74 int main()
75 {
76 f>>t>>n>>m;
77
78 if(t==1)
79 g<<peLinii()<<’\n’;
80 else
81 g<<peColoane()<<’\n’;
82
83 return 0;
84 }

Listing 2.2.2: paralele2.cpp

1 #include <fstream>
2 #include <iostream>
3
4 #define DIM 503
5
6 using namespace std;
7
8 int a[DIM][DIM], b[DIM][DIM];
9 int c;
10 int f[50005];
11 int n, i, j, t, L, k, ii, jj, m;
12 long long sol;
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 ()
CAPITOLUL 2. OJI 2019 20

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 }

Listing 2.2.3: paralele3.cpp

1 #include <fstream>
2 #include <iostream>
3
4 #define DIM 503
5
6 using namespace std;
7
8 int a[DIM][DIM], b[DIM][DIM];
9 int c;
10 int f[50005];
11 int n, i, j, t, L, k, ii, jj, m;
12 long long alterAll, sol;
13 int A[50005][22];
14
CAPITOLUL 2. OJI 2019 21

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;
89 }
90
CAPITOLUL 2. OJI 2019 22

91 sol += gauss(alterAll);
92 fout<<sol;
93 return 0;
94 }

2.2.3 *Rezolvare detaliat 


Capitolul 3

OJI 2018

3.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 aate pe K linii consecutive ale matricei
³i a mulµimii tuturor elementelor aate 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 deni valoarea unei cruci ca ind 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  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, repre-
zentând valoarea maxim  determinat  pentru o cruce de l µime K, respectiv linia ³i coloana care
exprim  poziµia acesteia în matrice.

Restricµii ³i preciz ri
a 1&N & 500;
a 1&K $ N;
a Numerele din matrice sunt din intervalul 5000, 5000;
a Liniile ³i coloanele se indexeaz  începând cu 1.
a Dac  exist  mai multe cruci de l µime K de valoare maxim , se va lua în considerare poziµia
cu indicele liniei mai mic, iar în caz de egalitate a liniilor poziµia celei cu indicele coloanei mai
mic.

Exemple

23
CAPITOLUL 3. OJI 2018 24

cruce.in cruce.out Explicaµii


5 2 23 2 4 Elementele care formeaz  crucea de valoare maxim 
1 -2 3 -1 4 sunt cele evidenµiate:
-3 2 2 -2 -1 1 -2 3 -1 4
1 2 3 4 5 -3 2 2 -2 -1
1 0 -7 1 1 1 2 3 4 5
3 2 1 2 3 1 0 -7 1 1
3 2 1 2 3
5 2 28 2 3 Valoarea maxim  a unei cruci de l µime 2 este 28.
0 0 1 1 1 În exemplu mai exist  cruci de valoare 28, dar cu
2 2 2 2 2 indicele de început al liniei sau coloanei mai mari.
2 2 2 2 2 De exemplu crucea care începe de pe linia 3 ³i coloana
2 2 2 2 2 3.
0 0 1 1 1

Timp maxim de executare/test: 0.5 secunde


Memorie: total 16 MB
Dimensiune maxim  a sursei: 10 KB

3.1.1 Indicaµii de rezolvare


Prof. Mot Nistor Colegiul National Nicolae Balcescu Braila

Rezolvarea se bazeaza pe precalcularea unor sume in O(n^2).

Se poate folosi varianta 2D a vectorilor de sume: se calculeaza intr-o matrice S,


suma elementelor unui ’’dreptunghi’’ din matricea A cu coltul stanga sus in (1,1)
si coltul dreapta-jos in (i,j).

Relatia de recurenta este S[i,j]=S[i,j-1]+S[i-1,j]+A[i,j]-S[i-1,j-1].

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].

De asemenea se pot precalcula sumele pe fiecare linie si coloana, tot in O(n^2).

Avand calculate aceste sume, o ’’cruce’’ se descompune in dreptunghiuri a caror


suma se calculeaza usor, in O(1), deci gasirea sumei maxime pentru cele
(n-k+1)*(n-k+1) cruci posibile se realizeaza in complexitate patratica.

Memoria folosita este de asemenea O(n^2).

3.1.2 Cod surs 

Listing 3.1.1: cruce_dl.cpp

1 #include <cstdio>
2 #include <cstring>
3 #include <algorithm>
4 #include <cassert>
5
6 #define Nmax 505
7
8 using namespace std;
9
10 int S[Nmax][Nmax], Smax, minL, minC, i, j, K, N, Sum, Dif;
11 bool ok;
12
13 int main()
CAPITOLUL 3. OJI 2018 25

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 }

Listing 3.1.2: cruce_RC.cpp

1 #include <fstream>
2 #include <cmath>
3
4 using namespace std;
5
6 #define N 501
7
8 ifstream f("cruce.in");
9 ofstream g("cruce.out");
10
11 int n, k, l=N, c=N;
12 int a[N][N];
13 int lin[N], col[N], mx=-5000*N*N-1;
14
15 int main()
16 {
17 int i, j;
18 f>>n>>k;
19 for(i=1;i<=n;i++)
20 for(j=1;j<=n;j++)
21 f>>a[i][j],
22 lin[i] = lin[i] + a[i][j], col[j] = col[j] + a[i][j],
23 a[i][j] = a[i][j] + a[i-1][j] + a[i][j-1] - a[i-1][j-1];
24
25 for(i=2;i<=n;i++)
26 lin[i]+=lin[i-1],col[i]+=col[i-1];
27
28 for(i=1;i<=n-k+1;i++)
29 for(j=1;j<=n-k+1;j++)
30 {
31 int sum = lin[i+k-1] - lin[i-1] + col[j+k-1] - col[j-1]
32 - (a[i+k-1][j+k-1]-a[i-1][j+k-1]-a[i+k-1][j-1]+a[i-1][j-1]);
33
34 if(sum>mx)
35 mx=sum, l=i, c=j;
36 else
37 if(sum==mx)
38 l=min(l,i),c=min(c,j);
39 }
40
41 g<<mx<<’ ’<<l<<’ ’<<c<<’\n’;
42 return 0;
43 }
CAPITOLUL 3. OJI 2018 26

Listing 3.1.3: cruceEm.cpp

1 // cruce EM
2 #include <fstream>
3
4 using namespace std;
5
6 #define M 512
7
8 int a[M][M],s[M][M];
9
10 int main()
11 {
12 int n,i,j,k,s1,mx=-2000000000, imax=0,jmax=0;
13
14 ifstream fi("cruce.in");
15 ofstream fo("cruce.out");
16
17 fi>>n>>k;
18 for(i=1;i<=n;i++)
19 for (j=1;j<=n;j++)
20 fi>>a[i][j];
21
22 for(i=1;i<=n;i++)
23 for (j=1;j<=n;j++)
24 s[i][j]=s[i-1][j]+s[i][j-1]+a[i][j]-s[i-1][j-1];
25
26 for (i=1;i<=n-k+1;i++)
27 for (j=1;j<=n-k+1;j++)
28 {
29 s1=s[i-1][j+k-1]-s[i-1][j-1]+s[i+k-1][n]-s[i-1][n]
30 +s[n][j+k-1]-s[i+k-1][j+k-1]
31 -s[n][j-1]+s[i+k-1][j-1];
32
33 if (s1>mx) {mx=s1; imax=i; jmax=j;}
34 }
35
36 fo<<mx<<" "<<imax<<" "<<jmax;
37
38 return 0;
39 }

3.1.3 *Rezolvare detaliat 

3.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, ecare 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  obµinut  prin înlocuirea ec 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 .

Cerinµe
CAPITOLUL 3. OJI 2018 27

Scrieµi un program care cite³te numerele din lista primit  de la sfetnicul regelui ³i a³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, a³ate în ordine cresc toare.
Dac  exist  mai multe astfel de ³iruri în lista corect  a Micului Prinµ, se va a³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  rezolvat . Pe a doua linie num rul natural
N de numere de pe lista primit  de la sfetnicul regelui. Pe a treia linie se a  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µ , ³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 ³ierului de ie³ire se va scrie lungimea celui mai lung
³ir magic. Dac  s-a rezolvat cerinµa 3, ³ierul de ie³ire va conµine numerele determinate ³i a³ate
conform cerinµei.

Restricµii ³i preciz ri
a 0$N & 50000;
a Numerele de pe lista sfetnicului sunt naturale nenule ³i ecare 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

pal.in pal.out Explicaµii


1 353 222 64346 “irul palindromurilor de pe lista corect 
3 obµinut  de Micul Prinµ
345 214 64325
2 3 Lista corect  conµine palindromurile 2 3 121
8 4 21212 434 5643465 7 ³i cel mai lung ³ir
2 3 120 4 432 5643461 7 21211 magic este 3 434 5643465
3 3 434 5643465 “irul magic 2 121 21212 are aceea³i lungime
8 dar se termin  într-un num r mai mic
2 3 5643461 7 120 4 21211 432

Tabela 3.2: pal

Timp maxim de executare/test: 0.5 secunde


Memorie: total 16 MB
Dimensiune maxim  a sursei: 10 KB

3.2.1 Indicaµii de rezolvare


Prof. Lica Daniela Centrul Judetean de Excelenta Prahova

Pentru prima cerinta se va verifica cifrele din prima jumatate a numarului cu


cifrele din a doua si se decide daca se mareste cifra din mijloc sau se copie
in oglinda cifrele din a doua jumatate.

Pentru a doua cerinta pe vectorul sortat se genereaza pentru fiecare palindrom,


numerele obtinute prin stergerea cifrelor de la capete. Fiecare astfel de numar
se cauta binar in sir daca exista, in acelasi timp determin’’andu-se lungimea
CAPITOLUL 3. OJI 2018 28

maxima a unui sir magic.

Se va retine si cel mai mare palindrom dintr-un sir de lungime maxima.


Afisarea se va face pornind de la numarul cel mai mare din sirul de lungime
maxima determinat.

Complexitatea algoritmului N * Nrcifre(Val Maxima) * log2 (N)

3.2.2 Cod surs 

Listing 3.2.1: pal_dl.cpp

1 #include <cstdio>
2 #include <algorithm>
3 #include <cassert>
4
5 #define ll long long
6
7 using namespace std;
8
9 const int Nmax = 1e5 + 5;
10
11 int N, i, P, cnt, Ans = 0, nr, cf[30];
12 ll a[50005], z, x, Win, v[30];
13
14 void load()
15 {
16 int i, j, k, nr;
17 bool ok; ll x;
18
19 for(i=1; i<=N; ++i)
20 {
21 scanf("%lld", &x);
22 assert(0<x && x<=(1e17));
23
24 nr = 0;
25 while(x)
26 {
27 cf[++nr] = x%10;
28 x /= 10;
29 }
30
31 assert(nr%2==1);
32
33 reverse(cf+1, cf+nr+1);
34
35 ok = 1;
36 for(j=nr/2+2; ok && j<=nr; ++j)
37 {
38 k = nr - j + 1;
39 if(cf[j] < cf[k]) break;
40 if(cf[j] > cf[k]) ok = 0;
41 }
42
43 if(ok)
44 for(j=nr/2+2; j<=nr; ++j) cf[j] = cf[nr-j+1];
45 else
46 {
47 cf[nr/2+1]++;
48 j = nr/2+1;
49 while(cf[j] == 10)
50 cf[j] = 0, ++cf[--j];
51 for(j=nr/2+2; j<=nr; ++j)
52 cf[j] = cf[nr-j+1];
53 }
54
55 a[i] = 0;
56 for(j=1; j<=nr; ++j)
57 a[i] = a[i] * 10 + cf[j];
58 }
59 }
60
CAPITOLUL 3. OJI 2018 29

61 bool cauta(ll x)
62 {
63 int st = 1, dr = N, mid;
64
65 while(st <= dr)
66 {
67 mid = (st+dr)/2;
68 if(a[mid] == x) return 1;
69 if(a[mid] < x) st = mid + 1;
70 else dr = mid - 1;
71 }
72
73 return 0;
74 }
75
76 int main()
77 {
78 freopen("pal.in","r",stdin);
79 freopen("pal.out","w",stdout);
80
81 scanf("%d %d",&P, &N);
82 assert(0<N && N<=50000);
83 assert(P == 1 || P == 2 || P == 3);
84
85 load();
86
87 if(P == 1)
88 {
89 for(i=1; i<=N; ++i)
90 printf("%lld ",a[i]);
91 printf("\n");
92 return 0;
93 }
94
95 sort(a + 1, a + N + 1);
96
97 for(i=1; i<N; ++i)
98 assert(a[i] != a[i+1]);
99
100 for(i = 1; i <= N; ++i)
101 {
102 z = 1;
103 x = a[i];
104 cnt = 1;
105
106 while(x >= z*10) z *= 10;
107
108 while(z>1)
109 {
110 x = (x % z - x % 10) / 10;
111 if(cauta(x)) ++cnt;
112 z /= 100;
113 }
114
115 if(cnt >= Ans)
116 Ans = cnt, Win = a[i];
117 }
118
119 if(P == 2)
120 {
121 printf("%d\n",Ans);
122 return 0;
123 }
124
125 v[nr = 1] = x = Win;
126 while(x >= z*10)
127 z *= 10;
128
129 while(z>1)
130 {
131 x = (x % z - x%10) / 10;
132 if(cauta(x)) v[++nr] = x;
133 z /= 100;
134 }
135
136 for(i=nr; i; --i)
CAPITOLUL 3. OJI 2018 30

137 printf("%lld ",v[i]);


138 printf("\n");
139
140 return 0;
141 }

Listing 3.2.2: pal_RC.cpp

1 #include <fstream>
2 #include <algorithm>
3
4 #define N 50001
5 #define ULL unsigned long long
6
7 using namespace std;
8
9 ifstream f("pal.in");
10 ofstream g("pal.out");
11
12 struct nr
13 {
14 ULL x;
15 int n;
16 } a[N];
17
18 int C, n, m, mx;
19 ULL z, sol[20], s[20], p10[20];
20
21 bool CB(ULL y)
22 {
23 int s=1,d=n,m;
24
25 while(s<=d)
26 {
27 m=(s+d)/2;
28 if(a[m].x==y)
29 return 1;
30 else
31 if(a[m].x<y)
32 s=m+1;
33 else
34 d=m-1;
35 }
36
37 return 0;
38 }
39
40 bool comp(nr a, nr b)
41 {
42 return a.x<b.x;
43 }
44
45 ULL pal(ULL x, int &n)
46 {
47 int a[20], i, ok=1, m, r=0,j;
48 n=0;
49
50 while(x)
51 a[++n]=x%10,x/=10;
52
53 m=n/2;
54 for(i=1;i<=m && ok;i++)
55 if(a[i]>a[n-i+1])
56 {
57 for(r=1, j=i+1;j<=m;j++)
58 r=r+a[j], a[j]=r%10, r/=10;
59
60 a[m+1]+=r;
61 a[i]=a[n-i+1];
62 }
63 else
64 a[i]=a[n-i+1];
65
66 if(a[m+1]>9)
67 {
CAPITOLUL 3. OJI 2018 31

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 }

3.2.3 *Rezolvare detaliat 


Capitolul 4

OJI 2017

4.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 ecare dintre cele N  N celule. Valorile din tablou pot  modicate prin aplicarea a dou 
operaµii codicate 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
1) Dându-se o succesiune de K operaµii (L nr sau C nr) asupra liniilor/coloanelor tabloului
iniµial (în care toate celulele conµin valoarea 1) s  se determine num rul valorilor pozitive din
tablou la nalul execut rii celor K operaµii.
2) S  se determine num rul minim de operaµii L nr sau C nr, care, aplicate tabloului iniµial,
îl modic  astfel încât tabloul obµinut s  conµin  exact Z valori negative.

Date de intrare
Fi³ierul tablou.in conµine pe prima linie num rul p 1 sau p 2, reprezentând num rul
cerinµei ce trebuie rezolvat .
a Dac  p 1 atunci linia a doua a ³ierului de intrare conµine numerele N ³i K, separate
printr-un spaµiu, iar urm toarele K linii conµin ecare câte o liter  mare (L sau C) ³i un num r
nr, separate printr-un spaµiu, reprezentând codicarea uneia dintre cele dou  operaµii (L nr sau
C nr).
a Dac  p 2 atunci linia a doua a ³ierului de intrare conµine numerele N ³i Z, separate
printr-un spaµiu.

Date de ie³ire
a Dac  p 1, atunci ³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 nalul execut rii celor K operaµii
asupra tabloului iniµial (r spunsul la cerinµa 1).
a Dac  p 2, atunci ³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 modic 
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, ³ierul va conµine pe prima linie valoarea 0 (zero).

Restricµii ³i preciz ri
a N, K, Z ³i nr sunt numere naturale
a 3 & N & 20000; 1 & K & 43000; 1 & Z & N ˜ N ; 1 & nr & N
a Prin schimbare de semn, valoarea 1 se transform  în 1 ³i valoarea 1 se transform  în 1
a Se acord  10 puncte din ociu ³i câte 45 puncte pentru rezolvarea corect  a ec rei cerinµe.

32
CAPITOLUL 4. OJI 2017 33

Exemple
tablou.in tablou.out Explicaµii
1 10 N=4. La nalul aplic rii succesiunii de K=4 operaµii, tablou
4 4 modicat are conµinutul:
L 1 -1 1 1 1
L 3 -1 1 1 1
C 1 1 -1 -1 -1
L 1 -1 1 1 1
Astfel, tabloul conµine 10 valori pozitive.
2 3 Sunt necesare minimum 3 operaµii, de exemplu:
3 5 L 3
L 1
C 1
2 0 Nu exist  nicio succesiune de operaµii pentru care s  se obµin 
4 7 Z=7 valori negative.

Timp maxim de executare/test: 0.1 secunde


Memorie: total 4 MB
Dimensiune maxim  a sursei: 10 KB

4.1.1 Indicaµii de rezolvare


Autor prof. Carmen Minca
Colegiul National de Informatica Tudor Vianu - Bucuresti

Presupunem ca s-a aplicat de xi ori operatia L i si de yk ori operatia C k.


Valoarea memorata in celula situata in linia i si coloana k isi va schimba
semnul de xi+yk ori.

Astfel, ea va fi negativa daca suma xi+yk este impara.

Deducem ca numarul de valori negative din tabloul modificat va depinde de


paritatile numerelor xi si yk. Daca sirul valorilor x1,x2,x3,..,xN contine
x valori impare iar sirul valorilor y1,y2,y3,..,yN contine y valori impare
atunci tabloul modificat va contine x*(N-y)+y*(N-x) valori negative.

Astfel raspunsul la cerinta 1) este valoarea expresiei N2 - x*(N-y) + y*(N-x)


Pentru cerinta 2), vom folosi relatia x*(N-y)+y*(N-x)=Z.

Cautam prima valoare a lui x din multimea {0,1,2,âò


e,N} pentru care exista un y
din {0,1,2,..,N} si cu proprietatea ca y=(Z-N*x)/(N-2*x) si NâL’ă2*x

Daca exista aceste valori x si y, atunci numarul minim de operatii este M=x+y

4.1.2 Cod surs 

Listing 4.1.1: tabCS.cpp

1 ///prof. Cristina Sichim


2 #include <fstream>
3 #include <cmath>
4 #include <bitset>
5
6 using namespace std;
7
8 ifstream fin("tablou.in");
9 ofstream fout("tablou.out");
10
11 bitset <20005> L,C;
12
13 char x;
CAPITOLUL 4. OJI 2017 34

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 }

Listing 4.1.2: tablou_CM.cpp

1 //sursa oficiala-prof. Carmen Minca


2 #include <fstream>
3
4 using namespace std;
5
6 #define Nmax 20005
7
8 ifstream fin("tablou.in");
9 ofstream fout("tablou.out");
10
11 int N,Z,K,M,P;
12 char linie[Nmax];
13 char coloana[Nmax];
14
15 void cerinta1()
16 {
17 fin>>N>>K;
18 char LC;
CAPITOLUL 4. OJI 2017 35

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;
93 }
CAPITOLUL 4. OJI 2017 36

4.1.3 *Rezolvare detaliat 

4.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 ind distincte.

Cerinµe
Cunoscând N ³i coordonatele celor N puncte, s  se determine:
1) Num rul maxim de puncte care au aceea³i abscis .
2) Num rul triunghiurilor care se pot desena respectând urm toa-
rele condiµii: Figura 4.1: triunghiuri
- au toate vârfurile în puncte dintre cele date;
- au o latur  paralel  cu OX ;
- nu au laturi paralele cu OY ;

Date de intrare
Datele de intrare se citesc din ³ierul triunghiuri.in, care are urm toarea structur :
a Pe prima linie se a  num rul p, care indic  cerinµa ce trebuie rezolvat  (p are valoarea 1
sau 2);
a Pe a doua linie se a  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
Fi³ierul triunghiuri.out va avea urm toarea structur :
Dac  p 1 se va scrie în ³ier, pe prima linie, num rul maxim de puncte care au aceea³i
abscis  (cerinµa 1).
Dac  p 2 se va scrie în ³ier, pe prima linie, num rul triunghiurilor care se pot desena
respectând condiµiile date, modulo 1000003, adic  restul împ rµirii num rului de triunghiuri la
1000003 (cerinµa 2).

Restricµii ³i preciz ri
a 3 & N & 100000
a 0 & x $ 1000
a 0 & y $ 1000
a Se acord  10 puncte din ociu, 25 puncte pentru rezolvarea corect  a cerinµei 1 ³i 65 puncte
pentru rezolvarea corect  a cerinµei 2.

Exemple
triunghiuri.in triunghiuri.out Explicaµii
1 2 Se rezolv  cerinµa 1).
5 Sunt maximum dou  puncte care au aceea³i abscis ,
2 1 (3, 4) ³i (3,2)
1 4
3 4
3 2
6 4
2 4 Se rezolv  cerinµa 2).
5 Se pot trasa 4 triunghiuri care satisfac cerinµele.
2 1 Dac  not m cele 5 puncte din ³ier cu A, B, C, D,
1 4 E (ca în imagine), atunci, cele 4 triunghiuri care
3 4 satisfac cerinµele sunt : ABC, ACE, ABE ³i BDE.
3 2
6 4
CAPITOLUL 4. OJI 2017 37

Timp maxim de executare/test: 0.3 secunde


Memorie: total 4 MB
Dimensiune maxim  a sursei: 5 KB

4.2.1 Indicaµii de rezolvare


Autor prof. Alin Burta Colegiul National "B.P. Hasdeu" Buzau

Solutie de complexitate O(n)

Cerinta 2

Consideram vectorii nx si ny cu semnificatia:

nx [i] = numarul punctelor care au abscisa egala cu i;


ny[i] = numarul punctelor care au ordonata egala cu i;

Valorile celor doi vectori pot fi calculate inca de la citirea


coordonatelor punctelor.

In acelasi timp memoram, pentru fiecare ordonata y intre 0 si 999,


lista absciselor punctelor care au ordonata egala cu y, obtinind
un tablou bidimensional

H (H[i][j] - al j-lea punct din lista punctelor de ordonata i).

Numarul triunghiurilor cu proprietatea ceruta se calculeaza astfel:

Pentru fiecare ordonata i din plan, pentru care numarul punctelor


de pe aceasta este cel putin egal cu 2, calculam cite triunghiuri
se pot forma avind doua puncte cu ordonata egala cu i si al treilea
punct de ordonata diferita de i.

Pentru aceasta vom scadea din numarul total de triunghiuri care


se pot forma (cu o latura paralela cu OX, aflata pe dreapta y = i)
numarul triunghiurilor cu o latura paralela cu OY, adica

( N - ny[i]) * ( ny[i] * (ny[i] - 1) / 2 ) - sumTrParaleleOY

Valoarea sumTrParaleleOY se calculeaza luand fiecare punct


de ordonata i si contorizand cate triunghiuri dreptunghice cu un varf
in acel punct se pot forma, adica:

sumTrParaleleOY = 0;

for(j = 1; j <= ny[i]; ++j)


sumTrParaleleOY += ( nx[ H[i][j] ] - 1 ) * (ny[i] - 1);

Algoritmul va parcurge practic toata lista de puncte astfel ca ordinul sau de


complexitate este O(n).

4.2.2 Cod surs 

Listing 4.2.1: tri_On.cpp

1 ///sursa oficiala - prof. Alin Burta


2 #include <fstream>
3
4 #define Nmax 100001 // numarul maxim de puncte
5 #define Cmax 1001 // coordonata maxima
CAPITOLUL 4. OJI 2017 38

6 #define IN "triunghiuri.in"
7 #define OU "triunghiuri.out"
8
9 using namespace std;
10
11 long N; // numarul punctelor
12 long long NrTr; // numarul triunghiurilor gasite
13
14 short nx[Cmax]; // nx[i] - cate puncte sunt pe absisa i
15 short ny[Cmax]; // ny[i] - cate puncte sunt pe ordonata i
16 short H[Cmax][Cmax]; // memorez punctele ordonate H[i] - lista
17 // punctelor cu ordonata i
18
19 long long aux, aux1, aux2, sumLin;
20
21 int main()
22 {
23 long int i, j, Max, V;
24 short x, y;
25
26 //citire date
27 ifstream Fin(IN);
28 Fin >> V >> N;
29 for(i = 0; i <= Cmax - 1; ++i)
30 nx[i] = ny[i] = 0;
31 for(i = 1; i <= N; ++i)
32 {
33 Fin >> x >> y;
34 nx[x]++; ny[y]++;
35 H[ y ][ ny[y] ] = x;
36 }
37
38 Fin.close();
39
40 if( V == 1)
41 {
42 Max = 0;
43 for(i = 0; i <= 999; ++i)
44 if(nx[i] > Max)
45 Max = nx[i];
46 ofstream Fou(OU);
47 Fou << Max << ’\n’;
48 Fou.close();
49 }
50 else
51 {
52 NrTr = 0;
53 for( i = 0; i< Cmax-1; ++i)
54 if(ny[i] > 1)
55 {
56 sumLin = 0;
57 for(j = 1; j<= ny[i]; ++j)
58 sumLin += ( nx[ H[i][j] ] - 1 ) * (ny[i] - 1);
59 aux1 = ( ny[i] * (ny[i] - 1) / 2 );
60 aux2 = ( N - ny[i]);
61 aux = aux1 * aux2;
62 NrTr += aux - sumLin ;
63 NrTr %= 1000003;
64 }
65
66 ofstream Fou(OU);
67 Fou << NrTr << ’\n’;
68 Fou.close();
69 }
70
71 return 0;
72 }

Listing 4.2.2: triCS.cpp

1 ///prof. Cristina Sichim


2 #include <fstream>
3 #include <vector>
4
5 #define MOD 1000003
CAPITOLUL 4. OJI 2017 39

6
7 using namespace std;
8
9 ifstream fin("triunghiuri.in");
10 ofstream fout("triunghiuri.out");
11
12 int x[1005],n,v,i,a,b,k;
13 long long t, aux1, aux2, aux,aux3;
14
15 vector <int> Y[1005];
16 vector <int> :: iterator it;
17
18 int main()
19 {
20 fin>>v>>n;
21 for(i=1;i<=n;++i)
22 {
23 fin>>a>>b;
24 x[a]++;
25 Y[b].push_back(a);
26 }
27
28 if(v==1)
29 {
30 a=x[0];
31 for(i=0;i<=999;++i)
32 if(x[i]>a)
33 a=x[i];
34 fout<<a<<’\n’;
35 }
36 else
37 {
38 for(i=0;i<=999;++i)
39 {
40 k=Y[i].size();
41 if(k>=2)
42 {
43 aux1=n-k;
44 aux2=k*(k-1)/2;
45 aux=aux1*aux2;
46
47 for(it=Y[i].begin();it!=Y[i].end();++it)
48 {
49 aux3=x[ *it ]-1;
50 aux3=aux3*(k-1);
51 aux=aux-aux3;
52 }
53
54 t=(t+aux)%MOD;
55 }
56 }
57
58 fout<<t<<’\n’;
59 }
60
61 fin.close();
62 fout.close();
63 return 0;
64 }

4.2.3 *Rezolvare detaliat 


Capitolul 5

OJI 2016

5.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  î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 a  cartierul general al unui atacator, acesta va  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  î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 ecare 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  e rezolvat  (1 sau 2). Pe cea de a doua linie se a  num rul natural n, reprezentând
num rul atacatorilor. Pe urm toarele n linii se a  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 a  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 ³ierului arma.out va  scris un num r natural
reprezentând cantitatea minim  de narun necesar  distrugerii tuturor cartierelor generale inamice.
Dac  cerinµa este c 2, atunci ³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.

Restricµii ³i preciz ri
a 1 & n & 10000
a Distanµele pân  la cartierele generale inamice sunt numere naturale nenule & 2000000000.
a Pentru 50% dintre teste cerinµa este 1.

40
CAPITOLUL 5. OJI 2016 41

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 9 4

Timp maxim de executare/test: 0.6 secunde


Memorie: total 4 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 10 KB

5.1.1 Indicaµii de rezolvare


Prof. Sandor Lukacs Liceul Teoretic Onisifor Ghibu Oradea

Metoda 1

Pentru a distruge centrul general al inamicilor situat la distanta d folosind


o cantitate minima de narun k, trebuie sa determinam puterea maxima p pentru
care d=k^p (k la puterea p).

Pentru aceasta vom descompune d in factori primi.

Fie d=k^p=x1^a1 x2^a2 ... xh^ah descompunerea in factori primi a lui d.

p trebuie sa divida a1, a2, ..., ah si sa fie maxim posibil.

Deci p=cmmdc(a1, a2, ..., ah).

Prin urmare k= x1^(a1/p) x2^(a2/p) ... xh^(ah/p)

Pentru a ne incadra in timp trebuie sa optimizam descompunerea in factori primi.

Pentru aceasta vom precalcula numerele prime <2*10^9, folosind ciurul lui Eratosten

Metoda 2.

Se pot genera pe rand valorile de la k <-- 2 la sqrt(2000000000) si se ridica fieca


valoare pe rand la puterile p=2,..., 32 (datorita restrictiilor, nivelul maxim poat
doar 32). Daca se gaseste egalitatea kp = d s-a obtinut cantitatea minima.

Aceasta solutie nu se incadreaza in timp pentru toate testele.

5.1.2 Cod surs 

Listing 5.1.1: arma.cpp

1 //Sandor Lukacs 100 puncte


2 #include <iostream>
3 #include <fstream>
CAPITOLUL 5. OJI 2016 42

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 {
79 if(p % 2 == 1)
CAPITOLUL 5. OJI 2016 43

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;
155 }
CAPITOLUL 5. OJI 2016 44

Listing 5.1.2: arma_ema.cpp

1 //Em. Cerchez 100 puncte


2 #include <fstream>
3
4 #define VMAX 50000
5 #define NMAX 10000
6 #define NRMAXPRIME 50000
7
8 using namespace std;
9
10 ifstream fin("arma.in");
11 ofstream fout("arma.out");
12
13 int n, nrprim, nrf, cerinta;
14 bool ciur[VMAX];
15 int prim[NRMAXPRIME];
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 {
46 int d, j;
47 for (d=2; d*d<VMAX; d++)
48 if (!ciur[d])
49 for (j=d*d; j<VMAX; j+=d)
50 ciur[j]=1;
51
52 // transfer intr-un vector numerele prime <=vmax
53 prim[0]=2;
54 nrprim=1;
55 for (d=3; d<VMAX; d+=2)
56 if (!ciur[d])
57 prim[nrprim++]=d;
58 }
59
60 void determina(int d, int &p, int &c)
61 {
62 int m, j;
63 nrf=0;
64 for (j=0; j<nrprim && prim[j]*prim[j]<=d; j++)
65 {
66 for (m=0; d%prim[j]==0; m++,d/=prim[j]);
67 if (m)
68 {
69 fp[nrf]=prim[j];
70 mf[nrf++]=m;
71 }
72 }
73
74 if (d>1)
CAPITOLUL 5. OJI 2016 45

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 }

Listing 5.1.3: arma_ema_no_rec.cpp

1 //Em. Cerchez 100 puncte


2 #include <fstream>
3
4 #define VMAX 50000
5 #define NMAX 10000
6 #define NRMAXPRIME 50000
7
8 using namespace std;
9
10 ifstream fin("arma.in");
11 ofstream fout("arma.out");
12
13 int n, nrprim, nrf, cerinta;
14 bool ciur[VMAX];
15 int prim[NRMAXPRIME];
16 int fp[NRMAXPRIME];
17 int mf[NRMAXPRIME];
18 int p[NMAX];
CAPITOLUL 5. OJI 2016 46

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]);
92 return d;
93 }
94
CAPITOLUL 5. OJI 2016 47

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 }

5.1.3 *Rezolvare detaliat 

5.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 ecare jeton ind 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  e 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 semnicaµia din enunµ. Pe cea de a doua linie se a  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  scris un num r natural repre-
zentând punctajul maxim pe care îl poate obµine Bogdan la mutarea sa, ³tiind c  ambii juc tori
joac  optim.

Restricµii ³i preciz ri
a 3 & n & 100000
a 1 & k & n©3
a Valorile înscrise pe jetoane sunt numere naturale nenule & 109 .
a Dup  ce Ana extrage jetoanele sale, jetoanele r mase pe tabl  î³i vor p stra poziµiile iniµiale.

Exemple
CAPITOLUL 5. OJI 2016 48

ks.in ks.out Explicaµii


10 3 12 Exist  mai multe mut ri optime pentru Ana. Una dintre
1 2 5 4 15 2 4 5 1 6 acestea este de a selecta jetoanele 5, 4, 15. În acest caz
cea mai bun  mutare pentru Bogdan este de a selecta
jetoanele 5, 1, 6 care i-ar aduce un punctaj egal cu 12,
maxim posibil. O alt  mutare optim  pentru Ana este de
a alege jetoanele 4, 15, 2, sau 15, 2, 4 punctajul maxim
pe care îl poate obµine Bogdan r mânând acela³i, 12.

Timp maxim de executare/test: 0.2 secunde


Memorie: total 4 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 10 KB

5.2.1 Indicaµii de rezolvare


Prof. Emanuela Cerchez Colegiul National ’’Emil Racovita’’ Iasi

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)

maxst[i]=max{S[j]| 1 <= j <= i-K}


maxdr[i]=max{S[j]|i <= j <= N-k+1}

Valorile pentru care nu se poate calcula maxst/maxdr le initializam cu -1


(cazul in care nu exista o secventa de K elemente)

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.

Sa consideram ca Ana elimina secventa a[i], a[i+1], ..., a[i+K-1]

Punctajul maxim pe care il poate obtine Bogdan este pctmax=max{maxst[i-1], maxdr[i+

Prin urmare realizam o parcurgere si alegem acel i pentru care pctmax este minim.

Timp de executie: O(n).


O solutie avand complexitatea O(n^2) va obtine punctaj partial.

5.2.2 Cod surs 

Listing 5.2.1: ks_dan.cpp

1 //Popa Daniel - 100 puncte


2 #include <iostream>
3 #include <fstream>
4 #include <algorithm>
5
6 using namespace std;
7
8 ifstream fin("ks.in");
9 ofstream fout("ks.out");
10
11 const int nm=100005,gol=-1;
12 long long int a[nm],st[nm],dr[nm],s[nm],i,n,k,su=0,rez=200000000000000000;
13
14 int main()
15 {
CAPITOLUL 5. OJI 2016 49

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 }

Listing 5.2.2: ks_ema_n.cpp

1 //Em. Cerchez 100 puncte O(n)


2 #include <fstream>
3 #define NMAX 100002
4
5 using namespace std;
6
7 int n, K;
8 long long int S[NMAX];
9 long long int maxst[NMAX];
10 long long int maxdr[NMAX];
11 int a[NMAX];
12
13 ifstream fin("ks.in");
14 ofstream fout("ks.out");
15
16 void citire();
17 void det_sume();
18 long long int rezolva();
19
20 int main()
21 {
22 citire();
23 det_sume();
24 fout<<rezolva()<<’\n’;
25 fout.close();
26 return 0;
27 }
28
29 void citire()
30 {int i;
31 fin>>n>>K;
32 for (i=1; i<=n; i++) fin>>a[i];
33 }
34
35 void det_sume()
36 {int i;
37 for (i=1; i<=K; i++) {S[1]+=a[i]; maxst[i]=-1;}
38 maxst[K]=S[1];
39 for (i=2; i<=n-K+1; i++)
40 {S[i]=S[i-1]-a[i-1]+a[i+K-1];
41 if (S[i]>maxst[i+K-2])
42 maxst[i+K-1]=S[i];
43 else
44 maxst[i+K-1]=maxst[i+K-2];
45 }
46
47 for (i=n; i>n-K+1; i--) maxdr[i]=-1;
48 maxdr[n-K+1]=S[n-K+1];
CAPITOLUL 5. OJI 2016 50

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 }

5.2.3 *Rezolvare detaliat 


Capitolul 6

OJI 2015

6.1 dominant
Problema 1 - dominant 100 de puncte
Considerând un ³ir de valori binare, numim secvenµ  dominant  un set de elemente aate 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 ³ierului de ie³ire va  un singur num r
natural reprezentând lungimea unei secvenµe dominante maximale.
a varianta 2: dac  V 2, atunci pe prima linie a ³ierului de ie³ire va  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.

51
CAPITOLUL 6. OJI 2015 52

Timp maxim de executare/test: 0.3 secunde


Memorie: total 32 MB
Dimensiune maxim  a sursei: 10 KB

6.1.1 Indicaµii de rezolvare


Autor: prof. Dan Pracsiu, Liceul ’’Stefan Procopiu Vaslui’’

Fie s sirul de biti si fie n lungimea acestuia. Prin s[i..j] vom nota secventa
si, si+1, ..., sj.

Se construieste vectorul dif de lungime n in care, pentru i=1..n, dif[i] memoreaza


diferenta dintre numarul de valori de 1 si numarul de valori de 0 din secventa s[1.
Vom considera ca dif[0]=0. Evident ca in sir pot sa apara si valori negative.

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,

st[x] = cea mai din stanga pozitie unde apare valoarea x


dr[x] = cea mai din dreapta pozitie unde apare valoarea x

Pentru ca x poate lua valori intre âĂŞn si n, atunci trebuie avut grija la modul in
care se contruiesc vectorii st si dr.

Complexitatea algoritmului este O(n).

De remarcat in final ca un algoritm care cauta binar lungimea maxima a secventei do


este eronat. In exemplul 11000001111, in cautarea binara daca la un pas s-ar cauta
o secventa de lungime 8 nu s-ar gasi, ceea ce ar duce la presupunerea ca lungimea e
sigur mai mica. Dar intreg sirul la lungime 11 este in acest exemplu o secventa dom

6.1.2 Cod surs 

Listing 6.1.1: domDP_citire_elem_cu_elem.cpp

1 /*
2 Complexitate O(n) - 100 puncte
3 */
4 #include <iostream>
5 #include <fstream>
6 #include <cstring>
7
8 #define Nmax 300005
9 #define inFile "dominant.in"
10 #define outFile "dominant.out"
11
12 using namespace std;
13
14 char s[Nmax];
15 int x[2*Nmax], y[2*Nmax], *st, *dr;
16 int optiune;
17
18 int main()
19 {
20 int i, n, suma, vmin, vmax, lgMax;
21 // citire
22 ifstream fin(inFile);
23 fin >> optiune;
CAPITOLUL 6. OJI 2015 53

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 6. OJI 2015 54

100 fout.close();
101
102 return 0;
103 }

Listing 6.1.2: domDPliniar.cpp

1 /*
2 Complexitate O(n) - 100 puncte
3 */
4 #include <iostream>
5 #include <fstream>
6 #include <cstring>
7
8 #define Nmax 300005
9 #define inFile "dominant.in"
10 #define outFile "dominant.out"
11
12 using namespace std;
13
14 char s[Nmax];
15 int x[2*Nmax], y[2*Nmax], *st, *dr;
16 int optiune;
17
18 int main()
19 {
20 int i, n, suma, vmin, vmax, lgMax;
21 // citire
22 ifstream fin(inFile);
23
24 fin >> optiune;
25 fin >> (s + 1);
26
27 s[0] = ’*’;
28 n = strlen(s) + 1;
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
CAPITOLUL 6. OJI 2015 55

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)
79 suma++;
80
81 for (i = lgMax + 1; s[i]; ++i)
82 {
83 if (s[i-lgMax] == ’0’)
84 s0--;
85 else
86 s1--;
87
88 if (s[i] == ’0’)
89 s0++;
90 else
91 s1++;
92
93 if (s1 > s0)
94 suma++;
95 }
96
97 ofstream fout(outFile);
98 if (optiune == 1)
99 fout << lgMax << "\n";
100 else
101 fout << suma << "\n";
102
103 fout.close();
104
105 return 0;
106 }

Listing 6.1.3: domMN_n.cpp

1 //O(N)
2 #include <fstream>
3 #include <cstring>
4
5 #define DIM 300010
6 using namespace std;
7
8 char S[DIM];
9
10 int d[2*DIM + 22];
11 int f[2*DIM + 22];
12 int *D = d + DIM + 4, *F = f + DIM + 4, V;
13
14 int n, i, maxim, sc, nr, sum;
15
16 int main()
17 {
18 ifstream fin ("dominant.in");
19 ofstream fout("dominant.out");
20 fin>>V;
21
22 memset(f, -1, sizeof(f));
23
24 fin>>S+1;
25 F[0] = 0; // F[i] = indicele primei aparitii a sumei i
26 D[0] = 0; // D[i] = pozitia de inceput a unei secvente
27 // ce se termina cu suma i
28
29 sum = 0;
30 for (i=1;S[i];i++)
31 {
32 if (S[i] == ’1’)
33 sum++;
34 else
CAPITOLUL 6. OJI 2015 56

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 }

Listing 6.1.4: domMN_n_ccc.cpp

1 //O(N)
2 #include <cstdio>
3 #include <cstring>
4
5 #define DIM 300110
6 using namespace std;
7
8 char S[DIM];
9
10 int d[2*DIM + 22];
11 int f[2*DIM + 22];
12 int *D = d + DIM + 4, *F = f + DIM + 4, V;
13
14 int n, i, maxim, sc, nr, sum;
15 char c;
16
17 int main()
18 {
19 FILE *fin = fopen("dominant.in", "r");
20 FILE *fout = fopen("dominant.out", "w");
21
22 fscanf(fin,"%d",&V);
23 //fin>>V;
24
25 memset(f, -1, sizeof(f));
26
27 int k = 0;
28 while (fscanf(fin,"%c",&c) == 1)
29 {
30 S[++k] = c;
31 }
32
33 // fin>>S+1;
34 F[0] = 0; // F[i] = indicele primei aparitii a sumei i
35 D[0] = 0; // D[i] = pozitia de inceput a unei secvente ce se termina
36 // cu suma i
37
38 sum = 0;
CAPITOLUL 6. OJI 2015 57

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 }

Listing 6.1.5: domMN_nlogn.cpp

1 // n log n
2 #include <fstream>
3 #include <cstring>
4 #include <algorithm>
5
6
7 #define INF 1000000
8 #define DIM 300010
9
10 using namespace std;
11
12 char S[DIM];
13 int v[DIM];
14
15 pair<int, int> w[DIM];
16
17 int n, i, minim1, minim2, maxim, nr, j, m, V;
18
19 int main()
20 {
21 ifstream fin ("dominant.in");
22 ofstream fout("dominant.out");
23
24 fin>>V;
25 fin>>S+1;
26 m = n = strlen(S+1);
27
28 for (i=1;i<=n;i++)
29 {
30 if (S[i] == ’1’)
CAPITOLUL 6. OJI 2015 58

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 }

6.1.3 *Rezolvare detaliat 


CAPITOLUL 6. OJI 2015 59

6.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 e mai mare ca 1, iar prima ³i ultima dal  s  e 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 lexicograc.
Compararea lexicograc  a dou  ³iruri de numere este o extensie la numere a compar rii
alfabetice a dou  cuvinte. Astfel, ind 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 lexicograc 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 lexicograc
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 aate dup  aceasta.

Cerinµe
Cunoscând lungimea drumului, determinaµi num rul minim de dale necesare pav rii ³i posibi-
litatea de pavare cu num r minim de dale, care este cea mai mic  din punct de vedere lexicograc.

Date de intrare
Prima linie a ³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 ³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 ³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 lexicograc.

Restricµii ³i preciz ri
a V " r1, 2x
a 1 & N & 1000000000
a Pentru 30% din punctaj V 1.

Exemple
pavare.in pavare.out Explicaµii
1 5 Pentru drumul de lungime 7 sunt necesare 5 dale.
7
2 1 1 2 2 1 Soluµiile cu num r minim de dale sunt: 1 1 2 2 1, 1 2 1 2 1, 1 2
7 2 1 1.

Timp maxim de executare/test: 0.3 secunde


Memorie: total 4 MB
Dimensiune maxim  a sursei: 5 KB

6.2.1 Indicaµii de rezolvare


Autor: prof. Daniel Popa, Colegiul National ’’Aurel Vlaicu’’, Orastie

Calculam distanta maxima ce poate fi acoperita folosind x dale ce respecta


cerinta si obtinem:
x 1 2 3 4 5 6 7 8 9
Dist(x) 1 2 4 6 9 12 16 20 25
CAPITOLUL 6. OJI 2015 60

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.

Se observa ca pentru x impar Dist(x)=((x+1)/2)2,


iar pentru x par Dist(x)=x/2*(x/2+1).

Calculand, se poate obtine x=2*sqrt(n)-1+( sqrt(n) != (int)sqrt(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.

2) Bazandu-ne pe ideea de mai sus generam rand pe rand lungimile dalelor


(1, 2, 3, .... ; pe pozitia i avand o dala de lungime i) avand grija ca
dalele de dinaintea pozitiei x/2+x%2 (de la x/2-Dist(x)+n+1+x%2 pana la
x/2+x%2) sa fie mai mici cu o unitate fata de cele calculate prin metoda
anterioara, iar dalele de la pozitia x/2+x%2+1 pana la final vor avea
valori descrescatoare de la x/2 pana la 1.

Rezolvarea primei cerinte se face in timp constant.

Constructia solutiei minim lexicografice de lungime minima are complexitatea


in timp de ordin sqrt(n).

6.2.2 Cod surs 

Listing 6.2.1: pavareAB.cpp

1 /* ===================================
2 Pavare Alin
3 =====================================*/
4 #include <fstream>
5 #include <iostream>
6
7 #define FIN "pavare.in"
8 #define FOU "pavare.out"
9 #define NMAX 100001
10
11 using namespace std;
12
13 long int N; //lungimea drumului;
14 short int V; //tipul testului
15 short int NrApar[ NMAX ]; //numarul de aparitii pentru fiecare posibila dala
16 long int K, i, CN, NrDale, Dala;
17
18 int main()
19 {
20 ifstream IN(FIN);
21 // citire date de intrare
22 IN>>V>>N;
23 IN.close();
24
25 //rezolvare
26 ofstream OU(FOU);
27 if( V == 1)
28 {
29 //rezolvarea cerintei 1
30 CN = N;
31 NrDale = 0;
32
CAPITOLUL 6. OJI 2015 61

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 }

Listing 6.2.2: pavareBackMN.cpp

1 #include <iostream>
2 #include <fstream>
3 #define INF 1000000010
4
5 using namespace std;
6
7 int v[100002], vminim[100002], minim = INF, k, i, s, n, V;
8
9 void back(int k)
10 {
11 if (s == 0)
12 {
CAPITOLUL 6. OJI 2015 62

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 }

Listing 6.2.3: pavareCT.cpp

1 #include <iostream>
2 #include <fstream>
3
4 using namespace std;
CAPITOLUL 6. OJI 2015 63

5
6 long n, s=0, a[1000000],i=1;
7 int v,k;
8
9 int main()
10 {
11 fstream f("pavare.in", ios::in);
12 f>>v;
13 f>>n;
14 a[1]=1;
15
16 while(s<n/2)
17 {
18 i++;
19 a[i]=a[i-1]+1;
20 s=s+a[i];
21 }
22
23 while (a[i]!=1)
24 {
25 i++;
26 a[i]=a[i-1]-1;
27 }
28
29 fstream g("pavare.out", ios::out);
30
31 if (v==1)
32 g<<i;
33 else
34 {
35 k=s-n;
36 for(int j=i/2+i%2; j>i/2+i%2-k; j--)
37 {
38 a[j]=a[j]-1;
39 }
40
41 for(int j=1; j<=i; j++)
42 g<<a[j]<<" ";
43 }
44 }

Listing 6.2.4: pavaredan.cpp

1 #include <iostream>
2 #include <fstream>
3 #include <cmath>
4
5 using namespace std;
6
7 ifstream fin("pavare.in");
8 ofstream fout("pavare.out");
9
10 int i,n,k,c,t,p,z;
11
12 int main()
13 {
14 fin>>c>>n;
15 k=2*sqrt(n)-1+(sqrt(n)!=(int)sqrt(n));
16 if(c==1)
17 fout<<k;
18 else
19 for(i=1,z=1,p=((k+k%2)/2)*((k+k%2)/2+1-k%2),t=1;i<=k;i++,z+=t)
20 {
21 fout<<z-((i>=(k/2+n-p+1+k%2))&&(i<=k/2+k%2))<<" ";
22 if(i>=k/2+k%2)
23 t=-1;
24 if(i==k/2&&(k%2==0))
25 t=0;
26 }
27
28 fout.close();
29 fin.close();
30 return 0;
31 }
CAPITOLUL 6. OJI 2015 64

Listing 6.2.5: pavareDP.cpp

1 #include <fstream>
2 #include <cmath>
3
4 using namespace std;
5
6 int a[100000];
7 int V;
8
9 int main()
10 {
11 int k, n, i, s;
12
13 ifstream fin("pavare.in");
14 fin >> V;
15 fin >> s;
16 fin.close();
17
18 k = (int)(-1 + sqrt(1.0 + 4 * s)) / 2;
19
20 for (i = 1; i <= k; i++)
21 a[i] = i;
22
23 n = k;
24 s = s - k * (k + 1);
25 if (s > 0)
26 {
27 if (s >= k + 2)
28 {
29 a[++n] = k+1;
30 s -= (k+1);
31 }
32 }
33
34 for (i=k; i >= 1; --i)
35 a[++n] = i;
36
37 // inserez s
38 if (s > 0)
39 {
40 for (i = n; i >= s; i--)
41 a[i+1] = a[i];
42 a[s] = s;
43 n++;
44 }
45
46 ofstream fout("pavare.out");
47 if (V == 1)
48 {
49 fout << n << "\n";
50 return 0;
51 }
52
53 for (i = 1; i <= n; i++)
54 fout << a[i] << " ";
55
56 fout << "\n";
57 fout.close();
58 return 0;
59 }

Listing 6.2.6: pavareMN.cpp

1 #include <fstream>
2 #include <iostream>
3 #include <cmath>
4
5 using namespace std;
6
7 long long n, i, r, x, k, V;
8
9 ifstream fin ("pavare.in");
10 ofstream fout("pavare.out");
11
CAPITOLUL 6. OJI 2015 65

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 }

6.2.3 *Rezolvare detaliat 


Capitolul 7

OJI 2014

7.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 ecare celul  se a  o s geat  (sus, jos, stânga sau dreapta), ca în gura
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 7.1: arrows
va p r si niciodat  tabla de joc. De exemplu, toate poziµiile din gur  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 conguraµ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 specicat ;
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  e rezolvat  (1, 2 sau
3). Pe a doua linie se a  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 a  câte M numere din mulµimea r1, 2, 3, 4x
reprezentând s geµile aate în celulele de pe tabla de joc (1 semnicâ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 a  poziµia de start specicat . Valorile
scrise pe aceea³i linie în ³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  scris un num r natural
reprezentând r spunsul pentru cerinµa specicat  pe prima linie a ³ierului de intrare.

Restricµii ³i preciz ri
a 1 & N, M & 500
a Liniile sunt numerotate de la 1 laN , 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.

66
CAPITOLUL 7. OJI 2014 67

Exemple
arrows.in arrows.out Explicaµii
1 2000 Exemplul corespunde tablei de joc din gur .
6 5 Punctajele pentru ecare 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 aat  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

Timp maxim de executare/test: 0.5 secunde


Memorie: total 5 MB din care pentru stiv  1 MB
Dimensiune maxim  a sursei: 10 KB

7.1.1 Indicaµii de rezolvare


prof. Emanuela Cerchez, C. N. ’’Emil Racovita’’ Iasi

Cerinta 1.

Pentru a rezolva cerinta 1. este suficient sa parcurgem traseul indicat


de sageti si sa contorizam celulele parcurse. Traseul se incheie la iesirea
de pe tabla de joc sau cand am ajuns intr-o pozitie care a mai fost deja
parcursa (in acest caz pozitia de start este o pozitie favorabila si
punctajul se obtine inmultind cu 1000 numarul de celule distincte
de pe traseu).

Pentru a testa cu usurinta daca am iesit de pe tabla de joc vom borda


matricea (adaugand linia 0, coloana 0, linia n+1 si coloana m+1) cu
valoarea 0. intalnirea unei pozitii pe care se afla valoarea 0 semnifica
iesirea de pe tabla de joc.

Pentru a ne deplasa cu usurinta in sensul sagetilor, vom defini doi vectori


de deplasare dl (deplasare pe linie) si dc (deplasare pe coloana) care
contin deplasarea relativa corespunzatoare celor 4 sageti:

int dl[5]={0,0,-1,0,1};
int dc[5]={0,1,0,-1,0};

Daca pozitia curenta este pe linia x si coloana y atunci pozitia urmatoare


CAPITOLUL 7. OJI 2014 68

se determina astfel:

dir=a[x][y];
x+=dl[dir];
y+=dc[dir];

(considerand ca matricea a retine configuratia tablei de joc).

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).

Cand parcurgem un traseu, vom marca cu 1 pozitiile parcurse de pe traseul


curent in matricea pct, pentru a sti daca traseul se termina intr-o pozitie
deja vizitata de pe traseul curent.

Dupa ce am identificat un traseu, putem determina punctajul asociat acestuia.

In pct[i][j] retinem punctajul care se obtine pe traseul care incepe in


pozitia i, j, daca pozitia i, j este favorabila, respectiv - punctajul
respectiv (daca pozitia este nefavorabila).

Cerintele 2 si 3

Pe tabla de joc se afla doua tipuri de pozitii: favorabile si nefavorabile.


O pozitie nefavorabila este o pozitie care se afla pe un traseu liniar,
care se termina in afara tablei de joc. O pozitie favorabila fie se afla
pe un circuit, fie se afla pe un traseu liniar care se termina intr-o pozitie
aflata pe un circuit.

Tabla de joc este de dimensiuni mari, prin urmare trebuie sa rezolvam eficient
cerintele b si c.

O abordare care parcurge fiecare pozitie din matrice si calculeaza punctajul


pentru pozitia respectiva obtine intre 20 si 60 puncte, in functie de eficienta
acesteia.

Pentru a rezolva cerintele 2 si 3, vom parcurge matricea si cand gasim o


pozitie care nu a mai fost vizitata o vom alege ca pozitie de start.

Parcurgem traseul care incepe in pozitia de start respectiva, calculand si


numarul de pozitii parcurse.

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).

Pentru a determina natura si punctajul pentru toate pozitiile de pe traseul


curent il vom parcurge inca o data si vom marca in matricea pct punctajele
pozitiilor de pe traseu, conform codificarii descrise mai sus.

Pentru a calcula punctajul, atunci cand ne intersectam cu un traseu deja


parcurs, vom adauga numarul de celule vizitate pana la momentul intersectiei
cu punctajul traseului cu care ne-am intersectat.

Pentru a determina toate punctajele de pe un traseu, trebuie sa fim atenti


ca toate punctele de pe un circuit trebuie sa aiba acelasi punctaj (egal cu
CAPITOLUL 7. OJI 2014 69

lungimea circuitului). In cazul unui traseu liniar, punctajul descreste


cu 1 la fiecare pozitie parcursa din pozitia de start (pana la iesirea din
matrice sau pana la intalnirea unui circuit).

Numarul de elemente pozitive din matricea pct reprezinta raspunsul la


cerinta b (numarul de pozitii favorabile). Elementul maxim (considerat
in valoare absoluta, adica in modul) din matricea pct este raspunsul
la cerinta 3.

7.1.2 Cod surs 

Listing 7.1.1: arrows_100_ok.cpp

1 //Emanuela Cerchez - 100 puncte


2 #include <fstream>
3 #include <cassert>
4
5 #define NMAX 502
6
7 using namespace std;
8
9 int n, m, xs, ys, nr_fav, pct_max, cerinta;
10 char a[NMAX][NMAX];
11 int pct[NMAX][NMAX];
12
13 int dl[5]={0,0,-1,0,1};
14 int dc[5]={0,1,0,-1,0};
15
16 ifstream fin("arrows.in");
17 ofstream fout("arrows.out");
18
19 void citire();
20 int goxy(int, int);
21
22 int main()
23 {int i, j;
24 citire();
25
26 for (i=1; i<=n; i++)
27 for (j=1; j<=m; j++)
28 if (!pct[i][j])
29 goxy(i,j);
30
31 if (cerinta==1) {
32 if (pct[xs][ys]<0) fout<<-pct[xs][ys]<<’\n’;
33 else fout<<1000*pct[xs][ys]<<’\n’;
34 }
35 else
36 {
37 for (i=1; i<=n; i++)
38 for (j=1; j<=m; j++)
39 if (pct[i][j]>0)
40 {
41 nr_fav++;
42 if (pct_max<1000*pct[i][j]) pct_max=1000*pct[i][j];
43 }
44 else
45 {
46 if (pct_max<-pct[i][j]) pct_max=-pct[i][j];
47 }
48
49 if (cerinta==2)
50 fout<<nr_fav<<’\n’;
51 else
52 fout<<pct_max<<’\n’;
53 }
54 fout.close();
55 return 0;
56 }
57
CAPITOLUL 7. OJI 2014 70

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 }

Listing 7.1.2: arrows_100_soa.cpp


CAPITOLUL 7. OJI 2014 71

1 //Sofia Vitelaru - 100 puncte


2 #include <fstream>
3
4 using namespace std;
5
6 ifstream f("arrows.in");
7 ofstream g("arrows.out");
8
9 int t[502][502],n,m,i,j,nr,x,y,
10 dx[]={0,0,-1,0,1},dy[]={0,1,0,-1,0},tk,val,l,c,Max,nr1,tip;
11 bool ciclu[501][501];
12 char a[501][501];
13
14 int main()
15 {
16 f>>tip>>n>>m;
17 for(i=1;i<=n;i++){
18 for(j=1;j<=m;j++){
19 f>>x;
20 a[i][j]=x;
21 }
22 }
23
24 int mx=n*m*1000;
25
26 for(i=0;i<=m+1;i++)
27 t[0][i]=t[n+1][i]=-1;
28
29 for(i=0;i<=n+1;i++)
30 t[i][0]=t[i][m+1]=-1;
31
32 for(i=1;i<=n;i++)
33 for(j=1;j<=m;j++)
34 if(t[i][j]==0){ //daca punctul nu a fost atins
35 nr=mx;x=i;y=j;
36 while(t[x][y]==0&&t[x][y]!=-1){ //incerc sa determin un ciclu
37 nr++;
38 t[x][y]=nr;
39 int k=a[x][y];
40 x=x+dx[k];
41 y=y+dy[k];
42 }
43
44 if(t[x][y]!=-1){ // am gasit un ciclu
45 if(t[x][y]>mx){
46 nr=nr-mx;
47 tk=tk+nr;
48 int l=x, c=y;nr1=t[x][y]-mx;
49 int val=(nr-nr1+1)*1000;
50 if(val>Max)
51 Max=val;
52
53 do{
54 t[x][y]=val;ciclu[x][y]=1;
55 int k=a[x][y];
56 x=x+dx[k];
57 y=y+dy[k];
58 }while(!(l==x&&c==y));
59
60 if(!(x==i&&y==j)){
61 val=t[l][c];
62 nr=nr1-1;
63 l=i;c=j;
64 if(nr*1000+val>Max)
65 Max=nr*1000+val;
66
67 while(!(l==x&&c==y)){
68 t[l][c]=nr*1000+val;ciclu[l][c]=1;
69 nr--;
70 int k=a[l][c];
71 l=l+dx[k];
72 c=c+dy[k];
73 }
74 }
75
CAPITOLUL 7. OJI 2014 72

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 }

7.1.3 *Rezolvare detaliat 

7.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 .

Cerinµe
CAPITOLUL 7. OJI 2014 73

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 a  num rul natural N.

Date de ie³ire
Fi³ierul de ie³ire tcif.out va conµine o singur  linie pe care va  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.

Restricµii ³i preciz ri
a N va avea cel puµin o cifr  ³i cel mult 1000 de cifre.
a Pentru 10% dintre teste, N 30000
a Pentru alte 40% dintre teste, N va avea cel mult 14 cifre
a 0 & c1, c2, c3 & 9; c1, c2 ³i c3 sunt distincte dou  câte dou 
a 1 & A, 1 & B, 1 & C, A  B  C & 1000
a Datele de intrare sunt alese astfel încât va exista o soluµie.

Exemple
tcif .in tcif .out Explicaµii
2 2 2 3 2 4 322344 Num rul minim strict mai mare decât 293187 care conµine dou 
293187 cifre 3, dou  cifre 2 și dou  cifre 4 este 322344
2 3 1 1 0 6 100016 Num rul minim strict mai mare decât 44589 care conµine dou 
44589 cifre 1, trei cifre 0 și o cifr  6 este 100016

Timp maxim de executare/test: 0.5 secunde


Memorie: total 2 MB din care pentru stiv  1 MB
Dimensiune maxim  a sursei: 10 KB

7.2.1 Indicaµii de rezolvare


prof. Dan Pracsiu, Gr. sc. ’’Stefan Procopiu’’ Vaslui

Solutia 1 - prof. Dan Pracsiu

Mai intai vom considera ca avem relatiile c1 < c2 < c3 (in caz contrar
ordonam crescator cele trei cifre).

Apar doua cazuri:

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.

2. A+B+C este egal cu numarul cifrelor lui N.


Unui numar format din A cifre c1, B cifre c2 si C cifre c3 ii putem asocia
un numar in baza 3, in care sunt exact A cifre 0, B cifre 1 si C cifre 2.

Vom genera in ordine crescatoare toate numerele in baza 3 de A+B+C cifre si


la fiecare pas verificam daca numarul generat este mai mic sau mai mare
decat N. Primul numar strict mai mare decat N care a fost gasit este si
solutia problemei.
CAPITOLUL 7. OJI 2014 74

Complexitatea este O(3A+B+C)

Solutia 2 - prof. Emanuela Cerchez

100 puncte

Cazul I (N=A+B+C) il tratam in mod similar ca la solutia 1.

Cazul al II-lea:

Plasam cifrele numarului N intr-un vector.

Parcurgem vectorul incepand cu cifra cea mai semnificativa a numarului si


ne oprim pe prima pozitie care contine fie o cifra diferita de c1, c2, c3,
fie o cifra egala cu c1, c2 sau c3 dar pentru care am depasit deja numarul
de aparitii disponibile.

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).

Maresc cifra de pe aceasta pozitie, apoi completez pozitiile urmatoare din


vectorul de cifre cu cele mai mici valori posibile (ca in cazul I).

Complexitatea este O(lg) unde lg este numarul de cifre din N.

Exista numeroase alte solutii care obtin punctaje partiale. De exemplu,


o solutie care trateaza cazul I, iar pentru cazul al doilea porneste de
la valoarea N+1 si incrementeaza aceasta valoare cu 1 pana se obtine un
numar care verifica restrictiile din enunt obtine 35 de puncte.

7.2.2 Cod surs 

Listing 7.2.1: tcif_100p.cpp

1 // implementare Dan Pracsiu


2 // Complexitate O(nrcif), unde nrcif este numarul
3 // cifrelor lui N
4 // 100 puncte
5
6 #include <fstream>
7
8 #define inFile "tcif.in"
9 #define outFile "tcif.out"
10 #define Dim 1003
11
12 using namespace std;
13
14 int t[Dim]; // in a retin cifrele lui n
15 int sol[Dim]; // in sol retin numarul solutie
16 int n; // numarul de cifre ale lui N
17 int a[3], cif[3];
18
19 int AlegeCifra(int x)
20 {
21 if (cif[0] >= x && a[0] > 0)
22 {
23 //cout << x << "\n";
24 return 0;
25 }
26 if (cif[1] >= x && a[1] > 0) return 1;
CAPITOLUL 7. OJI 2014 75

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]]++;
100 sol[i] = k;
101 a[k]--;
102 for (k = 0; k < 3; ++k)
CAPITOLUL 7. OJI 2014 76

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 }

Listing 7.2.2: tcif_ema100p.cpp

1 //Em. Cerchez, C. N. "E. Racovita" Iasi - 100 puncte


2 #include <fstream>
3 #include <cstring>
4
5 #define LGMAX 1024
6
7 using namespace std;
8
9 ifstream fin("tcif.in");
10 ofstream fout("tcif.out");
11
12 int cif[4];
13 int nr[10];
14 char N[LGMAX];
15 int cn[LGMAX];
16 int lg;
17
18 int apare(int x);
19 int potmari(int x);
20
21 int main()
22 {int i, j, A, B, C, aux, poz;
23 fin>>A>>B>>C;
24 for (i=1; i<=3; i++) fin>>cif[i];
25 fin>>N;
26 nr[cif[1]]=A; nr[cif[2]]=B; nr[cif[3]]=C;
27
28 //ordonez cifrele crescator
29 for (i=1; i<3; i++)
30 for (j=i+1; j<=3; j++)
31 if (cif[i]>cif[j])
32 {aux=cif[i]; cif[i]=cif[j]; cif[j]=aux;}
33
34 //extrag cifrele lui N
35 //clasez cifrele lui N in vectorul cn in ordine descrescatoare
36 lg=strlen(N);
37 for (i=0; N[i]; i++) cn[lg-i-1]=N[i]-’0’;
38
39 //cazul I lg<A+B+C
40 if (lg<A+B+C)
41 {
42 if (!cif[1])
43 {fout<<cif[2]; nr[cif[2]]--;}
44 for (i=1; i<=3; i++)
45 for (j=0; j<nr[cif[i]]; j++) fout<<cif[i];
46 fout<<’\n’;
47 fout.close();
48 return 0;
49 }
50
51 //cazul II lg==A+B+C
52 //caut prima cifra din N care poate fi marita
53 //sigur exista, altfel nu am avea solutie
54
55 for (i=lg-1; i>0; i--)
56 {poz=apare(cn[i]);
57 if (poz) nr[cif[poz]]--;
58 else break;
59 }
CAPITOLUL 7. OJI 2014 77

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 }

Listing 7.2.3: Tcif_GCC_100p.cpp

1 //Claudiu Gorea - 100 puncte


2 #include <cstdio>
3 #include <cstring>
4
5 using namespace std;
6
7 int f[5],c[5],i,j,aux,d,l,a[1005],b[1005],st[1005],egale,gata;
8 char s[1005];
9
10 void solutie(int vf)
11 {
12 int k;
13 for(k=1;k<=vf;k++) b[k]=st[k];
14 k=vf;
15 while(f[1]>0) {b[++k]=c[1]; f[1]--; }
16 while(f[2]>0) {b[++k]=c[2]; f[2]--; }
17 while(f[3]>0) {b[++k]=c[3]; f[3]--; }
18
19 }
20
21 int valid(int vf)
22 {
23 if (vf==1 && st[vf]==0) return 0;
24 return 1;
25 }
CAPITOLUL 7. OJI 2014 78

26
27 void back(int vf)
28 {
29 int k;
30 for(k=1;k<=3 && egale==1;k++)
31 if(c[k]>=a[vf] && f[k]>0)
32 {
33 st[vf]=c[k];
34 f[k]--;
35 if (valid(vf))
36 {
37 if (st[vf]>a[vf] || vf==l)
38 {
39 egale=0;/// oprim back, cand nu mai avem cifre identice...
40 /// de la pozitia vf, pana la l, vom pune cifrele
41 /// in ordinea crescatoare
42 solutie(vf);
43 }
44 else
45 back(vf+1);
46 }
47
48 f[k]++;
49 }
50 }
51
52 int main()
53 {
54 freopen("tcif.in","r",stdin);
55 freopen("tcif.out","w",stdout);
56
57 scanf("%d %d %d %d %d %d\n",&f[1],&f[2],&f[3],&c[1],&c[2],&c[3]);
58
59 for(i=1;i<=2;i++)
60 for(j=i+1;j<=3;j++)
61 if(c[i]>c[j])
62 {
63 aux=c[i]; c[i]=c[j]; c[j]=aux;
64 aux=f[i]; f[i]=f[j]; f[j]=aux;
65 }
66
67 /// for(i=1;i<=3;i++) printf("%d %d\n",c[i],f[i]);
68
69 l=f[1]+f[2]+f[3];
70 gets(s);
71 d=l-strlen(s);
72 for(i=1;i<=d;i++)
73 a[i]=0;
74 for(i=1;i<=strlen(s);i++)
75 a[d+i]=s[i-1]-’0’;
76
77 //marim cu o unitate nr original
78 j=l;
79 a[j]++;
80 while(a[j]>9)
81 {
82 a[j]-=10;
83 a[j-1]++;
84 j--;
85 }
86
87 /// for(i=1;i<=l;i++) printf("%d",a[i]); printf("\n");
88
89 egale=1;///presupun ca primele cifre ce le fixam sunt la fel cu originalul
90
91 back(1);
92
93 for(i=1;i<=l;i++) printf("%d",b[i]); printf("\n");
94
95 return 0;
96 }

7.2.3 *Rezolvare detaliat 


Capitolul 8

OJI 2013

8.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. Denim puterea unui element ai ca ind num rul de
secvenµe care-l conµin pe ai ³i în care ai este strict mai mare decât celelalte elemente ale ec reia
dintre respectivele secvenµe. Astfel în ³irul 2 3 4 3 puterea elementului a1 este 1 (ind maxim
doar în secvenµa format  din el însu³i), a elementului a2 este 2 (a2 ind maxim în secvenµele 2 3
³i 3), a elementului a3 este 6 (ind 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.

Restricµii ³i preciz ri
a 2 & N & 200000
a Elementele ³irului sunt numere naturale ³i au cel mult 6 cifre
a Se acord  50% din punctaj pentru determinarea corect  a celei mai mari puteri a unui
element din șir și 50% din punctaj pentru determinarea num rului 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:
9 3 4 5 1 2 2 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,
1 0 7 7 2 6 2 deci puterea lor este 3. Celelalte elemente au puteri mai
mici.

Timp maxim de executare/test: 0.5 secunde


Memorie: total 32 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB
79
CAPITOLUL 8. OJI 2013 80

8.1.1 Indicaµii de rezolvare


Descrierea solutiei (Dan Pracsiu)

Solutia 1 - complexitate O(n x n)

Se memoreaza sirul de numere in vectorul a de lungime n.


Pentru fiecare pozitie i (i = 1..n), calculam:

x = numarul de elemente aflate imediat in stanga lui ai si care sunt strict


mai mici ca ai.

y = numarul de elemente aflate imediat in dreapta lui ai si care sunt strict


mai mici ca ai.

Pentru a calcula pe x, parcurgem de la pozitia i-1 spre stanga vectorul pana


se gaseste o valoare mai mare sau egala cu ai. Asemanator procedam pentru y
parcurgand vectorul de la pozitia i + 1 spre dreapta.

Puterea lui ai este data de (x + 1) * (y + 1), deoarece in atatea secvente


care-l contin pe ai in acesta este maxim.

Solutia 2 - complexitate O(n)

Vom determina pentru fiecare element a[i] al sirului numarul p[i] de


secvente in care a[i] este maxim. Pentru aceasta, se construiesc doi
vectori st si dr de lungime n in care:

st[i] = numarul de valori mai mici ca a[i] aflate imediat in stanga


pozitiei i.

dr[i] = numarul de valori mai mici ca a[i] aflate imediat in dreapta


pozitiei i.

Constructia vectorului st se poate realiza in O(n), tinand cont de faptul ca


daca un element a[i] este maxim in secventa a1, a2, ..., ai, atunci orice
numar mai mare ca ai si aflat in dreapta lui ai va fi automat mai mare ca
toate elementele precedente.

Vom construi deci un vector suplimentar q de lungime n in care elementele


depuse vor fi mereu in ordine descrescatoare. Avem n pasi, la fiecare pas
prelucrand pe fiecare element ai; luand pe x=ai, extragem din q toate
elementele mai mici strict decat x. Numarul de elemente mai mici strict
decat x sunt acum date de diferenta dintre pozitia lui x in vector
(care este i) si pozitia elementului mai mare sau egal cu x si care a ramas
in q, plus 1. Se depune acum x in q.

Asemanator se construieste si vectorul dr parcurgand de la dreapta la stanga


elementele sirului a.

Elementul a[i] va fi maxim intr-un numar de secvente egal cu


p[i]=(st[i]+1)*(dr[i]+1).

Cunoscand valorile din vectorul p, putem imediat determina valoarea maxima


si de cate ori apare.

8.1.2 Cod surs 


CAPITOLUL 8. OJI 2013 81

Listing 8.1.1: maxpCS.cpp

1 //prof.Cristina Sichim
2 #include <fstream>
3 #include <vector>
4 #include <algorithm>
5
6 #define limita 1000000
7
8 using namespace std;
9
10 ifstream f("maxp.in");
11 ofstream g("maxp.out");
12
13 struct element{ int x,p;}e;
14
15 vector<element> v;
16
17 int i,j,n,x,k;
18 long long p,pmax;
19
20 int main()
21 {
22 f>>n;
23 e.x=limita;
24 e.p=0;
25 v.push_back(e);
26
27 for(i=1;i<=n;i++)
28 {
29 f>>x;
30 e.x=x;
31 j = i-1;
32
33 while(v[j].x<x)
34 j=j-v[j].p;
35
36 e.p=i-j;
37 v.push_back(e);
38 }
39
40 e.x=limita;
41 e.p=0;
42 v.push_back(e);
43
44 reverse(v.begin(), v.end());
45
46 for(i=1;i<=n;i++)
47 {
48 j=i-1;
49 while(v[j].x<v[i].x)
50 j= j-v[j].p;
51
52 p=(long long)v[i].p*(i-j);
53 v[i].p=i-j;
54 if(p>pmax)
55 pmax=p,k=1;
56 else
57 if(p==pmax)
58 k++;
59 }
60
61 g<<pmax<<’\n’<<k<<’\n’;
62 f.close();
63 g.close();
64 return 0;
65 }

Listing 8.1.2: maxpDP_cstdio.cpp

1 //prof.Dan Pracsiu
2 #include<cstdio>
3 #include<fstream>
4
5 #define inFile "maxp.in"
CAPITOLUL 8. OJI 2013 82

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 }

Listing 8.1.3: maxpDP_fstream.cpp

1 // O(n) - 100 puncte - prof.Dan Pracsiu


2 #include<fstream>
CAPITOLUL 8. OJI 2013 83

3 #include<iostream>
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 ifstream fin(inFile);
20 fin >> n;
21 for (i = 1; i <= n; i++)
22 fin >> a[i];
23 fin.close();
24
25 // constructie st
26 k = 0;
27 q[k] = dim + 2;
28 poz[k] = 0;
29 st[k] = 0;
30 for (i = 1; i <= n; i++)
31 {
32 x = a[i];
33 while (q[k] < x) k--;
34 st[i] = i - poz[k] - 1;
35 k++;
36 q[k] = x;
37 poz[k] = i;
38 }
39
40 // constructie dr
41 k = 0;
42 q[k] = dim + 2;
43 poz[k] = n + 1;
44 dr[k] = 0;
45 for (i = n; i >= 1; i--)
46 {
47 x = a[i];
48 while (q[k] < x) k--;
49 dr[i] = poz[k] - i - 1;
50 k++;
51 q[k] = x;
52 poz[k] = i;
53 }
54
55 // calcul
56 nrsol = 1;
57 pmax = (st[1] + 1);
58 pmax *= (dr[1] + 1);
59 for (i = 2; i <= n; i++)
60 {
61 p = (st[i] + 1);
62 p = (p * (dr[i] + 1));
63 if (p > pmax)
64 {
65 pmax = p;
66 nrsol = 1;
67 }
68 else
69 if (p == pmax)
70 nrsol++;
71 }
72
73 ofstream fout(outFile);
74 fout << pmax << "\n" << nrsol << "\n";
75 fout.close();
76
77 return 0;
78 }
CAPITOLUL 8. OJI 2013 84

Listing 8.1.4: maxpPRI.cpp

1 //prof.PIT-RADA IONEL VASILE


2 #include<fstream>
3
4 using namespace std;
5
6 int n, a[200005],st[200005],dr[200005],i,s,j,p;
7 long long maxs,pw;
8
9 ifstream fin("maxp.in");
10 ofstream fout("maxp.out");
11
12 int main()
13 {
14 fin>>n;
15 for (i=1;i<=n;i++)
16 {
17 fin>>a[i];
18 }
19
20 st[1]=0;
21 for (i=2;i<=n;i++)
22 {
23 j=i-1;
24 while (j>=1 && a[j]<a[i])
25 {
26 j=st[j];
27 }
28 st[i]=j;
29 }
30
31 dr[n]=n+1;
32 for (i=n-1;i>=1;i--)
33 {
34 j=i+1;
35 while (j<=n && a[i]>a[j])
36 {
37 j=dr[j];
38 }
39 dr[i]=j;
40 }
41
42 maxs=0;
43 for (i=1;i<=n;i++)
44 {
45 pw=(dr[i]-st[i]-1+(long long)(dr[i]-i-1)*(i-st[i]-1));
46 if (pw>maxs)
47 {
48 maxs=pw;
49 s=1;
50 }
51 else
52 {
53 if (maxs==pw) {s++;}
54 }
55 }
56
57 fout<<maxs<<"\n"<<s<<"\n";
58 fin.close();
59 fout.close();
60
61 return 0;
62 }

Listing 8.1.5: maxpPRV.cpp

1 //prof.PIT-RADA IONEL VASILE


2 #include<fstream>
3
4 using namespace std;
5
6 int n, a[200004], mx, j3, j2, nr, i;
7 long long s,pw;
8
CAPITOLUL 8. OJI 2013 85

9 ifstream fin("maxp.in");
10 ofstream fout("maxp.out");
11
12 int main()
13 {
14 fin>>n;
15 for (i=1;i<=n;i++)
16 {
17 fin>>a[i];
18 }
19
20 a[n+1]=a[n];
21 a[0]=a[1];
22 s=0;
23 for (i=2;i<=n-1;i++)
24 {
25 if (a[i-2]<=a[i] && a[i-1]<a[i] && a[i]>a[i+1] && a[i]>=a[i+2])
26 {
27 mx=a[i];
28 j3=i;
29 while (j3-1>=1 && a[j3-1]<mx)
30 j3--;
31
32 j2=i;
33 while (j2+1<=n && a[j2+1]<mx)
34 j2++;
35
36 pw=(i-j3)*(j2-i)+1+j2-j3;
37
38 if (pw>s)
39 {
40 s=pw;
41 nr=1;
42 }
43 else
44 if (pw==s)
45 nr++;
46 }
47 }
48 fout<<s<<"\n"<<nr<<"\n";
49 fin.close();
50 fout.close();
51 return 0;
52 }

Listing 8.1.6: maxpSC.cpp

1 //prof.Stelian Ciurea
2 #include <fstream>
3
4 using namespace std;
5
6 int a[200001],st[200001], dr[200001];
7 long long rez;
8 int i,j,k,n,ct;
9 long long pmax, putere;
10
11 ifstream f("maxp.in");
12
13 int main()
14 {
15 f >> n;
16 for (i=1;i<=n;i++)
17 f>>a[i];
18
19 a[n+1]=a[0]=2000000000;
20 st[1]=0;
21 dr[n]=0;
22 for (i=1;i<=n;i++)
23 {
24 j=i-1;
25 while (a[j]<a[i])
26 j = j - st[j] - 1;
27 st[i]=i-j-1;
28 }
CAPITOLUL 8. OJI 2013 86

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 }

8.1.3 *Rezolvare detaliat 

8.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) aate
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 veric  cel puµin una dintre relaµiile x y sau xy 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:

Figura 8.1: Puncte

Cerinµe
CAPITOLUL 8. OJI 2013 87

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) aate 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 veric  cel puµin una dintre relaµiile x y sau xy 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 ecare dintre urm -
toarele N linii, câte dou  numere întregi, desp rțite printr-un spațiu, 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 .

Restricµii ³i preciz ri
a 5&N & 250000
a coordonatele punctelor sunt numere întregi ce au maximum 3 cifre;
a Se acord  20 % din punctaj pentru rezolvarea corect  a punctului a), 20 % din punctaj pentru
rezolvarea corect  a punctului b) ³i 60 % din punctaj pentru rezolvarea corect  a punctului c).

Exemple

Figura 8.2: Puncte

Timp maxim de executare/test: 0.8 secunde


Memorie: total 32 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB

8.2.1 Indicaµii de rezolvare


Descriere solutie (Intuneric Ana)

a)
La primul nivel este suficient sa construim doua tablouri X si Y
cu semnificatia:

X[1000+i]=numarul de puncte cu abscisa i


si
Y[1000+i]=numarul de puncte cu ordonata i
si apoi sa determinam valoarea maxima memorata in ele.

b)
Pentru al II-lea nivel memoram in variabila b1 numarul de puncte aflate pe
CAPITOLUL 8. OJI 2013 88

prima bisectoare\ {O}, in variabila b2 numarul de puncte aflate pe a doua


bisectoare\ {O} si in variabila nr0 memoram aparitia punctului {O}.

Cu ajutorul unei formule determinam numarul de drepte care se pot forma


cu aceste puncte:
0 , daca b1=0 si b2=0
nrd= b1*b2+2, daca b1+nr0>1 si b2+nr0>1
b1*b2+1, altfel

c)
Pentru al III-lea nivel efectuam o stergere falsa a punctelor (prin marcare)
utilizand vectorul poz cu semnificatia:

poz[i]=0, daca al i-lea punct (in ordinea ceruta) a fost sters.

Utilizand apoi vectorul X creat anterior determinam abscisa punctului ramas.

8.2.2 Cod surs 

Listing 8.2.1: puncteAI.cpp

1 //prof.Ana Intuneric
2 #include <fstream>
3
4 using namespace std;
5
6 ifstream f("puncte.in");
7 ofstream g("puncte.out");
8
9 int poz[500000];
10 int X[2000],Y[2000];
11 int maxi,i,xp,yp,n,n1,j,nrp,nrabs,marcate,gasite,b1,b2,nr0,nrd;
12
13 int main()
14 {
15 //citire-numarare puncte pe abscisa, ordonata si bisectoare
16 f>>n;
17 for(i=1;i<=n;i++)
18 {
19 f>>xp>>yp;
20 if(xp==yp)b1++;
21 if(xp==-yp)b2++;
22 if(xp==0 && yp==0)nr0++;
23 poz[i]=i;
24 X[xp+1000]++;Y[yp+1000]++;
25 }
26
27 b1=b1-nr0;b2=b2-nr0;
28
29 //a) determinare max
30 maxi=0;
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;
CAPITOLUL 8. OJI 2013 89

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 }

Listing 8.2.2: puncteCS.cpp

1 //prof.Cristina Sichim
2 #include <fstream>
3 #include <vector>
4
5 using namespace std;
6
7 ifstream f("puncte.in");
8 ofstream g("puncte.out");
9
10 int n,nn,m,i,p,x,y,z,X[2005],Y[2005],b1,b2;
11
12 struct punct
13 {
14 int x,h;
15 } P;
16
17 vector <punct> v;
18
19 int main()
20 {
21 f>>n;nn=n;
22 while(nn--)
23 {
24 f>>x>>y;
25 if(x==0 && y==0)
26 z=1;
27 else
28 if(x==y)
29 b1++;
30 else
31 if(x+y==0)
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
CAPITOLUL 8. OJI 2013 90

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 }

Listing 8.2.3: puncteDP.cpp

1 //prof. Dan Pracsiu


2 #include<fstream>
3
4 #define inFile "puncte.in"
5 #define outFile "puncte.out"
6
7 using namespace std;
8
9 int ox[2010], oy[2010], b1, b2, n;
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);
CAPITOLUL 8. OJI 2013 91

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;
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
CAPITOLUL 8. OJI 2013 92

104 return 0;
105 }

Listing 8.2.4: punctePRI.cpp

1 //prof.Pit-Rada Ionel Vasile


2 #include<fstream>
3
4 using namespace std;
5
6 int lin[2013], col[2013];
7 int bis1,bis2,oo,i,n,nrp,nrd,xp,u,v;
8
9 void abscisa()
10 {
11 int d,w,i,p,t;
12 d=0;
13 w=1;
14 do
15 {
16 for (i=0;i<=2000;i++)
17 if (col[i]>=w)
18 {
19 p=(col[i]-w)/3;
20 if (w+3*p==col[i])
21 w=3;
22 else
23 if (w+3*p==col[i]-1)
24 w=2;
25 else
26 w=1;
27 col[i]=col[i]-p-1;
28 d=d+p+1;
29 xp=i;
30 }
31 else
32 w=w-col[i];
33 } while (d<n);
34 }
35
36 int main()
37 {
38 ifstream fin("puncte.in");
39 ofstream fout("puncte.out");
40
41 fin>>n;
42 bis1=0;
43 bis2=0;
44 oo=0;
45 for (i=1;i<=n;i++)
46 {
47 fin>>u>>v;
48 col[u+1000]++;
49 lin[v+1000]++;
50 if (u==v)bis1++;
51 if (u==-v)bis2++;
52 if (u==0 && v==0) oo++;
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 {
CAPITOLUL 8. OJI 2013 93

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 }

Listing 8.2.5: puncteSC.cpp

1 //prof.Stelian Ciurea
2 #include <fstream>
3 #include <algorithm>
4 #include <vector>
5
6 using namespace std;
7
8 ifstream f("puncte.in");
9
10 struct punct
11 {
12 short int x,y;
13 };
14
15 vector <punct> v, v2;
16
17 int n,i,j,ct,ct1,ct2,ctmax,zero,r,val,tip;
18
19 int rez2(int ct1, int ct2, int zero)
20 {
21 if (ct1 + ct2 + zero <= 1) //un singur punct
22 return 0;
23 if (ct2 == 0) //niciun punct pe bis2
24 return 1;
25 if (ct1 == 0) //niciun punct pe bis1
26 return 1;
27 if (ct1 == 1 && ct2 == 1) //1 pe bis1, 1 pe bis2
28 if (zero==1)
29 return 3;
30 else
31 return 1;
32 if (ct1 == 1) //1 pe bis1 mai multe pe bis2
33 return ct2+zero + 1;
34 if (ct2 == 1) //1 pe bis2 mai multe pe bis1
35 return ct1+zero + 1;
36 return ct1*ct2 + 2; //2 multe pe bis1 si 2 bis 2
37 }
38
39 int rez3(int n,int ct)
40 {
41 if (n==4)
42 {
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 }
CAPITOLUL 8. OJI 2013 94

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;
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++)
CAPITOLUL 8. OJI 2013 95

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 }

8.2.3 *Rezolvare detaliat 


Capitolul 9

OJI 2012

9.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  e î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  e 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 a  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  scris un num r natural
reprezentând cerinµa problemei.

Restricµii ³i preciz ri
a 2 & N & 100000
a 1 & în lµimile turnurilor & 100000
a Dac  dup  aranjarea turnurilor hi & hi1 atunci turnurile i ³i i  1 fac parte din acela³i deal.

Exemple
deal.in deal.out Explicaµii
7 22 O soluµie posibil  cu suma în lµimilor 22 ar : 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).

Timp maxim de executare/test: 0.5 secunde


Memorie: total 2 MB din care pentru stiv  1 MB
Dimensiune maxim  a sursei: 5 KB

9.1.1 Indicaµii de rezolvare


prof. Emanuela Cerchez C. N. ’’Emil Racovita’’ Iasi

Solutia optima se obtine construind un numar maxim de dealuri, formate


convenabil din doua elemente. Daca n este impar, va ramane un element

96
CAPITOLUL 9. OJI 2012 97

pe care il vom "lipi" la unul dintre dealurile existente.

La citirea celor n inaltimi vom contoriza numarul de aparitii ale fiecarei


valori intr-un vector nr

nr[i]= numarul de aparitii ale valorii i.

Acest vector il vom utiliza pentru a parcurge intr-o ordine convenabila


inaltimile turnurilor.

Vom determina de asemenea maximul m (cea mai mare inaltime).

Problema are doua cazuri pe care le tratam distinct.

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).

La final vom parcurge vectorul solutie si vom determina rezultatul.


Atentie, in cazul in care n este impar este posibil sa obtineti un singur
turn izolat (acesta nu formeaza un deal!).

Solutie 2 100 puncte

prof. Chesca Ciprian


Grup scolar "Costin Nenitescu" Buzau

Pe masura ce se citesc inaltimile turnurilor se formeza un vector cu numarul


de aparitii (’’frecvente’’) ale fiecarei inaltimi din fisierul de intrare.

Avem astfel la dispozitie mai multe ’’gramezi’’ de turnuri, fiecare


’’gramada’’ continand un numar cunoscut de turnuri cu inaltimi identice,
din care putem forma dealuri de cate doua turnuri.

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.

Daca o ’’gramada’’ din stanga se epuizeaza atunci trecem la urmatoarea


’’gramada’’ de la stanga la dreapta.

Daca o ’’gramada’’ din dreapta se epuizeaza trecem la urmatoarea ’’gramada’’


de la dreapta la stanga.
CAPITOLUL 9. OJI 2012 98

In final va ramane o singura ’’gramada’’ cu un numar oarecare de turnuri


disponibile.

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.

Calculam acest numar astfel: calculam numarul de dealuri deja formate,


din care scadem numarul de dealuri care contin deja turnuri cu inaltimea
egala cu turnurile ramase in ultima gramada. Acest numar il comparam cu
numarul de dealuri care se pot forma din turnurile ramase in ultima gramada
si alegem minimul!

Am ajuns la acesta solutie imaginandu-mi vizual turnurile pe care le am


la dispozitie sub forma unor gramezi din care pot forma dealuri de cate
doua turnuri!!!

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.

9.1.2 Cod surs 

Listing 9.1.1: deal_100_ch.cpp

1 // sursa de 100 puncte Ciprian Chesca


2
3 #include <fstream>
4 #define nmax 100001
5
6 using namespace std;
7
8 int main()
9 {
10 ifstream f("deal.in");
11 ofstream g("deal.out");
12
13 long n,w[nmax],v[nmax],i,x,st,dr,pdp;
14 long long s=0;
15
16 f>>n;
17 for(i=1;i<nmax;i++)
18 w[i]=0;
19 for(i=1;i<=n;i++)
20 {f>>x;w[x]++;}
21 for(i=1;i<=n;i++)
22 v[i]=w[i];
23
24
25 st=1;dr=nmax-1;
26 while (w[st]==0) st++;
27 while (w[dr]==0) dr--;
28
29 while (st!=dr)
30 {
31 if (w[st]==0) st++;
32 else
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
CAPITOLUL 9. OJI 2012 99

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 }

Listing 9.1.2: deal_100_ct.cpp

1 //100 puncte Cristian Toncea


2 #include<fstream>
3
4 using namespace std;
5
6 long a[100001];
7
8 int main()
9 {
10 long long i,n,s,max=0,q=0;
11 ifstream f("deal.in");
12 ofstream g("deal.out");
13 f>>n;
14 for(i=1;i<=n;i++)
15 {
16 f>>s;
17 a[s]++;
18 if (a[s]>n/2) q=s;
19 if(s>max) max=s;
20 }
21
22 i=max;
23 s=0;
24 if(q==0)
25 n=n/2;
26 else
27 if (a[q]-n/2>1)
28 n=n-a[q]+1;
29 else
30 n=n-a[q];
31
32 while(n>a[i])
33 {
34 s+=i*a[i];
35 n=n-a[i];
36 i--;
37 }
38
39 s+=i*n;
40 g<<s;
41 f.close();
42 g.close();
43
44 return 0;
45 }

Listing 9.1.3: DEAL_100_em.CPP

1 //Em. Cerchez 100 puncte


2 #include <cstdio>
3 #include <cassert>
4 #include <fstream>
5
6 #define MAXN 100008
7
8 using namespace std;
9
10 int nr[MAXN];//nr[i]=numarul de aparitii ale valorii i
11 int n;
12 int sol[MAXN]; //aici construiesc solutia
13
CAPITOLUL 9. OJI 2012 100

14 int main()
15 {
16 int m = 0, i, a;
17 freopen("deal.in", "r", stdin);
18 freopen("deal.out", "w", stdout);
19
20 scanf("%d", &n);
21 assert((1 <= n) && (n <= 100000));
22
23 m=0; //in m calculez maximul
24 for (i = 0; i < n; ++i)
25 {
26 scanf("%d", &a);
27 assert((1 <= a) && (a <= 100000));
28 ++nr[a];
29 if (m<a) m=a;
30 }
31
32 int k = -1;
33 //verific daca exista un element majoritar
34 int jum=(n+1)/2;
35 for (i=0; i <= m; ++i)
36 if (nr[i] > jum)
37 { k = i; break; }
38
39 //k este o valoare care apare de cel putin jum ori
40 if (k != -1)
41 {
42 int l = 0;
43 if (k != m) //k nu este maximul
44 {
45 //parcurg valorile i mai mari decat k si formez perechi de forma k, i
46 for (i = m; i > k; --i)
47 {
48 while (nr[i] > 0)
49 {
50 sol[l++] = k; --nr[i];
51 sol[l++] = i; --nr[k];
52 }
53 }
54 }
55 int r = n - 1;
56 //parcurg valorile pana la k si incep completarea vectorului solutie
57 // de la coada
58 for (i = 0; i <= k; ++i)
59 {
60 while (nr[i] > 0)
61 {
62 if (r < l){break;}
63 sol[r--] = k; --nr[k];
64 if (r < l) {break; }
65 sol[r--] = i; --nr[i];
66 }
67 }
68 }
69 else
70 //if (k == -1) //nu exista un element majoritar
71 {
72 for (k=m,i=1; i<n; )
73 if (nr[k]) {sol[i]=k; nr[k]--; i+=2;}
74 else k--;
75 for (i=0; i<n; )
76 if (nr[k]) {sol[i]=k; nr[k]--; i+=2;}
77 else k--;
78 }
79
80 //calculez suma inaltimilor dealurilor
81 long long int rez = 0;
82 for (i = 0; i < n; ++i)
83 {
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
CAPITOLUL 9. OJI 2012 101

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 }

9.1.3 *Rezolvare detaliat 

9.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
ecare astfel de OZN se a  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 aaµi în OZN-ul respectiv.
Din p cate, în preajm  se a  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 extrate-
re³tri.

Cerinµe
Ajutaµi autorit µile s  determine num rul de extratere³tri care pot  anihilaµi cu ecare 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  scris num rul total de extra-
tere³tri care pot  distru³i cu arma i, considerând armele numerotate în ordinea în care acestea
apar în ³ierul de intrare.

Restricµii ³i preciz ri
a 1 & N & 20000
a 1 & K & 20000
a 1 & orice coordonat  din ³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 9. OJI 2012 102

Exemple

Figura 9.1: Puncte

Timp maxim de executare/test: 0.5 secunde


Memorie: total 20 MB din care pentru stiv  10 MB
Dimensiune maxim  a sursei: 5 KB

9.2.1 Indicaµii de rezolvare


Autor prof. Dana Lica C.N. ’’I.L.Caragiale’’ Ploiesti

1. Solutia oficiala porneste de la urmatoarea codificare:


Consideram intervalul [x1,x2] de pe axa OX determinat de capetele segmentului
reprezentat de o nava. Memorarea numarului de extraterestri nr ce se gasesc in
acest OZN se va face folosind un vector care se comporta asemanator unui vector
de frecventa, avand ca indici abscisele punctelor. Diferenta consta in faptul
ca nu se vor modifica toate elementele din vectorul cu indici cuprinsi intre
x1 si x2 ci doar elementele corespunzatoare capetelor intervalului. Mai exact,
doar elementele de indici x1 si x2 + 1se vor modifica. Daca notam vectorul cu
V atunci V[x1] se incrementeaza cu nr iar V[x2 + 1] se decrementeaza cu nr.

Pentru a determina cati extraterestri sunt anihilati de arma aflata la


abscisa a, vom calcula suma V[1]+V[2]+...+V[a]. Aceste sume partiale vor fi
precalculate inainte de a raspunde la fiecare interogare din cele K.

Complexitatea O(N + K)

Alte solutii

2. O varianta de rezolvare ar presupune verificarea succesiva a fiecarui


segment daca este intersectat de arma curenta situata la abscisa ai.
Conditia ca nava sa fie doborata este ca x1âL’d’aiâL’d’x2; Complexitatea O(N*K).
Aceasta varianta obtine 50 puncte.

3. O alta solutie de 100 de puncte, propusa de prof. Marius Nicoli rezolva


problema astfel:

Se creeaza un sir de 2N+K structuri, elementele sale continand informatiile


urmatoare:

tipul elementului (inceput de interval, sfarsit de interval, query), abscisa,


pozitia initiala (pentru queryuri), valoare. Acestea se sorteaza dupa X.
La parcurgerea sirului sortat se trateaza evenimente de 3 tipuri :

(in functie de tipul fiecaruia gestionez valoarea unei variabile sum):

- la intalnirea inceputurilor de interval adun la sum pe nr


CAPITOLUL 9. OJI 2012 103

- la intalnirea unui query k asociez lui k valoarea sum


- la intalnirea finalurilor de interval scad din sum pe nr

Complexitate (2N+K) log (2N+K), data de sortare.

9.2.2 Cod surs 

Listing 9.2.1: ozn_stream100.cpp

1 #include <fstream>
2
3 #define MAXX 2000010
4 #define MAXN 100010
5
6 using namespace std;
7
8 ifstream f("ozn.in");
9 ofstream g("ozn.out");
10
11 int A[MAXX];
12 int N,K;
13 int X1,Y1,X2,Y2,nr,i;
14
15 int main()
16 {
17
18 f>>N>>K;
19 for (i=1; i <= N; ++i)
20 {
21
22 f>>X1>>Y2>>X2>>Y2>>nr;
23 A[X1]+=nr;
24 A[X2+1]-=nr;
25 }
26
27 for (i = 1; i < MAXX; ++i)
28 A[i] += A[i-1];
29
30 for (i = 1; i <= K; ++i)
31 {
32 f>>X1;
33 g<<A[X1]<<’\n’;
34 }
35
36 g.close();
37 f.close();
38 return 0;
39 }

Listing 9.2.2: ozn100.cpp

1 #include <stdio.h>
2
3 #define MAXX 2000010
4 #define MAXN 100010
5
6 int A[MAXX];
7 int N,K;
8 int x1,y1,x2,y2,nr,i;
9
10 int main()
11 {
12 freopen("ozn.in","r",stdin);
13 freopen("ozn.out","w",stdout);
14
15 scanf("%d %d",&N,&K);
16 for (i=1; i <= N; ++i)
17 {
18 scanf("%d %d %d %d %d",&x1,&y2,&x2,&y2,&nr);
19 A[x1]+=nr;
20 A[x2+1]-=nr;
CAPITOLUL 9. OJI 2012 104

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 }

Listing 9.2.3: oznarbint100.cpp

1 //Marius Nicoli
2 #include <stdio.h>
3
4 #define DIM 1000009
5 #define DN (nod<<1)
6
7 int N, K, k, a, b, c, i, val, aux;
8 int V[DIM], A[DIM];
9
10 void update(int st, int dr, int nod, int a, int b, int val)
11 {
12 int mij;
13 if (a<=st && dr<=b)
14 {
15 A[nod] += val;
16 }
17 else
18 {
19 mij = (st+dr)/2;
20 if (a<=mij)
21 {
22 A[DN] += A[nod];
23 A[DN+1] += A[nod];
24 A[nod] = 0;
25 update(st, mij, DN, a, b, val);
26 }
27
28 if (b>mij)
29 {
30 A[DN] += A[nod];
31 A[DN+1] += A[nod];
32 A[nod] = 0;
33 update(mij+1, dr, DN+1, a, b, val);
34 }
35
36 V[nod] = V[DN] + V[DN+1];
37 }
38 }
39
40 int query(int st, int dr, int nod, int a, int b)
41 {
42 int sum,x,mij;
43 if ((a<=st) && (dr<=b))
44 {
45 V[nod] += A[nod]*(dr-st+1);
46 A[nod] = 0;
47 return V[nod];
48 }
49 else
50 {
51 mij = (st+dr)/2;
52 sum = 0;
53
54 if (a<=mij)
55 {
56
57 A[DN] += A[nod];
58 A[DN+1] += A[nod];
59 A[nod] = 0;
CAPITOLUL 9. OJI 2012 105

60 x = query(st, mij, DN, a, b);


61
62 sum += x;
63 }
64
65 if (b>mij)
66 {
67 A[DN] += A[nod];
68 A[DN+1] += A[nod];
69 A[nod] = 0;
70 x = query(mij+1,dr, DN+1, a, b);
71 sum += x;
72 }
73
74 V[nod] = V[DN] + A[DN]*(mij-st+1) + V[DN+1] + A[DN+1]*(dr-mij);
75 return sum;
76 }
77 }
78
79 int main()
80 {
81
82 FILE *f = fopen("ozn.in","r");
83 FILE *g = fopen("ozn.out","w");
84 fscanf(f,"%d %d",&N,&K);
85
86 for (i=1;i<=N;i++)
87 {
88 fscanf(f,"%d %d %d %d %d",&a, &c, &b, &c, &val);
89 update(1, 500003, 1, a, b, val);
90 }
91
92 for (i=1;i<=K;i++)
93 {
94 fscanf(f,"%d",&k);
95 fprintf(g,"%d\n",aux = query(1, 500003, 1, k, k));
96 }
97
98 fclose(g);
99 fclose(f);
100 return 0;
101 }

Listing 9.2.4: oznEvenimente100.cpp

1 //Marius Nicoli
2 #include <stdio.h>
3 #define DIM 20010
4 #include <algorithm>
5
6 using namespace std;
7
8 struct inregistrare
9 {
10 int t;
11 int x;
12 int v;
13 int p;
14 };
15
16 inregistrare V[DIM*3], inr;
17
18 int cmp (inregistrare a, inregistrare b)
19 {
20 if (a.x != b.x)
21 return a.x < b.x;
22 else
23 return a.t < b.t;
24 }
25
26 int cmp1 (inregistrare a, inregistrare b)
27 {
28 return a.p < b.p;
29 }
30
CAPITOLUL 9. OJI 2012 106

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 }

9.2.3 *Rezolvare detaliat 


Capitolul 10

OJI 2011

10.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  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 ³ierului de intrare ³i ³irul
de M cifre de pe linia urm toare ³i determin  ³i a³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 ³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.

Restricµii ³i preciz ri
a 180 & N & 180
a 2 & M & 20
a în ³irul citit cifrele se pot repeta
a toate cifrele din ³ir trebuie s  apar  ³i în expresia aritmetic , în aceea³i ordine în care au
fost citite
a în expresia aritmetic , orice cifr  trebuie s  e precedat  de un operator; în cazul în care
prima cifr  este precedat  de operatorul '+' acesta nu se pune în expresie
a în expresia aritmetic  nu exist  spaµii
a expresia aritmetic  se termin  cu caracterul sfâr³it de linie
a în cazul în care soluµia nu este unic  se va a³a o soluµie corect 

Exemple
adunscad.in adunscad.out Explicaµii
21 4 3+9+1+8 Soluµie corect  utilizând numai operatorul '+'
3 9 1 8
-1 4 -1+2+3-5 Soluµie corect . O alt  soluµie corect  este: -1-2-3+5
1 2 3 5
-7 7 -1-1-1-1-1-1-1 Soluµie corect  utilizând numai operatorul '-'
1 1 1 1 1 1 1
12 3 0 Nu exist  soluµie
1 2 3

107
CAPITOLUL 10. OJI 2011 108

Timp maxim de executare/test: 1.0 secunde

10.1.1 Indicaµii de rezolvare


adunscad - solutie Marinel Serban (sursa ADUNSCAD.C)
------------------------------------------------------
Se genereaza toate posibilitatile de a pune semnele ’+’ si ’-’ inaintea fiecarei
cifre. Acest lucru presupune un algoritm de tip succesor, pentru a genera toate
combinatiile de 0 si 1 - cea mai simpla implementare realizata prin adunarea in
baza 2.

Avand in vedere faptul ca jumatate dintre combinatii sunt ’’negatele’’ celorlalte,

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

Solutie Adrian Panaete


----------------------
Algoritmul se bazeaza pe observatia ca daca dintr-o secventa de cifre pot
obtine o suma atunci pot obtine si opusa sa schimband semnele.

Se imparte orice secventa de la mijloc.


Se determina valorile pozitive care se pot obtine pe fiecare parte
Se combina valorile pozitive astfel incat sa obtin tot valori pozitive
varianta 1: se aduna
varianta 2: se scade valoare mare - valoare mica (eventual egale)

In ambele cazuri codurile de semne ale celor doua subsecvente


( retinute binar ca la Soutia lui Marinel )
genereaza codul valori pe intreaga secventa

varianta 1: concatenand codurile celor doua valori


varianta 2 concatenand codul valorii mari cu negatul codului valorii mici
(cu semnificatia la valoarea mica schimbam semnul la toti termenii)

In ambele cazuri se pastreaza ordinea bitilor adica pozitia subcodurilor in


codul cumulat

Facand apelul DEI de la prima la ultima cifra la final vom obtine


1: Orice valoare pozitiva genereaza cifrele
2: Codul de semne cu care acea valoare e generata
CAPITOLUL 10. OJI 2011 109

Daca valoarea dorita la final e negativa trebuie doar sa schimbam semnele


deci sa negam codul binar.

Explicatii Ilie Vieru - Adunscad


--------------------------------
Fie t = suma celor M cifre;
x suma cifrelor care participa la rezultat cu semnul plus ;
y suma cifrelor care participa la rezultat cu semnul minus.

Avem : x+y = t
x-y = N
adica : 2y = t-N

a) daca (t-N)%2>0 atunci solutia va fi 0 ; altfel


b) y= (t-N)/2, ŧi problema se reduce la a gasi cifre care adunate
sa se obtina y, iar aceste cifre (daca exista) vor participa
la solutie cu semnul minus.
b1) daca nu exista o astfel de combinatie atunci solutia va fi 0 ; altfel
b2) se marcheaza termenii care participa la suma y, iar la tiparire
se tine cont de acest lucru ŧi se vor afiŧa cu semnul minus.

Solutie - Genoiu Nicolae


------------------------
Mi-a fost greu sa inventez alta solutie si sa renunt la programare dinamica.

Am ales variata cu arbore binar cu cele 20 de nivele ale sale.

Am pastrat ideea lui Adrian Panaete: ’’Pentru fiecare cifra se construiesc


valorile ce pot fi obtinute din toate cifrele pana la cifra curenta inclusiv.’’
si pentru fiecare cifra am obtinut un nivel complet al arborelui.

Fiecare nod contine valoarea corespunzatoare obtinuta prin adunare (daca


nodul este descendent stang) sau scadere (daca nodul este descendent drept)
a cifrei citite la valoarea din nodul tata.

O parcurgere a arborelui in inordine va cauta nodul frunza care contine


valoarea cautata (N). Rezultatul poate fi NULL (N nu se poate obtine) sau
altul (am gasit un nod terminal cu valoarea N) caz in care se opreste
cautarea.

Cifrele le-am retinut in vector in timpul citirii.


Semnele se deduc prin traversarea arborelui de la nodul gasit la radacina
si se retin in vector. Am accelerat traversarea retinand in fiecare nod
adresa pentru tatal acestuia.

In fine. E o solutie ’’grea’’ si mie-mi plac lucrurile complicate.

Solutie - Adrian Panaete


------------------------
Am verificat sursa cu evaluatorul autorului si obtine 100 puncte

Ideea : programare dinamica

Pentru fiecare cifra se construiesc valorile ce pot fi obtinute din toate


cifrele pana la cifra curenta inclusiv.
CAPITOLUL 10. OJI 2011 110

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 :

exponential O(2^m) -> evident din descrierea solutiei

Solutia propusa de mine :


Polinomial O(m^2) -> dictata de etapa in care se realizeaza tabloul in care
numarul de linii este m iar numarul de coloane efectiv utilizate nu depaseste
2*suma cifrelor numarului deci prin supraestimare 9*m

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.

apelul back(int cifra,int rezultat) functioneaza astfel:

Am ajuns sa utilizez o cifra ’c’


Prin utilizarea cifrelor <’c’ am obtinut un rezultat ’r’

Cifra ’c’ apare de cnt[c] ori


Construiesc toate valorile pe care le pot obtine adaugand si
aparitia cifrei c

Parcurg toate variantele incepand cu ’zero de +’ si ’toate de -’


pana la ’toate cu +’ si ’zero cu -’.

Pentru fiecare rezultat obtinut fac apel la cifra urmatoare.


Daca am ajuns la ’’cifra’’ 10 si la rezultatul ’’cerut’’ am obtinut o solutie
si inchei fortat toate apelurile recursive memorand la intoarcere numarul de + si -
pentru fiecare cifra.

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.

Se observa ca daca am ajuns la o pozitia p unde am cifra c si as avea nevoie


ca cifrele incepand cu pozitia p sa imi genereze rezultatul r atunci avem
doua variante
1. Sa folosesc +c si atunci la pozitia p+1 am nevoie de valoarea r-c
2. Sa folosesc -c si atunci la pozitia p+1 am nevoie de valoarea r+c

10.1.2 Cod surs 


CAPITOLUL 10. OJI 2011 111

Listing 10.1.1: ADUNSCAD.C

1 //serban marinel - februarie 2011


2 //100 puncte
3 //algoritm de tip succesor pentru generarea combinatiilor de semne
4 //adunare in baza 2
5 #include <stdio.h>
6
7 FILE * Fin, *Fout;
8
9 int N, M, gasit;
10 int cifre[31], comb[31];
11
12 void afiseaza(int care)
13 {
14 int i;
15 gasit = 1;
16 if (care == 1 || care == 3)
17 {
18 for (i = 1; i < M; i++)
19 {
20 fprintf(Fout, ’’%d’’, cifre[i]);
21 if (comb[i+1])
22 fprintf(Fout, ’’-’’);
23 else
24 fprintf(Fout, ’’+’’);
25 }
26 fprintf(Fout, ’’%d\n’’, cifre[M]);
27 }
28 else
29 {
30 fprintf(Fout, ’’-’’);
31 for (i = 1; i < M; i++)
32 {
33 fprintf(Fout, ’’%d’’, cifre[i]);
34 if (comb[i+1])
35 fprintf(Fout, ’’+’’);
36 else
37 fprintf(Fout, ’’-’’);
38 }
39 fprintf(Fout, ’’%d\n’’, cifre[M]);
40 }
41 }
42
43 int OK(void)
44 {
45 int i, Sum = 0, Sum_ = 0;
46
47 for (i = 1; i <= M; i++)
48 if (comb[i])
49 Sum -= cifre[i];
50 else
51 Sum += cifre[i];
52 for (i = 1; i <= M; i++)
53 if (comb[i])
54 Sum_ += cifre[i];
55 else
56 Sum_ -= cifre[i];
57
58 if (Sum == N && Sum_ == N) return 3;
59 if (Sum == N) return 1;
60 if (Sum_ == N) return 2;
61 return 0;
62 }
63
64 int main(void)
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);
CAPITOLUL 10. OJI 2011 112

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 }

Listing 10.1.2: adrian1.cpp

1 //AUTOR SURSA: Panaete Adrian


2 //COMPILAT: Borland
3 //PUNCTAJ: 100 puncte
4 //ALGORITM: PROGRAMARE DINAMICA
5
6 #include<stdio.h>
7
8 int val,m,i,v[21],st,dr,j;
9 char y[21][370],*x[21],s[21];
10
11 int main()
12 {
13 freopen("adunscad.in","r",stdin);
14 freopen("adunscad.out","w",stdout);
15
16 scanf("%d%d",&val,&m);
17 for(i=1;i<=m;i++) scanf("%d",&v[i]);
18 for(i=0;i<=m;i++) x[i]=y[i]+181;
19
20 x[0][0]=3;
21 for(i=1;i<=m;i++)
22 {
23 for(j=st;j<=dr;j++)
24 if(x[i-1][j])
25 {
26 x[i][j+v[i]]=1;
27 x[i][j-v[i]]=2;
28 }
29 st-=v[i];dr+=v[i];
30 }
31
32 if(!x[m][val])
33 printf("0\n");
34 else
35 {
36 for(i=m;i>=1;i--)
37 {
38 if(x[i][val]==1)
39 {s[i]=’+’;val-=v[i];}
40 else
41 {s[i]=’-’;val+=v[i];}
42 }
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 }
CAPITOLUL 10. OJI 2011 113

Listing 10.1.3: adrian2.cpp

1 //AUTOR SURSA: Panaete Adrian


2 //COMPILAT: Borland
3 //PUNCTAJ: 100 puncte
4 //ALGORITM: Divide et impera
5
6 #include<stdio.h>
7
8 long v,n,x[25],p[25],VF[185],CF[185],*init();
9
10 void read(),solve(),dei(long,long,long*,long*);
11
12 int main()
13 {
14 read();
15 solve();
16 return 0;
17 }
18
19 void read()
20 {
21 freopen("adunscad.in","r",stdin);
22 freopen("adunscad.out","w",stdout);
23 scanf("%ld%ld",&v,&n);//citesc valoarea solicitata si nr de cifre
24 for(int i=0;i<n;i++)scanf("%ld",&x[i]);//citesc cifrele
25 }
26
27 void solve()
28 {
29 p[0]=1;for(int i=1;i<=20;i++)p[i]=2*p[i-1];
30 //GENEREZ PUTERILE LUI 2 pentru a simula operatiile pe biti
31
32 dei(0,n-1,VF,CF);
33 //Apelul principal DIVIDE ET IMPERA pe intreaga secventa de cifre
34
35 if(v<0){v=-v;CF[v]=p[n]-CF[v]-1;}
36 //daca valoarea este negativa schimb codul de semne in negatul sau pe biti
37 //pentru a schimba fiecare semn al fiecarei cifre
38
39
40 if(!VF[v]){printf("0\n");return;}
41 //daca valoarea nu este generata nu am solutia
42
43 (CF[v]%2)?printf("-%ld",x[0]):printf("%ld",x[0]);CF[v]/=2;
44 //AFISAREA PRIMEI CIFRE
45
46 for(int i=1;i<n;i++)
47 {
48 (CF[v]%2)?printf("-%ld",x[i]):printf("+%ld",x[i]);
49 CF[v]/=2;
50 }
51 printf("\n");
52 }
53
54 void dei(long st,long dr,long *V,long *C)
55 {
56 //st=indicele stanga al secventei de cifre
57 //dr=indicele drepata al secventei de cifre
58 //deci o secvanta de L=dr-st+1 cifre
59
60 //V[] initializat cu 0
61 //vector de lungime utila de la 0 la 9*L (daca toate cifrele ar fi 9)
62 //V[y]=1 daca valoarea y>=0 se genereaza cu cifrele secventei
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 {
CAPITOLUL 10. OJI 2011 114

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 }

Listing 10.1.4: adrian3.cpp

1 //AUTOR SURSA: Panaete Adrian


2 //COMPILAT: Borland
3 //PUNCTAJ: 100 puncte
4 //ALGORITM: BACKTRAKING RECURSIV
5
6 #include<stdio.h>
7
8 int cerut,n,i,Cnt[10],Plus[10],Minus[10],semn[21],x[21],gata,MINUS=-1,PLUS=1;
9 void read(),solve(),back(int,int);
10
11 int main()
12 {
CAPITOLUL 10. OJI 2011 115

13 read();
14 solve();
15 return 0;
16 }
17
18 void read()
19 {
20 freopen("adunscad.in","r",stdin);
21 freopen("adunscad.out","w",stdout);
22 scanf("%d%d",&cerut,&n);
23 for(i=1;i<=n;i++)
24 {
25 scanf("%d",&x[i]);
26 Cnt[x[i]]++;
27 }
28 }
29
30 void solve()
31 {
32 back(1,0);
33 if(!gata){printf("0\n");return;}
34 for(i=1;i<=n;i++)
35 {
36 if(Minus[x[i]])
37 { semn[i]=MINUS;Minus[x[i]]--;}
38 else
39 { semn[i]=PLUS;Plus[x[i]]--;}
40 }
41 semn[1]==MINUS?printf("-%d",x[1]):printf("%d",x[1]);
42 for(i=2;i<=n;i++)
43 semn[i]==MINUS?printf("-%d",x[i]):printf("+%d",x[i]);
44 printf("\n");
45 }
46
47 void back(int c,int r)
48 {
49 int plus,minus;
50 if(c==10)
51 {
52 if(r==cerut)gata=1;
53 return;
54 }
55 if(!Cnt[c]){back(c+1,r);return;}
56 for(plus=0,minus=Cnt[c];minus>=0;plus++,minus--)
57 {
58 back(c+1,r+(plus-minus)*c);
59 if(gata){Plus[c]=plus;Minus[c]=minus;return;}
60 }
61 }

Listing 10.1.5: adrian4.cpp

1 //AUTOR SURSA: Panaete Adrian


2 //COMPILAT: Borland
3 //PUNCTAJ: 100 puncte
4 //ALGORITM: BACKTRAKING RECURSIV
5
6 #include<stdio.h>
7
8 char *ver(int,int),*sol,SOL[45];
9 int c,n,i,x[21];
10 void read(),solve();
11
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++;
CAPITOLUL 10. OJI 2011 116

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 }

Listing 10.1.6: ADDSUBBK.C

1 //serban marinel - februarie 2011


2 //solutie de tip backtracking pentru generarea combinatiilor de semne
3 //100 puncte
4
5 #include <stdio.h>
6 #include <stdlib.h>
7
8 FILE * Fin, *Fout;
9
10 int N, M, gasit = 0;
11 int cifre[31], comb[31];
12
13 void afiseaza(int care)
14 {
15 int i;
16 gasit = 1; //marchez ca am gasit o solutie
17 if (care == 1 || care == 3) //primul semn este ’+’ NU il afisez
18 {
19 for (i = 1; i < M; i++)
20 {
21 fprintf(Fout, "%d", cifre[i]);
22 if (comb[i+1])
23 fprintf(Fout, "-");
24 else
25 fprintf(Fout, "+");
26 }
27 fprintf(Fout, "%d\n", cifre[M]);
28 }
29 else //primul semn este ’-’
30 {
31 fprintf(Fout, "-"); //il afisez separat
32 for (i = 1; i < M; i++)
33 {
34 fprintf(Fout, "%d", cifre[i]);
35 if (comb[i+1])
36 fprintf(Fout, "+");
37 else
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])
CAPITOLUL 10. OJI 2011 117

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 }

Listing 10.1.7: ADUNSCADM.C

1 //serban marinel - februarie 2011


2 //100 puncte
3 //adunare in baza 2 - genereaza TOATE combinatiile
4
5 #include <stdio.h>
6
7 FILE * Fin, *Fout;
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]);
CAPITOLUL 10. OJI 2011 118

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 }

Listing 10.1.8: ADSC_nodea.CPP

1 //Solutie operatii pe biti - Eugen Nodea


2 //compilata in MinGW =1 00p
3 //compilate in BC3.11 - 65p (peste 10 cifre!!!)
4
5 #include <stdio.h>
6
7 int n,m,a[31],i;
8 unsigned long fin;
9
10 int solutie(unsigned long x)
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
CAPITOLUL 10. OJI 2011 119

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 }

Listing 10.1.9: ADUNSC_A.CPP

1 //adriana simulescu - 100 puncte


2 //bactracking
3
4 #include<fstream>
5 #include<stdlib.h>
6
7 using namespace std;
8
9 ifstream f("adunscad.in");
10 ofstream g("adunscad.out");
11
12 int N,M,x[21],s[21];
13
14 void testare_afisare()
15 {
16 int i,E=0;
17
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++)
CAPITOLUL 10. OJI 2011 120

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 }

Listing 10.1.10: dana_ad.cpp

1 //dana lica - 100 p


2 //algoritm de tip backtracking nerecursiv
3
4 #include <stdio.h>
5
6 int c[25],sol[25], ok, N;
7 long M, nr, ns, i, k;
8
9 void afis()
10 {
11
12 int i,s1=0,s2=0;
13
14 nr++;
15 for (i=1;i<=M; i++)
16 if (sol[i]==1) {s1+=c[i]; s2-=c[i];}
17 else {s2+=c[i]; s1-=c[i];}
18
19 if (s1==N)
20 {
21 for (i=1;i<=M;i++)
22 if (sol[i]==1) if (i>1) printf("+%d" ,c[i]); else printf("%d", c[i]);
23 else printf("-%d",c[i]);
24 printf("\n");
25 ok=1;
26 return;
27 }
28
CAPITOLUL 10. OJI 2011 121

29 if (s2==N)
30 {
31 for (i=1;i<=M;i++)
32 if (sol[i]==1) printf("-%d" ,c[i]);
33 else if (i>1) printf("+%d" ,c[i]); else printf("%d", c[i]);
34 printf("\n");
35 ok=1;
36 return;
37 }
38 return;
39 }
40
41 int main()
42 {
43 freopen("adunscad.in","r",stdin);
44 freopen("adunscad.out","w",stdout);
45
46 scanf("%d%d", &N, &M);
47 for (i=1;i<=M; i++) scanf("%d",&c[i]);
48
49 k=1;
50 ns=(long)1<<(M-1);
51 while (k && !ok && nr<ns)
52 if (sol[k]<2)
53 {
54 sol[k]++;
55 if(k==M) afis();
56 else
57 {
58 k++;
59 sol[k]=0;
60 }
61 }
62 else k--;
63
64 if (!ok) printf("0\n");
65 fclose(stdout);
66 return 0;
67 }

Listing 10.1.11: vi_adunscad.cpp

1 //vieru ilie
2 #include<fstream>
3
4 using namespace std;
5
6 ifstream f("adunscad.in");
7 ofstream g("adunscad.out");
8
9 int n,m,t,s,y,a[200],b[200],c[200];
10
11 int main()
12 {
13 f>>n>>m;
14 int i,k;
15 for(i=1;i<=m;i++)
16 {
17 f>>c[i];
18 t+=c[i];
19 }
20
21 if((t-n)%2)
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;
CAPITOLUL 10. OJI 2011 122

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 }

10.1.3 *Rezolvare detaliat 

10.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  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
semnicaµ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 ³ier text ce conµine N linii, ecare linie ind 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 %.
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 ec rei comparaµii dintre cele N date (a³ând 0 dac  acea comparaµie e
fals , respectiv 1 dac  acea comparaµie e adev rat ).

Date de intrare
CAPITOLUL 10. OJI 2011 123

Fi³ierul comp.in conµine pe prima linie num rul natural nenul N , reprezentând num rul de
comparaµii, iar pe ecare dintre urm toarele N linii câte un ³ir de caractere corespunz tor unei
comparaµii.

Date de ie³ire
Fi³ierul comp.out va conµine pe prima linie un num r natural reprezentând num rul de com-
paraµii în care se utilizeaz  semnul $. Urmeaz  N linii, ecare linie conµinând doar valoarea 0
sau valoarea 1. Valoarea de pe a i-a linie dintre cele N este 0, dac  cea de-a i-a comparaµie din
³ierul de intrare este fals , respectiv 1 în caz contrar.

Restricµii ³i preciz ri
a 0$N & 1000
a Numerele din ³ier nu dep ³esc în valoare num rul eudorian 1000m1000s1000z1000u.
a Lungimea ec rei linii din ³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
comp.in comp.out Explicaµii
2 1 O comparaµie folose³te semnul <.
120u+7z13u>2s13u 0 Prima comparaµie e fals  (203>213).
1m11s+2z+1u<2m1s2z5u+0u 1 A doua comparaµie e adev rat  (2121<2125).

Timp maxim de executare/test: 1.0 secunde

10.2.1 Indicaµii de rezolvare


Citim din fisier, pe rand, cate o linie si efectuam urmatoarele operatii:

-extragem din sir partea stanga a inegalitatii respectiv partea dreapta


a inegalitatii;

-Pentru fiecare dintre cele doua parti ale inegalitatii:


o extragem pe rand cate un numar scris in forma eudoriana si-l transformam
in numar intreg;
o calculam suma valorilor transformate
o comparam cele doua sume obtinute si scriem rezultatul compararii in fisierul
de iesire.

Rezolvarea problemei nu necesita utilizarea unor algoritmi complicati ci doar


folosirea atenta a functiilor de lucru cu siruri de caractere.

Complexitatea rezolvarii este liniara.

10.2.2 Cod surs 

Listing 10.2.1: comp.pas

1 {Nodea Eugen - 100 puncte}


2 type vect= array[1..1001] of boolean;
3 var
4 f : text;
5 i,n,k,semn : integer;
6 sol : vect;
7 e : string;
8 es,ed : longint;
9 p : byte;
10
11 Function numar(t:string) : longint;
CAPITOLUL 10. OJI 2011 124

12 var x,y : longint;


13 i : byte;
14 begin
15 i := 1; x := 0;
16 while ( i <= length(t) ) do
17 begin
18 y:=0;
19 while (t[i] in [’0’..’9’]) do
20 begin
21 y := y*10 + ord(t[i]) - 48;
22 inc(i);;
23 end;
24 case t[i] of
25 ’m’: x := x + 1000 * y;
26 ’s’: x := x + 100 * y;
27 ’z’: x := x + 10 * y;
28 ’u’: x := x + 1 * y;
29 end;
30 inc(i);
31 end;
32 numar := x;
33 end;
34
35 Function eval(s:string) : longint;
36 var sol : longint;
37 p : byte;
38 t : string;
39 begin
40 sol := 0;
41 repeat
42 p := pos(’+’, s);
43 if (p>0) then begin
44 t := copy(s,1,p-1);
45 delete(s,1,p);
46 end
47 else t := s;
48 sol := sol + numar(t);
49 until p = 0;
50 eval := sol;
51 end;
52
53 Begin
54
55 assign (f, ’comp.in’); reset (f);
56 readln (f, n);
57
58 for i := 1 to n do
59 begin
60 readln (f,e);
61 semn := -1;
62 p := pos(’<’, e);
63 if (p > 0) then begin inc(k); semn := 1; end
64 else p := pos(’>’, e);
65 {extragem partea stanga a expresiei si partea dreapta}
66 es := eval( copy(e, 1, p-1));
67 ed := eval( copy(e, p+1, length(e)-p ));
68 sol[i] := es*semn < ed*semn;
69 end;
70
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.

Listing 10.2.2: alin.cpp

1 //burtza alin
2 #include <fstream>
3 #include <string.h>
4 #include <ctype.h>
5
CAPITOLUL 10. OJI 2011 125

6 using namespace std;


7
8 #define Fin "comp.in"
9 #define Fou "comp.out"
10 #define Max 251
11
12 //transform numarul reprezentat ca sir de caractere intr-un numar natural
13 long transforma(char *nr)
14 {
15 long rez;
16 int i,nm, ns, nz, nu, rp;
17 rez=0; i = nm = ns = nz = nu =rp = 0;
18
19 while(isdigit(nr[i])) { rp = rp * 10 + nr[i] - 48; i++;}
20 if(nr[i]==’m’) nm = rp;
21 if(nr[i]==’s’) ns = rp;
22 if(nr[i]==’z’) nz = rp;
23 if(nr[i]==’u’) nu = rp;
24
25 i++; rp = 0;
26 while(i<strlen(nr) && isdigit(nr[i])) { rp = rp * 10 + nr[i] - 48; i++;}
27 if(nr[i]==’s’) ns = rp;
28 if(nr[i]==’z’) nz = rp;
29 if(nr[i]==’u’) nu = rp;
30
31 i++; rp = 0;
32 while(i<strlen(nr) && isdigit(nr[i])) { rp = rp * 10 + nr[i] - 48; i++;}
33 if(nr[i]==’z’) nz = rp;
34 if(nr[i]==’u’) nu = rp;
35
36 i++; rp = 0;
37 while(i<strlen(nr) && isdigit(nr[i])) { rp = rp * 10 + nr[i] - 48; i++;}
38
39 if(nr[i]==’u’) nu = rp;
40 return nm * 1000 + ns * 100 + nz * 10 + nu;
41 }
42
43 //calculeaza valoarea expresiei reprezentate de parametrul sir
44 long suma(char *sir)
45 {
46 int i = 0, j;
47 long rez = 0;
48 char *termen = new char[Max];
49 while(i<strlen(sir))
50 {
51 j = 1;
52 while(’+’!=sir[i+j-1] && i+j-1<strlen(sir)) j++;
53 strncpy(termen, sir+i,j-1);
54 termen[j-1] = ’\0’;
55 rez += transforma(termen);
56 strcpy(termen,"");
57 i = i + j ;
58 }
59 return rez;
60 }
61
62 int rezolva(char *linie)
63 {
64 int i,j;
65 char *sleft, *sright, semn;
66 long stanga, dreapta;
67
68 sleft = new char[Max];
69 sright = new char[Max];
70
71 //separ cele doua parti ale inegalitatii
72 //memorand si operatorul de comparare
73
74 if(strchr(linie,’<’))
75 {
76 sright = strchr(linie,’<’) + 1;
77 strncpy(sleft, linie, sright - 1 - linie);
78 sleft[sright - 1 - linie] = ’\0’;
79 semn = ’<’;
80 }
81 else
CAPITOLUL 10. OJI 2011 126

82 {
83 sright = strchr(linie,’>’) + 1;
84 strncpy(sleft, linie, sright - 1 - linie);
85 sleft[sright - 1 - linie] = ’\0’;
86 semn = ’>’;
87 }
88
89 stanga=suma(sleft); //calculez valoarea expresiei din stanga inegalitatii
90 dreapta=suma(sright);//calculez valoarea expresiei din dreapta inegalitatii
91
92 //returnez 0 sau 1, in functie de valorile obtinute si de operatorul memorat
93 return ( semn==’<’ ? stanga < dreapta : stanga > dreapta);
94 }
95
96 int main()
97 {
98 char linie[Max];
99 int N;
100 int i;
101
102 ifstream f(Fin);
103 ofstream g(Fou);
104
105 f>>N;
106 f.get();
107
108 for(i=1;i<=N;i++)
109 {
110 f.get(linie,Max-1); //citesc o linie din fisier
111 f.get(); //"sar" peste caracterul de sfarsit de linie
112 g<<rezolva(linie)<<’\n’; //determin si afisez rezultatul
113 }
114
115 f.close();
116 g.close();
117 return 0;
118 }

Listing 10.2.3: comp_r.cpp

1 //PANAETE ADRIAN
2 //100 p pe evaluatorul lui Alin
3 //Algoritm: Parsarea expresiilor prin recursivitate inversa
4
5 #include<stdio.h>
6 #include<ctype.h>
7
8 int n,cnt,x[1001],comp(),member(),number(),digit();
9 char L[300],*p;
10
11 int main()
12 {
13 freopen("comp.in","r",stdin);
14 freopen("comp.out","w",stdout);
15 scanf("%ld",&n);
16 for(int i=1;i<=n;i++)
17 {
18 scanf("%s",L);p=L;
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
CAPITOLUL 10. OJI 2011 127

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();
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
CAPITOLUL 10. OJI 2011 128

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 }

Listing 10.2.4: em.cpp

1 //cerchez emanuela
2 #include <fstream>
3 #include <string.h>
4 #include <stdlib.h>
5
6 using namespace std;
7
8 #define LgMax 256
9
10 int n, nr;
11 int r[1001];
12 char s[LgMax];
13
14 long int expresie (char *);
15 long int numar (char *);
16
17 int main()
18 {
19 ifstream fin("comp.in");
20 ofstream fout("comp.out");
21 char *p;
22 int semn, i;
23 long int st, dr;
24
25 fin>>n;
26 for (i=0; i<n; i++)
27 {fin>>s;
28 p=strchr(s,’<’);
29 if (p) {nr++; semn=1;}
30 else {p=strchr(s,’>’);semn=0;}
31 *p=0;
32 st=expresie(s);
33 dr=expresie(p+1);
34 if (semn)
35 if (st<dr) r[i]=1; else r[i]=0;
36 else
37 if (st>dr) r[i]=1; else r[i]=0;
38 }
39 fout<<nr<<’\n’;
40 for (i=0; i<n; i++) fout<<r[i]<<’\n’;
41 fout.close();
42 return 0;
43 }
44
45 long int expresie (char *s)
46 {
47 char *p;
48 long int sum=0, x;
49 p=strtok(s,"+");
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;}
CAPITOLUL 10. OJI 2011 129

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 }

Listing 10.2.5: nodea.cpp

1 //nodea eugen
2 # include <fstream>
3 # include <iostream>
4 # include <cstring>
5
6 using namespace std;
7
8 ifstream f("comp.in");
9 ofstream g("comp.out");
10
11 int i,n;
12 char e[256];
13
14 long numar (char t[22])
15 {
16 long x=0,y=0;
17 int i=0;
18
19 while (i<strlen(t))
20 {
21 y=0;
22 while (t[i]>=’0’ && t[i]<=’9’)
23 {
24 y=y*10+t[i]-48;
25 i++;
26 }
27 if (t[i]==’m’) x+=y*1000,i++;
28 if (t[i]==’s’) x+=y*100,i++;
29 if (t[i]==’z’) x+=y*10,i++;
30 if (t[i]==’u’) x+=y,i++;
31 }
32 return x;
33 }
34
35 int eval(char s[256])
36 {
37 char *p,dr[256],sm,t[256];
38 long xs=0,xd=0,y,j;
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 {
CAPITOLUL 10. OJI 2011 130

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 }

Listing 10.2.6: p.cpp

1 //panaete adrian
2 #include<stdio.h>
3
4 long n,i,k,val,T[2];
5 char L[300],ORD;
6
7 int main()
8 {
9 freopen("comp.in","r",stdin);
10 freopen("comp.out","w",stdout);
11 scanf("%d",&n);
12 for(;n;n--)
13 {
14 k=0;
15 T[0]=T[1]=0;
16 scanf("%s",L);
17 for(i=0;L[i];i++)
18 {
19 if(L[i]==’+’)continue;
20 if(L[i]==’<’||L[i]==’>’){k=1;ORD=L[i];continue;}
21 if(L[i]==’m’){T[k]+=val*1000;val=0;continue;}
22 if(L[i]==’s’){T[k]+=val*100 ;val=0;continue;}
23 if(L[i]==’z’){T[k]+=val*10 ;val=0;continue;}
24 if(L[i]==’u’){T[k]+=val; ;val=0;continue;}
25 val*=10;val+=L[i]-’0’;
26 }
27 if(ORD==’>’)
28 T[0]>T[1]?printf("1\n"):printf("0\n");
29 else
30 T[0]>T[1]?printf("0\n"):printf("1\n");
31 }
32 return 0;
33 }

Listing 10.2.7: vi_comp.cpp

1 //vieru ilie
2 #include<fstream>
3
4 using namespace std;
5
6 ifstream f("comp.in");
7 ofstream g("comp.out");
8
9 int m1,s1,z1,u1,m2,z2,s2,u2,i,S,n,k;
10 char s[251],c;
11
12 int main()
13 {
14 f>>n;
15 f.get();
CAPITOLUL 10. OJI 2011 131

16 for(k=1;k<=n;++k)
17 {
18 f.getline(s,252);
19 i=S=m1=s1=z1=u1=m2=s2=z2=u2=0;
20 while(s[i]!=’<’ && s[i]!=’>’)
21 if(s[i]>=’0’ && s[i]<=’9’)
22 {
23 S=S*10+s[i]-48;
24 i++;
25 }
26 else
27 if(s[i]==’+’)
28 i++;
29 else
30 {
31 if(s[i]==’m’)
32 m1+=S;
33 else
34 if(s[i]==’s’)
35 s1+=S;
36 else
37 if(s[i]==’z’)
38 z1+=S;
39 else
40 u1+=S;
41 S=0;
42 i++;
43 }
44
45 c=s[i++];
46 while(s[i]!=0)
47 if(s[i]>=’0’ &&s[i]<=’9’)
48 { S=S*10+s[i]-48;
49 i++;
50 }
51 else
52 if(s[i]==’+’)
53 i++;
54 else
55 {
56 if(s[i]==’m’)
57 m2+=S;
58 else
59 if(s[i]==’s’)
60 s2+=S;
61 else
62 if(s[i]==’z’)
63 z2+=S;
64 else
65 u2+=S;
66 S=0;
67 i++;
68 }
69
70 z1+=u1/10; u1=u1%10;
71 s1+=z1/10; z1=z1%10;
72 m1+=s1/10; s1=s1%10;
73 z2+=u2/10; u2=u2%10;
74 s2+=z2/10; z2=z2%10;
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 }
CAPITOLUL 10. OJI 2011 132

10.2.3 *Rezolvare detaliat 


Capitolul 11

OJI 2010

11.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 gura al turat .
Fiecare dintre cl dirile aate 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 11.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 ecare
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
Fi³ierul de intrare cladiri.in conµine:
a pe prima linie, trei numere naturale Long Lat Intensitate, separate prin câte un spaµiu,
reprezentând coordonatele centrului seismului ³i respectiv intensitatea sa;
a pe ecare dintre urm toarele linii, pân  la sfâr³itul ³ierului, câte trei numere naturale Long
Lat Grad, separate prin câte un spaµiu, reprezentând coordonatele unei cl diri, respectiv gradul
de rezistenµ  la cutremur.

Date de ie³ire
Fi³ierul de ie³ire cladiri.out va conµine trei linii:
a pe prima linie se va scrie num rul natural N reprezentând num rul total de cl diri afectate;
a pe a doua linie se va scrie num rul natural M reprezentând num rul maxim de cl diri afectate
pe un nivel;

133
CAPITOLUL 11. OJI 2010 134

a pe a treia linie se vor scrie numerele nivelelor cu M cl diri afectate, în ordinea cresc toare a
numerelor acestor nivele.

Restricµii ³i preciz ri
a 0 & Long, Lat, Grad, Intensitate & 10000;
a 0 $ num r cl diri & 100000;
a în centrul seismului se pot aa cl diri;
a nu exist  mai multe cl diri cu acelea³i coordonate;
a 52% din punctaj se poate obµine pe teste de intrare cu 0 & Long, Lat, Grad, Intensitate & 100
a se acord  punctaje parµiale din punctajul acordat pe ecare test, astfel: 25% pentru cerinµa
a), 25% pentru cerinµa b), respectiv 50% pentru cerinµa c).

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 2 4 3 ³i este atins pe nivelele 2 ³i 4.
13 5 1
8 11 4
8 7 6
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

Timp maxim de executare/test: 1.0 secunde

11.1.1 Indicaµii de rezolvare


Solutia problemei consta in crearea unui vector al nivelelor, care va pastra
numarul cladirilor de pe acel nivel care sunt afectate de cutremur (gradul de
rezistenta mai mic sau egal cu intensitatea cutremurului in acel loc).

Identificarea nivelului pe care se gaseste o cladire se realizeaza prin


calculul expresiei

max(cx-x,cy-y),

unde (cx,cy) - coordonatele cladirii


(x, y) - coordonatele epicentrului cutremurului

Odata creat vectorul nivelelor, se identifica elementul maxim si care


dintre nivele au numar maxim de cladiri afectate.

11.1.2 Cod surs 

Listing 11.1.1: sol2.cpp

1 // Chesca Ciprian - sursa alternativa la "Cladiri" - OJI 2010


2 #include <fstream>
3 #include <math.h>
4
5 using namespace std;
6
7 typedef long vector[10001];
8
CAPITOLUL 11. OJI 2010 135

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 }

Listing 11.1.2: sol3.cpp

1 #include <fstream>
2 #include <math.h>
3
4 using namespace std;
5
6 long v[10001], nr_clad=0;
CAPITOLUL 11. OJI 2010 136

7
8 int main()
9 {
10 long lg1,lt1,ic1,lg,lt,ic,i,m=0,dist;
11
12 ifstream fin("cladiri.in");
13 ofstream fout("cladiri.out");
14
15 fin>>lg>>lt>>ic;
16
17 while (fin>>lg1>>lt1>>ic1)
18 {
19 dist = labs(lg-lg1)>labs(lt-lt1) ? labs(lg-lg1) : labs(lt-lt1);
20 if (ic1+dist<=ic)
21 {
22 v[dist]++;
23 nr_clad++;
24 }
25 }
26
27 fout<<nr_clad<<’\n’;
28
29 for(i=1;i<10001;i++)
30 if (v[i]>v[m])
31 m=i;
32
33 fout<<v[m]<<’\n’;
34
35 if (v[m])
36 for(i=m;i<10001;i++)
37 if (v[i]==v[m])
38 fout<<i<<’ ’;
39
40 fout<<’\n’;
41 fin.close();
42 fout.close();
43 return 0;
44 }

11.1.3 *Rezolvare detaliat 

11.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, ecare num r construit ind un produs de forma p
y
(y " N, y j 0) sau
m"N
m
q p , ³i q un num r prim, numindu-le numere p-prime. De exemplu, numerele 2, 3, 4, 5,
1 0 2
6, 7, 8, 10, 12, 13, 14, 16, 17 sunt primele 13 numere 2-prime deoarece 2 2 , 3 3 2 ,4 2 ,
0 1 0 3 1 2 0 1 4
5 5 2 ,6 3 2 , 7 7 2 , 8 2 , 10 5 2 , 12 3 2 , 13 13 2 , 14 7 2 , 16 2 ,
0
17 17 2 .
Într-o zi Maria a g sit o foaie de hârtie, pe care era scris un ³ir format din n numere naturale
nenule. Cum pe lâng  numerele p-prime ea este pasionat  ³i de secvenµe, ³i-a pus urm toarea
întrebare: câte secvenµe sunt pe foaie cu urm toarele propriet µi:
a conµin exact k numere p-prime;
a încep ³i se termin  cu un num r p-prim.
În plus, Maria dore³te s  ³tie care este poziµia de început ³i cea de nal, pentru ecare secvenµ 
descoperit , relative la ³irul scris pe foaia de hârtie.

Cerinµe
Scrieµi un program care s  citeasc  mai multe seturi de date, ecare set ind format din
numerele n, p, k , cu semnicaµiile din enunµ, ³i ³irul cu n elemente a1 , a2 , a3 , ..., an , numerele
Mariei. Programul va determina pentru ecare set de date num rul secvenµelor ce conµin exact k
numere p-prime, precum ³i poziµiile de început ³i de nal ale acestor secvenµe în ³irul din set.
CAPITOLUL 11. OJI 2010 137

Date de intrare
Pe prima linie a ³ierului secvente.in se a  num rul D reprezentând num rul de seturi de
date din ³ier. Seturile de date sunt scrise în ³ier pe linii succesive. Pentru ecare set de date,
prima linie conµine câte trei numere naturale: n (num rul de elemente de pe foaie), p ³i k (cu
semnicaµia din enunµ), separate prin câte un spaµiu, iar ecare dintre urm toarele n linii conµine
câte un num r natural al ³irului a1 , a2 , a3 , ..., an , numerele din ³irul Mariei.

Date de ie³ire
Fi³ierul secvente.out va conµine D soluµii corespunz toare celorD seturi de date. Pentru
ecare soluµie prima linie va conµine un num r x reprezentând num rul de secvenµe ce îndeplinesc
propriet µile cerute, iar ecare dintre urm toarele x linii vor conµine câte 2 numere naturale,
separate printr-un spaµiu, reprezentând poziµia de început, respectiv de nal a ec rei secvenµe,
linii ordonate cresc tor dup  poziµia de început. Dac  în ³ir nu exist  o astfel de secvenµ , prima
linie a setului va conµine valoarea 0.

Restricµii ³i preciz ri
a 1 & D & 15;
a 1 & k & n & 15000;
a 2 & p & 30000; p este un num r natural prim
˜
a 1 & a1 , a2 , a3 , ..., an & 30000; a1 , a2 , a3 , ..., an " N (poziµiile din ³ir sunt numerotate de la 1)
a num rul 1 nu este p-prim.
a o secvenµ  dintr-un ³ir este format  din elemente aate pe poziµii consecutive în ³irul dat.
CAPITOLUL 11. OJI 2010 138

Exemple
secvente.in secvente.out Explicaµii
2 2 Cum D=2, ³ierul de intrare conµine dou  seturi de date.
5 3 2 1 2 Primul set de date: n 5, p 3, k 2 ³i 7, 27, 4, 45, 1.
a
7 2 4 “irul din acest set conµine urm toarele numere 3-prime: a1
3
27 0 7 (num r prim), a2 27 3 (putere a lui 3) ³i a4 45
2
4 5 ˜ 3 (num r prim înmulµit cu o putere a lui 3). În ³ir sunt
45 dou  secvenµe cu câte 2 numere 3-prime: a1 , a2 respectiv a2 ,
1 a3 , a4 . Pe prima linie a ³ierului de ie³ire se va scrie valoarea
3 5 7 2, iar pe urm toarele dou  linii, poziµiile de început ³i de nal
3 ale celor dou  secvenµe determinate.
4 “irul a din al doilea set de date, n 3, p 5, k 7, a
5 3, 4, 5, nu conµine nici o secvenµ  cu proprietatea cerut .
Astfel, în ³ierul de ie³ire, pe cea de-a patra linie, se va scrie
valoarea 0.

Timp maxim de executare/test: 1.0 secunde

11.2.1 Indicaµii de rezolvare


1. Pana la 90 puncte

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.

Pozitiile numerelor prime se memoreaza intr-un vector V. Sa notam cu m dimensiunea


lui V. Daca avem k>m se afiseaza 0. In caz contrar sunt m-k+1 perechi care indeplin
conditia, acestea determinandu-se afisand perechi de valori din V cu proprietatea c
se afla la distanta k-1 una de cealalta.

in prima etapa, verificarea primalitatii unui numar se face parcurgand divizorii ac


pana la radicalul sau, adica in O( ).
Complexitate O(n)

2. Alte doua variante de 100 puncte

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.

11.2.2 Cod surs 

Listing 11.2.1: BERATO.PAS

1 {solutie Szabo Zoltan - GrSc"Petru Maior" Reghin}


2 program secvente3; {rezolvare cu ciurul lui Eratostene}
3
4 const MAX = 30000;
5
6 const fin=’secvente.in’;
7 fout=’secvente.out’;
8
9 var f,g:text;
CAPITOLUL 11. OJI 2010 139

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.

Listing 11.2.2: BPREPRO.PAS


CAPITOLUL 11. OJI 2010 140

1 {solutie Szabo Zoltan - GrSc"Petru Maior" Reghin}


2 program secvente4; {rezolvare cu preprocesarea numerelor prime}
3 {si cautare binara}
4 const fin=’secvente.in’;
5 fout=’secvente.out’;
6 prime:array[1..3246] of integer=
7 (1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
8 71, 73, 79, 83, 89, 97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,
9 ...
10 29921,29927,29947,29959,29983,29989);
11
12
13 var f,g:text;
14 t,d,n,p,k,m,i,x:integer; {n-atatea numere de pe foaie}
15 {p-baza componentei putere a numarului p-prim}
16 {k-cate numere prime vor fi in secventele cerute}
17 a:array[1..15000] of integer; {a- contine pozitiile numerelor p-prime}
18
19 function prim(x:integer):boolean;
20 var st,dr,mij:integer;
21 begin
22 st:=1;dr:=3246;
23 while st<=dr do
24 begin
25 mij:=(st+dr) div 2;
26 if prime[mij]=x then
27 begin
28 prim:=true;
29 exit;
30 end
31 else
32 if prime[mij]>x then
33 dr:=mij-1
34 else
35 st:=mij+1;
36 end;
37 prim:=false;
38 end;
39
40 function pprim(x:integer):boolean;
41 begin
42 if x=1 then pprim:=false {numarul 1 nu e pprim}
43 else
44 begin
45 while x mod p=0 do {simplific cu p}
46 x := x div p;
47 pprim:= prim(x); {si verific daca e prim}
48
49 {- de data asta se accepta si 1}
50 end;
51 end;
52
53 begin
54 assign(f,fin);
55 reset(f);
56 assign(g,fout);
57 rewrite(g);
58 readln(f,D);
59 for t:=1 to D do
60 begin
61 readln(f,n,p,k);
62 m:=0;
63 for i:=1 to n do
64 begin
65 readln(f,x);
66 if pprim(x) then
67 begin
68 inc(m);
69 a[m]:=i; {am memorat pozitia de aparitie a unui numar p-prim}
70 end;
71 end;
72
73 if m-k+1<=0 then
74 writeln(g,0)
75 else
CAPITOLUL 11. OJI 2010 141

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.

11.2.3 *Rezolvare detaliat 


Partea II

ONI - Olimpiada naµional  de


informatic 

142
Capitolul 12

ONI 2020

12.1 ***
Problema 1 - ... 100 de puncte

Cerinµe
Date de intrare
Date de ie³ire
Restricµii ³i preciz ri

Exemple:

***.in ***.out Explicaµii

Timp maxim de executare/test: 0.5 secunde pe Windows, 0.5 secunde pe Linux


Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 15 KB

12.1.1 *Indicaµii de rezolvare

12.1.2 *Cod surs 

12.1.3 *Rezolvare detaliat 

12.2 ***
Problema 2 - ... 100 de puncte

Cerinµe
Date de intrare
Date de ie³ire
Restricµii ³i preciz ri

143
CAPITOLUL 12. ONI 2020 144

Exemple:
CAPITOLUL 12. ONI 2020 145

***.in ***.out Explicaµii

Timp maxim de executare/test: 0.5 secunde pe Windows, 0.5 secunde pe Linux


Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 15 KB

12.2.1 *Indicaµii de rezolvare

12.2.2 *Cod surs 

12.2.3 *Rezolvare detaliat 

12.3 ***
Problema 3 - ... 100 de puncte

Cerinµe
Date de intrare
Date de ie³ire
Restricµii ³i preciz ri

Exemple:

***.in ***.out Explicaµii

Timp maxim de executare/test: 0.5 secunde pe Windows, 0.5 secunde pe Linux


Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 15 KB

12.3.1 *Indicaµii de rezolvare

12.3.2 *Cod surs 

12.3.3 *Rezolvare detaliat 


Capitolul 13

ONI 2019

13.1 criptograe - ONI 2019


Problema 1 - criptograe 100 de
puncte
Zedd a descoperit frumuseµea aplicaµiilor din domeniul criptograei. Astfel, el ³i-a activat
abilit µile de hacker ³i s-a lovit de urm toarea problem : ind dat un ³ir format doar din litere
mici ale alfabetului englez, Zedd trebuie s  g seasc  secvenµe pe care le poate forma f r  ca vreo
liter  s  apar  de prea multe ori.

Cerinµe
Cunoscând textul lui Zedd, s  se determine:
1. Num rul de secvenµe distincte în care ecare liter  poate s  apar  de maximum k ori. Dou 
secvenµe sunt considerate distincte dac  difer  e prin poziµia de început, e prin cea de nal.
2. Cea mai lung  secvenµ  care conµine doar litere distincte. Dac  sunt mai multe secvenµe
de lungime maxim  formate din litere distincte se alege prima din punct de vedere lexicograc
(alfabetic).

Date de intrare
Fi³ierul de intrare criptograe.in conµine pe prima linie cerinµa C (care poate  1 sau 2),
pe linia a doua num rul natural k, cu semnicaµia de mai sus, precum ³i un num r natural n,
separate printr-un spaµiu. Pe a treia linie se a  un text format din n litere mici ale alfabetului
englez (neseparate prin spaµii).

Date de ie³ire
Fi³ierul de ie³ire criptograe.out va conµine pe prima linie:
Dac  C 1 un num r natural ce reprezint  r spunsul la cerinµa 1.
Dac  C 2 ³irul ce reprezint  r spunsul la cerinµa 2.
Restricµii ³i preciz ri
a o secvenµ  este format  dintr-o succesiune de litere aate 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
criptograe.in criptograe.out Explicaµii
1 8 Pentru textul dat, variantele care ar putea  obµinute
1 4 conform cerinµei sunt: a, ab, b, ba, bac, a, ac, c
abac În total num rul de secvenµe cu caractere distincte
(k 1) ce pot  formate este 8.
2 acb Lungimea maxim  a unei secvenµe de elemente dis-
1 6 tincte este 3. Sunt trei astfel de secvenµe. Prima din
abacba punct de vedere alfabetic este acb.

146
CAPITOLUL 13. ONI 2019 147

Timp maxim de executare/test: 0.2 secunde


Memorie: total 20 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 10 KB
Sursa: criptograe.cpp, criptograe.c sau criptograe.pas va  salvat  în folderul care are drept
nume ID-ul t u.

13.1.1 Indicaµii de rezolvare


prof. Alina Pintescu

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.

Imbunatatirea care aduce timpul de executare de ordin N este urmatoarea:


Pentru fiecare pozitie i calculam lungimea maxima a unei secvente terminata
pe pozitia i si in care fiecare litera apare de maxim k ori. Daca pentru
pozitia curenta i avem p ca fiind pozitia de inceput a secventei determinate,
la trecerea la pozitia urmatoare, daca litera noua ajunge sa apara de k+1 ori,
va trebui sa crestem p pana intalnim o aparitie a literei de pe pozitia i.
Astfel, odata cu marirea lui i, p va putea doar sa creasca. Se adauga la
solutie i-p+1 (numarul de secvente terminate la pozitia i).

Pentru cerinta 2, literele fiind distincte, lungimea secventei cerute nu poate


depasi 26. Astfel, o abordare bruta, cu numar de operatii de ordin n*26 se va
incadra in timp

13.1.2 Cod surs 

Listing 13.1.1: criptograe.cpp

1 #include <fstream>
2 #include <cstring>
3
4 using namespace std;
5
6 long long sol;
7 char s[100010];
8 int c, k, n, i, p, maxim;
9 char ch;
10 char smaxim[27];
11 int f[26];
12
13 int main ()
14 {
15 ifstream fin ("criptografie.in");
16 ofstream fout("criptografie.out");
17
18 fin>>c;
19 fin>>k>>n;
20 for (i=1;i<=n;i++)
21 fin>>s[i];
22
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;
CAPITOLUL 13. ONI 2019 148

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 }

13.1.3 *Rezolvare detaliat 

13.2 drept - ONI 2019


Problema 2 - drept 100 de
puncte
Numim poligon drept un poligon cu laturile consecutive perpendiculare ³i lungimile laturilor
numere naturale nenule. Un poligon drept cu n laturi este descris de un ³ir de n numere întregi
nenule în care lungimile laturilor sunt date de valoarea absolut  a numerelor din ³ir, iar semnul
precizeaz  poziµia laturilor, un num r pozitiv însemnând latur  spre dreapta sau în sus faµ  de
extremitatea laturii precedente, iar un num r negativ însemnând latur  în jos sau spre stânga faµ 
de extremitatea laturii precedente; de exemplu ³irul 1, 1, 1, 1 reprezint  un p trat de latur  1
(prima latur  spre dreapta, a doua în sus, a treia spre stânga, a patra în jos). Vom considera
laturile ca ind orizontale sau verticale, prima latur  enumerat  ind orizontal  spre dreapta,
dac  num rul este pozitiv, sau spre stânga, dac  num rul este negativ.

Cerinµe
Se dau unul sau mai multe ³iruri de numere întregi nenule.
1. S  se stabileasc , pentru ecare dintre ele, dac  reprezint  un poligon drept.
2. ³tiind c  ³irurile date reprezint  poligoane drepte, s  se determine aria ec ruia.

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 ecare test. Pe prima
linie corespunz toare unui test se a  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
Fi³ierul drept.out va conµine pe o singur  linie rezultatele corespunzatoare celor T teste,
separate prin câte un spaµiu. Dac  C 1 pentru ecare ³ir rezultatul este 1, dac  acesta reprezint 
un poligon drept, sau 0 altfel. Dac  C 2 pentru ecare ³ir rezultatul este aria poligonului drept
corespunz tor.

Restricµii ³i preciz ri
CAPITOLUL 13. ONI 2019 149

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 gura sunt întregi
nenule aparµinând intervalului 100, 100
a Pentru celelalte 15 puncte ³i C 1 numerele ce descriu gura 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 e vid , e 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
1 2 1 0
8
5 3 -3 -1 2 -1 -4 -1
8
-2 1 3 1 -4 -3 2 1
2 2 9 1
8
5 3 -3 -1 2 -1 -4 -1
4
1 1 -1 -1

Explicaµii:

Figura 13.1: Drept

Timp maxim de executare/test: 0.2 secunde


Memorie: total 20 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 10 KB
Sursa: drept.cpp, drept.c sau drept.pas va  salvat  în folderul care are drept nume ID-ul t u.

13.2.1 Indicaµii de rezolvare


prof. Nistor Mot

Consideram ca punctul de start al primei laturi este centrul sistemului de


coordonate si vom calcula mai intai coordonatele celorlalte varfuri ale
figurii.

Fie yminim si ymaxim valorile minima resprectiv maxima ale coordonatelor y


pentru varfurile ce apar in figura iar xminim si xmaxim valorile minima
resprectiv maxima ale coordonatelor x pentru varfurile ce apar in figura.

Pentru testele in care se indica faptul ca orice orizontala taie poligonul


intr-un singur segment aria se putea calcula astfel:

Parcurgem toate valorile y aflate intre yminim si ymaxim-1 iar la un pas


determinam minimul (min) si maximul(max) dintre coordonate x ale segmentelor
verticale care includ [y, y+1].
CAPITOLUL 13. ONI 2019 150

Adunam la valoarea solutiei diferenta max-min.

Pentru aflarea ariei pe cazul general, determinam toate pozitiile caroiajului


delimitat de xminim, xmaxim, yminim, ymaxim. Pentru o pozitie numaram cate
verticale sunt in stanga ei.

Contorizam la solutie daca si numai daca valoarea determinata este impara.

Pentru a testa daca linia poligonala este inchisa, suma valorilor


corespunzatoare laturilor orizontale trebuie sa fie 0, analog pentru
cele verticale.

Pentru a testa intersectia a doua segmente, verificam pentru oricare doua


neconsecutive daca extremitatile vreunuia se afla pe celalalt sau daca ele
se intersecteaza in alt punct.

Ambele teste se bazeaza pe a verifica daca o valoare se afla intr-un interval.

13.2.2 Cod surs 

Listing 13.2.1: drept.cpp

1 #include <fstream>
2 #include <iostream>
3
4 using namespace std;
5
6 struct punct
7 {
8 int x;
9 int y;
10 };
11
12 punct v[210];
13 int C, t, n, xmin, xmax, ymin, ymax, i, d, j, k;
14 int poligon, aria, jos, stanga;
15
16 int punctPeSegment(int i, int j)
17 {
18 if (v[i].x >= min(v[j].x, v[j+1].x) && v[i].x <= max(v[j].x, v[j+1].x))
19 if (v[i].y >= min(v[j].y, v[j+1].y) &&
20 v[i].y <= max(v[j].y, v[j+1].y))
21 return 1;
22 return 0;
23 }
24
25 int intersectieSegmente(punct a, punct b, punct c, punct d)
26 {
27 punct aux;
28
29 if (a.x > b.x) { aux = a; a = b; b = aux; }
30 if (c.y > d.y) { aux = c; c = d; d = aux; }
31
32 if ((c.y - a.y) * 1LL * (d.y - a.y) <= 0)
33 if ((c.x - a.x) * 1LL * (c.x - b.x) <= 0)
34 return 1;
35 return 0;
36 }
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 {
CAPITOLUL 13. ONI 2019 151

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;
113 for (k=0;k<n;k++)
114 {
115 if (k%2 == 1)
116 {
117 if (v[k].x < i && min(v[k].y, v[k+1].y) < j &&
118 max(v[k].y, v[k+1].y) >= j)
119 stanga++;
120 }
121 else
122 {
CAPITOLUL 13. ONI 2019 152

123 if (v[k].y < j && min(v[k].x, v[k+1].x) < i &&


124 max(v[k].x, v[k+1].x) >= i)
125 jos++;
126 }
127 }
128
129 if (stanga % 2 && jos % 2)
130 aria++;
131 }
132 }
133
134 if (C == 1)
135 fout<<poligon<<" ";
136 else
137
138 fout<<aria<<" ";
139 }
140
141 return 0;
142 }

13.2.3 *Rezolvare detaliat 

13.3 lumini - ONI 2019


Problema 3 - lumini 100 de puncte
Privit  din spaµiu, harta insulei din povestea noastr  are forma unui caroiaj p tratic cu L linii
³i L coloane. Liniile ³i coloanele sunt numerotate de la 1 la L. În ecare dintre cele L˜L celule
se a  câte un far. Iniµial cel de la poziµia 1, 1 este aprins ³i toate respect  regula: orice far are
farurile vecine (pe linie ³i coloan , deci maximum 4) în starea opus  faµ  de starea sa.
În urma unei furtuni, s-au întâmplat lucruri ciudate: fulgerele au lovit unul dup  altul ³i au
afectat starea unor faruri.
Sunt trei tipuri de fulgere:
- Fulger de tip 1. Pentru acesta se indic  linia pe care love³te ³i el afecteaz  starea farurilor
de pe linia respectiv  ³i de pe liniile cu num r de ordine mai mare. Mai exact, toate farurile de
pe aceste linii î³i schimb  instantaneu starea.
- Fulger de tip 2. Pentru acesta se indic  un num r ce reprezint  coloana pe care love³te ³i
el afecteaz  starea farurilor de pe coloana respectiv  ³i de pe coloanele cu num r de ordine mai
mare. Mai exact, toate farurile de pe aceste coloane î³i schimb  instantaneu starea.
- Fulger de tip 3. Pentru acesta se indic  linia apoi coloana unui element din caroiaj. Toate
farurile aate pe aceea³i paralel  cu diagonala secundar  cu elementul specicat ³i pe paralelele
cu diagonala secundar  aate sub aceasta, î³i schimb  starea.
Prin schimbarea st rii unui far înµelegem c  acesta se aprinde dac  este stins ³i se stinge dac 
este aprins.

Cerinµe
Se dau date despre fulgere, în ordinea în care acestea acµioneaz . Se cere ca la nalul furtunii
s  se indice care este starea anumitor faruri, aate la coordonate precizate de pe insul .

Date de intrare
Prima linie a ³ierului lumini.in conµine un num rul natural L, cu semnicaµia de mai sus.
Pe linia a doua se a  un num r natural F, reprezentând num rul de fulgere.
Pe urm toarele F linii se a  date despre câte un fulger, în ordinea în care acestea apar.
Primul num r de pe ecare linie este tipul de fulger (1 sau 2 sau 3). Dac  acest num r este 1
sau 2, se mai a  pe aceast  linie un spaµiu ³i înc  un num r. Acesta reprezint  linia pe care
love³te fulgerul (dac  linia din ³ier are la început 1), respectiv coloana pe care love³te fulgerul
(dac  linia respectiv  din ³ier are la început 2). Dac  este vorba despre fulger de tip 3, pe acea
linie se mai a  dou  numere, reprezentând linia ³i coloana elementului de pe insul  unde love³te
fulgerul, separate prin spaµiu.
CAPITOLUL 13. ONI 2019 153

Linia urm toare conµine un num r Q. Pe urm toarele Q linii se a  câte dou  numere (separate
prin spaµiu) reprezentând linia ³i coloana unui far de pe insul  pentru care se dore³te aarea 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 ³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 ecare far a c rei stare trebuie aat  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
lumini.in lumini.out Explicaµii
4 1 0 0 1 0 Iniµial starea becurilor de pe insul  poate  reprezentat  astfel:
3 1 0 1 0
1 2 0 1 0 1
3 3 1 1 0 1 0
2 3 0 1 0 1
5 Dup  aplicarea primului fulger starea becurilor devine
1 1 1 0 1 0
2 4 1 0 1 0
3 2 0 1 0 1
4 2 1 0 1 0
4 4 Dup  aplicarea celui de-al doilea fulger starea becurilor devine
1 0 0 1
1 1 0 1
1 0 1 0
0 1 0 1
Dup  aplicarea celui de-al treilea starea becurilor devine
1 0 1 0
1 1 1 0
1 0 0 1
0 1 1 0

Timp maxim de executare/test: 0.3 secunde


Memorie: total 20 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 10 KB
Sursa: lumini.cpp, lumini.c sau lumini.pas va  salvat  în folderul care are drept nume ID-ul
t u.

13.3.1 Indicaµii de rezolvare


prof. Marius Nicoli

Solutia 1
CAPITOLUL 13. ONI 2019 154

Pentru fiecare update (fulger) simulam intr-o matrice LXL comportamentul


acestuia. Ulterior raspundem la fiecare interogare prin verificarea
valorii corespunzatoare a matricei.
Timpul de executare este de ordinul L*L*F.

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.

Trebuie pentru aceasta doar trei vectori (doi de lungime L si unul de


lungime 2*L, cel pentru diagonala). Sa notam acesti vectori l (pentru linii),
c (pentru coloane) si d (pentru diagonale). La aparitia unui fulger de
tipul 1 la linia x, schimbam starea valorilor din l aflate pe pozitii de la
x la L. La aparitia unui fulger de tipul 2 la linia x, schimbam starea
valorilor din c aflate pe pozitii de la x la L.

La aparitia unui fulger de tipul 3 la linia x si coloana y schimbam starea


valorilor din d aflate pe pozitii de la x+y la 2*L. La final putem obtine
rezultatul unei interogari pe baza valorilor celor trei vectori.

Aceasta solutie are timp de calcul de ordin F*L.

Solutia 3

Facem o observatie suplimentara pornind de la solutia anterioara:


la aparitia fiecarui fulger putem nota doar in pozitia de inceput
din vectorului corespunzator. La final, folosind o parcurgere a
vectorilor putem afla valorile finale ale lor (pe o idee asemanatoare
cu ceea ce se cunoaste in peisajul informatic romanesc drept
’’trucul lui Mars’’). O astfel de solutie are timpul de calcul de ordin L.

Solutia 4

Observam ca valoarea dintr-o anume pozitie p din oricare dintre vectorii


despre care am discutat anterior depinde de numarul de actualizari aparute
inaintea pozitiei p.

Putem asadar sa stocam pozitiile la care apar actualizari, sa le sortam,


iar la o interogare vom cauta binar in acesti vectori. Pentru a tine cont
si de starea initiala a becurilor mai luam in calcul si paritatea sumei
coordonatelor locului la care se face interogatea. Aceasta solutie are
timp de calcul F*F + Q*P (unde P este de ordinul puterii la care trebuie
ridicat 2 pentru a obtine valoarea F). Evident ca valoarea lui F putea fi
data mai mare dar nu s-a urmarit testarea elevilor la cunoasterea unui
algoritm de sortare rapid scris de mana sau dintr-o biblioteca.

13.3.2 Cod surs 

Listing 13.3.1: lumini.cpp

1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 ifstream f("lumini.in");
6 ofstream g("lumini.out");
7
CAPITOLUL 13. ONI 2019 155

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++)
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--;
CAPITOLUL 13. ONI 2019 156

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 }

13.3.3 *Rezolvare detaliat 


Capitolul 14

ONI 2018

14.1 gene
Problema 1 - gene 100 de puncte
Gigel este curios s  ae î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  identice 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  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 ecare element reprezint  structura genetic  a unei persoane din zona respectiv .

Cerinµe
Cunoscând N , M , K, structura genetic  pentru Gigel ³i reprezentarea h rµii identicat  de
acesta, s  se determine:
1) poziµia pe hart  ³i structura genetic  pentru persoana, sau persoanele, pentru care gradul
de similaritate cu structura genetic  a lui Gigel este maxim;
2) o zon  p tratic , de dimensiune maxim  în care toate persoanele ar putea  str mo³i ai lui
Gigel.

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  rezolvat .
Pe a doua linie numerele naturale N, M ³i K, separate prin câte un spaµiu, cu semnicaµia de
mai sus.
Pe a treia linie se a  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
Fi³ierul de ie³ire gene.out va conµine r spunsul la cerinµa rezolvat .
Dac  s-a rezolvat prima cerinµ , ³ierul de ie³ire va conµine, pe linii separate, datele de iden-
ticare pentru persoanele cu cel mai mare grad de similaritate, dup  cum urmeaz : câte dou 
numere naturale, separate prin câte un spaµiu, care reprezint  poziµia pe hart  a unei persoane
urmate de un spaµiu ³i secvenµa care indic  structura ei genetic .
Dac  s-a rezolvat cerinµa 2, pe prima linie a ³ierului de ie³ire se vor scrie, separate prin câte un
spaµiu, trei numere naturale x y lgmax reprezentând poziµia colµului din stânga sus, exprimat  în
ordine prin indicele liniei ³i al coloanei, respectiv lungimea maxim  a laturii p tratului identicat
pe hart .

157
CAPITOLUL 14. ONI 2018 158

Restricµii ³i preciz ri
a 0$N & 500, 0 $ M & 500, 0 $ K & 20
a colµul din stânga sus al h rµii are poziµia 1, 1;
a dac  pentru cerinµa 1 exist  mai multe persoane cu grad de similaritate maxim, se vor a³a
în ordine lexicograc  a poziµiei pe hart  (în ordine cresc toare a indicelui de linie, iar în caz de
egalitate pentru acesta, în ordine cresc toare a indicelui de coloan );
a dac  pentru cerinµa 2 exist  mai multe zone maximale, se va a³a cea cu indicele de linie cel
mai mic, iar în caz de egalitate ³i pentru acesta, cea cu indicele de coloan  mai mic;
a pentru rezolvarea corect  a primei cerinµe se acord  30 de puncte, pentru rezolvarea corect 
a celei de a doua cerinµe se acord  70 de puncte.

Exemple
gene.in gene.out Explicaµii
1 1 1 aaacctg Secvenµa corespunz toare lui Gigel este: acgt
3 3 3 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 2 3 2 Pentru harta dat , gradele de similaritate cu secvenµa acgt
4 4 2 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 2 3 1 4
ctg 2 1 3 4
abvf 1 2 4 4
acgtaaa 3 3 0 1
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

Timp maxim de executare/test: 0.5 secunde


Memorie: total 64 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 15 KB

14.1.1 Indicaµii de rezolvare

prof. Raluca Costineanu - Colegiul Naµional “tefan cel Mare, Suceava

Pentru determinarea gradului de similaritate dintre dou  secvenµe putem num ra, folosind doi
vectori de frecvenµ , num rul de apariµii a ec rui caracter în ecare din cele dou  ³iruri.
Gradul de similaritate îl obµinem însumând num rul minim de apariµii pentru ecare caracter.
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 ecare element
CAPITOLUL 14. ONI 2018 159

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 ecare 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 
e 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  valoarea maxim  din matricea a.

14.1.2 *Cod surs 

14.1.3 *Rezolvare detaliat 

14.2 jocxzero
Problema 2 - jocxzero 100 de puncte
Pe o foaie dintr-un caiet de matematic  de dimensiune N  M (N X X X X 0
num rul de linii ³i M num rul de coloane) sunt completate toate p tr - 0 X X X 0
µelele cu X sau 0. Pentru un num r natural K dat, numim ³ir corect, o
0 0 X 0 0
secvenµ  de K elemente consecutive pe linie, coloan  sau diagonale care
0 0 0 X X
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.
Exemplu din gura 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  P1 atunci ³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 ³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 ³ierul de intrare.

Restricµii ³i preciz ri
CAPITOLUL 14. ONI 2018 160

a 1 & N & 100


a 2 & M & 10 000
a 1 & K & 100
a 1 & Q & 100 000
a 0 & B & 1 000 000 000
a în ³ierele de intrare caracterul X este majuscul  iar 0 este caracterul cifra zero.
a Pentru rezolvarea corect  a cerinµei 1) se acord  40 puncte, pentru rezolvarea corect  a
cerinµei 2) se acord  60 de puncte

Exemple
jocxzero.in jocxzero.out Explicaµii
1 6 5 Pe prima linie sunt 2 ³iruri corecte de X, pe a doua un ³ir
4 5 3 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 
4 5 3 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
0 1
X 1

Timp maxim de executare/test: 0.7 secunde


Memorie: total 64 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 15 KB

14.2.1 Indicaµii de rezolvare

prof. Dan Octavian Dumitra³cu - Colegiul Naµional Dinicu Golescu, Câmpulung, Arge³

Pentru punctul 1 vom construi 4 matrici de dimensiune N  M în care


susij  lungimea ³irului curent care se termin  în i, j ³i este în sus
stij  lungimea ³irului curent care se termin  în i, j ³i este în stânga
stsij  lungimea ³irului curent care se tremin  în i, j ³i este în stânga sus
stj ij  lungimea ³irului curent care se tremin  în i, j ³i este în stânga jos.

Apoi calcul m pentru ecare i ³i j dac  exist  un ³ir de lungime mai mare sau egal  cu k din
cele 4 posibilit µi.
Complexitatea este O N  M pentru punctul 1.

Pentru punctul 2 vom construi în plus un vector în care vom memora


în sir0j  câte ³iruri de 0 sunt în partea stânga a foii, dac  t iem foaia dup  coloana j
³i în sir1j  câte ³iruri de X sunt în partea stânga a foii, dac  t iem foaia dup  coloana j
Evident cele 2 ³iruri sunt crescatoare (nu strict crescatoare) ³i vom c uta binar pentru ecare
interogare cea mai mic  poziµie pe care se g seste B în ³irul dorit ³i cea mai mare poziµie pe care
se g se³te B.
Complexitate este O NM  Qlog M  pentru punctul 2.

14.2.2 *Cod surs 

14.2.3 *Rezolvare detaliat 


CAPITOLUL 14. ONI 2018 161

14.3 laser
Problema 3 - laser 100 de puncte
Consider m N segmente în plan identicate prin coordonatele extremit µilor lor. Toate seg-
mentele sunt închise, adic  ecare 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 a  un
laser care poate transmite câte un fascicul de lumin  în orice punct cu ordonata pozitiv  ( ' 0).
Fasciculul poate  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  ecare 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  alese pentru a obtura orice fascicul
de lumin  care ar pleca din origine c tre un punct cu ordonata pozitiv .

Date de intrare
Fi³ierul de intrare laser.in conµine pe prima linie num rul natural N de segmente.
Pe urm toarele N linii se a  câte cinci numere întregi x1 y1 x2 y2 cost, separate prin câte
un spaµiu.
Primele patru numere reprezint  coordonatele extremit µilor ec rui segment, pentru ecare
dintre ele în ordine abscisa ³i ordonata, iar ultimul num r de pe linie reprezint  costul segmentului.

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.

Restricµii ³i preciz ri
a 1&N & 5000
a 10 & abscisele punctelor & 109
9

a 0 & ordonatele punctelor & 10


9

a 0 & costurile segmentelor & 10


9

a Se garanteaz  c  punctul O 0, 0 nu se a  pe niciunul din cele N segmente


a La evaluare se vor folosi ³iere de intrare în valoare de 30 de puncte care au pentru toate
segmentele costul egal cu 1

Exemple
laser.in laser.out Explicaµii
4 4 S-au ales segmentele de cost total minim
2 3 5 0 2 [(-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)] ;
2 0 1 1 1 [(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
2 0 1 1 5

Timp maxim de executare/test: 1.0 secunde


Memorie: total 64 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 15 KB
CAPITOLUL 14. ONI 2018 162

14.3.1 Indicaµii de rezolvare

prof. Lica Elena Daniela - Centrul Judeµean de Excelenµ  Prahova, Ploie³ti

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  ordonate antitrigono-
metric (în sensul acelor de ceasornic), adic  pantaunghi1 % pantaunghi2.
Toate cele N segmente vor  reµinute într-un vector A. Toate segmentele se vor ordona
descresc tor dup  pantaunghi1.
Pentru determinarea costului minim, ne a 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 ecare
segment i:
dpi - costul minim al unei sub³ir care îl are ca utim element pe segmentul i ³i care obtureaz 
OX , mai mare sau egal decât pantaunghi2.
orice fascicul de lumin  cu panta unghiul format cu axa
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 & i  1) a.î. printre segmentele
i ³i j nu trece niciun fascicul de lumin  ³i vom retine min dpj . Dac  nu exist  vom avea
dpi inf .

14.3.2 *Cod surs 

14.3.3 *Rezolvare detaliat 


Capitolul 15

ONI 2017

15.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.
Denim o nav  linie (L) ca un set de p tr µele umbrite, consecutive pe un rând al foii de
matematic .
Denim 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  e rezolvat  1 sau 2 ). Pe cea de a doua linie ³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 a 
câteN 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 ³ierului boats.out va  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 ³ierul boats.out vor  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 ecare dintre liniile urm toare vor  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.

Restricµii ³i preciz ri
a 2&M & 1000; 2 & N & 1000
a Se garanteaz  existenµa a cel puµin unei nave
a Pentru rezolvarea corect  a primei cerinµe se va acorda 20% din punctaj, iar pentru rezolvarea
corect  a celei de a doua cerinµe se va acorda 80% din punctaj.

163
CAPITOLUL 15. ONI 2017 164

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

L 2 1 Se rezolv  doar cerinµa 2.


L 5 1 Exist  3 nave linie: o nav  linie de lun-
L 6 1 gime 2, o nav  linie de lungime 5 ³i o
C 3 2 nav  linie de lungime 6.
C 4 1 Exist  3 nave coloan : 2 nave coloan 
de lungime 3, o nav  coloan  de lungime
4.

Timp maxim de executare/test: 1.1 secunde


Memorie: total 4 MB
Dimensiune maxim  a sursei: 5 KB

15.1.1 Indicaµii de rezolvare

prof. Liliana Chira - Colegiul Naµional Mihai Eminescu, Boto³ani

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 tablo-
ului 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.

15.1.2 Cod surs 

Listing 15.1.1: boatsAB.cpp

1 //Alin Burta
2 #include <fstream>
3
CAPITOLUL 15. ONI 2017 165

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
79 Este = false;
CAPITOLUL 15. ONI 2017 166

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 }

Listing 15.1.2: boatsCM.cpp

1 //Carmen Minca
2 #include <iostream>
3 #include <fstream>
4
5 using namespace std;
6
7 ifstream f("boats.in");
8 ofstream g("boats.out");
9
10 short int a[1003][1003],n,m,p;
11 short int oriz[1002],vert[1002];
12
13 void citire()
14 {
15 int i,j;
16 f>>p>>n>>m;
17 for(i=1;i<=n;i++)
18 for(j=1;j<=m;j++)
19 f>>a[i][j];
20 }
21
22 void scrie()
23 {
24 int i,j;
25 for(i=1;i<=n;i++)
26 {
27 for(j=1;j<=m;j++)
28 cout<<a[i][j]<<" ";
29 cout<<endl;
30 }
31 }
CAPITOLUL 15. ONI 2017 167

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 }

Listing 15.1.3: boatsLC.cpp

1 //prof.Liliana Chira
2 #include <iostream>
3 #include <fstream>
4
5 using namespace std;
6
7 ifstream f("boats.in");
8 ofstream g("boats.out");
9
10 int m,n,v[1001][1001],singur_patratel,max_orizontal=2,max_vertical=2,p;
11
12 struct
13 {
14 int numar=0;
15 } lungime_orizontal[1001],lungime_vertical[1001];
16
17 void citire() {
18 f>>p>>m>>n;
19 for(int i=1;i<=m;i++)
20 for(int j=1;j<=n;j++)
21 f>>v[i][j];
22 }
23
24 void determinare_nave() {
25 for(int i=1;i<=m;i++)
26 for(int j=1;j<=n;j++)
27 {
28 if(v[i][j]==1) {
29 v[i][j]=0;
30 int lungime=1,k;
31 if(v[i-1][j]==1) {
32 k=i-1;
33 while(v[k][j]==1) {
CAPITOLUL 15. ONI 2017 168

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 15. ONI 2017 169

15.1.3 *Rezolvare detaliat 

15.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
Fi³ierul doilan.in conµine pe prima linie num rul natural n.
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 .

Restricµii ³i preciz ri
a 1 & n & 100
a Pentru 30% din punctaj, n & 18

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 ³ierului de
ie³ire doilan.out
Timp maxim de executare/test: 0.2 secunde
Memorie: total 2 MB
Dimensiune maxim  a sursei: 5 KB

15.2.1 Indicaµii de rezolvare

prof. Carmen Minc  - Colegiul Naµional de Informatic  Tudor Vianu, Bucure³ti

Demonstr m existenµa num rului X prin inducµie matematic  dup  n, (n num r natural ne-
nul).
...
Acest num r X (soluµia problemei) este al n-lea termen al ³irului Xn construit conform relaµiilor
de mai sus:

1
X1 2 1 2
2
X2 2 3 12
...
k
Xk 2 p ...
k
2 10  Xk , dac  p 2 q
Xk1 w k
1 10  Xk , dac  p 2 q1
CAPITOLUL 15. ONI 2017 170

15.2.2 Cod surs 

Listing 15.2.1: doilan.cpp

1 # include <fstream>
2
3 using namespace std;
4
5 int y[103],x[103],n;
6
7 ifstream f("doilan.in");
8 ofstream g("doilan.out");
9
10 void imparte_la_2(int k)
11 {
12 int t=0,i;
13 for(i=1; i<=k; i++)
14 {
15 t=y[i]+t*10;
16 y[i]=t/2;
17 t=t%2;
18 }
19 }
20
21 int paritate_cat_impartirea_lui_x_la_2lan(int k)
22 {
23 int i;
24 for(i=1; i<=k; i++)
25 imparte_la_2(k);
26 return y[k]%2;
27 }
28
29 int main()
30 {
31 y[1]=x[1]=2;
32 f>>n;
33 int k,i,c;
34 for(k=1; k<n; k++)
35 {
36 if(paritate_cat_impartirea_lui_x_la_2lan(k)==1)
37 c=1;
38 else
39 c=2;
40
41 for(i=k; i>=1; i--)
42 y[i+1]=x[i+1]=x[i];
43
44 y[1]=x[1]=c;
45 }
46
47 for(int i=1; i<=n; i++)g<<x[i];
48 g<<endl;
49 return 0;
50 }

15.2.3 *Rezolvare detaliat 

15.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 identi-
care a coordonatelor unui punct din plan, într-un sistem de axe ortogonale.

Numim semn Z în planul xOy gura obµinut  cu ajutorul a 4 puncte


distincte A xA , yA , B xB , yB , C xC , yC , D xD , yD , unite ca în g.1, în
care xA xC , xB xD , yA yB , yC yD .
CAPITOLUL 15. ONI 2017 171

Pe ecran este a³at  o foaie de matematic  ³i sistemul de axe ortogonale


xOy . Succesiv, apar coordonatele întregi ale unor puncte din plan. Juc torul
trebuie s  marcheze pe foaie ecare punct ³i s  traseze un segment care s  uneasc  punctul (cu
excepµia primului punct marcat) cu cel marcat anterior.

În exemplul din g.2 au fost marcate succesiv punctele: 8, 4,


5, 6, 2, 6, 1, 6, 2, 2, 5, 2, 5, 2, 1, 2, 5, 2,
1, 2, 1, 2, 2, 6, 1, 2, 1, 2. Se observ  c  punctele se pot
repeta.

La sfâr³itul jocului, juc torul trebuie s  numere de câte ori a trecut


prin originea sistemului de coordonate O 0, 0 ³i care este num rul maxim
al semnelor Z distincte, formate cu puncte marcate.

Cerinµe
Cunoscându-se n (num rul de puncte a³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  e rezolvat  (1 sau 2).
Pe cea de a doua linie se a  num rul natural n, reprezentând num rul punctelor a³ate
succesiv pe ecran.
Pe urm toarele n linii se a  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 ³ierului z.out va  scris un num r natural
reprezentând num rul de treceri prin originea sistemului de coordonate.
Dac  cerinµa este P 2, atunci ³ierul de ie³ire z.out va conµine num rul maxim al semnelor
Z distincte, formate cu puncte marcate.

Restricµii ³i preciz ri
a 4 & n & 1000
a 1000 & x & 1000; 1000 & y & 1000
a O trecere prin originea sistemului de coordonate este determinat  de trasarea unui segment
care conµine originea sistemului ³i are capetele diferite de origine.
a Pentru rezolvarea corect  a primei cerinµe se va acorda 20% din punctaj, iar pentru rezolvarea
corect  a celei de a doua cerinµe se va acorda 80% din punctaj.

Exemple
z.in z.out Explicaµii
1 2 A trecut de dou  ori prin originea sistemului de coordonate.
14
-8 4
-5 6
-2 6
1 6
-2 2
-5 -2
-5 2
1 2
-5 -2
1 -2
-1 2
2 6
-1 2
1 -2
CAPITOLUL 15. ONI 2017 172

2 3 S-au format 3 semne Z, reprezentate în gurile 3, 4 ³i 5.


14
-8 4
-5 6
-2 6
1 6
-2 2
-5 -2
-5 2
1 2
-5 -2
1 -2
-1 2
2 6
-1 2
1 -2

Timp maxim de executare/test: 1.0 secunde


Memorie: total 20 MB
Dimensiune maxim  a sursei: 10 KB

15.3.1 Indicaµii de rezolvare

prof. Cristina Sichim - Colegiul Naµional Ferdinand I Bac u

1) Pentru determinarea num rului de treceri prin originea sistemului xOy , se veric  pentru
ecare 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 ecare ordonat  Y: mulµimea
punctelor marcate, distincte, cu ordonata Y ³i intervalele orizontale  x1, Y , x2, Y .
Se calculeaz  reuninea intervalelor orizontale, pentru ecare ordonat  Y.
Se memoreaz  segmentele oblice SO care ar putea forma un semn Z ³i se realizeaz  reuniunea
segmentelor oblice.
Pentru ecare segment oblic astfel determinat, se identic  mulµimea perechilor de puncte
marcate care se a  pe acest segment ³i se veric , pentru ecare diagonal  determinat  de
punctele perechii, dac  determin  un semn Z.

15.3.2 Cod surs 

Listing 15.3.1: z.cpp

1 #include <fstream>
2 #define L 2001
3
4 using namespace std;
5
6 ifstream fin("z.in");
7 ofstream fout("z.out");
8
9 struct punct
10 {
11 short x,y;
12 } p[L];
13
14 struct segmentOrizontal
15 {
16 short st,dr;
17 } S[L][L];
18
19 bool P[L][L];
20
CAPITOLUL 15. ONI 2017 173

21 struct segmnetOblic
22 {
23 short xs,ys,xj,yj;
24 } SO[L];
25
26 short ymin,ymax,k,n,x,y,i,xa,ya,nro;
27 unsigned long long z;
28
29 void adaugaSegmentOrizontal(int y, int x1, int x2)
30 {
31 if(x1>x2) swap(x1,x2);
32 S[y][0].st++;
33 S[y][S[y][0].st].st=x1;
34 S[y][S[y][0].st].dr=x2;
35 }
36
37 int peAceeasiDreapta(int x1,int y1, int x2, int y2, int x3, int y3)
38 {
39 return (x2-x1)*(y3-y1)==(x3-x1)*(y2-y1);
40 }
41
42 void reuniuneIntervale()
43 {
44 int i,j,y,st,dr;
45 for(y=ymin;y<=ymax;y++)
46 if(S[y][0].st)
47 {
48 int nr=S[y][0].st;
49 for(i=1;i<nr;i++)
50 for(j=i+1;j<=nr;j++)
51 if(S[y][i].st>S[y][j].st ||
52 S[y][i].st==S[y][j].st && S[y][i].dr>S[y][j].dr)
53 swap(S[y][i],S[y][j]);
54 k=0;
55 st=S[y][1].st;
56 dr=S[y][1].dr;
57 for(i=2;i<=nr;i++)
58 if(S[y][i].st<=dr)
59 {
60 if(S[y][i].dr>dr)
61 dr=S[y][i].dr;
62 }
63 else
64 {
65 k++;
66 S[y][k].st = st;
67 S[y][k].dr = dr;
68 st=S[y][i].st;
69 dr=S[y][i].dr;
70 }
71
72 k++;
73 S[y][k].st=st;
74 S[y][k].dr=dr;
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;
CAPITOLUL 15. ONI 2017 174

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 {
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
CAPITOLUL 15. ONI 2017 175

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 }

15.3.3 *Rezolvare detaliat 


Capitolul 16

ONI 2016

16.1 cercetasi
Problema 1 - cercetasi 100 de puncte
Un grup de N cerceta³i, numerotaµi de la 1 la N, se a  î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 ecare 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  pre-
zinte 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 aaµi în tab r  se poate scrie ca sum  de M numere consecutive în mulµi-
mea 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 
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 ae 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 a  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 aat î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 identicare 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 ae pe locurile lor.

Date de intrare
Fi³ierul de intrare cercetasi.in conµine pe prima linie num rul natural N cu semnicaµia din
enunµ. Pe a doua linie, se a  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.

176
CAPITOLUL 16. ONI 2016 177

Restricµii ³i preciz ri
a 0$N & 10000 ³i N Š rx " N¶x 4 ˜ k  2, k " Nx
a Un joc Mutare odat  început, se va încheia doar atunci când cerceta³ul din interiorul
cercului se a³az  pe locul s u.
a Din punctajul acordat pe un test, 40% se acord  dac  num rul M a³at pe prima linie este
corect, 40% dac  valorile scrise pe a doua linie sunt corecte, respectiv 20% dac  num rul scris pe
a treia linie este corect.

Exemple
cercetasi.in cercetasi.out Explicaµii
8 2 Dac  N 8, atunci M este 2, iar exerciµiile vor  prezentate
2 3 4 1 5 8 6 7 3 5 de cerceta³ii numerotaµi cu 3, respectiv cu 5.
4 Cerceta³ul cu num rul 3 nu se a  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 aat î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
a  deja pe locul s u, num rul de cerceta³i care î³i schimb 
locul r mâne 4.

Timp maxim de executare/test: 0.1 secunde


Memorie: total 16 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 10 KB

16.1.1 Indicaµii de rezolvare

prof. Filonela Rodica B la³a - Colegiul Naµional Grigore Moisil, Bucure³ti

Rezolvarea are 2 etape:


1. Determinarea num rului M de cerceta³i care vor prezenta exerciµiile ³i a numerelor de
identicare a acestora
2. Determinarea num rului minim de cerceta³i care trebuie s  î³i schimbe locul

Pentru determinarea num rului M ³i a celor M numere impare, în funcµie de valoarea lui N,


avem 2 cazuri:

Cazul I: N este impar.


In acest caz, M 1, iar cerceta³ul care trebuie s  se ae pe locul s u are num rul N
Cazul II: N este multiplu de 4:
În acest caz, putem calcula num rul maxim de termeni µinând cont de urm toarele condiµii
care trebuie îndeplinite simultan:
M M
a 2 divide pe N (2 a  num rul de termeni)
M
a câtul împarµirii lui N la 2 trebuie s  e num r par
M
a câtul împarµirii lui N la 2 trebuie s  e mai mare decât num rul de termeni din descom-
punere - 1 (astfel încât primul termen s  e num r natural)

Pentru determinarea num rului minim de cerceta³i care î³i vor schimba locul, se poate utiliza
un tablou cu 3 coloane, în care ecare linie p corespunde cerceta³ului numerotat cu p.
Pentru o cerceta³ul numerotat cu p, vom reµine:
a 1 în coloana 0, dac  p este num rul unui cerceta³ care face parte dintre cei M prezentatori
³i trebuie s  se ae pe locul s u, ³i 0 altfel;
a 1 în coloana 1, dac  p este num rul unui cerceta³ care a fost de la început pe locul s u sau
a ajuns în urma unei mut ri pe locul s u, ³i 0 altfel;
a în coloana 2 reµinem poziµia iniµial  a cerceta³ului cu num rul p.
CAPITOLUL 16. ONI 2016 178

Pentru ecare dintre cei M cerceta³i, în cazul în care nu se a  pe locul lor, efectu m schim-
b rile jocului Mutare, actualizând tabloul caracteristic ³i num rul de cerceta³i care î³i schimb 
locul.

16.1.2 Cod surs 

Listing 16.1.1: cercetasi_ema.cpp

1 //Em. Cerchez 100 puncte


2 #include <fstream>
3
4 #define NMAX 10002
5
6 using namespace std;
7
8 ifstream fin("cercetasi.in");
9 ofstream fout("cercetasi.out");
10
11 int N, C, nr;
12 int p[NMAX];
13 int unde[NMAX];
14
15 bool verif (int x, int& lg, int& prim);
16 int mutare (int x);
17
18 int main()
19 {int i, x, prim, lg, rez;
20 fin>>N;
21 for (i=1; i<=N; i++) {fin>>p[i]; unde[p[i]]=i;}
22 rez=verif(N,lg,prim);
23
24 if (rez)
25 {
26 fout<<lg<<’\n’;
27 for (i=0; i<lg-1; i++)
28 {fout<<prim+i*2<<’ ’;
29 if (p[prim+2*i]!=prim+2*i) nr+=mutare(prim+2*i);}
30 fout<<prim+i*2<<’\n’;
31 if (p[prim+2*i]!=prim+2*i) nr+=mutare(prim+2*i);
32 fout<<nr<<’\n’;
33 }
34 fout.close();
35 return 0;
36 }
37
38 bool verif (int x, int& lg, int& prim)
39 { int q=0, putere=1;
40 if (x&1) {lg=1; prim=x; return 1;}
41 while (!(x&1)) {q++; x>>=1; }
42 if (q==1) return 0;
43 do
44 {
45 q--;
46 lg=1<<q;
47 putere<<=1;
48 prim=putere*x-lg+1;
49 }
50 while (prim<0);
51 return 1;
52 }
53
54 int mutare (int x)
55 {int i, cate=0;
56 for (i=unde[x]; i!=x; i=unde[i], cate++)
57 p[i]=i;
58 p[x]=x;
59 return cate+1;
60 }

Listing 16.1.2: cercetasi2_RBalasa.cpp

1 #include <fstream>
CAPITOLUL 16. ONI 2016 179

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
76 cx=cx+2;
77 }
CAPITOLUL 16. ONI 2016 180

78
79 fout<<nr<<"\n";
80 }
81
82 return 0;
83 }

16.1.3 *Rezolvare detaliat 

16.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 ecare
substanµ  activ  prescris .
Farmacista de la care cump r medicamentele mi-a f cut o list  în care pentru ecare 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 
administrate simultan, deoarece ar produce reacµii adverse. De aceea, farmacista mea mi-a dat
³i o list  de incompatibilit µi, în list  ind specicate perechi de medicamente incompatibile, sub
forma:

medicament1 /medicament2
Când cump r reµeta, eu trebuie s  iau câte un medicament pentru ecare 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 ecare 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 a  num rul natural n, reprezentând num rul de substanµe active prescrise
de medic.
Pe urm toarele n linii se a  lista pe care mi-a dat-o farmacista, pe ecare linie ind specicat 
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 a  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, ³ierul de ie³ire farma.out va conµine n linii, pe linia i (1 & i & n) ind
scris num rul de medicamente disponibile pentru substanµa activ  descris  pe linia i  1 în ³ierul
de intrare.
Dac  cerinµa este 2, ³ierul de ie³ire farma.out va conµine o singur  linie pe care va  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 16. ONI 2016 181

Restricµii ³i preciz ri
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 ecare substanµ  activ  exist  cel mult 9 medicamente care s  conµin  substanµa
activ  respectiv .
a În ecare pereche din lista de incompatibilit µi se a  medicamente care conµin substanµe
active diferite.
a În lista de medicamente corespunz toare ec 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 pentru
3 3 prima substanµ  activ , 3 me-
metformin:siofor 10,glibomet 30,bidiab 60,gliformin 10 3 dicamente 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

Timp maxim de executare/test: 0.1 secunde


Memorie: total 16 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 10 KB

16.2.1 Indicaµii de rezolvare

prof. Emanuela Cerchez - Colegiul Naµional Emil Racoviµ  Ia³i

Rezolvarea cerinµei 1 nu necesit  comentarii.

Pentru cerinµa 2 putem proceda în modul urm tor.

n (denumirile acestora sunt irelevante în problem ).


S  numerot m substanµele active de la 1 la
S  not m cu lg i=num rul de medicamente existente pentru substanµa activ  i. Problema const 
în generarea elementelor produsului cartezian

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  abordat  în moduri diferite:


CAPITOLUL 16. ONI 2016 182

1. Gener m toate elementele produsului cartezian printr-un algoritm de tip succesor. Pentru
ecare element generat, veric m dac  medicamentele sunt compatibile dou  câte dou . În caz
armativ, 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  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  convenabil .
Pentru a obµine mai rapid o sum  mai mic , vom sorta cresc tor dup  preµ medicamentele din
lista ec rei substanµe active.
Aceast  soluµie obµine 100 de puncte (f arma_ok ).
O implementare recursiv  pentru aceast  idee este concis  ³i ecient  (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 ecare 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  vericarea rapid  a compatibilit µii medicamentelor
selectate în soluµie. Pentru aceast  propunem urm toarea codicare 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). Identic 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 identicare c1 ³i c2 sunt
incompatibile, respectiv 0 în caz contrar.

Atenµie! Codicarea medicamentelor se va face dup  sortarea acestora dup  preµ, pentru c  la
sortare medicamentele î³i schimb  poziµiile în lista de medicamente.

16.2.2 Cod surs 

Listing 16.2.1: farma_ok.cpp

1 //Em. Cerchez
2 #include <fstream>
CAPITOLUL 16. ONI 2016 183

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 16. ONI 2016 184

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 16. ONI 2016 185

155 for (j=1; j<=lg[i]; j++)


156 if (!strcmp(s,a[i][j].nume)) return i*10+j;
157 return 0;
158 }

Listing 16.2.2: farma_raluca.cpp

1 //Raluca Costineanu
2 #include <fstream>
3 #include <cstring>
4
5 using namespace std;
6
7 ifstream fin("farma.in");
8 ofstream fout("farma.out");
9
10 char activ[11][10][31];
11 int C, n, m, pret[11][11],
12 incomp[100][100],sumMin=999999999, sum, x[11], ales[11];
13
14 void trim(char s[])
15 {
16 int i;
17 for(i=0;s[i];i++)
18 if(s[i]==’ ’)
19 strcpy(s+i,s+i+1),i--;
20 }
21
22 void ordoneaza(int m)
23 {
24 int i, ok;
25 do
26 {
27 ok=1;
28 for(i=1;i<pret[m][0];i++)
29 if(pret[m][i]>pret[m][i+1])
30 {
31 int aux=pret[m][i];
32 pret[m][i]=pret[m][i+1];
33 pret[m][i+1]=aux;
34
35 char s[31];
36 strcpy(s,activ[m][i]);
37 strcpy(activ[m][i],activ[m][i+1]);
38 strcpy(activ[m][i+1],s);
39 ok=0;
40 }
41 } while(ok==0);
42 }
43
44 int cauta(char p[])
45 {
46 int i, j;
47 for(i=1;i<=n;i++)
48 for(j=1;j<=pret[i][0];j++)
49 if(strcmp(activ[i][j], p)==0)
50 return i*10+j;
51 return 0;
52 }
53
54 int compatibil(int k, int i)
55 {
56 for(int j=1;j<k;j++)
57 if(incomp[j*10+x[j]][k*10+i])
58 return 0;
59 return 1;
60 }
61
62 void calc(int k)
63 {
64 for(int i=1;i<=pret[k][0];i++)
65 if(compatibil(k,i))
66 {
67 x[k]=i;
68 sum+=pret[k][i];
CAPITOLUL 16. ONI 2016 186

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 }

Listing 16.2.3: lucia_farma.cpp


CAPITOLUL 16. ONI 2016 187

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 16. ONI 2016 188

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 }

16.2.3 *Rezolvare detaliat 

16.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 codic  aceste numere într-un sistem propriu,
transformându-le într-o în³iruire de litere ³i cifre dup  algoritmul urm tor:
26
- noteaz  ecare putere a lui 2, strict mai mic  decât 2 , cu o liter  a alfabetului, astfel:

- reprezint  ecare num r ca un ³ir de cifre ³i litere obµinut din scrierea acelui num r ca sum 
CAPITOLUL 16. ONI 2016 189

de puteri ale lui 2; dac  o putere este folosit  de mai multe ori în descompunerea num rului atunci
ea va  precedat  în ³ir de num rul de utiliz ri.
Un num r poate  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
Scrieµi un program care rezolv  urm toarele cerinµe:
1. cunoscând s num rul de stele dintr-o galaxie, determin  o reprezentare codicat  a acestui
num r format  doar din litere mici distincte ordonate alfabetic;
2. cunoscând g , reprezentând num rul de galaxii ³i g numere în scriere codicat , reprezentând
num rul de stele din ecare galaxie, determin  scrierea zecimal  a num rului total de stele din
cele g galaxii.

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 a  un num r natural s, ce reprezint  num rul care
trebuie codicat.
Dac  cerinµa este 2, pe a doua linie se a  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,
codicat 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  scris un ³ir de litere mici
distincte, ordonate alfabetic, reprezentând scrierea codicat  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).

Restricµii ³i preciz ri
1&s&2 1
26
a
a 1 & g & 1000
a Reprezent rile codicate din ³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

Timp maxim de executare/test: 0.1 secunde


Memorie: total 16 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 10 KB
CAPITOLUL 16. ONI 2016 190

16.3.1 Indicaµii de rezolvare

prof. Raluca Costineanu - Colegiul Naµional "“tefan cel Mare" - Suceava

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,
s$2
26
³i , vom determina direct scrierea num rului. Pentru aceasta, putem lucra în dou  moduri:
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 nal a³ m codicarea puterilor reµinute în ordine invers .
II. Folosim scrierea în baza 2 a num rului s, a³â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 ec 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 ecare num r în baza 10 ³i adun m numerele obµinute, folosind operaµii cu numere
mari. (70 de puncte)
Varianta 2 - transform m ecare numar în scriere zecimal  ³i determin m suma lor, f r  a
utiliza operaµii cu numere mari. (20 de puncte)

16.3.2 Cod surs 

Listing 16.3.1: stele_ema_100.cpp

1 #include <fstream>
2
3 using namespace std;
4
5 ifstream fin("stele.in");
6 ofstream fout("stele.out");
7
8 int cerinta;
9 long long int s;
10 long long int p[30];
11 char rez[30];
12 char sir[1000];
13 int sum[100], lgs;
14 int rezultat[100], lgr;
15 int prod[100], lgprod;
16
17 void decodifica();
18 void produs (long long int a, long long int b, int c[], int& lgc);
19 void suma(int a[], int lga, int b[], int lgb, int s[], int &lgs);
20
21 int main()
22 {int i, j, nr;
23 for (i=p[0]=1; i<26; i++) p[i]=2*p[i-1];
24 fin>>cerinta;
25 if (cerinta==1)
26 {
27 fin>>s;
28 for (i=25, j=0; s; i--)
29 if (p[i]<=s) {rez[j++]=i+’a’; s-=p[i];}
30 for (i=j-1; i>=0; i--) fout<<rez[i];
31 }
32 else
33 {
34 fin>>nr;
35 for (i=0; i<nr; i++)
36 {fin>>sir;
37 decodifica();
38 suma(sum,lgs,rezultat,lgr, sum, lgs);}
39 for (i=lgs-1; i>=0; i--) fout<<sum[i];
40 }
41 fout<<’\n’;
CAPITOLUL 16. ONI 2016 191

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 }

Listing 16.3.2: stele_raluca.cpp

1 #include <fstream>
2
3 using namespace std;
4
5 ifstream fin("stele.in");
6 ofstream fout("stele.out");
7
8 long long v[26];
9 long long nr[26];
10
11 void cerinta1(int nr, char s[])
12 {
13 char x;
14 int i=0, j=0;
15
16 while(i<25 && v[i]<=nr)
17 i++;
18
19 while(nr>0)
20 {
21 while(v[i]>nr)
22 i--;
23 s[j++]=i+’a’;
24 nr-=v[i];
25 }
26
27 s[j]=0;
CAPITOLUL 16. ONI 2016 192

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 16. ONI 2016 193

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 }

16.3.3 *Rezolvare detaliat 


Capitolul 17

ONI 2015

17.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 ecare 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 nalul etapei în creuzet sunt 5 pic turi (1+4=5);
- etapa a 3-a: se toarn  o pic tur ; la nalul 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 nalul 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 ecare 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 ecare pagin  s  poat  cuprinde o reµet  ca a noastr , oricât de lung  ar
 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
Se cere num rul de reµete publicate în ultimul tratat.

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 semnicaµ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.

Restricµii ³i preciz ri

194
CAPITOLUL 17. ONI 2015 195

a 1$C & 107 ; 2 & N & 107 ; 1 & P & 107 , numere naturale;
a pentru 30% dintre teste, C & 10 ³i N $ 10, iar pentru 70% dintre teste, N & 10 .
4

Exemple
magic.in magic.out Explicaµii
4 2 3 1 Creuzetul are capacitatea C 4, sunt N 2 etape de aplicare
a ec rei reµete, tratatele au câte P 3 pagini. Reµetele apli-
cate în dou  etape, în urma ec 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 .

Timp maxim de executare/test: 0.1 secunde


Memorie: total 4 MB
Dimensiune maxim  a sursei: 5 KB

17.1.1 Indicaµii de rezolvare

prof. Livia •OCA, Colegiul Naµional de Informatic  Tudor Vianu, Bucure³ti

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 ecare 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/le-
xicograc , cifrele de control ale acestora vor forma un ³ir de secvenµe de numere consecutive,
ecare secvenµ  ind 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 1 N 1 C
Astfel, num rul total de secvenµe de numere consecutive este
C 1
C .

Întrucât cifra 1 apare o singur  dat  în ecare 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 .

17.1.2 Cod surs 

Listing 17.1.1: magicN2.cpp

1 #include <fstream>
2 #define DIM 40002
3
4 using namespace std;
5
6 int S[2][DIM], D[2][DIM];
7
8 int C, N, P, i, j, c;
9
10 int main()
11 {
12 ifstream fin("magic.in");
13 ofstream fout("magic.out");
14
15 fin>>C>>N>>P;
CAPITOLUL 17. ONI 2015 196

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 }

Listing 17.1.2: sursaGenerarePas1.cpp

1 #include <iostream>
2 #include <fstream>
3
4 using namespace std;
5
6 ifstream in("magic.in");
7 ofstream out("magic.out");
8
9 long C,N,P;
10 long reteta[10000];
11
12 long increment()
13 {
14 long transport=1, k;
15 for(long i=N-1;transport && i>=0;i--)
16 {
17 k=reteta[i]+transport;
18 reteta[i]=k%C;
19 transport=k/C;
20 }
21 return transport;
22 }
23
24 long calculeazaMagic()
25 {
26 long mag=0;
27 for(long i=0;i<N;i++)
28 if(reteta[i]+mag==C)
29 mag=1;
30 else
31 if(reteta[i]+mag>C)
32 mag=1+reteta[i]+mag-C;
33 else
34 mag=mag+reteta[i];
CAPITOLUL 17. ONI 2015 197

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 }

Listing 17.1.3: sursaGenerarePasC.cpp

1 #include <iostream>
2 #include <fstream>
3 #include <cstring>
4
5 using namespace std;
6
7 ifstream in("magic.in");
8 ofstream out("magic.out");
9
10 long C,N,P;
11 long reteta[10000];
12
13 long aduna(long k)
14 {
15 long transport=k;
16 for(long i=N-1;transport&&i>=0;i--)
17 {
18 k=reteta[i]+transport;
19 reteta[i]=k%C;
20 transport=k/C;
21 }
22 return transport;
23 }
24
25 int main()
26 {
27 long i;
28 long long nr;
29 in>>C>>N>>P;
30
31 reteta[0]=1;
32 nr=0;
33 do
34 {
35 nr=(nr+1)%P;
36 } while(aduna(C-1)==0);
37
38 if(nr==0)
39 nr=P;
40 out<<nr;
41
42 return 0;
43 }

Listing 17.1.4: sursaNumarareBKT.cpp

1 #include <iostream>
2 #include <fstream>
CAPITOLUL 17. ONI 2015 198

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 }

Listing 17.1.5: sursaPutereLogN.cpp

1 #include <iostream>
2 #include <fstream>
3
4 using namespace std;
5
6 ifstream in("magic.in");
7 ofstream out("magic.out");
8
9 long long C;
10 long N,P;
11
12 int main()
13 {
14 long i;
15 long long nr;
16 in>>C>>N>>P;
17
18 N--;
19 nr=1;
20 while(N!=0)
CAPITOLUL 17. ONI 2015 199

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 }

Listing 17.1.6: sursaPutereN.cpp

1 #include <iostream>
2 #include <fstream>
3
4 using namespace std;
5
6 ifstream in("magic.in");
7 ofstream out("magic.out");
8
9 long C,N,P;
10
11 int main()
12 {
13 long i;
14 long long nr;
15 in>>C>>N>>P;
16
17 nr=1;
18 for(i=1;i<=N-1;i++)
19 nr=(nr*C)%P;
20
21 if (nr==0)
22 nr=P;
23
24 out<<nr;
25 return 0;
26 }

17.1.3 *Rezolvare detaliat 

17.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, ecare cu l µimea de 1 metru, a³ezaµi
unul lâng  altul (lipiµi). Se cunoa³te în lµimea, în metri, a ec 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, ecare dintre acestea ocupând, eventual, spaµiul aat deasupra mai multor piloni.
Arheologii au analizat situaµia, independent, pentru Q valori posibile ale lui H.
CAPITOLUL 17. ONI 2015 200

Cerinµe
Pentru ecare 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
Fi³ierul restaurare.in conµine:
- pe prima linie, num rul N de piloni;
- pe a doua linie, N numere naturale, separate prin câte un spaµiu, reprezentând în lµimile
iniµiale ale pilonilor, în ordine, de la stânga la dreapta;
- pe linia a treia, num rul natural Q, reprezentând num rul de valori posibile pentru în lµimea
H;
- pe a patra linie, Q numere naturale, separate prin câte un spaµiu, reprezentând valorile
posibile ale lui H.

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 ecare dintre în lµimile H, în ordinea în care acestea apar
în ³ierul de intrare.

Restricµii ³i preciz ri
a 1&N & 100 000;
a în lµimea ec rui pilon este un num r natural din intervalul [1, 100 000];
a 1 & Q & 100 000;
a 1 & H &valoarea maxim  dintre în lµimile iniµiale ale pilonilor;
a pentru 35% dintre teste N≤1000, iar pentru alte 40% dintre teste Q 1.

Exemple
restaurare.in restaurare.out Explicaµii
5 0 Forma iniµial  Pentru H=1 toµi pilonii au aceea³i în lµime,
4 3 2 4 2 4 a zidului deci nu mai este necesar  nicio c r mid ,
3 2 pentru H=4, sunt necesare 4 c r mizi, zidul
1 4 3 având, dup  restaurare structura din g. a,
iar pentru H=3, sunt necesare 2 c r mizi, zi-
dul având, dup  restaurare structura din g.
b.

Timp maxim de executare/test: 0.6 secunde


Memorie: total 8 MB
Dimensiune maxim  a sursei: 10 KB

17.2.1 Indicaµii de rezolvare

prof. Marius NICOLI, Colegiul Naµional Fraµii Buze³ti, Craiova

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
CAPITOLUL 17. ONI 2015 201

sunt 1, reprezentând secvenµa iniµial , aat  deasupra zidului). Observ m c  o valoare a lui U
odat  setat  la 0 va r mâne cu aceast  valoare pân  la nalul algoritmului.

Analizând o în lµime K (la care ajungem parcurgînd în lµimile în ordine descresc toare), tre-
buie 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), secv
vom actualiza valoarea variabilei astfel:
dac  U p  1 0 ³iU 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 ecare dintre ele odat  cu aplicarea algoritmului prezentat mai sus.

Algoritmul descris anterior are complexitate în timp liniar .

17.2.2 Cod surs 

Listing 17.2.1: restaurare.cpp

1 #include <fstream>
2 #include <iostream>
3
4 #include <bitset>
5 #include <vector>
6
7 #define DIM 100010
8 #define INF 100010
9
10 using namespace std;
11
12 int u[DIM], f[DIM], x[DIM], y[DIM], crt, S[DIM], m, h;
13
14 int v[DIM];
15 pair<int, int> A[DIM];
16 int n, maxim, i, j, last, p, d;
17
18 long long secv, sol;
19
20 int main()
21 {
22 ifstream fin ("restaurare.in");
23 ofstream fout("restaurare.out");
24
25 fin>>n;
26 for (i=1;i<=n;i++)
27 {
28 fin>>v[i];
29 if (v[i] > maxim)
30 maxim = v[i];
31 }
32
33 for (i=1;i<=n;i++)
34 {
35 v[i] = maxim - v[i] + 1;
36 f[ v[i] ]++;
37 }
38
39 for (i=1;i<=maxim;i++)
40 f[i] += f[i-1];
41
42 for (i=n;i>=1;i--)
43 {
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;
CAPITOLUL 17. ONI 2015 202

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 }

Listing 17.2.2: restaurare_stdio.cpp

1 #include <stdio.h>
2 #include <bitset>
3 #include <vector>
4
5 #define DIM 100010
6 #define INF 100010
7
8 using namespace std;
9
10 int u[DIM], f[DIM], x[DIM], y[DIM], crt, S[DIM], m, h;
11
12 int v[DIM];
13 pair<int, int> A[DIM];
14 int n, maxim, i, j, last, p, d;
15
16 long long secv, sol;
17
18 int main()
19 {
20 FILE *fin=fopen("restaurare.in","r"), *fout=fopen("restaurare.out","w");
CAPITOLUL 17. ONI 2015 203

21 fscanf(fin,"%d\n",&n);
22
23 for (i=1;i<=n;i++)
24 {
25 fscanf(fin, "%d",&v[i]);
26 if (v[i] > maxim)
27 maxim = v[i];
28 }
29
30 for (i=1;i<=n;i++)
31 {
32 v[i] = maxim - v[i] + 1;
33 f[ v[i] ]++;
34 }
35
36 for (i=1;i<=maxim;i++)
37 f[i] += f[i-1];
38
39 for (i=n;i>=1;i--)
40 {
41 x[ f[v[i] ] ]= v[i];
42 y[ f[v[i] ] ]= i;
43 f[ v[i] ]--;
44 }
45
46 for (i=1;i<=n;i++)
47 u[i] = 1;
48 secv = 1;
49
50 last = 0;
51 for (i=1;i<=n;i++)
52 {
53 if (x[i] != x[i-1])
54 {
55 sol += secv * (x[i]-last);
56 //
57 A[++d] = make_pair( maxim-x[i]+2, secv * (x[i]-last) );
58 //
59 last = x[i];
60
61 }
62 p = y[i];
63
64 u[p] = 0;
65 if (u[p-1] == 1 && u[p+1] == 1)
66 secv++;
67 if (u[p-1] == 0 && u[p+1] == 0)
68 secv--;
69 }
70
71 p = d;
72 for (i=1;i<=maxim;i++)
73 {
74 if (A[p].first == i)
75 {
76 crt = A[p].second / (A[p-1].first - A[p].first);
77 p--;
78 }
79 S[i] = S[i-1] + crt;
80 }
81
82 fscanf(fin,"%d",&m);
83 for (i=1;i<=m;i++)
84 {
85 fscanf(fin,"%d",&h);
86 fprintf(fout,"%d\n",S[h]);
87 }
88
89 fclose(fin);
90 fclose(fout);
91 return 0;
92 }

Listing 17.2.3: restaurareBrut1.cpp


CAPITOLUL 17. ONI 2015 204

1 #include <fstream>
2
3 #define DIM 100010
4 #define INF 100010
5
6 using namespace std;
7
8 long long sol;
9 int v[DIM], w[DIM];
10 int n, i, h, minim, maxim, m, H;
11
12 int main()
13 {
14 ifstream fin ("restaurare.in");
15 ofstream fout("restaurare.out");
16
17 fin>>n;
18 for (i=1;i<=n;i++)
19 {
20 fin>>v[i];
21 w[i] = v[i];
22 }
23
24
25 fin >> m;
26 for (;m--;)
27 {
28 fin>>H;
29
30 for (i=1;i<=n;i++)
31 {
32 v[i] = w[i];
33 if (v[i] > H)
34 v[i] = H;
35 }
36
37
38 maxim = 0;
39 minim = INF;
40
41
42 for (i=1;i<=n;i++)
43 {
44 if (v[i] > maxim)
45 maxim = v[i];
46 if (v[i] < minim)
47 minim = v[i];
48 }
49
50 long long sol = 0;
51 v[0] = INF;
52 for (h = minim+1;h<=maxim;h++)
53 {
54 for (i=1;i<=n;i++)
55 {
56 if (v[i] < h && v[i-1] >= h)
57 sol++;
58 }
59 }
60
61 fout<<sol<<"\n";
62 }
63 return 0;
64 }

Listing 17.2.4: restaurareLT.cpp

1 #include <iostream>
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;
CAPITOLUL 17. ONI 2015 205

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 }

17.2.3 *Rezolvare detaliat 

17.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, ind etichetate, în aceast  ordine, cu numere de la 1 la
N. Fiecare c suµ  conµine câte un num r natural, identicatorul câte unuia dintre prietenii s i,
roboµei, ca ³i el. Identicatorii 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 xa î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  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 ecare dat  când consider  convenabil  distanµa
dintre braµe, realizeaz  o interschimbare.

Cerinµe
Se cere ca Robo s  a³eze identicatorii în c suµe în ordine cresc toare, prin maximum 12 500
interschimb ri de tipul celei precizate mai sus.

Date de intrare
CAPITOLUL 17. ONI 2015 206

Fi³ierul sort2dist.in conµine:


- pe prima linie num rul natural N, cu semnicaµia din enunµ;
- pe urm toarele N linii, N numere, reprezentând, în aceast  ordine, identicatorii conµinuµi
în c suµele tabletei (identicatorul 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 ecare 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 identicatorii sunt numere naturale de maximum 30 de cifre;
a pentru 25% din punctaj, ³ierele de test conµin numere cu maximum 18 cifre;
a pentru 25% din punctaj, N & 100.

Exemple
sort2dist.in sort2dist.out Explicaµii
4 2 Tableta are 4 c suµe, conµinând, în aceast  ordine, identica-
5 2 4 torii (5,7,6,2).
7 2 1 Pentru ordonarea cresc toare s-au realizat 2 interschimb ri:
6 - s-a interschimbat conµinutul c suµelor 2 ³i 4 (distanµa dintre
2 centrele lor ind 2), identicatorii din c suµe ind acum (5, 2,
6, 7);
- s-a interschimbat conµinutul c suµelor 1 ³i 2 (distanµa dintre
centrele lor ind 1), identicatorii din c suµe ind acum (2, 5,
6, 7), ordonaµi cresc tor.

Tabela 17.1: sort2dist

Timp maxim de executare/test: 0.4 secunde


Memorie: total 8 MB
Dimensiune maxim  a sursei: 5 KB

17.3.1 Indicaµii de rezolvare

prof. Marius NICOLI, Colegiul Naµional Fraµii Buze³ti, Craiova

Aplic m ideea algoritmului de sortare prin selecµie, adic  identic m mai întâi poziµia maxi-
mului din tot ³irul (e aceasta p). Pentru a duce aceast  valoare maxim  pe ultima poziµie (n),
calcul m valoarea np ³i scriem acest num r ca sum  de puteri ale lui 2. Aceste puteri ale lui 2
sunt de fapt distanµe posibile între poziµiile de interschimbat, cuprinse între poziµiile p ³i n. A³a-
dar, folosind x interschimb ri plas m cea mai mare valoare pe ultima poziµie, unde x reprezint 
num rul de cifre 1 din scrierea în baza 2 a valorii n  p.
Relu m procedeul ³i pentru celelalte elemente ale ³irului, identicând elementele maxime dintre
cele r mase.
O abordare care obµine un punctaj parµial (deoarece num rul de interschimb ri dep ³e³te
valoarea precizat  în enunµ) este simularea metodei de sortare bubble sort (interschimbând valori
0
aate pe poziµii vecine, adic  la o distanµ  egal  cu 1 (2 ).
CAPITOLUL 17. ONI 2015 207

17.3.2 Cod surs 

Listing 17.3.1: 2dist_alin.cpp

1 #include <fstream>
2 #include <iostream>
3 #include <cstring>
4
5 #define Nmax 1001
6 #define MutMax 125000
7 #define IN "sort2dist.in"
8 #define OU "sort2dist.out"
9
10 using namespace std;
11 int N;
12 int x[MutMax], y[MutMax];
13 int p2[11]={1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
14 char v[Nmax][35], tmp[35];
15
16 int myStrcmp(char *s1, char *s2)
17 {
18 if (strlen(s1) == strlen(s2))
19 return strcmp(s1, s2);
20 else
21 if (strlen(s1) < strlen(s2))
22 return -1;
23 else
24 return 1;
25 }
26
27 int main()
28 {
29 int i, j, dif, NrMut, k, val, bec;
30 ifstream F(IN);
31 F >> N;
32 for(i = 1; i <= N; ++i) F >> v[i];
33 F.close();
34
35 //aflu cea mai mare putere a lui 2 <= N
36 for(i = 0; i<=10 && p2[i] <= N; i++);
37 val = p2[i - 1];
38
39 //sortez
40 NrMut = 0;
41 for(k = 1; k <= N; k++)
42 {
43 dif = val; bec = 0;
44 for(i = dif; i >= 1; --i )
45 {
46 for(j = 1; j <= N - dif; ++j)
47 if( myStrcmp( v[j], v[j + dif] ) > 0 )
48 {
49 bec = 1;
50 strcpy(tmp,v[j]);
51 strcpy(v[j], v[j+dif]);
52 strcpy(v[j+dif], tmp);
53 NrMut++; x[NrMut] = j;
54 y[NrMut] = j + dif;
55 }
56 dif = dif / 2;
57 }
58 if(!bec) break;
59 }
60
61 //scriu rezultatele
62 ofstream G(OU);
63 G << NrMut << ’\n’;
64 for(i = 1; i <= NrMut; ++i)
65 G << x[i] << ’ ’ << y[i] << ’\n’;
66 G.close();
67 //for(i = 1; i <= N; ++i) cout<<v[i]<<’ ’;
68 return 0;
69 }
CAPITOLUL 17. ONI 2015 208

Listing 17.3.2: sort2_distBrute1FNM.cpp

1 #include <fstream>
2 #include <vector>
3 #include <iostream>
4
5 #define INF ((1<<31)-1)
6
7 using namespace std;
8
9 ifstream fin ("sort2dist.in");
10 ofstream fout("sort2dist.out");
11
12 vector< pair<int, int> > sol;
13
14 int v[3000];
15 int n, N, i, j, p, aux;
16
17 int main()
18 {
19 fin>>n;
20 for (i=1;i<=n;i++)
21 fin>>v[i];
22
23
24
25 N = 1;
26 while (N < n)
27 N*=2;
28
29 // cout<<INF;
30
31 for (i=n+1;i<=N;i++)
32 v[i] = INF;
33
34 for (p = 1;p<=N/2;p*=2)
35 {
36 for (i=1;i<=p;i++)
37 for (j=i;j+p <= N; j+=p)
38 if (v[j] > v[j+p])
39 {
40 sol.push_back( make_pair(j, j+p) );
41
42 aux = v[j];
43 v[j] = v[j+p];
44 v[j+p] = aux;
45 }
46
47 }
48
49 fout<<sol.size()<<"\n";
50
51 for (i=0;i<sol.size();i++)
52 fout<<sol[i].first<<" "<<sol[i].second<<"\n";
53
54 return 0;
55 }

Listing 17.3.3: sort2_distBSFBM.cpp

1 #include <fstream>
2 #include <vector>
3
4 #define DIM 1010
5
6 using namespace std;
7
8 ifstream fin ("sort2dist.in");
9 ofstream fout("sort2dist.out");
10
11 vector< pair<long long, long long> > sol;
12
13 long long v[DIM];
14
15 long long n, i, j, aux, ok;
CAPITOLUL 17. ONI 2015 209

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 }

Listing 17.3.4: sort2dist.cpp

1 #include <fstream>
2 #include <vector>
3 #include <cstring>
4
5 #define DIM 1010
6
7 using namespace std;
8
9 vector < pair<int, int> > sol;
10
11 char v[DIM][35], aux[35], m[35];
12 int n, putere, p, d, i, j, last, t, k, st, dr, mid;
13
14 ifstream fin ("sort2dist.in");
15 ofstream fout("sort2dist.out");
16
17 int myStrcmp(char *s1, char *s2)
18 {
19 if (strlen(s1) == strlen(s2))
20 return strcmp(s1, s2);
21 else
22 if (strlen(s1) < strlen(s2))
23 return -1;
24 else
25 return 1;
26 }
27
28 void schimba(int x, int y)
29 {
30 sol.push_back( make_pair(x, y) );
31 char aux[15];
32 // int aux = v[x];
33 // v[x] = v[y];
34 // v[y] = aux;
35 strcpy(aux, v[x]);
36 strcpy(v[x], v[y]);
37 strcpy(v[y], aux);
38 }
39
40 int main()
41 {
42
43 fin>>n;
44 for (i=1;i<=n;i++)
CAPITOLUL 17. ONI 2015 210

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 }

Listing 17.3.5: sort2dist1.cpp

1 #include <fstream>
2 #include <vector>
3
4 #include <cstring>
5 #define DIM 1010
6
7 using namespace std;
8
9 vector < pair<int, int> > sol;
10
11 int v[DIM];
12 char s[DIM][35], aux[35];
13 char w[DIM][35];
14 int n, m, putere, p, d, i, j, last, t, k, st, dr, mid;
15
16 ifstream fin ("sort2dist.in");
17 ofstream fout("sort2dist.out");
18
19 int myStrcmp(char *s1, char *s2)
20 {
21 if (strlen(s1) == strlen(s2))
22 return strcmp(s1, s2);
23 else
24 if (strlen(s1) < strlen(s2))
25 return -1;
26 else
CAPITOLUL 17. ONI 2015 211

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 17. ONI 2015 212

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 }

Listing 17.3.6: sort2distBS.cpp

1 #include <fstream>
2 #include <vector>
3 #include <cstring>
4
5 #define DIM 1010
6
7 using namespace std;
8
9 ifstream fin ("sort2dist.in");
10 ofstream fout("sort2dist.out");
11
12 vector< pair<int, int> > sol;
13
14 char v[DIM][35], aux[35];
15
16 int myStrcmp(char *s1, char *s2)
17 {
18 if (strlen(s1) == strlen(s2))
19 return strcmp(s1, s2);
20 else
21 if (strlen(s1) < strlen(s2))
22 return -1;
23 else
24 return 1;
25 }
26
27
28 int n, i, j, ok;
29
30 int main()
31 {
32 fin>>n;
33 for (i=1;i<=n;i++)
34 fin>>v[i];
35
36 do
37 {
38 ok = 1;
39 for (i=1;i<n;i++)
40 if (myStrcmp(v[i], v[i+1]) > 0)
41 {
42 strcpy(aux, v[i]);
43 strcpy(v[i], v[i+1]);
44 strcpy(v[i+1], aux);
45 // aux = v[i];
46 // v[i] = v[i+1];
CAPITOLUL 17. ONI 2015 213

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 }

Listing 17.3.7: sort2distFNM.cpp

1 #include <fstream>
2 #include <vector>
3
4 #define DIM 1010
5
6 using namespace std;
7
8 vector < pair<long long, long long> > sol;
9
10 long long v[DIM];
11
12 long long n, m, putere, p, d, i, j, last;
13
14 ifstream fin ("sort2dist.in");
15 ofstream fout("sort2dist.out");
16
17 void schimba(long long x, long long y)
18 {
19 sol.push_back( make_pair(x, y) );
20 long long aux = v[x];
21 v[x] = v[y];
22 v[y] = aux;
23 }
24
25 int main()
26 {
27 fin>>n;
28 for (i=1;i<=n;i++)
29 fin>>v[i];
30
31 for (i=n;i>=2;i--)
32 {
33 m = v[1];
34 p = 1;
35 for (j=2;j<=i;j++)
36 if (v[j] > m)
37 {
38 m = v[j];
39 p = j;
40 }
41
42 if (p != i)
43 {
44 d = i-p;
45
46 putere = 1;
47 last = p;
48 while (d!=0)
49 {
50 if (d%2 == 1)
51 {
52 schimba(last+putere, last);
53 last += putere;
54 }
55
56 d = d/2;
57 putere = putere*2;
58 }
59 }
CAPITOLUL 17. ONI 2015 214

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 }

Listing 17.3.8: sort2distFNMDouble.cpp

1 #include <fstream>
2 #include <vector>
3
4 #define DIM 1010
5
6 using namespace std;
7
8 vector < pair<double, double> > sol;
9
10 double v[DIM];
11
12 double n, m, putere, p, d, i, j, last;
13
14 ifstream fin ("sort2dist.in");
15 ofstream fout("sort2dist.out");
16
17 void schimba(double x, double y)
18 {
19 sol.push_back( make_pair(x, y) );
20 double aux = v[(int)x];
21 v[(int)x] = v[(int)y];
22 v[(int)y] = aux;
23 }
24
25 int main()
26 {
27 fin>>n;
28 for (i=1;i<=n;i++)
29 fin>>v[(int)i];
30
31 for (i=n;i>=2;i--)
32 {
33 m = v[1];
34 p = 1;
35 for (j=2;j<=i;j++)
36 if (v[(int)j] > m)
37 {
38 m = v[(int)j];
39 p = j;
40 }
41
42 if (p != i)
43 {
44 d = i-p;
45
46 putere = 1;
47 last = p;
48 while (d!=0)
49 {
50 if ((int)d%2 == 1)
51 {
52 schimba(last+putere, last);
53 last += putere;
54 }
55
56 d = d/2;
57 putere = putere*2;
58 }
59 }
60 }
61
62 fout<<sol.size()<<"\n";
CAPITOLUL 17. ONI 2015 215

63 for (i=0;i<sol.size();i++)
64 {
65 fout<<sol[i].first<<" "<<sol[i].second<<"\n";
66 }
67
68 return 0;
69 }

Listing 17.3.9: sort2distFNMLongDouble.cpp

1 #include <fstream>
2 #include <vector>
3
4 #define DIM 1010
5
6 using namespace std;
7
8 vector < pair<long double, long double> > sol;
9
10 long double v[DIM];
11
12 long double n, m, putere, p, d, i, j, last;
13
14 ifstream fin ("sort2dist.in");
15 ofstream fout("sort2dist.out");
16
17 void schimba(long double x, long double y)
18 {
19 sol.push_back( make_pair(x, y) );
20 long double aux = v[(int)x];
21 v[(int)x] = v[(int)y];
22 v[(int)y] = aux;
23 }
24
25 int main()
26 {
27 fin>>n;
28 for (i=1;i<=n;i++)
29 fin>>v[(int)i];
30
31 for (i=n;i>=2;i--)
32 {
33 m = v[1];
34 p = 1;
35 for (j=2;j<=i;j++)
36 if (v[(int)j] > m)
37 {
38 m = v[(int)j];
39 p = j;
40 }
41
42 if (p != i)
43 {
44 d = i-p;
45
46 putere = 1;
47 last = p;
48 while (d!=0)
49 {
50 if ((int)d%2 == 1)
51 {
52 schimba(last+putere, last);
53 last += putere;
54 }
55
56 d = d/2;
57 putere = putere*2;
58 }
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";
CAPITOLUL 17. ONI 2015 216

66 }
67
68 return 0;
69 }

Listing 17.3.10: sort2gv.cpp

1 #include <iostream>
2 #include<fstream>
3 #include<cstring>
4
5 using namespace std;
6
7 ifstream f("sort2dist.in");
8 ofstream g("sort2dist.out");
9
10 char a[1500][30];
11 int n,m;
12
13 struct coor
14 {
15 int x,y;
16 } b[1500];
17
18 void citire(int &n)
19 {
20 f>>n;
21 for(int i=1;i<=n;i++)
22 f>>a[i];
23 }
24
25 void afisare(int n)
26 {
27 for(int i=1;i<=n;i++)
28 g<<a[i]<<endl;
29 g<<endl;
30 }
31
32 void afis(int n)
33 {
34 g<<m<<’\n’;
35 for(int i=1;i<=n;i++)
36 g<<b[i].x<<’ ’<<b[i].y<<’\n’;
37 g<<endl;
38 }
39
40 void sortat(int n)
41 {
42 int i,j,k;
43 char aux[30];
44 for(i=1;i<n;i++)
45 for(k=1,j=i+k;j<=n;k=k*2,j=j+k)
46 if(strcmp(a[i],a[j])>0)
47 {
48 m++;
49 b[m].x=i;
50 b[m].y=j;
51 strcpy(aux,a[i]);
52 strcpy(a[i],a[j]);
53 strcpy(a[j],aux);
54 }
55 }
56
57 int main()
58 {
59 citire(n);
60 // afisare(n);
61 sortat(n);
62 // afisare(n);
63 afis(m);
64 return 0;
65 }
CAPITOLUL 17. ONI 2015 217

17.3.3 *Rezolvare detaliat 


Capitolul 18

ONI 2014

18.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  ae 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
Scrieµi un program care s  determine
a) suma maxim  ce se poate obµine schimbând între ele cifrele numerelor iniµiale;
b) un ³ir de numere pentru care se obµine suma maxim , respectând restricµiile din enunµ.

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 ecare 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  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µ.

Restricµii ³i preciz ri
a 2&n& 100 000
a Numerele din ³irul iniµial sunt numere naturale & 230  1
a Numerele din ³irul a³at nu vor conµine zerouri nesemnicative.
a Dac  exist  mai multe ³iruri pentru care se obµine suma maxim  conform restricµiilor din
enunµ, se va a³a oricare dintre acestea.
a Pentru a³area corect  a sumei maxime se acord  40% din punctaj, punctajul integral
obµinându-se pentru rezolvarea corect  a ambelor cerinµe.

Exemple

218
CAPITOLUL 18. ONI 2014 219

cifre.in cifre.out Explicaµii


8 14280 Se observ  c  atât în ³irul iniµial, cât ³i în cel nal sunt 2
3120 6410 numere de 4 cifre, un num r de 3 cifre, 3 numere de dou  cifre
400 500 ³i dou  numere de o cifr .
1000 10 Deasemenea, numerele din ³irul a³at conµin în total acelea³i
50 20 cifre ca numerele din ³irul din ³ierul de intrare.
1 10 Suma maxim  care se poate obµine este 14280.
0 0
37 7330
60 0

Timp maxim de executare/test: 0.5 secunde


Memorie: total 16 MB din care pentru stiv  1 MB
Dimensiune maxim  a sursei: 10 KB

18.1.1 Indicaµii de rezolvare

prof. Carmen Popescu, C. N. Gh. Laz r Sibiu

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 m1
cifre. Complet m cifra de ordin 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  nr m  1
Vom completa acum numerele cifra de ordin m  2 a numerelor a0, ..., ax  nr m  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 semnicativ  poziµie a unui num r.

Varianta 2.
Vom construi pe rând ecare 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

adic  am poziµionat 8 cifre de 0 pe poziµia unitaµilor

a[1][2][1]=3
a[1][3][0]=1
a[1][4][2]=1
a[1][4][3]=2
CAPITOLUL 18. ONI 2014 220

adic  am distribuit pe poziµia zecilor o cifra de zero la numerele de 3 cifre, 3 cifre de 1 la


numerele de dou  cifre ³i câte o cifr  de 3 ³i de 4 la numerele de 4 cifre. etc.
Dup  construirea acestui tablou, construirea numerelor se face plecând de la cea mai semni-
cativ  cifr  a celui mai mare num r.
Suma de la prima cerinµ  se poate face f r  a construi numerele, adunând valorile:
i
a[i][j][k]*(10 )*k
pentru i=0, 1, ..., num rul maxim de cifre -1
j=1, 2, ..., num rul maxim de cifre
k=1,...,9

Soluµie - prof. Claudiu Gorea

Pentru toate cele N numere citite vom determina:


- F[X] - num rul cifrelor X, folosite în numerele iniµiale
- C[1] - num rul de cifre de la unit µi
- C[2] - num rul de cifre de la zeci
...
- C[kmax] - num rul de cifre situate pe poziµia kmax
Pornesc de la cifra 0 c tre cifra 9, punând cifrele în faµa num rului, iar când adaug 0, veric
³i c  la urm toarea unitate (suntem la zeci, atunci la sute în pasul viitor etc) mai punem ceva în
faµa lui (excepµie f când poziµia unit µilor).
Dac  nu pot pune 0, plasez cea mai mic  cifr  disponibil .

Soluµie 3 - prof. Emanuela Cerchez

Reprezentarea informaµiilor

#define NMAX 100001 //numarul maxim de numere


#define LGMAX 20 //lungimea maxima a numerelor

int nr[LGMAX]; //nr[i]=numarul de numere cu i cifre

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  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 ecare 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 18. ONI 2014 221

Distribui apoi cifrele nenule descresc tor

c=9; for (i=LGMAX-1; i>=0; i--)


//plasez cifra c pe pozitia i in numerele pentru care exista cifra i disponibila
for (j=s[i]; j<=n; j++)
{while (!nrc[c]) c--;
if (lg[j]<i) {nrc[c]--; v[j][i]=c;}
}

La nal construiesc numerele ³i calculez suma

for (i=1; i<=n; i++) //construiesc numarul v[i]


{lg[i]=0; for (j=LGMAX-1; j>0; j--) lg[i]=lg[i]*10+v[i][j];
smax+=lg[i]; }

18.1.2 Cod surs 

Listing 18.1.1: cifre_carmen_ok.cpp

1 //Carmen Popescu - 100 puncte


2 #include <fstream>
3
4 using namespace std;
5
6 int nr[10], // nr[k] = numarul de cifre k din toate numerele date
7 m, // numarul maxim de cifre
8 a[9][10][10], // a[i][j][k] numarul de numere de j cifre
9 // care contin pe pozitia i
10 // cifra k
11 t[10], // t[k] numarul de numere avand k cifre
12 b[10][10], // b[i][j] = numarul de numere cu j cifre care nu au inca
13 // completata cifra de pe pozitia i
14 sum[10000]; // suma numerelor finale, numar mare
15 // sum[0] cifra unit, sum[1]=cifra zecilor etc
16
17 ifstream fin("cifre.in");
18 ofstream fout("cifre.out");
19
20 void citire()
21 {
22 int x,k,n,i;
23 fin>>n;
24 for (i=0;i<n;i++)
25 {
26 fin>>x;
27
28 if (x==0)
29 {
30 nr[0]++;
31 t[1]++;
32 }
33 else
34 {
35 k=0;
36 while (x>0)
37 {
38 nr[x%10]++;
39 x=x/10;
40 k++;
41 }
42 t[k]++;
43 if (k>m)
44 m=k;
45 }
46 }
47 }
48
49 int minim(int &a,int &b)
50 {
51 int m;
52 m=a;
CAPITOLUL 18. ONI 2014 222

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 18. ONI 2014 223

129 int main()


130 {
131 citire();
132 distrib();
133 constr();
134 return 0;
135 }

Listing 18.1.2: cifre_ema.cpp

1 //Emanuela Cerchez 100 puncte


2 #include <fstream>
3
4 #define NMAX 100001
5 #define LGMAX 20
6
7 using namespace std;
8
9 int n;
10 long long int smax;
11 int nr[LGMAX], s[LGMAX];
12 //nr[i]=numarul de numere cu i cifre
13 int nrc[10];
14 //nrc[i]=numarul de cifre i folosite in scrierea celor n numere
15 int v[NMAX][LGMAX];
16 int lg[NMAX];
17
18 ifstream fin("cifre.in");
19 ofstream fout("cifre.out");
20
21 int main()
22 {int i, j, c, x, lgx;
23 fin>>n;
24 for (i=0; i<n; i++)
25 {
26 fin>>x;
27 lgx=0;
28 do {nrc[x%10]++; lgx++; x/=10;} while (x);
29 nr[lgx]++;
30 }
31 s[1]=1;
32 for (i=2; i<LGMAX; i++)
33 s[i]=s[i-1]+nr[i-1];
34
35 //numerele de i cifre sunt plasate in v de la s[i] la s[i]+nr[i]-1;
36 //plasez zerourile necesare in numere de o cifra
37 for (i=1; i<=nr[1] && nrc[0]; i++) {nrc[0]--; lg[i]=1;}
38
39 //plasez celelalte zerouri in mod echilibrat, pana le epuizez
40 for (i=1; i<LGMAX-1; i++) //plasez zerouri pe pozitia i
41 {
42 if (!nrc[0]) break;
43 for (j=s[i+1]; j<=n && nrc[0]; j++) lg[j]++, nrc[0]--;
44 }
45
46 //distribui cifrele descrescator
47 c=9;
48 for (i=LGMAX-1; i>=0; i--) // plasez cifra c pe pozitia i in numerele
49 // pentru care exista cifra i disponibila
50 for (j=s[i]; j<=n; j++)
51 {
52 while (!nrc[c]) c--;
53 if (lg[j]<i) {nrc[c]--; v[j][i]=c;}
54 }
55
56 //construiesc numerele si calculez suma
57 for (i=1; i<=n; i++) //construiesc numarul v[i]
58 {
59 lg[i]=0; for (j=LGMAX-1; j>0; j--) lg[i]=lg[i]*10+v[i][j];
60 smax+=lg[i];
61 }
62
63 fout<<smax<<’\n’;
64 for (i=1; i<=n; i++) fout<<lg[i]<<’\n’;
65 fout.close();
CAPITOLUL 18. ONI 2014 224

66 return 0;
67 }

Listing 18.1.3: cifre_GCC.cpp

1 //prof. Gorea Claudiu-Cristian


2 #include <cstdio>
3
4 using namespace std;
5
6 long long n,i,j,c[25],f[11],a[100002],x,kmax,k,s,cif,u[100002],p[100002],sol;
7
8 int main()
9 {
10 freopen("cifre.in","r",stdin);
11 freopen("cifre.out","w",stdout);
12 scanf("%lld",&n);
13
14 for(i=1;i<=20;i++) c[i]=0; //nr de cifre unit,zeci, sute, etc...
15 for(i=0;i<=9;i++) f[i]=0; //frecventa cifrelor
16 kmax=0;
17 for(i=1;i<=n;i++)
18 {
19 a[i]=0;
20 scanf("%lld",&x);
21 k=0;
22 if (x==0) {c[1]++; f[0]++;}
23 while(x>0)
24 {
25 k++;
26 c[k]++;
27 f[x%10]++;
28 x/=10;
29 }
30 if (k>kmax) kmax=k;
31 }
32
33
34 // for(i=kmax;i>=1; i--) printf("%lld, ",c[i]); printf("\n");
35 // for(i=9;i>=0; i--) printf("%lld, ",f[i]); printf("\n");
36
37 cif=9; i=1;
38
39 for(i=1;i<=n;i++) { u[i]=0; p[i]=1;}
40
41 for(j=1;j<=kmax;j++)
42 {
43 for(i=1;i<=c[j];i++) /// nr la care mai punem ceva in fata
44 {
45 ///cautam cifra cea mai mica... 0 doar daca
46 ///mai punem ceva in viitor in fata
47 cif=-1;
48 sol=0;
49 u[i]=j; ///adaugam inca o cifra la linia i
50 while(sol==0)
51 {
52 cif++;
53 if ((cif==0 && f[cif]>0)&&
54 ( (u[i]>1 && c[u[i]+1]>=i) || (u[i]==1) ))
55 ///cifra 0, iar in fata lui mai punem in tura urmatoare
56 {
57 a[i]=a[i]+p[i]*cif;
58 p[i]*=10;
59 f[cif]--;
60 sol=1;
61 }
62 else
63 if (cif==0 && f[cif]>0 && c[u[i]+1]<i )
64 {
65 cif++; while(f[cif]==0) cif++;
66 a[i]=a[i]+p[i]*cif;
67 p[i]*=10;
68 f[cif]--;
69 sol=1;
70 }
CAPITOLUL 18. ONI 2014 225

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 }

Listing 18.1.4: cifre_soa.cpp

1 //prof. Sofia Vitelaru


2 #include <fstream>
3 #include<algorithm>
4
5 using namespace std;
6
7 int x[100001],cif[10],nr[100001],n,xx,i,nc,Max,k,a[100001],p[100001];
8
9 ifstream f("cifre.in");
10 ofstream g("cifre.out");
11
12 int main()
13 {
14 f>>n;
15 for(i=1;i<=n;i++)
16 {
17 f>>xx;nc=0;p[i]=1;
18 if(xx==0)
19 {
20 nc++;
21 cif[0]++;
22 }
23
24 while(xx!=0)
25 {
26 cif[xx%10]++;
27 nc++;xx/=10;
28 }
29
30 nr[i]=nc;
31 }
32
33 sort(nr+1,nr+n+1);
34
35 int t=1;
36 while(cif[0]>0)
37 {
38 if(a[t]<nr[t]-1||(nr[t]==1&&a[t]==0))
39 {
40 x[t]=p[t]*0+x[t];
41 p[t]*=10;
42 cif[0]--;
43 a[t]++;
44 }
45
CAPITOLUL 18. ONI 2014 226

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 }

18.1.3 *Rezolvare detaliat 

18.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 ind vide (f r  c rµi). Numim sec-
venµ  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 gurile 1 ³i 2 sunt reprezentate dou  astfel de coloane cu câte
6 c rµi având numere între 1 ³i 6. În gura 1, secvenµa de la sfâr³itul coloanei este
format  doar din cartea 1. În gura 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 gura 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
aate 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 conguraµia iniµial  din gura 2 se va obµine, printr-o operaµie A, conguraµia
1 de mai jos. Apoi, printr-o operaµie B, se obµine conguraµia 2, printr-o nou  operaµie B se obµine
conguraµ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, conguraµia
nal  cerut .
CAPITOLUL 18. ONI 2014 227

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
Fi³ierul solitar.out va conµine o singur  linie pe care va  scris un num r natural M repre-
zentând num rul minim de operaµii prin care se poate obµine secvenµa cerut .

Restricµii ³i preciz ri
a 2&N & 100 000

Exemple
solitar.in solitar.out Explicaµii
6 5 Cele 5 mut ri sunt descrise în enunµul problemei
1 6 2 5 4 3

Timp maxim de executare/test: 0.2 secunde


Memorie: total 2 MB din care pentru stiv  1 MB
Dimensiune maxim  a sursei: 10 KB

18.2.1 Indicaµii de rezolvare

prof. Rodica Pintea, Colegiul Naµional Grigore Moisil Bucure³ti

Se citesc ³i se memoreaz  secvenµele de pe coloana 1.

Soluµie O 3
N 
Se caut  perechi de coloane cu secvenµe care se pot uni ³i, dac  nu exist , se mut  o nou 
secvenµ  de pe coloana 1 pe o coloan  vid .

Soluµie O 2
N 
În timpul citirii se num r  secvenµele existente pe coloana 1 (S), secvenµe a c ror unire necesit 
S-1 mut ri, unirea acestora nemaiind efectuat . La o parcurgere în ordine invers  a secvenµelor,
se num r  toate secvenµele care trebuie date de-o parte deoarece nu s-a ajuns înc  la secvenµele
peste care acestea ar trebui mutate. Fiecare astfel de situaµie necesit  câte o mutare în plus.

Soluµie O N
2
Putem optimiza soluµia de O N  utilizând un vector de prezenµ , care ne ajut  s  identic m
rapid dac  secvenµa peste care ar trebui plasat  secvenµa curent  a fost deja mutat  sau nu s-a
ajuns înc  la ea.
b[i]=true, dac  i apare deja ca extremitate iniµial  a unei secvenµe deja construite.
CAPITOLUL 18. ONI 2014 228

18.2.2 Cod surs 

Listing 18.2.1: solitar_cp.cpp

1 // prof. Carmen Popescu - Col. Nat. "Gh. Lazar" Sibiu 64 puncte


2 #include <fstream>
3
4 using namespace std;
5
6 ifstream f("solitar.in");
7 ofstream g("solitar.out");
8
9 struct interval{
10 int x,y;
11 };
12
13 interval a[100001];
14 int n; // numarul de carti
15 int m; // numarul de "coloane" ocupate, cu exceptia celei initiale
16 int x[100001]; // x[i] = cartea de la sfarsitul coloanei i
17 // (cea mai mica valoare)
18 int p; // numarul de grupe din stiva initiala
19
20 void citire()
21 {
22 int k;
23 f>>n;
24 f>>k;
25 a[p].x=k; a[p].y=k; p=1;
26 for (int i=1;i<n;i++)
27 {
28 f>>k;
29 if (a[p-1].x==k+1)
30 a[p-1].x--;
31 else
32 {
33 a[p].x=k; a[p].y=k;
34 p++;
35 }
36 }
37 }
38
39 void muta()
40 {
41 int i,j,ok;
42 if (p==1)
43 {
44 g<<0<<"\n";
45 return;
46 }
47 m=0;
48 for (i=p-1;i>=0;i--)
49 {
50 ok=0;
51 for (j=0;j<m;j++)
52 if (x[j]==a[i].y+1)
53 {
54 x[j]=a[i].x;
55 ok=1;
56 break;
57 }
58 if (!ok)
59 x[m++]=a[i].x;
60 }
61 g<<p+m-1<<"\n";
62 }
63
64 int main()
65 {
66 citire();
67 muta();
68 return 0;
69 }
CAPITOLUL 18. ONI 2014 229

Listing 18.2.2: solitar_ema.cpp

1 //Emanuela Cerchez - 67 puncte


2 #include <fstream>
3 #define NMAX 100001
4
5 using namespace std;
6
7 ifstream fin("solitar.in");
8 ofstream fout("solitar.out");
9
10 struct interval
11 {
12 int a, b;
13 };
14
15 interval S[NMAX], V[NMAX];
16 int n, nr, lgS, lgV;
17 void citire();
18
19 int main()
20 {int i;
21 citire();
22 while (lgS>1)
23 {
24 nr++;
25 for (i=0; i<lgV; i++)
26 //pot lipi ultimul interval din S la unul din intervalele din V
27 if (S[lgS-1].b==V[i].a-1) {V[i].a=S[lgS-1].a; break;}
28 if (i==lgV)
29 V[lgV++]=S[lgS-1];
30 lgS--;
31 }
32
33 fout<<nr+lgV<<’\n’;
34 fout.close();
35 return 0;
36 }
37
38 void citire()
39 {int x, i;
40 fin>>n>>x;
41 S[0].a=x; S[0].b=x; lgS=1;
42 for (i=2; i<=n; i++)
43 {
44 fin>>x;
45 if (S[lgS-1].a==x+1)
46 S[lgS-1].a=x;
47 else
48 {S[lgS].a=x; S[lgS].b=x; ++lgS;}
49
50 }
51 }

18.2.3 *Rezolvare detaliat 

18.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 18. ONI 2014 230

Fi³ierul de intrare tdrept.in conµine pe prima linie num rul natural N, care reprezint  nu-
m rul de puncte.
Pe urm toarele N linii se a  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  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  foarte mare, rezultatul va  a³at modulo 666013 (adic  restul împ rµirii
rezultatului la 666013).

Restricµii ³i preciz ri
a 3 & N & 100 000
a 0 & x, y & 100 000
a Cele N puncte din ³ierul de intrare sunt distincte dou  câte dou .

Exemple
tdrept.in tdrept.out Explicaµii
8 5
Triunghiurile
1 1
dreptunghice
1 4
formate
10 8
sunt:
4 1
9 1
(1,1) (1,4) (4,1)
5 5
(1,1) (9,1) (1,4)
7 4
(5,5) (7,4) (7,5)
7 5
(1,4) (7,4) (7,5)
(1,1) (1,4) (7,4)

Timp maxim de executare/test: 0.3 secunde


Memorie: total 4 MB din care pentru stiv  1 MB
Dimensiune maxim  a sursei: 10 KB

18.3.1 Indicaµii de rezolvare

prof. Emanuela Cerchez, Colegiul Naµional Emil Racoviµ  Ia³i

Soluµie O 3
N  - 15 puncte
Construim toate tripletele de N puncte distincte ³i veric m dac  cele 3 puncte reprezint 
vârfurile unui triunghi dreptunghic cu laturile respectiv paralele cu axele.

Soluµie O 2
N logN  - 38 puncte
Sortez punctele cresc tor dup  abscis . Dac  sunt mai multe puncte cu aceea³i abscis , vor 
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 verica dac  acest lucru se întâmpl , caut binar în
vectorul sortat de puncte dac  exist  cele dou  puncte care ar putea  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 ecare 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 ecare abscis  contorizez num rul de puncte având abscisa respectiv 
nrxi = num rul de puncte având abscisa i
CAPITOLUL 18. ONI 2014 231

În mod similar, pentru ecare ordonat  contorizez num rul de puncte având ordonata respec-
tiv 
nry i = num rul de puncte având ordonata i
Un triunghi dreptunghic cu unghiul drept în punctul P xp, yp va avea celelalte dou  vârfuri
într-un punct de coordonate xp, y , iar cel lalt într-un punct de coordonate x, yp.
Ca urmare, pentru punct P xp, yp num rul de triunghiuri dreptunghice cu unghiul drept în
punctul xp, yp va  egal cu nrxxp  1 ˜ nry yp  1 (am sc zut 1 pentru a nu contoriza
ca triunghi ³i punctul însu³i).

18.3.2 Cod surs 

Listing 18.3.1: tdrept_ema_n2logn.cpp

1 //Emanuela Cerchez O(n*n*log n)


2 #include <fstream>
3 #include <algorithm>
4 #define NMAX 100002
5 #define DMAX 100002
6 #define MOD 666013
7
8 using namespace std;
9 ifstream fin("tdrept.in");
10 ofstream fout("tdrept.out");
11 struct punct {int x, y;} p[NMAX];
12 int n, nr;
13
14 void citire();
15 int cautbin(punct);
16
17 bool cmp(punct a, punct b)
18 {
19 return a.x<b.x || a.x==b.x&& a.y<b.y;
20 }
21
22 int main()
23 {int i, j;
24 punct p1, p2;
25 citire();
26 sort (p+1, p+n+1,cmp);
27 for (i=1; i<n; i++)
28 for (j=i+1; j<=n; j++)
29 if (p[i].x!=p[j].x && p[i].y!=p[j].y)
30 {
31 p1.x=p[i].x; p1.y=p[j].y;
32 p2.x=p[j].x; p2.y=p[i].y;
33 nr+=cautbin(p1);
34 nr+=cautbin(p2);
35 if (nr>MOD) nr-=MOD;
36 }
37 fout<<nr<<’\n’;
38 fout.close();
39 return 0;
40 }
41
42 void citire()
43 {int i;
44 fin>>n;
45 for (i=1; i<=n; i++)
46 fin>>p[i].x>>p[i].y;
47 }
48
49 int cautbin(punct a)
50 {
51 int st=0, dr=n+1, mij;
52 while (dr-st>1)
53 {
54 mij=(st+dr)/2;
55 if (cmp(p[mij],a))
56 st=mij;
57 else dr=mij;
58 }
CAPITOLUL 18. ONI 2014 232

59 if (dr<n+1 && a.x==p[dr].x && a.y==p[dr].y) return 1;


60 return 0;
61 }

Listing 18.3.2: tdrept_ema_ok.cpp

1 //Emanuela Cerchez - O(n)


2 #include <fstream>
3
4 #define NMAX 100002
5 #define DMAX 100002
6 #define MOD 666013
7
8 using namespace std;
9
10 ifstream fin("tdrept.in");
11 ofstream fout("tdrept.out");
12
13 int n;
14 int xp[NMAX];
15 int yp[NMAX];
16
17 int nrx[DMAX];
18 int nry[DMAX];
19
20 void citire();
21 int numarare();
22
23 int main()
24 {
25 citire();
26 fout<<numarare()<<’\n’;
27 fout.close();
28 return 0;
29 }
30
31 void citire()
32 {int i;
33 fin>>n;
34 for (i=1; i<=n; i++)
35 {fin>>xp[i]>>yp[i];
36 nrx[xp[i]]++;
37 nry[yp[i]]++;
38 }
39 }
40
41 int numarare()
42 {int i, nr=0;
43 for (i=1; i<=n; i++)
44 nr=(nr+((long long int)(nrx[xp[i]]-1)*(nry[yp[i]]-1))%MOD)%MOD;
45
46 return nr;
47 }

Listing 18.3.3: tdrept_eman3.cpp

1 //Emanuela Cerchez - O(n^3)


2 #include <fstream>
3 #include <algorithm>
4
5 #define NMAX 100002
6 #define DMAX 100002
7 #define MOD 666013
8
9 using namespace std;
10
11 ifstream fin("tdrept.in");
12 ofstream fout("tdrept.out");
13
14 struct punct
15 {
16 int x, y;
17 } p[NMAX];
18
CAPITOLUL 18. ONI 2014 233

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 }

Listing 18.3.4: tdrept_emanlogn.cpp

1 //Emanuela Cerchez - O(n*log n)


2 #include <fstream>
3 #include <algorithm>
4
5 #define NMAX 100002
6 #define DMAX 100002
7 #define MOD 666013
8
9 using namespace std;
10
11 ifstream fin("tdrept.in");
12 ofstream fout("tdrept.out");
13
14 struct punct
15 {
16 int x, y;
17 } px[NMAX], py[NMAX];
18
19 int v[NMAX];
20 int n, nr;
21
22 void citire();
23 int cautbin(int);
24
25 bool cmpx(punct a, punct b)
26 {
27 return a.x<b.x;
28 }
29
30 bool cmpy(punct a, punct b)
31 {
32 return a.y<b.y;
33 }
34
35 int main()
36 {int i, nr1, j, k, lg, poz;
37 citire();
38
CAPITOLUL 18. ONI 2014 234

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 }

Listing 18.3.5: tdrept_n_soa.cpp

1 #include <fstream>
2
3 using namespace std;
4
5 int x[100001],y[100001],n,i,sol,a,b;
6
7 ifstream f("tdrept.in");
8 ofstream g("tdrept.out");
9
10 struct punct
11 {
12 int x,y;
13 } v[100001];
14
15 int main()
16 {
17 f>>n;
18 for(i=1;i<=n;i++)
19 {
20 f>>v[i].x>>v[i].y;
21 x[v[i].x]++;
22 y[v[i].y]++;
23 }
24
25 sol=0;
26 for(i=1;i<=n;i++)
CAPITOLUL 18. ONI 2014 235

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 }

Listing 18.3.6: tdrept_n2logn_soa.cpp

1 #include <fstream>
2 #include<algorithm>
3
4 using namespace std;
5
6 ifstream f("tdrept.in");
7 ofstream g("tdrept.out");
8
9 struct punct
10 {
11 int x,y;
12 } v[100001];
13
14 int cmp(punct x,punct y)
15 {
16 if(x.x==y.x)
17 return x.y<y.y;
18 return x.x<y.x;
19 }
20
21 int n,i,j,sol;
22
23 int caut(int p,int q)
24 {
25 int st,dr,m;
26 st=1;
27 dr=n;
28 while(st<=dr)
29 {
30 int m=(st+dr)/2;
31 if(v[m].x==v[p].x&&v[m].y==v[q].y)
32
33 return 1;
34 if(v[m].x>v[p].x||v[m].x==v[p].x&&v[m].y>v[q].y)
35 dr=m-1;
36 else
37 st=m+1;
38 }
39
40 return 0;
41 }
42
43 int main()
44 {
45 f>>n;
46 for(i=1;i<=n;i++)
47 f>>v[i].x>>v[i].y;
48
49 sort(v+1,v+n+1,cmp);
50
51 for(i=1;i<=n;i++)
52 for(j=i+1;j<=n;j++)
53 if(v[i].x!=v[j].x&&v[i].y!=v[j].y)
54 {
55 sol+=caut(i,j);
56 if(sol>=666013)
57 sol-=666013;
58
59 sol+=caut(j,i);
60
61 if(sol>=666013)
62 sol-=666013;
63 }
CAPITOLUL 18. ONI 2014 236

64
65 g<<sol;
66 return 0;
67 }

18.3.3 *Rezolvare detaliat 


Capitolul 19

ONI 2013

19.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 .
Deplasarea moleculelor se face dup  urm toarele reguli:
a moleculele se deplaseaz  numai pe orizontal  când forµa este aplicat  în Est sau Vest sau
numai pe vertical  când forµa este aplicat  în Nord sau Sud;
a moleculele se deplaseaz  c tre locul în care este amplasat  forµa ³i, într-o secund , o molecul 
se deplaseaz  cel mult cu o singur  poziµie;
a o molecul  se deplaseaz  numai dac  în faµa ei, în direcµia de deplasare, exist  o molecul 
de alt tip, cu care face schimb de locuri, altfel r mâne pe aceea³i poziµie;
a o forµ  acµioneaz  asupra tuturor moleculelor de tipul precizat.

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 semnicaµia de mai sus. Pe ecare din urm toarele n linii se g se³te câte un
³ir de n caractere 0 sau 1. Pe ecare 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 semnicaµia de mai sus, neseparate prin spaµiu.

Date de ie³ire
Fi³ierul de ie³ire amestec.out va conµine matricea amestecului nal. Pe ecare din cele n linii
ale ³ierului de ie³ire se va scrie câte un ³ir de n caractere 0 sau 1, neseparate prin spaµiu.

237
CAPITOLUL 19. ONI 2013 238

Restricµii ³i preciz ri
a 2 & n & 100
a 1 & k & 100
1 & di & 10 , 1 & i & k
9
a

Exemple
amestec.in amestec.out Explicaµii
3 2 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.

Timp maxim de executare/test: 0.5 secunde


Memorie: total 4 MB din care pentru stiv  4 MB
Dimensiune maxim  a sursei: 15 KB

19.1.1 *Indicaµii de rezolvare

19.1.2 *Cod surs 

19.1.3 *Rezolvare detaliat 

19.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  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  diferite de
la o turbin  la alta.

Primarul a achiziµionat turbinele ³i a angajat echipa inginerului Eol pentru a le construi fun-
daµ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 19. ONI 2013 239

a dac  vor instala turbinele conform planului, atunci pot  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
 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
semnicaµia din enunµ) ³i s  determine num rul minim M de turbine ce pot  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 semnicaµia din enunµ.

Date de ie³ire
Fi³ierul de ie³ire eoliene.out va conµine pe prima linie num rul natural M determinat.

Restricµii ³i preciz ri
a Numerele N , D1 , D2 , ..., DN , L1 , L2 , ..., LN sunt numere naturale nenule.
a 1&N & 1000; 1 & D1 , D2 , ..., DN & 5000; 1 & L1 , L2 , ..., LN & 2500
a Numerele D1 , D2 , ..., DN sunt distincte dou  câte dou .
a Lungimea pilonilor este strict mai mare decât lungimea palelor.

Exemple
eoliene.in eoliene.out
7 27 9 28 37 3 54 50 3
1 5 5 4 5 2 2

Explicaµii:
Sunt N 7 turbine. în planul primarului ele gureaz  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).

Timp maxim de executare/test: 0.5 secunde


Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 15 KB

19.2.1 *Indicaµii de rezolvare

19.2.2 *Cod surs 

19.2.3 *Rezolvare detaliat 


CAPITOLUL 19. ONI 2013 240

19.3 sudoku1
Problema 3 - sudoku1 100 de puncte
Numim tablou Sudoku o matrice cu nn elemente ce conµine doar cifrele 1, 2 ³i 3 astfel încât
în ecare p trat format din 22 elemente al turate s  existe toate cele 3 cifre ³i oricare dou 
elemente al turate pe linie sau pe coloan  s  e 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 dene³te ³irul S n ca ind 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 ind dou  numere naturale n ³i k s  se determine:
a) num rul tablourilor Sudoku din ³irul S n;
b) tabloul Sudoku aat pe poziµia k în ³irul S n.

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
Fi³ierul de ie³ire sudoku.out va conµine:
a pe prima linie un num r natural ce reprezint  num rul tablourilor Sudoku din ³irul S n;
a pe urm toarele n linii se g sesc câte n cifre, separate prin câte un spaµiu, ce reprezint , în
ordine, liniile tabloului Sudoku aat pe poziµia k în ³irul S n.

Restricµii ³i preciz ri
a 2 & n & 32;
1 & k $ 10 ;
19
a
a Pentru rezolvarea corect  a cerinµei a) se acord  20% din punctaj, iar pentru rezolvarea
corect  cerinµei b) se acord  80% din punctaj.
a Acordarea punctajului pentru a doua cerinµ  se face numai dac  în ³ierul de ie³ire exist 
un r spuns pentru prima cerinµ , indiferent de corectitudinea acestuia.

Exemple
sudoku1.in sudoku1.out Explicaµii
2 6 12 ³irul S(2) conµine 12 tablouri Sudoku, pe poziµia 6 în ³ir
2 1 aându-se tabloul:
3 2 2 1
3 2

Timp maxim de executare/test: 0.3 secunde


Memorie: total 4 MB din care pentru stiv  4 MB
Dimensiune maxim  a sursei: 15 KB

19.3.1 *Indicaµii de rezolvare


CAPITOLUL 19. ONI 2013 241

19.3.2 *Cod surs 

19.3.3 *Rezolvare detaliat 


Capitolul 20

ONI 2012

20.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 ecare 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. Veric 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 ecare scorbur  din ³irul t u, se obµine acela³i rest. Notez pe hârtie, în continuare,
rezultatul veric rii (0 sau 1). încheiem jocul atunci când termin m de vericat, dup  aceast 
regul , toate cele D scorburi ale mele.

Cerinµe
Scrieµi un program care cite³te din ³ierul alune.in numerele naturale nenule C ³i D ³i num rul
de alune din ecare 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 ecare scorbur  a lui Chip, iar pe a treia
linie D numere naturale, reprezentând num rul de alune din ecare scorbur  a lui Dale. Toate
numerele situate pe aceea³i linie a ³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 a  ³irul determinat. Cifrele din acest ³ir
nu sunt separate prin spaµii.

Restricµii ³i preciz ri
a 1 & C, D & 100 000;
a Numerele de alune din scorburile lui Chip, scrise pe a doua linie a ³ierului de intrare, sunt
numere naturale din intervalul [1, 2 000 000 000].
a Numerele de alune din scorburile lui Dale, scrise pe a treia linie a ³ierului de intrare, sunt
numere naturale din intervalul [0, 2 000 000 000].

Exemple

242
CAPITOLUL 20. ONI 2012 243

alune.in alune.out Explicaµii


3 2 01 Prima scorbur  a lui Dale este umplut  incorect, deoarece resturile
3 4 5 împ rµirii lui 8 la numerele 3, 4 ³i 5 sunt diferite, deci rezultatul
8 2 veric rii este 0.
A doua scorbur  a lui Dale este umplut  corect deoarece resturile
împ rµirii lui 2 la 3, 4 ³i 5 sunt egale, iar rezultatul veric rii este 1.

Timp maxim de executare/test: 0.8 secunde


Memorie: total 4 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 5 KB

20.1.1 Indicaµii de rezolvare

prof. Marius Nicoli, Colegiul Naµional Fraµii Buze³ti - Craiova

Se calculeaz  cel mai mic multiplu comun al celor C numere de pe a doua linie a ³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 ³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.

20.1.2 Cod surs 

Listing 20.1.1: alune.cpp

1 //Solutia oficiala, Marius Nicoli 100p N log(VMAX) + Q


2 #include <stdio.h>
3 #define DIM 100010
4
5 #define MAX 2000000000
6
7 long long mc;
8 int m, X, Q, N, i, ok, q, depaseste;
9
10 int cmmdc(int a, int b)
11 {
12 int r;
13 while (b)
14 {
15 r = a % b;
16 a = b;
17 b = r;
18 }
19 return a;
20 }
21
22 int minim (int a, int b)
23 {
24 return a < b ? a : b;
25 }
26
27 int main()
28 {
29 FILE *f = fopen("alune.in","r");
30 FILE *g = fopen("alune.out","w");
31
32 fscanf(f,"%d %d",&N, &Q);
33
34 fscanf(f,"%d",&X);
35 mc = m = X;
36 depaseste = 0;
37 for (i = 2; i<=N; i++)
38 {
CAPITOLUL 20. ONI 2012 244

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 }

Listing 20.1.2: alune_DT.cpp

1 #include<fstream>
2
3 using namespace std;
4
5 ifstream f("alune.in");
6 ofstream g("alune.out");
7
8 long long v[100000];
9
10 int main()
11 {
12 int n,q,i,j,ok; long long min,x,r;
13 f>>n>>q;
14 f>>v[1];
15
16 min=v[1];
17 for(i=2;i<=n;i++)
18 {
19 f>>v[i];
20 if(v[i]<min) min=v[i];
21 }
22
23 for(j=1;j<=q;j++)
24 {
25 f>>x;
26 if(x<min)
27 g<<1;
28 else
29 {
30 ok=1;
31 r=x%v[1];
32 for(i=2;i<=n;i++)
33 if(r!=x%v[i])
34 ok=0;
35 g<<ok;
CAPITOLUL 20. ONI 2012 245

36 }
37 }
38
39 g<<’\n’;
40 f.close();
41 g.close();
42 return 0;
43 }

Listing 20.1.3: aluneBrute.cpp

1 //Marius Nicoli O(N*Q)


2 #include <stdio.h>
3 #define DIM 100010
4
5 char S[DIM];
6
7 int V[DIM];
8 int N, Q, i, ok, q, mod, s;
9
10 int main()
11 {
12 FILE *f = fopen("alune.in","r");
13 FILE *g = fopen("alune.out","w");
14
15 fscanf(f,"%d %d",&N, &Q);
16 for (i=1;i<=N;i++)
17 {
18 fscanf(f,"%d",&V[i]);
19 }
20
21 for (;Q;--Q)
22 {
23 fscanf(f,"%d",&q);
24 ok = 1;
25 mod = q % V[1];
26
27 for (i = 1; i<=N; i++)
28 if (q % V[i] != mod)
29 {
30 ok = 0;
31 break;
32 }
33
34 if (ok)
35 {
36 S[s++] = ’1’;
37 }
38 else
39 {
40 S[s++] = ’0’;
41 }
42 }
43
44 S[s] = 0;
45 fprintf(g,"%s\n",S);
46 fclose(f);
47 fclose(g);
48 return 0;
49 }

Listing 20.1.4: aluneBrute1.cpp

1 //Marius Nicoli O(N*Q) cu elminarea dublurilor la primul sir


2 #include <stdio.h>
3 #include <algorithm>
4
5 #define DIM 100010
6
7 using namespace std;
8
9 char S[DIM];
10 int A[DIM], B[DIM];
11 int N, Q, a, i, j, ok, r, s;
CAPITOLUL 20. ONI 2012 246

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 }

Listing 20.1.5: aluneBrute2.cpp

1 //Marius Nicoli O(N*Q) cu elminarea dublurilor la ambele siruri


2 #include <stdio.h>
3 #include <algorithm>
4
5 #define DIM 100010
6
7 using namespace std;
8
9 struct query
10 {
11 int q;
12 int p;
13 int v;
14 };
15
16 int A[DIM];
17 query B[DIM];
18 char S[DIM];
19 int N, Q, a, i, j, ok, r, s;
20
21 int cmp1 (const query &a, const query &b)
22 {
23 return a.v < b.v;
24 }
25
26 int cmp2 (const query &a, const query &b)
27 {
28 return a.p < b.p;
29 }
30
31
CAPITOLUL 20. ONI 2012 247

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 }

Listing 20.1.6: aluneBruteParse.cpp

1 //Marius Nicoli O(N*Q) cu elminarea dublurilor si parsarea citirii


2 #include <stdio.h>
3 #include <algorithm>
4 #include <cstring>
5
6 #define DIM 100010
7
8 using namespace std;
9
10 char S[DIM];
11 int A[DIM], B[DIM];
12 int N, Q, a, i, j, ok, r, s;
13 char buff[1000010], *p, *u;
14
CAPITOLUL 20. ONI 2012 248

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 }

Listing 20.1.7: alunedanal.cpp

1 #include <stdio.h>
2
3 #define INF 2000000010
4
5 long long cmmmc;
6 int x, i, minim;
7 int N, Q;
8
9 inline int cmmdc(int a, int b)
10 {
11 int r;
12 while (b)
13 {
CAPITOLUL 20. ONI 2012 249

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 }

Listing 20.1.8: aluneS.cpp

1 //Solutia oficiala, Marius Nicoli 100p N log(VMAX) + Q


2 //#include <stdio.h>
3 #include <fstream>
4
5 using namespace std;
6
7 #define DIM 100010
8
9 #define MAX 2000000000
10
11 long long mc;
12 int m, X, Q, N, i, ok, q, depaseste;
13
14 int cmmdc(int a, int b)
15 {
16 int r;
17 while (b)
18 {
19 r = a % b;
20 a = b;
21 b = r;
22 }
23 return a;
24 }
25
26 int minim (int a, int b)
27 {
28 return a < b ? a : b;
29 }
30
31 int main() {
32 // FILE *f = fopen("alune.in","r");
33 // FILE *g = fopen("alune.out","w");
34 ifstream f("alune.in");
35 ofstream g("alune.out");
CAPITOLUL 20. ONI 2012 250

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 }

20.1.3 *Rezolvare detaliat 

20.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 ecare 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 ecare num r
citit cu num rul obµinut prin operaµia de mai
sus, Ionuµ obµine un nou ³ir ³i scrie termenii
CAPITOLUL 20. ONI 2012 251

acestuia pe feµele unor cuburi astfel: primele ³ase numere din ³ir le scrie pe primul cub ³i îl no-
teaz  pe acesta cu 1, urm toarele ³ase numere din ³ir le scrie pe un alt cub pe care îl noteaz  cu
2 ³.a.m.d.
Aceste cuburi au fost distribuite în piramide dup  modelul din gura de mai sus. Piramidele
au fost numerotate cu numere naturale consecutive. Piramida cu num rul de ordine 1 este format 
numai din cubul cu num rul de ordine 1 ³i are un singur nivel, piramida cu num rul de ordine 2
are pe primul nivel cuburile 2, 3 ³i 4 iar pe ultimul nivel cubul 5 ³.a.m.d.
Dou  niveluri al turate în cadrul unei piramide difer  prin exact dou  cuburi. Primul nivel
al unei piramide conµine cu dou  cuburi mai mult decât primul nivel al piramidei precedente.
Piramida se consider  complet  dac  pe ultimul nivel are un singur cub.

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 ecare linie a ³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.

Restricµii ³i preciz ri
a 6&N & 100 000
a Se garanteaz  c  se pot construi cel puµin K piramide complete.
a Cele N numere naturale de pe a doua linie a ³ierului de intrare au minimum patru cifre ³i
maximum 9 cifre.
a Se acord  20% din punctajul acordat problemei pentru rezolvarea corect  doar a primei
cerinµe.

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 insucient, numerele nu apar
26080 90280 scrise pe exact dou  linii, ca în ³ierele
de intrare/ie³ire.

Timp maxim de executare/test: 0.5 secunde


Memorie: total 2 MB din care pentru stiv  1 MB
Dimensiune maxim  a sursei: 5 KB

20.2.1 Indicaµii de rezolvare

prof. Daniela Taras  - C.N. Gheorghe Vrânceanu Bac u


CAPITOLUL 20. ONI 2012 252

Pentru determinarea numerelor obµinute prin modicare, se poate proceda în mai multe mo-
duri.

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
aa 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  ³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 e 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), e prin
xarea 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
ecare 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  a³a primele k k  1 2k  1 numere obµinute dup  modic ri.

20.2.2 Cod surs 

Listing 20.2.1: cubmin.cpp

1 #include <fstream>
2 #include <math.h>
3
4 using namespace std;
5
6 ifstream f("cuburi.in");
7 ofstream g("cuburi.out");
8
9 int w[100001];
10 int nrmin(int x,int nrc)
11 {
12 int v[10],i,Min,imin,in,sf;
13 for(i=nrc;i>=1;i--)
14 {
15 v[i]=x%10;
16 x=x/10;
17 }
18
19 Min=v[1];
20 imin=1;
21 for(i=1;i<=4;i++)
22 if(v[i]<Min && v[i]!=0)
23 {
24 Min=v[i];
25 imin=i;
26 }
27
28 int nr=Min;
29 in=imin+1;
30 sf=5;
31 while(sf<=nrc)
32 {
33 Min=v[in];imin=in;
34 for(i=in;i<=sf;i++)
35 if(v[i]<Min)
36 {
37 Min=v[i];
CAPITOLUL 20. ONI 2012 253

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 }

Listing 20.2.2: cubminC.cpp

1 #include <stdio.h>
2 #include <math.h>
3
4 //using namespace std;
5
6 //ifstream f("cuburi.in");
7 //ofstream g("cuburi.out");
8
9 int w[100001];
10 int nrmin(int x,int nrc)
11 {
12 int v[10],i,Min,imin,in,sf;
13
14 for(i=nrc;i>=1;i--)
15 {
16 v[i]=x%10;
17 x=x/10;
18 }
19
20 Min=v[1];
21 imin=1;
22 for(i=1;i<=4;i++)
23 if(v[i]<Min && v[i]!=0)
24 {
25 Min=v[i];
26 imin=i;
27 }
28
29 int nr=Min;
30 in=imin+1;
31 sf=5;
32 while(sf<=nrc)
CAPITOLUL 20. ONI 2012 254

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 }

Listing 20.2.3: cuburiBackMN.cpp

1 #include <stdio.h>
2 #include <string.h>
3
4 #define DIM 100011
5
6 #define INF 2100000000
7 int X, C[20], i, v, c, k, D[20], d, nr, mi, aux, K, N, sum, cc, cub, M, B, sm;
8 int V[DIM], W[DIM];
9
10 int minim(int a, int b)
11 {
12 return a<b ? a : b;
13 }
14
15 int f(int *, int);
16
17 FILE *fin = fopen("cuburi.in","r");
18 FILE *fout = fopen("cuburi.out","w");
19
20 int main()
CAPITOLUL 20. ONI 2012 255

21 {
22 fscanf(fin,"%d %d",&N, &K);
23
24 for (i=1;i<=N;i++)
25 {
26 fscanf(fin,"%d",&V[i]);
27
28 X = V[i];
29 c = 0;
30 while (X)
31 {
32 C[++c] = X%10;
33 X/=10;
34 }
35
36 mi = INF;
37 W[i] = f(C, c);
38 }
39
40 fclose(fin);
41
42 M = N;
43 sum = 0;
44 for (B = 1;;B+=2)
45 {
46 cc += B*6;
47 sum += cc;
48 if (sum <= M)
49 {
50 if (K == B/2 + 1)
51 sm = sum;
52 cub++;
53 }
54 else
55 {
56 sum -= cc;
57 break;
58 }
59 }
60
61 fprintf(fout,"%d\n",cub);
62 for (i=1;i<=sm;i++)
63 fprintf(fout,"%d ",W[i]);
64
65 return 0;
66 }
67
68 int f(int *C, int c)
69 {
70 int i, j, k, nr, mn, p, ok;
71 mn = INF;
72 for (i=1;i<=c-2;i++)
73 for (j=i+1;j<=c-1;j++)
74 for (k=j+1;k<=c;k++)
75 {
76 ok = 1;
77 for (p=c;p>=1;p--)
78 {
79 if (p!=k && p!=j && p!=i && C[p] == 0)
80 {
81 ok = 0;
82 break;
83 }
84 if (p!=k && p!=j && p!=i)
85 break;
86 }
87
88 if (ok)
89 {
90 nr = 0;
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 }
CAPITOLUL 20. ONI 2012 256

97 }
98 return mn;
99 }

Listing 20.2.4: cuburiDL.cpp

1 #include <stdio.h>
2
3 #define MAXN 100001
4
5 int A[MAXN];
6 int N, i, K, x;
7
8 int g(int x)
9 {
10 int V[20];
11 int i,j;
12 int q = 3;
13 int nr = 0;
14
15 while (x)
16 {
17 V[++nr] = x % 10;
18 x /= 10;
19 }
20
21 for (i=1; i<=nr/2; ++i)
22 {
23 int aux = V[i];
24 V[i] = V[nr-i+1];
25 V[nr-i+1] = aux;
26 }
27
28 int last;
29 int minim = 1;
30
31 for (j=2; j<=q+1; ++j)
32 if (V[j]!=0 && V[minim] > V[j])
33 minim = j;
34 q -= minim - 1;
35
36 int sol = V[minim];
37
38 last = minim;
39 for (i=2; i<=nr-3; ++i)
40 {
41 minim = last + 1;
42 for (j=last+2; j<=last+q+1; ++j)
43 if (V[minim] > V[j])
44 minim = j;
45 q -= (minim-last-1);
46 last = minim;
47 sol = sol * 10 + V[minim];
48 }
49
50 return sol;
51 }
52
53 int main()
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;
CAPITOLUL 20. ONI 2012 257

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 }

Listing 20.2.5: cuburiDL20.cpp

1 #include <stdio.h>
2
3 #define MAXN 100001
4
5 int A[MAXN];
6 int N, i, K, x;
7
8 int g(int x)
9 {
10 int V[20];
11 int i,j;
12 int q = 3;
13 int nr = 0;
14
15 while (x)
16 {
17 V[++nr] = x % 10;
18 x /= 10;
19 }
20
21 for (i=1; i<=nr/2; ++i)
22 {
23 int aux = V[i];
24 V[i] = V[nr-i+1];
25 V[nr-i+1] = aux;
26 }
27
28 int last;
29 int minim = 1;
30
31 for (j=2; j<=q+1; ++j)
32 if (V[j]!=0 && V[minim] > V[j])
33 minim = j;
34 q -= minim - 1;
35
36 int sol = V[minim];
37
38 last = minim;
39 for (i=2; i<=nr-3; ++i)
40 {
41 minim = last + 1;
42 for (j=last+2; j<=last+q+1; ++j)
43 if (V[minim] > V[j])
44 minim = j;
45 q -= (minim-last-1);
46 last = minim;
47 sol = sol * 10 + V[minim];
48 }
49
50 return sol;
51 }
52
53 int main()
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)
CAPITOLUL 20. ONI 2012 258

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 }

Listing 20.2.6: cuburi-iv4.cpp

1 #include<stdio.h>
2
3 void transf(long x,long &y)
4 {
5 int cifre[11],i,j,p,cmin,im,u,v,aux;
6 j=1;
7 while(x>0)
8 {
9 cifre[j]=x%10;
10 x=x/10;
11 j++;
12 }
13 j--;
14 u=1;v=j;
15 while(u<v)
16 {
17 aux=cifre[u];
18 cifre[u]=cifre[v];
19 cifre[v]=aux;
20 u++; v--;
21 }
22 im=1; cmin=cifre[1];
23 for(i=2;i<=4;i++)
24 if(cifre[i]>0 && cmin>cifre[i])
25 {
26 cmin=cifre[i];
27 im=i;
28 }
29 y=cmin;
30 for(p=2;p<=j-3; p++)
31 {
32 i=im+1;;
33 cmin=10;
34 while(i<=p+3)
35 {
36 if(cifre[i]<cmin)
37 {
38 cmin=cifre[i];
39 im=i;
40 }
41 i++;
42 }
43 y=y*10+cmin;
44 }
45 }
46
47 int main()
48 {
49 long z[100000]={0},w,u,k,x,y;
CAPITOLUL 20. ONI 2012 259

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 }

Listing 20.2.7: cuburiStackMN.cpp

1 #include <stdio.h>
2 #include <string.h>
3
4 #define DIM 100011
5
6 #define INF 2100000000
7 int X, C[20], i, v, c, k, D[20], d, nr, mi,
8 aux, K, N, sum, cc, cub, M, B, sm;
9 int V[DIM], W[DIM];
10
11 int minim(int a, int b)
12 {
13 return a<b ? a : b;
14 }
15
16 int f(int *, int, int);
17
18 FILE *fin = fopen("cuburi.in","r");
19 FILE *fout = fopen("cuburi.out","w");
20
21 int main()
22 {
23 fscanf(fin,"%d %d",&N, &K);
24
25 for (i=1;i<=N;i++)
26 {
27 fscanf(fin,"%d",&V[i]);
28
29 X = V[i];
30 c = 0;
31 while (X)
CAPITOLUL 20. ONI 2012 260

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
102 while (k)
103 {
104 v++;
105 k--;
106 }
107
CAPITOLUL 20. ONI 2012 261

108 nr = 0;
109 if (C[c] == 0)
110 return INF;
111 for (i=c;i>=v;i--)
112 {
113 nr = nr * 10 + C[i];
114 }
115
116 return nr;
117 }

20.2.3 *Rezolvare detaliat 

20.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 aate 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  efectuate. Pe cea de a doua linie se a  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
a 2 & N & 30;
a 0 & K & 9; K $ N ;
a 8 & xi & 8, 1 & i & N ;
a Dac  ³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 20. ONI 2012 262

20.3.1 Indicaµii de rezolvare

prof. Dana Lica - C.N. I.L.Caragiale Ploie³ti

Exista dou  variante de abordare:


- generarea submulµimilor ³irului iniµial, identicarea celor care au K elemente, plasarea sem-
nului * faµa acestora ³i calcularea valorii corespunz toare acestei conguraµii
- aboradarea prin programare dinamic .

1. Prima implementare presupune generarea submultimilor folosind vectorii caracteristici (sau


reprezentarea pe biti), si pentru ecare submulµime de K elemente se calculeaz  valoare corespun-
z toare. Complexitatea O N ˜ 2N  N ˜ Comb N, K . Aceast  variant  obµine 70 puncte.

2. O alta variant  de implementarea presupune generarea doar a submulµimilor de K elemente


³i pentru ecare posibilitate obµinut , se calculeaz  valoarea reµinându-se valoarea minim  ³i
maxim . Complexitatea O N ˜ Comb N, K . Aceasta variant  obµine 80 puncte

3. Una din soluµiile de 100 de puncte presupune generarea doar a submulµimilor de K elemente
³i calcularea valorii corespunz toare, pe parcursul parcursul construcµiei ec rei submulµimi.

La ecare pas al gener rii unei submulµimi, se reµine valoarea ultimului termen (care nu a fost
ad ugat la valoarea submulµimii). Dac  pe nivelul curent se plaseaz  o adunare, atunci înseamn 
c  secvenµa de înmulµiri s-a terminat, iar ultimul termen poate  ad ugat la suma curent , dup 
care noul ultim termen se reseteaz  la valoarea elementului urm tor în ³ir. Plasarea adun rii pe
nivelul curent se face doar dac  nu împiedic  obµinerea în nal a tuturor celor K înmulµiri.

Dac  pe nivelul curent s-a plasat o înmulµire atunci se updateaz  valoarea ultimului termen ³i
se trece pe nivelul urm tor în generare. O înmulµire se poate plasa doar dac  nu s-au completat
deja cele K.
Datorit  acestor condiµii de continuare se obµine complexitatea O Comb N, K .
4. Varianta program rii dinamice:

Pentru determinarea valorii minime se va construi matricea X N K  în care elementul


X ij  reprezinta valoarea minima pe care o pot obµine din primele i numere folosind j ope-
raµii de înmulµire.

Pentru construcµia ec rui element ne vom folosi de utima secvenµ  de înmulµiri care se termin 
cu termenul Ai din ³ir.

Aceast ultim termen poate conµine t 0, 1, 2, ..., j înmulµiri. Elemenul X ij  va reµine
valoarea minim  a acestor variante de scriere.
X ij  = { min p  X i  t  1j  t | t reprezint  num rul de înmulµiri, iar p produsul
elementelor din a A de la poziµiile i  t, ..., i}
Analog se procedeaz  ³i pentru valoarea maxim .
2
Complexitatea O N ˜ K . Aceasta variant  obµine 100 puncte

20.3.2 Cod surs 

Listing 20.3.1: optim_back100.cpp

1 //O(Comb(N,K))
2 // Se genereaza fiecare posibilitate dar se si calculeaza suma pe parcurs
3
4 #include <cstdio>
5 #define inf 2000000000
6 #define MAXN 40
7
8 using namespace std;
9
10 int A[MAXN];
11 int maxim,minim, N, K,i;
12
13 void back(int lv, int k, int aux, int suma)
CAPITOLUL 20. ONI 2012 263

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 }

Listing 20.3.2: optim_dinamica100.cpp

1 #include <cstdio>
2 #define inf 2000000000
3
4 using namespace std;
5 int i, j, t, c, p, n, k, v, z, Max, Min,
6 a[100], d[100][100], x[100][100];
7
8 int main()
9 {
10 freopen("optim.in", "r", stdin);
11 freopen("optim.out", "w", stdout);
12
13 scanf("%d%d\n", &n, &k);
14 for (i=1; i<=n; ++i)
15 scanf("%d", &a[i]);
16
17 x[0][0] = 0; d[0][0] = 0;
18 for (i=1; i<=n; ++i)
19 {
20 x[i][0]=x[i-1][0] + a[i];
21 d[i][0]=d[i-1][0] + a[i];
22 for (j=1; j<i && j<=k; ++j)
23 {
24 if (j<i-1)
25 {
26 Min = x[i-1][j] + a[i];
27 Max = d[i-1][j] + a[i];
28 }
29 else
30 {
31 Min = inf;
32 Max = -inf;
33 }
34
35 p=a[i];
36 for (t=1; t<=j; t++)
37 {
38 p *=a[i-t];
39 if (i-1 > j || (i-t-1 == 0))
40 {
CAPITOLUL 20. ONI 2012 264

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 }

Listing 20.3.3: optim0MN.cpp

1 #include <stdio.h>
2 #define DIM 33
3
4 int V[DIM];
5 int X[DIM];
6 int S[DIM];
7 int N, K, k, i, Min = 2000000000, Max = -2000000000, s;
8
9 void sol()
10 {
11 int sum = 0;
12 S[1] = V[1];
13 s = 1; k = 1;
14 for (i=2;i<=N;i++)
15 {
16 if (X[k] == i-1)
17 {
18 S[s]*=V[i];
19 k++;
20 }
21 else
22 {
23 sum += S[s];
24 S[++s] = V[i];
25 }
26 }
27
28 sum += S[s];
29 //for (i=1;i<=s;i++)
30 // sum += S[i];
31 if (sum > Max)
32 Max = sum;
33 if (sum < Min)
34 Min = sum;
35 }
36
37 void back(int k)
38 {
39 if (k == K+1)
40 {
41 sol();
42 return;
43 }
44
45 for (int i = X[k-1]+1;i<N;i++)
46 {
47 X[k] = i;
48 back(k+1);
49 }
50 }
51
52 int main()
53 {
54 FILE *f = fopen("optim.in","r");
55 FILE *g = fopen("optim.out","w");
CAPITOLUL 20. ONI 2012 265

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 }

Listing 20.3.4: optim1MN.cpp

1 #include <stdio.h>
2 #include <string.h>
3
4 #define DIM 33
5
6 int Max = -2000000000, Min = 2000000000, N, K, i, j, sum, nr;
7
8 int X[DIM];
9 int V[DIM];
10
11 void sol()
12 {
13 int W[DIM];
14 int Y[DIM];
15
16 memcpy(W, V, sizeof(W));
17 memcpy(Y, X, sizeof(Y));
18
19 int n = N;
20 int ok;
21 do
22 {
23 ok = 1;
24 for (i=1;i<n;i++)
25 {
26 if (Y[i] == 1)
27 {
28 W[i]*=W[i+1];
29 for (j=i+1;j<n;j++)
30 W[j] = W[j+1];
31 for (j=i;j<n;j++)
32 Y[j] = Y[j+1];
33 n--;
34 ok = 0;
35 }
36 }
37 } while (!ok);
38
39 sum = 0;
40 for (i=1;i<=n;i++)
41 sum += W[i];
42 if (sum > Max)
43 Max = sum;
44 if (sum < Min)
45 Min = sum;
46 }
47
48 void back(int k)
49 {
50 if (k == N)
51 {
52 if (nr != K)
53 {
54 return;
55 }
56 sol();
57 return;
58 }
CAPITOLUL 20. ONI 2012 266

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 }

Listing 20.3.5: optim2MN.cpp

1 #include <stdio.h>
2 #define DIM 33
3
4 int V[DIM];
5 int X[DIM];
6 int S[DIM];
7 int N, K, k, i, Min = 2000000000, Max = -2000000000, s;
8
9 void sol()
10 {
11 S[1] = V[1];
12 s = 1;
13 int k = 1;
14 for (i=2;i<=N;i++)
15 {
16 if (X[k] == i-1)
17 {
18 S[s]*=V[i];
19 k++;
20 }
21 else
22 {
23 S[++s] = V[i];
24 }
25 }
26
27 int sum = 0;
28 for (i=1;i<=s;i++)
29 sum += S[i];
30 if (sum > Max)
31 Max = sum;
32 if (sum < Min)
33 Min = sum;
34 }
35
36 /*
37 void back(int k) {
38 if (k == K+1) {
39 sol();
CAPITOLUL 20. ONI 2012 267

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 }

Listing 20.3.6: optim3MN.cpp

1 #include <stdio.h>
2 #define DIM 33
3
4 struct per
5 {
6 int start;
7 int lung;
8 };
9
10 int V[DIM];
11 int X[DIM];
12 per S[DIM];
13 int SP[DIM];
14 int PP[DIM][DIM];
15 int N, K, k, i, Min = 2000000000, Max = -2000000000, s, j, Rez;
16
17 void sol()
18 {
19 int aux = Rez + PP[ S[s].start ][ S[s].lung + 1] -
20 SP[ S[s].start + S[s].lung ] + SP[ S[s].start - 1 ];
21 if (aux > Max)
22 Max = aux;
23 if (aux < Min)
24 Min = aux;
25 }
26
27 void back(int k)
CAPITOLUL 20. ONI 2012 268

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 20. ONI 2012 269

104 PP[i][j] = PP[i][j-1] * V[i+j-1];


105 }
106
107 }
108
109 fclose(f);
110
111 back(1);
112
113 fprintf(g,"%d %d\n",Min, Max);
114 fclose(g);
115 return 0;
116 }

Listing 20.3.7: optimDT.cpp

1 #include <fstream>
2
3 using namespace std;
4
5 ifstream f("optim.in");
6 ofstream g("optim.out");
7
8 int v[31],v1[31],n1,semn[31],semn1[31],n,k,i,j,S,nrs;
9 int Smin=100000,Smax=-100000;
10
11 int main()
12 {
13 f>>n>>k;
14 for(i=1;i<=n;i++)
15 {
16 f>>v[i];
17 S=S+v[i];
18 }
19
20 for(i=n-k;i<=n-1;i++)
21 semn[i]=1;
22 if(k==0)
23 g<<S<<’ ’<<S<<’\n’;
24 else
25 {
26 do
27 {
28 nrs=0;
29 for(i=1;i<=n-1;i++)
30 nrs=nrs+semn[i];
31
32 // imi convine doar vectorul caracteristic
33 // care are k de 1, adica de k ori semnul *
34 if(nrs==k)
35 {
36 S=0;n1=n;
37 //trebuie sa calculez suma S
38 for(i=1;i<=n1;i++)
39 v1[i]=v[i];
40 for(i=1;i<=n1-1;i++)
41 semn1[i]=semn[i];
42
43 //fac inmultirile
44 for(i=1;i<=n1-1;i++)
45 if(semn1[i]==1)
46 {
47 v1[i]=v1[i]*v1[i+1];
48
49 for(j=i+1;j<n1;j++)
50 v1[j]=v1[j+1];
51
52 for(j=i;j<n1-1;j++)
53 semn1[j]=semn1[j+1];
54
55 n1--;i--;
56 }
57
58 //fac adunarile
59 for(i=1;i<=n1;i++)
CAPITOLUL 20. ONI 2012 270

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 }

Listing 20.3.8: optim-iv.cpp

1 #include<fstream>
2
3 using namespace std;
4
5 int x[10],y[31],n,k;
6 long long valmin,valmax,a[31],val;
7
8 void comb(int t)
9 {
10 int i;
11 long long s,p;
12
13 if(t==k+1)
14 {
15 for(i=1;i<n;i++)
16 y[i]=1;
17
18 for(i=1;i<=k;i++)
19 y[x[i]]=2;
20
21 s=0;
22 p=a[0];
23 for(i=1;i<n;i++)
24 if(y[i]==2)
25 p=p*a[i];
26 else
27 {
28 s=s+p;
29 p=a[i];
30 }
31
32 s=s+p;
33 if(s<valmin)
34 valmin=s;
35 else if(s>valmax)
36 valmax=s;
37 }
38 else
39 for(i=x[t-1]+1;i<=n-1;i++)
40 {
41 x[t]=i;
42 comb(t+1);
43 }
44 }
45
CAPITOLUL 20. ONI 2012 271

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 }

20.3.3 *Rezolvare detaliat 


Capitolul 21

ONI 2011

21.1 butoane
Problema 1 - butoane 100 de puncte
Echipa SG1 se a  în faµa unei noi provoc ri. Un dispozitiv antic are un sistem foarte ciudat
prin care poate  pus în funcµiune. Dispozitivul are n butoane numerotate de la stânga la dreapta
de la 1 la n. Pe ecare 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  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 ecare buton astfel încât dispozitivul s  e
pornit astfel încât num rul total de atingeri s  e 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 a  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 a³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  & 2 000 000 000 (dou  miliarde).
a Punctaj. Dac  programul a³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 a³at  determin  deschiderea dispozitivului, se obµine 30% din
punctaj.

Exemple
butoane.in butoane.out Explicaµii
3 0
10 11 12 1
2

Timp maxim de executare/test: 1.0 secunde


272
CAPITOLUL 21. ONI 2011 273

21.1.1 Indicaµii de rezolvare

Prof. Adrian Panaete - C. N. A. T. Laurian, Boto³ani

Soluµie1 - Soluµie optim . Complexitatea O n


Observaµii iniµiale.

1. Pentru orice i $ n prin atingerea tuturor butoanelor de la 1 la i num rul de pe butonul i


scade cu o unitate ³i cel de pe butonul i  1 creste cu o unitate. Avem deci o mutare compus  care
decrementeaz  valoarea de pe un buton în favoarea butonului urm tor. Not m aceast  mutare
DOW N i.
2. Pentru orice i $ n prin atingerea tuturor butoanelor de la i  1 la n num rul de pe butonul i
cre³te cu o unitate ³i cel de pe butonul i  1 scade cu o unitate. Avem deci o mutare compus  care
incrementeaz  valoarea de pe un buton în defavoarea butonului urm tor. Not m aceast  mutare
U P i.
3. O soluµie este optim  dac  ³i numai dac  exist  m car un buton care nu este atins niciodat .
Orice soluµie se obµine din soluµia optim  prin adunarea unui num r strict pozitiv xat la numerele
corespunz toare soluµiei optime. Aceast  observaµie se justic  prin faptul c  orice soluµie poate
 determinat  rezolvând un sistem de ecuaµii u³or de formulat din care se observ  c  num rul de
ap s ri pe primul buton determin  în mod unic num rul de ap s ri pe celelalte butoane

4. La nal, pe ecare buton vom avea înscris  media aritmetic  a numerelor iniµiale

Pasul 1. Calcul m media aritmetic  a numerelor iniµiale

Pasul 2. Parcurgem ³irul numerelor iniµiale ³i în funcµie de valoarea întâlnit :

a. Dac  valoarea de pe buton este media, trecem la butonul urm tor.


b. Dac  valoarea de pe buton este sub medie, aplic m U P i de un num r de ori pentru a
obµine media ³i trecem la butonul urm tor.
c. Dac  valoarea de pe buton este peste medie aplic m DOW N i de un num r de ori pentru
a obµine media ³i trecem la butonul urm tor.
d. Dac  am ajuns la butonul n, ne oprim deoarece am realizat egalizarea.

OBS. Mut rile DOW N ³i UP pot  aplicate simultan de un numar de ori folosind SMENUL
LUI MARS de modicare, în timp 1, a sumei unei secvenµe, cu o valoare constant .

Pasul 3. Deoarece este posibil ca soluµia s  nu e 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 .

Soluµie 2 - Soluµie optim . Complexitatea O n


Justic  ³i observaµia 3 din Soluµia 1.
Pentru a înµelege aceast  soluµie voi considera cazul n 4.
Fie A B C D numerele aate iniµial pe butoane ³i M media aritmetic  a acestora.
Fie a, b, c, d o soluµie nu neaparat optim .
Se obµine sistemul:

(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:

- din ecuaµia 1 obµine b


- din ecuaµia 2 obµine c
- din ecuaµia 3 obµine d
- ecuaµia 4 este vericat .
CAPITOLUL 21. ONI 2011 274

În plus, printr-o analiz  atent  a sistemului se observ  c  diferenµele a  b, b  c, c  d pot 


bine determinate ³i valorile b, c, d sunt obµinute din a modicat cu constante unic determinate
din datele de intrare. Ideea e analog  pentru oricâte butoane.
Algoritm
Notez x[ ] = ³irul celor n numere din ³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  negativ) va trebui s  translat m soluµia prin sc derea valorii
V = min sol[k]

Soluµia 3. - Metoda Greedy (30 puncte)


La ecare pas se atinge un buton de valoare maxim . Procedeul se repet  pân  la egalizarea
tuturor butoanelor.
Complexitate: O n ˜ numar_total_atingeri

21.1.2 *Cod surs 

21.1.3 *Rezolvare detaliat 

21.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 ec rei cl diri este un dreptunghi cu laturile
respectiv paralele cu axele. Prin urmare, vederea de sus a unei cl diri poate  specicat  prin 4
valori x y Lx Ly cu semnicaµ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, identicând astfel toate cl dirile, Cristi prive³te
macheta perpendicular dinspre laterala sudic  (adic  prive³te perpendicular pe laterala machetei
pe care se a  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
Fi³ierul de intrare macheta.in conµine pe prima linie num rul natural N, reprezentând nu-
m rul de cl diri. Pe urm toarele N linii sunt descrise cele N cl diri, câte o cl dire pe o linie, în
ordinea de la 1 la N. O cl dire este specicat  prin cinci numere naturale x y Lx Ly H , separate
prin câte un spaµiu, unde x y Lx Ly specic  vederea de sus a cl dirii, iar H în lµimea acesteia.

Date de ie³ire
CAPITOLUL 21. ONI 2011 275

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 .

Restricµii ³i preciz ri
a 2 & N & 100
a 0 & x, y, Lx, Ly, H & 1000. Pentru 50% dintre teste 0 & x, y, Lx, Ly, H & 250
a Se garanteaz  c  în ³ierele de test dreptunghiurile reprezentând vederile de sus ale oric ror
dou  cl diri nu au niciun punct comun.

Exemple
macheta.in macheta.out Explicaµii
5 1 2 3 5
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

Timp maxim de executare/test: 1.0 secunde

21.2.1 Indicaµii de rezolvare

prof. Miron Lucia - C. N. C. Negruzzi Ia³i

Soluµia 1 - prof. Miron Lucia


Pentru reµinerea datelor folosim un vector C cu elemente de tip record (struct); vom folosi un
vector vizibil cu elemente de tip boolean.
Vom aplica tehnica Greedy:
- Vom sorta vectorul C dupa x y
³i în caz de egalitate dup 
- Dup  sortare parcurgem vectorul ³i pentru ecare cl dire C i, test m vizibilitatea cl dirii în
funcµie de cl dirile C j , j 1, i  1; dac  C i are o porµiune în spatele cl dirii C j  ³i în lµimea
este mai mic , atunci din C i vom decupa partea care nu este vizibil , modicând coordonata x
³i Lx, se reactualizeaz  vectorul ordonat ³i se continu  cu pasul i.
- Algoritmul se termin  cînd nu se mai face nicio decupare
- A³ m în ordinea iniµial  cl dirile pentru care Lx % 0

Soluµia 2 - prof. “erban Marinel


Din punctul de vedere al privitorului (dinspre Sud), cl dirile apar ca ind proiectate pe un
ecran. Deci este sucient s  marc m pe ecran zona dreptunghiular  care reprezint  proiecµia
pe ecran a cl dirii respective (cu num rul ei). Pentru ca proiecµiile s  reprezinte corect vederea
dinspre Sud este necesar s  sort m cl dirile dup  coordonata Y, iar proiecµiile s  e puse pe
ecran de la cea mai dep rtat  cl dire spre cea mai apropiat . Prin parcurgerea ecranului sunt
identicate cl dirile care sunt vizibile ³i sunt reµinute într-un vector caracteristic.

Problema care apare este dat  de dimensiunile ecranului pe care se realizeaz  proiecµia (1000
x 1000 caractere - bytes). Aceste valori dep ³esc zona de memorie care poate  alocat  unui
program care este compilat într-un mediu Borland (max. 64 K).

Soluµia o reprezint  împ rµirea ecranului în 16 zone de câte 250 x 250 bytes ³i realizarea
aceleia³i proceduri descrise mai sus pentru ecare zon  în parte.

Soluµia 3 - prof. Panaete Adrian


CAPITOLUL 21. ONI 2011 276

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  e înlocuite cu valori consecutive începând
cu valoarea 1.

Ideea prezint  valoarea ca în acest moment f r  s   modicat problema în esenµ  ecare zona
p trat  cu latura de o unitate de lungime poate  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 modicarea 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.

21.2.2 *Cod surs 

21.2.3 *Rezolvare detaliat 

21.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 ind
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 ecare 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 a  elevul 3 iar pe poziµia 3 se a  elevul 4. Dup  prima comand 
pe poziµia 1 va ajunge elevul pp1 p3 4. Pe poziµia 2 se a  elevul 1, iar pe poziµia 1 se
a  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 conguraµ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 conguraµia 1 2 3 4 5 6

Iar dup  a patra comand  se revine la conguraµia iniµial .


CAPITOLUL 21. ONI 2011 277

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 conguraµia iniµial ? ³i care ar
 cea mai mic  conguraµie iniµial  (considerând ordinea lexicograc ) pentru care este necesar
acela³i num r minim de comenzi pentru a reveni la conguraµ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 a  N valori distincte cuprinse între 1 ³i N reprezentând
conguraµ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  scris un num r natural
reprezentând num rul minim de comenzi ce trebuie date astfel încât elevii s  revin  la conguraµia
iniµial . Pe cea de a doua linie vor  scrise N valori distincte cuprinse între 1 ³i N reprezentând cea
mai mic  conguraµie iniµial  (considerând ordinea lexicograc ) pentru care este necesar acela³i
num r minim de comenzi pentru a reveni la conguraµia iniµial .

Restricµii ³i preciz ri
a 1&N & 500
a Valorile scrise pe aceea³i linie în ³ierul de intrare, respectiv în ³ierul de ie³ire sunt separate
prin spaµii.
a Spunem c  o conguratie p p1 , p2 , ..., pN  este mai mic  din punct de vedere lexicograc
decât o alt  conguraµie q q1 , q2 , ..., qN  dac  exist  un indice k , 1 & k & N astfel încât pi qi ,
pentru orice 1&i$k ³i pk $ qk .
a Num rul minim de comenzi ce trebuie s  e efectuate este & 2 000 000 000 (dou  miliarde).
a Punctaj. Se acord  50% din punctajul pe teste pentru prima cerinµ . Punctajul integral se
acord  pentru rezolvarea ambelor cerinµe.

Exemple
sport.in sport.out Explicaµii
6 4
3 1 4 2 6 5 1 2 4 5 6 3

Timp maxim de executare/test: 1.0 secunde

21.3.1 Indicaµii de rezolvare

prof. Emanuela Cerchez - C. N. Emil Racoviµ  Ia³i

În limbaj matematic problema poate  tradus  astfel:


Se consider  o permutare a mulµimii {1, 2, ..., n}.
k
Numim grad al permut rii cel mai mic num r k cu proprietatea c  p e (unde e este permu-
tarea identic  1 2 ... n).
Prima cerinµ  const  în a determina gradul permut rii date.

Orice permutare poate  descompus  în produs de cicluri p c1 c2 ...ck


S  not m cu lgi = lungimea ciclului i.
Gradul permut rii este egal cu cmmmc lg1 , lg2 , ..., lgk .
Cea de a doua cerinµ  const  în a determina cea mai mic  permutare care are gradul determinat.

S  consider m descompunerea în factori primi a lui cmmmc:


a a a
cmmmc lg1 , lg2 , ..., lgk  p1 1 p2 2 ...pmm .
Deoarece:
CAPITOLUL 21. ONI 2011 278

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 lexicograc  vom construi permutarea astfel:

1. Consider m c  lg1 & lg2 & ... & lgk


2. Pentru ca permutarea s  e minim  din punct de vedere lexicograc, 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.

21.3.2 *Cod surs 

21.3.3 *Rezolvare detaliat 


Capitolul 22

ONI 2010

22.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  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 ecare din cele dou  exemple, oricare ar  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 a³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
Fi³ierul fractie.in conµine pe o singur  linie un ³ir de maxim 80 de caractere reprezentând
fracµia zecimal . Caracterele pot : cifre, eventual virgula zecimal  ',' ³i parantezele '(' respectiv
')'.

Date de ie³ire
Fi³ierul fracµie.out va conµine dou  linii:
a pe prima linie num r torul fracµiei;
a pe a doua linie numitorul fracµiei.

Restricµii ³i preciz ri
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%.

279
CAPITOLUL 22. ONI 2010 280

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

Timp maxim de executare/test: 1.0 secunde

22.1.1 Indicaµii de rezolvare

prof. Szabo Zoltan - Gr. “c. Petru Maior Reghin

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 nesemnicative 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).

Dup  reducere apar urm toarele situaµiii:

a fracµie periodic  simpl  subunitar ;


a fracµie periodic  mixt  subunitar ;
a fracµie periodic  simpl  sau mixt  supraunitar  - se reduce la fracµie periodic  mixt  subu-
k
nitar  care trebuie înmulµit  cu 10 , unde k = num rul de cifre al p rµii întregi.

Rezolvarea prin conversii între tipurile numerice prime³te maxim 50 de puncte iar rezolvarea
folosind ³iruri de caractere obµine 100 de puncte.

22.1.2 Cod surs 

Listing 22.1.1: FLORE7.CPP

1 // Prof. Florentina Ungureanu Colegiul de Informatica Piatra Neamt


2 #include<fstream>
3 //#include<iostream>
4 #include<string.h>
5
6 using namespace std;
7
8 ifstream f("fractie.in");
9 ofstream g("fractie.out");
10
11 void norm(char *s, int &x, int&y, int&z)
12 { char *p=s;
13 x=0;
14 if(s[0]!=’0’) while(*p!=’,’)x++,p++;
15 else strcpy(p,p+1);
16
CAPITOLUL 22. ONI 2010 281

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--;
92 if(i)
CAPITOLUL 22. ONI 2010 282

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 }

22.1.3 *Rezolvare detaliat 

22.2 neuroni
Problema 2 - neuroni 100 de puncte
Cercet torii neurologi au identicat în retina uman  o zon  de neu-
roni bipolari, ce au exact dou  ramicaµ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  ramicaµ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 a  pe un nivel par, c tre neuronul din stânga, iar dac  se a 
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 aat pe ramicaµia din stânga, urm torul impuls va  transmis c tre
neuronul aat pe ramicaµ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 aat 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
ecare neuron de pe nivelul n.

Date de intrare
Prima linie a ³ierului de intrare neuroni.in conµine cele dou  numere naturale n ³i m separate
printr-un spaµiu, având semnicaµia de mai sus.

Date de ie³ire
Prima linie a ³ierului de ie³ire neuroni.out va conµine num rul de impulsuri receptate de
ecare neuron de pe nivelul n, scrise de la stânga la dreapta, separate prin câte un spaµiu.

Restricµii ³i preciz ri
a 2 & n & 100
a 1 & m & 100 000
a Ramicaµiile îngro³ate din gur  arat  direcµia de transmitere a primului impuls.
CAPITOLUL 22. ONI 2010 283

Exemple
neuroni.in neuroni.out Explicaµii
3 5 1 3 1 Traseul celor 5 impulsuri ³i num rul de impulsuri recepµionat
pe nivelul 3 va  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)

Timp maxim de executare/test: 1.0 secunde

22.2.1 Indicaµii de rezolvare

prof. Ciprian Che³c  - Gr. “c. C. Neniµescu Buz u

Dicultatea 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 ecare neuron.

A doua abordare se poate face cu ajutorul unei matrice cu nn 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 ecient  a memoriei în timp ce a doua structur  este
mai rapid  dar utilizeaz  inecient memoria.

22.2.2 Cod surs 

Listing 22.2.1: neuroni1.cpp

1 // sursa cu matrice prof. Chesca Ciprian - Grup Scolar "Costin Nenitescu" Buzau
2
3 #include <fstream.h>
4
5 ifstream f("neuroni.in");
6 ofstream g("neuroni.out");
7
8 int n;
9 long m;
10 long mat[100][100];
11
12 int main()
13 {
14 int i,j;
15
16 f>>n>>m;
17 long k=0;
18 for(i=1;i<n;i++)
19 for(j=1;j<=i;j++)
20 if (i%2==0) mat[i][j]=0;
21 else mat[i][j]=1;
22
23 for(k=1;k<=m;k++)
24 {
25 i=1;j=1;
CAPITOLUL 22. ONI 2010 284

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 }

Listing 22.2.2: neuroni2.cpp

1 // sursa cu vector - prof. Chesca Ciprian - Grup Scolar "Costin Nenitescu" Buzau
2 #include <fstream>
3
4 using namespace std;
5
6 typedef unsigned long vector[5051];
7
8 ifstream f("neuroni.in");
9 ofstream g("neuroni.out");
10
11 int n;
12 long m;
13 vector v,w;
14
15 int main()
16 {
17 long i,j,nc;
18
19 f>>n>>m;
20 long k;
21 for(i=1;i<n;i++)
22 for(j=1;j<=i;j++)
23 {
24 if (i%2==0) v[++k]=0;
25 else v[++k]=1;
26 w[k]=i;
27 }
28
29 for(i=1;i<=m;i++)
30 {
31 nc=1;
32 while (nc<=n*(n-1)/2)
33 if (v[nc]==0) {v[nc]=1;nc=nc+w[nc];}
34 else {v[nc]=0;nc=nc+w[nc]+1;}
35 v[nc]++;
36 }
37 for(i=n*(n-1)/2+1;i<=n*(n+1)/2;i++)
38 g<<v[i]<<" ";
39
40 g<<"\n";
41
42 f.close();
43 g.close();
44 return 0;
45 }

22.2.3 *Rezolvare detaliat 

22.3 raze
Problema 3 - raze 100 de puncte
CAPITOLUL 22. ONI 2010 285

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 ecare element aat pe conturul tabloului, adic 
pe prima linie, prima coloana, ultima linie ³i ultima coloan , se a  obiective inamice. Pe conturul
tabloului se g sesc numai elemente nule.
În interiorul tabloului (elementele care nu se a  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 ecare 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
 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 ecare set de date de intrare:

` Pe prima linie a setului se a  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 a  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
ecare 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  anihilat pentru ecare set de
date de intrare.

Exemple
raze.in raze.out Explicaµii
2 4 1 În ³ier se g sesc 2 seturi de date de intrare.
4 6 3 2 În primul set de date se pot anihila maximum 4 obiective inamice,
0 0 0 0 0 0 poziµionînd soldatul în linia 2 ³i coloana 2.
0 0 1 1 1 0 În al doilea set de date se pot anihila maximum 3 obiective ina-
0 0 0 0 0 0 mice, poziµionând soldatul în elementul de pe linia 3 ³i coloana 2
0 0 0 0 0 0 sau în elementul din linia 3 ³i coloana 6.
4 7
0 0 0 0 0 0 0
0 1 1 1 1 1 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0

Timp maxim de executare/test: 1.0 secunde


CAPITOLUL 22. ONI 2010 286

22.3.1 Indicaµii de rezolvare

prof. Marius Nicoli - C.N. Fraµii Buze³ti, Craiova

O n 
3
pentru 50 puncte
Se parcurge matricea (f r  contur) ³i, pentru ecare element, se parcurge în ecare dintre cele
4 direcµii diagonale pân  la contur sau pân  la întâlnirea unui obstacol, determinându-se câte
obiective sunt anihilate. Pe parcurs se calculeaz  valorile cerute.

O n 
2
pentru 100 puncte
Fiecare obiectiv de pe conturul matricei poate  anihilat pe 2 direcµii (în afar  de colµuri ³i
vecinii lor care se pot anihila pe o direcµie), a³a c  se poate parcurge conturul ³i, din ecare element,
se parcurge în cele 2 direcµii numai pe elemente 0, incrementându-se valoarea din elementele atinse.
La nal se parcurg elementele calculate ³i se determin  cele dou  valori cerute.

O n 
2
alternativ  pentru 100 de puncte
Parcurgând matricea clasic (de sus în jos ³i de la stânga la dreapta) marc m c  Aij  poate
atinge obiectiv în direcµia NV dac  în poziµia i, j  este liber ³i Ai  1j  1 a atins un obiectiv
în acea direcµie.
Se pot face 4 astfel de parcurgeri (de sus în jos ³i de la dreapta la stânga, si cele 2 similare de
jos în sus) ³i se determin  pentru ecare element dac  atinge conturul pe ecare dintre direcµii.
Pentru ecare poziµie a matricei se vor num r  direcµiile din care se poate anihila un obiectiv.
La nal se parcurge matricea cu valorile calculate si se determin  valorile cerute.
Observaµie: direcµiile NV ³i N E, respectiv SV ³i SE se pot prelucra în paralel.

22.3.2 Cod surs 

Listing 22.3.1: RAZE2.CPP

1 //100P - Nicoli Marius - CN "Fratii Buzesti - Craiova"


2 #include <stdio.h>
3 #include <string.h>
4 #define DIM 137
5
6 int DI[4] = {-1,1,1,-1};
7 int DJ[4] = {1,-1,1,-1};
8
9 char A[DIM][DIM];
10
11 char B[DIM][DIM];
12
13 int T, N, M, i, j, max, nMax, ii, jj, d;
14
15 int main()
16 {
17 FILE *f = fopen("raze.in","r");
18 FILE *g = fopen("raze.out","w");
19
20 fscanf(f,"%d",&T);
21
22 for (;T;T--)
23 {
24 fscanf(f,"%d %d",&N,&M);
25 for (i=1;i<=N;i++)
26 for (j=1;j<=M;j++)
27 fscanf(f,"%d",&A[i][j]);
28
29 memset(B,0,sizeof(B));
30
31 for (j=1;j<=M;j++)
32 { //liniile
33 i = 1;
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])
CAPITOLUL 22. ONI 2010 287

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 }

Listing 22.3.2: RAZE3.CPP

1 //60P - Nicoli Marius - CN "Fratii Buzesti - Craiova"


2 #include <stdio.h>
3 #define DIM 137
4
5 char A[DIM][DIM];
6
7 unsigned int T, N, M, i, j, max, nMax, d, ii, jj, k, ok;
8
9 int main()
10 {
11 FILE *f = fopen("raze.in","r");
12 FILE *g = fopen("raze.out","w");
13
14 fscanf(f,"%d",&T);
15 for (;T;T--)
16 {
17 fscanf(f,"%d %d",&N,&M);
18 ok = 1;
19 for (i=1;i<=N;i++)
20 for (j=1;j<=M;j++)
21 {
22 fscanf(f,"%d",&A[i][j]);
23 if (A[i][j] == 1)
24 ok = 0;
25 }
26
27 if (ok)
28 {
29 fprintf(g,"%u %u\n",4,(M-2)*(N-2));
30 continue;
CAPITOLUL 22. ONI 2010 288

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 }

22.3.3 *Rezolvare detaliat 


289
Anexa A

Un pic de matematic !

A.1 ...

A.1.1 Prezentare general 

...

A.1.2 Exemple

...

290
Anexa B

Un pic de programare!

B.1 ...

B.1.1 Prezentare general 

...

B.1.2 Exemple

...

291
Glosar

%I64d, 101 I.d.k.:


200 000 000 000 000 000, 49 I don't know who the author is., v
implementare recursiv , 182
algoritm de tip succesor, 112, 182 inducµie matematic , 169
atoi, 248 isdigit, 6
atol, 129 isupper, 6

backtracking, 78, 120, 188, 252 lungime ciclu în permutare, 278


backtracking nerecursiv, 121
make_pair, 202, 209
backtracking recursiv, 115, 116
memcpy, 261
baza de numeraµie, 195
memset, 56, 57, 287
bitset, 34, 202
metoda Greedy, 274
bool, 185
model matematic, 283
brute-force, 9
bubble sort, 206
new, 92, 114
bu, 248
normalizare, 276
bytes, 275
NULL, 129, 130, 185, 186, 188

c utare binar , 4, 141, 154, 160, 230 operaµii cu numere mari, 190
cel mai mic multiplu comun, 243 ordine lexicograc , 158, 195, 277, 278
char, 185, 186, 188
ciclu în permutare, 277 pair, 58, 202, 208, 209
cifra de control, 195 rst, 58
ciur, 44 make_pair, 58
cmmdc, 45, 244, 249, 278 second, 58
cmmmc, 249, 277 parsarea expresiilor, 128
codicare, 182 permutare, 277
complexitate logaritmic , 195 preprocesare, 141
condiµii de continuare, 262 produsului cartezian, 181
count, 34 programare dinamic , 113, 262
push_back, 208, 209
delete, 92, 114 putere, 45, 47
descompunere în factori primi, 277
divide et impera, 114 reprezentarea pe biti, 262
reverse, 30
eratostene, 45
scrierea în baza 2, 190, 206
euclid, 45
second, 202, 208, 209

fgets, 248 size(), 208, 209

rst, 202, 208, 209 SMENUL LUI MARS, 273


sort, 30, 31, 106, 246, 247
generarea combin rilor, 252 sortare, 182, 200, 230, 275, 276
generarea submultimilor, 262 sortare prin inserare, 4
get, 185, 186 sortare prin selecµie, 206
get(), 6, 130, 132, 186, 188 sqrt, 34
get(linie,500), 6 stiv , 182, 252
getline, 7, 130, 132, 185, 186, 188 strategie Greedy, 219
gets, 78 strchr, 6, 129, 130, 185, 188, 248
grad al permut rii, 277 strcmp, 6, 186, 188
Greedy, 182 strcpy, 6, 130, 148, 185, 186, 188

292
GLOSAR 293

strlen, 6, 58, 78, 130 unsigned long long, 31


strncat, 188
strncmp, 148
strncpy, 6, 148 vector, 39, 81, 90, 95, 202, 208, 209
strstr, 188 begin(), 39, 81, 90, 95
strtok, 129, 130, 185, 186, 188 clear(), 95
struct, 6, 31, 90, 92, 95, 106, 152, 156, 175, end(), 39, 81, 95
185, 275 erase, 90
structur , 3 iterator, 39
structur  de date, 283
push_back, 39, 81, 90, 95
reverse, 81
tehnica Greedy, 275
tip boolean, 275 size(), 39, 90, 95

tip record, 275 sort, 95

trage cu tunul, iv vector caracteristic, 262, 275

trim, 186 vector de frecvenµ , 158


trucul lui Mars, 154 vector de prezenµ , 227
Bibliograe
[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

[6] Calude C.; Teoria algoritmilor, Ed. Universit µii Bucure³ti, 1987

[7] Cerchez, E., “erban, M.; Informatic  - manual pentru clasa a X-a., Ed. Polirom, 2000

[8] Cerchez, E.; Informatic  - Culegere de probleme pentru liceu, Ed. Polirom, 2002

[9] Cerchez, E., “erban, M.; Programarea în limbajul C/C++ pentru liceu, Ed. Polirom, 2005

[10] Cori, R.; Lévy, J.J.; Algorithmes et Programmation, Polycopié, version 1.6;
http://w3.edu.polytechnique.fr/informatique/
[11] Cormen, T.H., Leiserson C.E., Rivest, R.L.; Introducere în Algoritmi, Ed Agora, 2000

[12] Cormen, T.H., Leiserson C.E., Rivest, R.L.; Pseudo-Code Language, 1994

[13] Cristea, V.; Giumale, C.; Kalisz, E.; Paunoiu, Al.; Limbajul C standard, Ed. Teora, Bucure³ti,
1992

[14] Erickson J.; Combinatorial Algorithms; http://www.uiuc.edu/~jeffe/

[15] Flanagan, D.; Java in a Nutshell, O'Reilly, 1997.

[16] Giumale C., Negreanu L., C linoiu S.; Proiectarea ³i analiza algoritmilor. Algoritmi de sortare,
Ed. All, 1997

[17] Halim S., Halim F., Competitive programming, 2013

[18] Knuth, D.E.; Arta program rii calculatoarelor, vol. 1: Algoritmi fundamentali, Ed. Teora,
1999.

[19] Knuth, D.E.; Arta programarii calculatoarelor, vol. 2: Algoritmi seminumerici, Ed. Teora,
2000.

[20] Knuth, D.E.; Arta programarii calculatoarelor, vol. 3: Sortare ³i c utare, Ed. Teora, 2001.

[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.

294
BIBLIOGRAFIE 295

[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

[31] R bâea, A.;


https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire/
2007/Info/Rezolvari_C10.pdf
[32] R bâea, A.;
https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire/
2007/Info/Rezolvari_C11.pdf

[33] R bâea, A.;


https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire/
2007/Info/Rezolvari_Baraj.pdf
[34] Skiena S.S., Revilla M.A.; Programming challenges - The Programming Contest Training
Manual, Springer, 2003

[35] Tomescu, I.; Probleme de combinatoric  ³i teoria grafurilor, Editura Didactic  ³i Pedagogic ,
Bucure³ti, 1981

[36] Tomescu, I.; Leu, A.; Matematic  aplicat  în tehnica de calcul, Editura Didactic  ³i Pedago-
gic , Bucure³ti, 1982

[37] Tudor, S.; Informatic  - prolul real intensiv, varianta C++; Editura L&S, Bucure³ti, 2004

[38] Tudor, S.; Hutanu, V,; Informatic  intensiv; Editura L&S, Bucure³ti, 2006

[39] V duva, C.M.; Programarea in JAVA. Microinformatica, 1999

[40] Vi³inescu, R.; Vi³inescu, V.; Programare dinamic  - teorie ³i aplicaµii; GInfo nr. 15/4 2005

[41] Vlada, M.; Conceptul de algoritm - abordare modern , GInfo, 13/2,3 2003

[42] Vlada, M.; Grafuri neorientate ³i aplicaµii. Gazeta de Informatic , 1993

[43] Vlada, M.; Gândirea Algoritmic  - O Filosoe Modern  a Matematicii ³i Informaticii, CNIV-
2003, Editura Universit µii din Bucure³ti, 2003

[44] Weis, M.A.; Data structures and Algorithm Analysis, Ed. The Benjamin/Cummings Pu-
blishing Company. Inc., Redwoods City, California, 1995.

[45] Winston, P.H., Narasimhan, S.; On to JAVA, Addison-Wesley, 1996

[46] Wirth N.; Algorithms + Data Structures = Programs, Prentice Hall, Inc 1976

[47] *** - Gazeta de Informatic , Editura Libris, 1991-2005

[48] *** -
https://github.com/DinuCr/CS/blob/master/Info/stuff%20stuff/Re
zolvari_C09.pdf
[49] *** - https://dokumen.tips/documents/rezolvaric09.html

[50] *** - https://www.scribd.com/doc/266218102/Rezolvari-C09


[51] *** - https://www.scribd.com/document/396362669/Rezolvari-C10
[52] *** - https://www.scribd.com/document/344769195/Rezolvari-C11
[53] *** - https://www.scribd.com/document/364077679/Rezolvari-C11-pdf

[54] *** - https://needoc.net/rezolvari-c11-pdf


BIBLIOGRAFIE 296

[55] *** - https://vdocumente.com/algoritmi-i-structuri-de-date.html


[56] *** -
https://pdfslide.net/documents/algoritmi-si-structuri-de-date-
1-note-de-cuprins-1-oji-2002-clasa-a-ix-a-1-11.html
[57] *** - https://www.infoarena.ro/ciorna

[58] *** - https://infoarena.ro/olimpici


[59] *** - https://www.infogim.ro/
[60] *** - https://www.pbinfo.ro/

[61] *** - http://www.cplusplus.com/


[62] *** - http://www.cplusplus.com/doc/tutorial/operators/
[63] *** - http://www.info1cup.com/
[64] *** - http://www.olimpiada.info/

[65] *** - http://www.usaco.org/


[66] *** - http://algopedia.ro/
[67] *** - http://campion.edu.ro/
[68] *** - http://varena.ro/
[69] *** - http://rmi.lbi.ro/rmi_2019/
[70] *** - https://codeforces.com/
[71] *** - https://cpbook.net/
[72] *** - https://csacademy.com/
[73] *** - https://gazeta.info.ro/revigoram-ginfo/
[74] *** - https://oj.uz/problems/source/22
[75] *** - https://profs.info.uaic.ro/~infogim/2019/index.html
[76] *** - https://wandbox.org/
[77] *** - https://en.cppreference.com/w/cpp/language/operator_alternative
[78] *** - https://en.cppreference.com/w/cpp/algorithm

[79] *** - https://www.ejoi2019.si/


[80] *** - https://en.wikipedia.org/wiki/Algorithm
[81] *** - https://en.wikipedia.org/wiki/List_of_algorithms
[82] *** - https://en.wikipedia.org/wiki/List_of_data_structures
Lista autorilor
problemelor ³i indicaµiilor

Adrian Panaete, 108, 273, 275 Genoiu Nicolae, 109


Alin Burµa, 9, 37
Alina Pintescu, 147 Ilie Vieru, 109
Ana Intuneric, 87

Liliana Chira, 164


Carmen Minc , 33, 169
Livia •oca, 195
Carmen Popescu, 219
Ciprian Che³c , 283
Marinel “erban, 15, 108, 275
Cristina Sichim, 172
Marius Nicoli, 17, 153, 200, 243, 286
Dan Octavian Dumitra³cu, 160 Miron Lucia, 275
Dan Pracsiu, 52, 73, 80
Dana Lica, 27, 102, 162, 262 Nistor Moµ, 24, 149
Daniel Popa, 59
Daniela Taras , 251 Raluca Costineanu, 158, 190
Rodica Pintea, 227
Emanuela Cerchez, 3, 48, 67, 96, 181, 230,
277
Sandor Lukacs, 41
Filonela Rodica B la³a, 177 Szabo Zoltan, 280

297

S-ar putea să vă placă și