Sunteți pe pagina 1din 381

Informatică

Olimpiada - cls8

2022-12
PROBLEME DE INFORMATICĂ

date la olimpiade

OJI + ONI
ı̂n

**** **** 2022 2021 2020


2019 2018 2017 2016 2015
2014 2013 2012 2011 2010

... draft (ciornă) ...


*** Nobody is perfect ***

Adrian Răbâea, Ph.D.

*** https://en.wikipedia.org/wiki/Saint_Stephen#Eastern_Christianity ***

2022-12-27
Dedication

I would like to dedicate this book ...

a ”to myself” ...


1
2
in a time when ...
3
I will not be able ... ”to be”
That is because ...
4
”When I Die Nobody Will Remember Me”

a to people impacted by the book


a to my nephew Adam.

( in ascending order! )

1
https://www.femalefirst.co.uk/books/carol-lynne-fighter-1034048.html
2
https://otiliaromea.bandcamp.com/track/dor-de-el
3
https://en.wikipedia.org/wiki/To_be,_or_not_to_be
4
https://www.youtube.com/watch?v=eMtcDkSh7fU

ii
Prefaţă

Stilul acestor cărţi este ... ca şi cum aş vorbi cu nepoţii mei (şi chiar cu mine ı̂nsumi!) ... ı̂ncercând
ı̂mpreună să găsim o rezolvare cât mai bună pentru o problemă dată la olimpiadă.
Ideea, de a scrie aceste culegeri de probleme date la olimpiadele de informatică, a apărut acum
câţiva ani când am ı̂ntrebat un student (care nu reuşea să rezolve nişte probleme foarte simple):
”Ce te faci dacă un elev, care ştie că eşti student şi că studiezi şi informatică, te roagă, din când ı̂n
când, să-l ajuţi să rezolve câte o problemă de informatică dată la gimnaziu la olimpiadă, sau pur
şi simplu ca temă de casă, şi tu, aproape de fiecare dată, nu ı̂l poţi ajuta? Eu cred că nu este prea
bine şi poate că ... te faci ... de râs!” Pe vremea mea (!), când eram elev de gimnaziu, un student
era ca un fel de ... zeu! Cu trecerea anilor am ı̂nţeles că nu este chiar aşa! Şi ı̂ncă ceva: nu am
reuşit să ı̂nţeleg de ce, atunci când cineva nu poate să rezolve corect o problemă de informatică
de clasa a 6-a, dată la olimpiada de informatică sau ca temă de casă, foloseşte această scuză: ”eu
nu am făcut informatică ı̂n liceu!” şi acest cineva este ”zeul” sau ”zeiţa” despre care vorbeam!.
5
Sunt convins că este important să studiem cu atenţie cât mai multe probleme rezolvate! . Cred
că sunt utile şi primele versiuni ale acestor cărţi ... ı̂n care sunt prezentate numai enunţurile şi
indicaţiile ”oficiale” de rezolvare. Acestea se găsesc ı̂n multe locuri; aici ı̂ncerc să le pun pe ”toate
la un loc”! Fiecare urcă spre vârf ... cât poate! Sunt multe poteci care duc spre vârf iar această
carte este ... una dintre ele!
Limbajul de programare se alege ı̂n funcţie de problema pe care o avem de rezolvat. Cu nişte
ani ı̂n urmă alegerea era mai simplă: dacă era o problemă de calcul se alegea Fortran iar dacă era
o problemă de prelucrarea masivă a datelor atunci se alegea Cobol. Acum alegerea este ceva mai
6 7
dificilă! :-) Vezi, de exemplu, IOI2020 şi IOI2019 , IOI2015 .
Cred că, de cele mai multe ori, este foarte greu să gândim ”simplu” şi să nu ”ne complicăm”
atunci când cautăm o rezolvare pentru o problemă dată la olimpiadă. Acesta este motivul pentru
care vom analiza cu foarte mare atenţie atât exemplele date ı̂n enunţurile problemelor cât şi
”restricţiile” care apar acolo (ele sigur ”ascund” ceva interesant din punct de vedere al algoritmului
8
de rezolvare!) .
Am ı̂nceput câteva cărţi (pentru clasele de liceu) cu mai mulţi ani ı̂n urmă, pentru perioada
2000-2007 ([29] - [33]), cu anii ı̂n ordine crescătoare!). A urmat o pauză de câţiva ani (destul de
mulţi!). Am observat că acele cursuri s-au ”ı̂mprăştiat” un pic ”pe net” ([48] - [56])! Încerc acum
să ajung acolo unde am rămas ... plecând mereu din prezent ... până când nu va mai fi posibil ...
aşa că, de această dată, anii sunt ı̂n ordine ... descrescătoare! :-)
”Codurile sursă” sunt cele ”oficiale” (publicate pe site-urile olimpiadelor) sau publicate pe alte
site-uri (dacă mi s-a părut că sunt utile şi se poate ı̂nvăţa câte ceva din ele).
Pentru liceu perioada acoperită este de ”azi” (până când va exista acest ”azi” pentru mine!)
până ı̂n anul 2000 (aveam deja perioada 2000-2007!).
Pentru gimnaziu perioada acoperită este de ”azi” până ı̂n anul 2010 (nu am prea mult timp
9
disponibil şi, oricum, calculatoarele folosite la olimpiade ı̂nainte de 2010 erau ceva mai ’slabe’ şi

... restricţiile de memorie, din enunţurile problemelor, par ’ciudate’ acum!).


În perioada 2017-2020 cele mai puternice calculatoare din lume au fost: ı̂n noiembrie 2017
10
ı̂n China, ı̂n noiembrie 2019 ı̂n SUA şi ı̂n iunie 2020 ı̂n Japonia. În iunie 2022 ”Frontier”
5
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’ ...
6
IOI2019 şi IOI2020 au a permis utilizarea limbajelor de programare C++ şi Java
7
IOI2015 a permis utilizarea limbajelor de programare C++, Java, Pascal, Python şi Rubi (...)
8
Vezi cele 5 secunde pentru Timp maxim de executare/test din problema ”avârcolaci” - ONI2014 clasa a 11-a
9
https://en.wikipedia.org/wiki/Computer
10
https://www.top500.org/lists/top500/2022/06/

iii
11
depăşeşte pragul ”exascale”! (1.102 Exaflop/s). ”Peta” a fost depăşit ı̂n 2008, ”tera” ı̂n 1997,
12
”giga” ı̂n 1972. . Pentru ce a fost mai ı̂nainte, vezi https://en.wikipedia.org/wiki/Li
st_of_fastest_computers.
13
O mică observaţie: ı̂n 2017 a fost prima ediţie a olimpiadei EJOI ı̂n Bulgaria şi ... tot
14
ı̂n Bulgaria a fost şi prima ediţie a olimpiadei IOI ı̂n 1989. Dar ... prima ediţie a olimpiadei
15
IMO (International Mathematical Olympiad) a fost ı̂n România ı̂n 1959. Tot ı̂n România s-au
ţinut ediţiile din anii 1960, 1969, 1978, 1999 şi 2018. Prima ediţie a olimpiadei BOI (Balkan
Olympiad in Informatics) a fost ı̂n România ı̂n 1993 la Constanţa. Prima ediţie a olimpiadei
CEOI (Central-European Olympiad in Informatics) a fost ı̂n România ı̂n 1994 la Cluj-Napoca.
Revenind la ... “culegerile noastre” ... mai departe, probabil, va urma completarea unor
informaţii ı̂n ”Rezolvări detaliate” ... pentru unele probleme numai (tot din cauza lipsei timpului
necesar pentru toate!). Prioritate vor avea problemele de gimnaziu (nu pentru că sunt mai ’uşoare’
ci pentru că ... elevii de liceu se descurcă şi singuri!). Acum, ı̂n martie 2022, am ı̂nceput şi
redactarea unei culegeri de probleme date la bacalaureat ı̂n ultimii câţiva ani (câţi voi putea!).
Îmi aduc aminte că exista o interesantă vorbă de duh printre programatorii din generaţia mea:
”nu se trage cu tunul ı̂ntr-o muscă” . Sensul este: nu se scrie un cod complicat dacă se
poate scrie un cod simplu şi clar! Asta ı̂ncerc eu ı̂n ”Rezolvări detaliate”.
Vom ı̂ncerca, ı̂mpreună, şi câteva probleme de ... IOI ... dar asta este o treabă ... nu prea
uşoară! Cred totuşi că este mai bine să prezint numai enunţuri ale problemelor date la IOI ı̂n
ultimii câţiva ani! (asta aşa, ca să vedem cum sunt problemele la acest nivel!). Cei care ajung
acolo sau vor să ajungă acolo (la IOI) sigur nu au nevoie de ajutorul meu! Se descurcă singuri!
”ALGORITMI utili la olimpiadele de informatică”, separat pentru gimnaziu şi liceu, sper să
fie de folos, aşa cum cred că sunt [1] - [28], [34] - [47], [57] - [83], ... şi multe alte cărţi şi site-uri!.
Ar fi interesant să descoperim noi ı̂nşine cât mai mulţi algoritmi ... ı̂n loc să-i ı̂nvăţăm pur şi
simplu!
O altă mică observaţie: ce am strâns şi am scris ı̂n aceste cărţi
se adresează celor interesaţi de aceste teme! Nu cârcotaşilor! Sunt
evidente sursele ”de pe net” (şi locurile ı̂n care au fost folosite) aşa
că nu sunt necesare ”ghilimele anti-plagiat”, referinţe şi precizări
suplimentare la fiecare pas!
Şi un ultim gând: criticile şi sfaturile sunt utile dacă au valoare!
Dacă sunt numai aşa ... cum critică lumea la un meci de fotbal ...
sau cum, pe bancă ı̂n parc, ”ı̂şi dă cu părerea” despre rezolvarea
problemelor economice ale ţării ... atunci ... !!!
16
”I’m only responsible for what I say, not for what you understand.”
Adrese interesante (rezultatele elevilor români):
https://stats.ioinformatics.org/halloffame/
https://stats.ioinformatics.org/tasks/
http://stats.ioinformatics.org/results/ROU
Adresele acestor cursuri:
https://www.scribd.com/user/550183580/Adrian-Răbâea
https://www.scribd.com/user/552245048/Adi-Rabaea
https://drive.google.com/drive/folders/1hC5PZuslCdS95sl37SW46H-qy59GRDGZ
Adrese utile (programe şcolare):
http://programe.ise.ro/Portals/1/Curriculum/Progr_Lic/TH/Informatica_teoretic_vocatio
nal_intensiv_clasa%20a%20IX-a.pdf
http://programe.ise.ro/Portals/1/Curriculum/Progr_Lic/TH/Informatica_teoretic_vocatio
nal_intensiv_clasa%20a%20X_a.pdf
http://programe.ise.ro/Portals/1/Curriculum/Progr_Lic/TH/Informatica_teoretic_vocatio
nal_intensiv_clasa%20a%20XI-a.pdf

Bistriţa, 27th December 2022 Adrian Răbâea


11
https://en.wikipedia.org/wiki/Metric_prefix/
12
https://en.wikipedia.org/wiki/Computer_performance_by_orders_of_magnitude
13
https://ejoi.org/about/
14
https://stats.ioinformatics.org/olympiads/
15
https://en.wikipedia.org/wiki/International_Mathematical_Olympiad
16
https://www.facebook.com/johnwayne/photos/a.156450431041410/2645523435467418/?type=3
”Acknowledgements”

17
”I want to thank God most of all because without God I wouldn’t be able to do any of this.”

Bistriţa, 27th December 2022

Adrian R.

17
I.d.k.: ”I don’t know who the author is.”

v
Despre autor

18
nume: Răbâea Aurel-Adrian, 18.03.1953 - ...

telefon: +40 728 zz ll aa +40 363 xx 25 xx


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)
https://stiinte.utcluj.ro/departamente.html
Discipline predate (1992-2018):
Algoritmi şi structuri de date, Algoritmi ı̂n teoria opţiunilor financiare, Bazele matematice
ale calculatoarelor, Bazele tehnologiei informaţiei, Birotică, Capitole speciale de inteligenţă
artificială, 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):
Instituţia: Academia de Studii Economice, Bucureşti;
19
Titlul tezei: Algoritmi paraleli şi aplicaţii pe maşini virtual paralele
20
Conducător ştiinţific: Prof. dr. ing. Gheorghe Dodescu
Teme studiate: utilizarea algoritmilor paraleli ı̂n teoria opţiunilor financiare
Studii de specializare ı̂n informatică - Certificat 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
21
Conducător ştiinţific: 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ă
22
Conducător ştiinţific: 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 Infor-
23
matică, Departamentul de Informatică
24
- (1992-1979) Centrul de Informatică şi organizare CINOR, Bucureşti
25
Olimpiade: (fiind elev la Liceul Militar ”Dimitrie Cantemir” - Breaza, PH)
- 1971: Olimpiada Naţională de matematică: participare (fără rezultat notabil)
- 1970: Olimpiada Naţională de matematică: participare (fără rezultat notabil)
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
18
https://dmi.cunbm.utcluj.ro/?page_id=2
19
http://opac.biblioteca.ase.ro/opac/bibliographic_view/149021
20
http://www.ionivan.ro/2015-PERSONALITATI/Dodescu.htm
21
http://old.fmi.unibuc.ro/ro/prezentare/promotii/promotia1978informatica_10ani/
22
https://ro.wikipedia.org/wiki/Ion_V%C4%83duva
23
https://fmi.univ-ovidius.ro/
24
http://c3.cniv.ro/?q=2021/cinor/
25
https://www.cantemircml.ro/

vi
Cuprins

Prefaţă iii

Cuprins vii

Lista figurilor xiii

Lista tabelelor xv

Lista programelor xvi

I OJI - Olimpiada judeţeană de informatică 1


1 OJI 2022 2
1.1 pelican . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 strips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2 OJI 2021 - OSEPI 9


2.1 Cartofi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2 Tunel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3 OJI 2020 17
3.1 datorii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.2 triunghi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

4 OJI 2019 29
4.1 cate3cifre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.2 paralele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

vii
4.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

5 OJI 2018 38
5.1 Cruce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.2 pal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
5.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

6 OJI 2017 47
6.1 tablou . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
6.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
6.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
6.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.2 triunghiuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
6.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

7 OJI 2016 55
7.1 arma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
7.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
7.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
7.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
7.2 ks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
7.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
7.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
7.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

8 OJI 2015 66
8.1 dominant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
8.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
8.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
8.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
8.2 pavare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
8.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
8.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
8.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

9 OJI 2014 81
9.1 arrows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
9.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
9.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
9.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
9.2 tcif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
9.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
9.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
9.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

10 OJI 2013 95
10.1 maxp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
10.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
10.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
10.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
10.2 puncte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
10.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
10.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
10.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

11 OJI 2012 112


11.1 deal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
11.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
11.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
11.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
11.2 ozn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
11.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
11.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
11.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122

12 OJI 2011 123


12.1 adunscad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
12.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
12.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
12.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
12.2 comp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
12.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
12.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
12.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148

13 OJI 2010 149


13.1 cladiri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
13.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
13.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
13.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
13.2 secvente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
13.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
13.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
13.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

II ONI - Olimpiada naţională de informatică 158


14 ONI 2022 159
14.1 proeminenta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
14.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
14.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
14.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
14.2 rgb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
14.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
14.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
14.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
14.3 subsir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
14.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
14.3.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
14.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169

15 ONI 2021 170


15.1 Bile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
15.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
15.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
15.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
15.2 Secvente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
15.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
15.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
15.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
15.3 Valoare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
15.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
15.3.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
15.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178

16 ONI 2020 - suspendat !!! 179

17 ONI 2019 180


17.1 criptografie - ONI 2019 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
17.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
17.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
17.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
17.2 drept - ONI 2019 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
17.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
17.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
17.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
17.3 lumini - ONI 2019 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
17.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
17.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
17.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190

18 ONI 2018 191


18.1 gene . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
18.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
18.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
18.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
18.2 jocxzero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
18.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
18.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
18.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
18.3 laser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
18.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
18.3.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
18.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196

19 ONI 2017 197


19.1 boats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
19.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
19.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
19.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
19.2 doilan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
19.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
19.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
19.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
19.3 z . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
19.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
19.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
19.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209

20 ONI 2016 210


20.1 cercetasi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
20.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
20.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
20.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
20.2 farma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
20.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
20.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
20.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
20.3 stele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
20.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
20.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
20.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
21 ONI 2015 228
21.1 magic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
21.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
21.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
21.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
21.2 restaurare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
21.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
21.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
21.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
21.3 sort2dist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
21.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
21.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
21.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251

22 ONI 2014 252


22.1 cifre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
22.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
22.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
22.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
22.2 solitar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
22.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
22.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
22.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
22.3 tdrept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
22.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
22.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
22.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270

23 ONI 2013 271


23.1 amestec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
23.1.1 *Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
23.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
23.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
23.2 eoliene . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
23.2.1 *Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
23.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
23.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
23.3 sudoku1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
23.3.1 *Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
23.3.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
23.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275

24 ONI 2012 276


24.1 alune . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
24.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
24.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
24.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
24.2 cuburi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
24.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
24.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
24.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
24.3 optim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
24.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
24.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
24.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
25 ONI 2011 306
25.1 butoane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
25.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
25.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
25.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
25.2 macheta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
25.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
25.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
25.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
25.3 sport . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
25.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
25.3.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
25.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312

26 ONI 2010 313


26.1 fractie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
26.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
26.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
26.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
26.2 neuroni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
26.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
26.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
26.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
26.3 raze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
26.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
26.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
26.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322

Appendix A ”Instalare” C++ 324


A.1 Kit OJI 2017 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
A.1.1 Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
A.1.2 Folder de lucru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
A.1.3 Utilizare Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
A.1.4 Setări Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
A.1.5 Multe surse ı̂n Code Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
A.2 winlibs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
A.2.1 GCC şi MinGW-w64 pentru Windows . . . . . . . . . . . . . . . . . . . . . 334
A.2.2 PATH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
A.2.3 CodeBlocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337

Appendix B Exponenţiere rapidă 347


B.1 Analogie baza 2 cu baza 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
B.2 Notaţii, relaţii şi formule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
B.3 Pregătire pentru scrierea codului! . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
B.4 Codul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
B.5 Chiar este rapidă? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
B.6 Rezumat intuitiv! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353

Index 355

Bibliografie 357

Lista autorilor 360


Lista figurilor

6.1 triunghiuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

9.1 arrows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

10.1 Puncte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103


10.2 Puncte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103

11.1 Puncte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

13.1 cladiri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

17.1 Drept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183

A.1 Fişierele din Kit OJI 2017 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324


A.2 CodeBlocks & C++ Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
A.3 Ce conţine C:¯ OJI ¯ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
A.4 Folder de lucru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
A.5 New -¿ Text document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
A.6 Schimbare nume fişier şi nume extensie fişier . . . . . . . . . . . . . . . . . . . . . . 327
A.7 Confirmare schimbare extensie ı̂n .cpp . . . . . . . . . . . . . . . . . . . . . . . . . 328
A.8 Pregătit pentru Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
A.9 Pregătit pentru a scrie cod de program C++ ı̂n Code::Blocks . . . . . . . . . . . . 328
A.10 Primul cod de program C++ ı̂n Code::Blocks . . . . . . . . . . . . . . . . . . . . . 329
A.11 Build - compilare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
A.12 0 error(s), 0 warning(s) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
A.13 Run - execuţie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
A.14 Executat corect: a făcut “nimic” . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
A.15 Settings  % Compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
A.16 Toolchain executables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
A.17 Unde sunt acele programe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
A.18 Multe surse ı̂n Code Blocks - setări . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
A.19 Multe surse in Code Blocks - exemple . . . . . . . . . . . . . . . . . . . . . . . . . 333
A.20 mingw64 pe D: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
A.21 search path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
A.22 System properties –¿ Advanced . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
A.23 Environment Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
A.24 Edit Environment Variables –¿ New . . . . . . . . . . . . . . . . . . . . . . . . . . 336
A.25 Calea şi versiunea pentru gcc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
A.26 Settings –¿ Compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
A.27 Toolchain executables –¿ Auto-detect . . . . . . . . . . . . . . . . . . . . . . . . . . 338
A.28 New –¿ Text Document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
A.29 New text Document.txt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
A.30 Schimbare nume şi extensie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
A.31 Moore apps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
A.32 Look for another app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
A.33 Cale pentru codeblocks.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
A.34 Selectare codeblocks.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
A.35 Editare test01.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
A.36 Compilare test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
A.37 Mesaje după compilare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343

xiii
A.38 Execuţie test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
A.39 Rezultat execuţie test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
A.40 Fişiere apărute după compilare! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
A.41 Creare test02.cpp gol! + ¡dublu click¿ sau ¡Enter¿ . . . . . . . . . . . . . . . . . . 344
A.42 Lista programelor de utilizat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
A.43 Selectare Code::Blocks IDE pentru fişierele .cpp . . . . . . . . . . . . . . . . . . 345
A.44 Editare+Compilare+Execuţie pentru test02 . . . . . . . . . . . . . . . . . . . . . 345
A.45 Selectare tab ce conţine test01.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 346

B.1 Analogie B2 cu B10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347


Lista tabelelor

4.1 cate3cifre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

5.2 pal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

21.1 sort2dist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240

xv
Lista programelor

3.1.1 datorii ab.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19


3.1.2 datorii ec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.2.1 triunghi ab.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.2.2 triunghi ec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.1.1 cate3cifre.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.2.1 paralele1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.2.2 paralele2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.2.3 paralele3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
5.1.1 cruce dl.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.1.2 cruce RC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.1.3 cruceEm.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.2.1 pal dl.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
5.2.2 pal RC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6.1.1 tabCS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
6.1.2 tablou CM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
6.2.1 tri On.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
6.2.2 triCS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
7.1.1 arma.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
7.1.2 arma ema.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
7.1.3 arma ema no rec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
7.2.1 ks dan.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
7.2.2 ks ema n.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
8.1.1 domDP citire elem cu elem.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
8.1.2 domDPliniar.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
8.1.3 domMN n.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
8.1.4 domMN n ccc.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
8.1.5 domMN nlogn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
8.2.1 pavareAB.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
8.2.2 pavareBackMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
8.2.3 pavareCT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
8.2.4 pavaredan.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
8.2.5 pavareDP.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
8.2.6 pavareMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
9.1.1 arrows 100 ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
9.1.2 arrows 100 sofia.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
9.2.1 tcif 100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
9.2.2 tcif ema100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
9.2.3 Tcif GCC 100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
10.1.1maxpCS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
10.1.2maxpDP cstdio.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
10.1.3maxpDP fstream.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
10.1.4maxpPRI.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
10.1.5maxpPRV.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
10.1.6maxpSC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
10.2.1puncteAI.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
10.2.2puncteCS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
10.2.3puncteDP.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
10.2.4punctePRI.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
10.2.5puncteSC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

xvi
11.1.1deal 100 ch.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
11.1.2deal 100 ct.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
11.1.3DEAL 100 em.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
11.2.1ozn stream100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
11.2.2ozn100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
11.2.3oznarbint100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
11.2.4oznEvenimente100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
12.1.1 ADUNSCAD.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
12.1.2 adrian1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
12.1.3 adrian2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
12.1.4 adrian3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
12.1.5 adrian4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
12.1.6 ADDSUBBK.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
12.1.7 ADUNSCADM.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
12.1.8 ADSC nodea.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
12.1.9 ADUNSC A.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
12.1.10 dana ad.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
12.1.11 vi adunscad.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
12.2.1 comp.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
12.2.2 alin.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
12.2.3 comp r.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
12.2.4 em.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
12.2.5 nodea.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
12.2.6 p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
12.2.7 vi comp.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
13.1.1 sol2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
13.1.2 sol3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
13.2.1 BERATO.PAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
13.2.2 BPREPRO.PAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
14.1.1proeminenta.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
17.1.1 criptografie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
17.2.1 drept.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
17.3.1 lumini.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
19.1.1 boatsAB.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
19.1.2 boatsCM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
19.1.3 boatsLC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
19.2.1 doilan.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
19.3.1 z.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
20.1.1 cercetasi ema.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
20.1.2 cercetasi2 RBalasa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
20.2.1 farma ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
20.2.2 farma raluca.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
20.2.3 lucia farma.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
20.3.1 stele ema 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
20.3.2 stele raluca.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
21.1.1 magicN2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
21.1.2 sursaGenerarePas1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
21.1.3 sursaGenerarePasC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
21.1.4 sursaNumarareBKT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
21.1.5 sursaPutereLogN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
21.1.6 sursaPutereN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
21.2.1 restaurare.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
21.2.2 restaurare stdio.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
21.2.3 restaurareBrut1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
21.2.4 restaurareLT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
21.3.1 2dist alin.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
21.3.2 sort2 distBrute1FNM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
21.3.3 sort2 distBSFBM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
21.3.4 sort2dist.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
21.3.5 sort2dist1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
21.3.6 sort2distBS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
21.3.7 sort2distFNM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
21.3.8 sort2distFNMDouble.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
21.3.9 sort2distFNMLongDouble.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
21.3.10 sort2gv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
22.1.1 cifre carmen ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
22.1.2 cifre ema.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
22.1.3 cifre GCC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
22.1.4 cifre sofia.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
22.2.1 solitar cp.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
22.2.2 solitar ema.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
22.3.1 tdrept ema n2logn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
22.3.2 tdrept ema ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
22.3.3 tdrept eman3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
22.3.4 tdrept emanlogn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
22.3.5 tdrept n sofia.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
22.3.6 tdrept n2logn sofia.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
24.1.1 alune.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
24.1.2 alune DT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
24.1.3 aluneBrute.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
24.1.4 aluneBrute1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
24.1.5 aluneBrute2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
24.1.6 aluneBruteParse.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
24.1.7 alunedanal.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
24.1.8 aluneS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
24.2.1 cubmin.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
24.2.2 cubminC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
24.2.3 cuburiBackMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
24.2.4 cuburiDL.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
24.2.5 cuburiDL20.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
24.2.6 cuburi-iv4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
24.2.7 cuburiStackMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
24.3.1 optim back100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
24.3.2 optim dinamica100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
24.3.3 optim0MN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
24.3.4 optim1MN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
24.3.5 optim2MN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
24.3.6 optim3MN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
24.3.7 optimDT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
24.3.8 optim-iv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
26.1.1 FLORE7.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
26.2.1 neuroni1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
26.2.2 neuroni2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
26.3.1 RAZE2.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
26.3.2 RAZE3.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
B.4.1exponentiere rapida1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
B.4.2exponentiere rapida2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
B.4.3exponentiere rapida3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
B.4.4exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
B.5.1exponentiere naiva MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
B.5.2exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
B.6.1secventa cod.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
Part I

OJI - Olimpiada judeţeană de


informatică

1
Capitolul 1

OJI 2022

1.1 pelican
Problema 1 - Pelican 100 de puncte
Într-o minunată zi de primăvară, P răţuşte au ieşit la plimbare pe lac. Un pelican milităros,
care stătea pe mal, a decis să facă instrucţie cu nevinovatele raţe. Pentru aceasta, a cartografiat
imediat lacul şi l-a reprezentat ca o matrice cu N linii (numerotate de la 0 la N  1 de sus ı̂n jos)
şi N coloane (numerotate de la 0 la N  1 de la stânga la dreapta).
Astfel, poziţia oricărei raţe pe lac poate fi identificată prin
linia şi coloana pe care se află raţa. Raţele sunt orientate cu
faţa spre una dintre direcţiile Nord, Sud, Est, Vest. Pelicanul a
codificat direcţiile 1, 2, 3, 4 ca ı̂n figură.
Când pelicanul strigă: ”Comanda la mine!” raţele trebuie să
execute simultan cele K comenzi pe care le dă pelicanul. Comenzile pelicanului sunt codificate
astfel:

Comanda Efect Exemplu


A nr Raţa avansează cu nr poziţii ı̂n De exemplu, pe un lac 5  5, o raţă plasată ı̂n
direcţia spre care este orientată. poziţia (1, 3) cu orientare 1 (Nord), executând
Dacă avansând depăşeşte marginea comanda A 3 se va deplasa astfel:
tablei de joc va intra pe latura (1,3) (0, 3) (4, 3) (3, 3).
opusă.
o
R nr Raţa se roteşte cu nr 90 ı̂n sens De exemplu, dacă orientarea iniţială a raţei
orar, unde nr " r1, 2, 3, 4x. este 1 (Nord), comanda R 2 va schimba
orientarea spre 3 (Sud).
Z nr Raţa zboară pe linia nr©N De exemplu, pe un lac 5  5, după executarea
şi coloana nr%N , păstrând comenzii Z 7, raţa va ajunge pe linia 1 şi
orientarea. Se garantează că coloana 2.
nr©N " r0, 1, ..., N  1x.

Cerinţe

Scrieţi un program care, cunoscând poziţia iniţială pe lac a celor P raţe şi succesiunea comen-
zilor pelicanului, determină poziţia finală a fiecărei raţe.

Date de intrare

Fişierul de intrare pelican.in conţine pe prima linie trei numere naturale N P K, cu


semnificaţia din enunţ.
Pe următoarele P linii sunt date câte 3 numere naturale i j d (0 & i, j $ N şi 1 & d & 4) cu
semnificaţia că pe linia i şi coloana j se găseşte o raţă orientată ı̂n direcţia d.
Ultimele K linii conţin cele K comenzi, câte o comandă pe o linie, ı̂n formatul specificat ı̂n
enunţ (un caracter din mulţimea ’A’, ’R’, ’Z’ şi un număr natural). Valorile scrise pe aceeaşi linie
sunt separate de câte un spaţiu.

2
CAPITOLUL 1. OJI 2022 1.1. PELICAN 3

Date de ieşire

Fişierul de ieşire pelican.out va conţine P linii. Pe linia i va fi scrisă poziţia celei de a i-a
raţe din fişierul de intrare (linia şi coloana separate printr-un singur spaţiu) după executarea ı̂n
ordine a celor K comenzi.

Restricţii şi precizări

ˆ 1 & N & 1000


ˆ 1 & P & 10 000
ˆ 1 & K & 100 000
ˆ Mai multe raţe pot ocupa aceeaşi poziţie.
ˆ Se garantează că datele din fişierul de intrare sunt corecte.

# Punctaj Restricţii
1 76 Pentru teste valorând 76 de puncte fişierul de intrare nu conţine comanda Z.
2 20 Pentru teste valorând 20 de puncte N 100, P 100 şi K1000.
3 36 Pentru teste valorând 36 de puncte N % 100, 1000P 5000 şi K 50000.

Exemple:

pelican.in pelican.out
534 24
112 44
231 23
314
A3
R3
A1
A3

Explicaţii:

Lacul are 5 linii şi 5 coloane. Pe lac există 3 raţe poziţionate ca ı̂n figură.

Pelicanul dă 4 comenzi pe care toate cele 3 raţe le execută ı̂n ordine.
Raţele execută comanda A 3
CAPITOLUL 1. OJI 2022 1.1. PELICAN 4

o
Raţele execută comanda R 3 (se rotesc cu 270 ı̂n sens orar)

Raţele execută comanda A 1

Raţele execută comanda A 3

1.1.1 Indicaţii de rezolvare

Propusă de: prof. Nistor Moţ, şcoala Gimnazială ”Dr. Luca”, Brăila
Problema admite mai multe abordări. O posibilă abordare este de a citi succesiv comenzile
pelicanului şi de a executa fiecare comandă pentru fiecare dintre cele P raţe. Este evident o abor-
dare corectă, dar care va depăşi timpul de execuţie. Totuşi această abordare poate fi ı̂mbunătăţită
bazându-ne pe următoarele observaţii:

(1) Dacă ı̂n fişierul de ieşire există o comandă Z, atunci vom executa această comandă pentru
toate raţele. Dacă există mai multe comenzi Z, doar ultima contează, deci doar aceasta va fi
executată. Toate comenzile A până la comanda Z executată pot fi ignorate, doar comenzile
R contează. În plus, observăm că nu trebuie să executăm fiecare comandă R separat, putem
cumula numărul de grade şi putem executa o singură rotaţie finală pentru toate raţele.
(2) Ideea de cumulare poate fi utilizată mai departe. Pentru a reduce numărul de comenzi
executate de toate raţele, putem lucra pe secvenţe de comenzi A, respectiv secvenţe de
comenzi R. Pentru fiecare secvenţă de comenzi identice vom cumula nr şi la finalul secvenţei
executăm o deplasare/rotaţie cumulată pentru toate raţele. Desigur, pentru deplasări lucrăm
modulo N , iar pentru rotaţii lucrăm modulo 4.

Aceste observaţii pot conduce la un punctaj bun, dar pentru 100 de puncte trebuie observat că
două raţe care au iniţial aceeaşi orientare se vor deplasa ”solidar”, adică păstrează aceeaşi poziţie
relativă.
Dacă avem două raţe cu aceeaşi orientare ı̂n poziţiile iniţiale i1 , j1  şi i2 , j2 , comanda A nr
va duce ambele raţe ı̂n poziţii care păstrează aceeaşi distanţă ı̂ntre ele.
De exemplu, pentru orientarea Sud cele două raţe vor ajunge ı̂n poziţiile i1  nr, j1  şi respectiv
i2  nr, j2 . Această observaţie ne permite să efectuăm comenzile doar cu 4 raţe ”virtuale” ce
pornesc din poziţia 0, 0 având orientarile N, E, S, respectiv V.
CAPITOLUL 1. OJI 2022 1.2. STRIPS 5

La final, pentru fiecare raţă vom determina poziţia sa finală ı̂n funcţie de poziţia raţei virtuale
care avea iniţial aceeaşi orientare cu raţa respectivă. Dacă raţa virtuală ajunge ı̂n poziţia finală
x, y , atunci o raţă care iniţial avea aceeaşi orientare cu cea virtuală şi era plasată ı̂n poziţia
i1 , j1  va ajunge ı̂n poziţia finală i1  x, j1  y .
Evident, toate deplasările se fac modulo N . La fel, rotaţiile se execută modulo 4.
Mai mult, dacă dorim, putem renunţa la a calcula poziţia finală pentru toate cele 4 direcţii şi
să efectuăm mutările doar pentru o singură direcţie, celelalte putând fi deduse prin rotaţii cu câte
90 de grade. De exemplu, considerând că o raţă ce porneşte din poziţia 0, 0 având orientarea
iniţială N ajunge ı̂n poziţia finală x, y , atunci raţa din pozitia i1 , j1  ajunge:
ˆ dacă are orientarea iniţială N , ı̂n poziţia finală i1  x, j1  y 
ˆ dacă are orientarea iniţială S, ı̂n poziţia finală i1  x, j1  y 
ˆ dacă are orientarea iniţială E, ı̂n poziţia finală i1  y, j1  x
ˆ dacă are orientarea iniţială V , ı̂n poziţia finală i1  y, j1  x.

1.1.2 *Cod sursă

1.1.3 *Rezolvare detaliată

1.2 strips
Problema 2 - Strips 100 de puncte
Ana şi Bogdan au inventat un nou joc, pe care l-au denumit Strips. Este un joc de strategie,
dar şi de antrenare a memoriei, deoarece se joacă pe o tablă care nu este vizibilă pentru cei doi
jucători ı̂n timpul jocului.
Tabla de joc este o bandă albă de lungime N cm, pe care sunt marcate poziţii de lungime 1 cm.
Poziţiile sunt numerotate pe tablă de la 0 la N  1, poziţia 0 fiind marcată la ı̂nceputul tablei
(capătul din stânga), iar poziţia N  1 fiind marcată la sfârşitul tablei (capătul din dreapta).
La ı̂nceputul jocului fiecare jucător are N r benzi colorate, toate de aceeaşi lungime L cm.
Benzile Anei sunt de culoare roşie, iar benzile lui Bogdan sunt de culoare verde.
Jucătorii mută alternativ, prima la mutare fiind Ana. La o mutare, jucătorul care este la rând
alege o poziţie de pe tabla de joc şi dacă poziţia este validă, pe tabla de joc va fi plasată o bandă a
jucătorului respectiv, cu capătul din stânga ı̂n poziţia aleasă. Dacă poziţia nu este validă, mutarea
nu va fi executată, iar jucătorul respectiv va primi 1 punct de penalizare şi pierde banda care ar
fi trebuit plasată pe tablă la poziţia respectivă (aceasta este eliminată din joc).
O poziţie este considerată validă, dacă pe tabla de joc poate fi plasată o bandă de lungime
L cu capătul din stânga al benzii fixat la poziţia specificată, astfel ı̂ncât banda să fie integral
pe tabla de joc, fără a se suprapune sau a se atinge cu o zonă de pe bandă colorată ı̂n culoarea
adversarului. Jocul se termină când jucătorii nu mai au benzi.
Fiecare jucător are ca scop să obţină o zonă pe bandă de lungime cât mai mare colorată ı̂n
culoarea sa. O zonă de pe bandă este constituită din poziţii consecutive, colorate cu aceeaşi
culoare.

Cerinţe

Scrieţi un program care citeşte lungimea tablei de joc, numărul de benzi colorate pe care le are
fiecare jucător la ı̂nceputul jocului, lungimea benzilor, precum şi poziţiile specificate de jucători
pe parcursul jocului şi rezolvă următoarele două cerinţe:
1. determină numărul de puncte de penalizare pentru fiecare dintre cei doi jucători;
2. determină pentru fiecare jucător care este lungimea maximă a unei zone de pe tabla de joc
colorată ı̂n culoarea sa la sfârşitul jocului.

Date de intrare
CAPITOLUL 1. OJI 2022 1.2. STRIPS 6

# Punctaj Restricţii
1 50 Pentru teste valorând 50 de puncte cerinţa este 1.
2 40 Pentru teste valorând 40 de puncte 1 & N & 106 , 1 & L & 1 000 şi 1 & N r & 1 000.

Fişierul de intrare strips.in conţine pe prima linie un număr natural C care reprezintă cerinţa
care urmează a fi rezolvată (1 sau 2). Pe cea de-a doua linie se află trei numere naturale separate
prin câte un spaţiu N N r L, cu semnificaţia din enunt. Celelalte linii ale fişierului de intrare
conţin ı̂n ordine poziţiile specificate de jucători pe parcursul jocului, câte o poziţie pe o linie.

Date de ieşire

Fişierul de ieşire strips.out va conţine o singură linie pe care vor fi scrise două numere naturale
rezA rezB, separate printr-un singur spaţiu.
Dacă C 1 atunci rezA este numărul de puncte de penalizare acumulate de Ana, iar rezB
numărul de puncte de penalizare acumulate de Bogdan.
Dacă C 2 atunci rezA este lungimea maximă a unei zone de culoare roşie la sfârşitul jocului,
iar rezB este lungimea maximă a unei zone de culoare verde la sfârşitul jocului.

Restricţii şi precizări

1 & N & 10
9
ˆ

ˆ 1 & N r & 50 000


ˆ 1 & L & 20 000
ˆ Se garantează că pentru datele de test, la finalul jocului, pentru fiecare dintre cei doi jucători
numărul de zone disjuncte de pe tabla de joc colorate ı̂n culoarea jucătorului respectiv este
5 000.
ˆ Poziţiile sunt numere naturale mai mici decât N .
ˆ Fiindcă sunt ı̂ncepători, Ana şi Bogdan ı̂ncă nu joacă optim.

Exemple:

strips.in strips.out
1 01
20 4 3
9
15
2
13
5
17
0
12
2 87
20 4 3
9
15
2
13
5
17
0
12

Explicaţii:
CAPITOLUL 1. OJI 2022 1.2. STRIPS 7

Tabla de joc are 20 de cm, poziţiile fiind numerotate de la 0 la 19.


Jucătorii au fiecare câte 4 benzi de lungime 3.
La prima mutare Ana aplică o bandă roşie peste poziţiile 9, 10, 11.
La a doua mutare Bogdan aplică o bandă verde peste poziţiile 15, 16, 17.
La a treia mutare Ana aplică o bandă roşie peste poziţiile 2, 3, 4.
La a patra mutare Bogdan aplică o bandă verde peste poziţiile 13, 14, 15.
La a cincea mutare Ana aplică o bandă roşie peste poziţiile 5, 6, 7.
La a şasea mutare Bogdan aplică o bandă verde peste poziţiile 17, 18, 19.
La a şaptea mutare Ana aplică o bandă roşie peste poziţiile 0, 1, 2.
La a opta mutare Bogdan a ales o poziţie invalidă (pentru că atinge o zonă de culoare roşie),
ca urmare mutarea nu va fi executată (va avea 1 punct de penalizare).
Tabla de joc la finalul jocului va arăta astfel:

1.2.1 Indicaţii de rezolvare

Propusă de: prof. Emanuela Cerchez, Colegiul Naţional ”Emil Racoviţă” Iaşi
Soluţia 1 - 40 de puncte. Pentru N & 10 , putem utiliza un vector pentru a reprezenta
6

culorile benzilor plasate pe tabla de joc (0 pentru poziţie neocupată, 1 pentru o poziţie ocupată
de o bandă roşie, respectiv 2 pentru o poziţie ocupată de o bandă verde).
Citim succesiv mutările.
Pentru fiecare mutare verificăm dacă este validă. Dacă da, plasăm pe tabla de joc o bandă ı̂n
poziţia respectivă. Dacă nu, contorizăm un punct penalizare pentru jucătorul care este la rând.
Pentru cerinţa 2 este suficient să parcurgem tabla de joc şi să determinăm lungimea maximă
a unei secvenţe formată numai din 1, respectiv lungimea maximă a unei secvenţe formată numai
din 2.

Soluţia 2 - 100 de puncte. Pe măsură ce plasăm benzi pe tabla de joc formăm practic
zone continue de aceeaşi culoare, care nu se suprapun. În loc să reţinem tabla de joc, vom reţine
pentru fiecare jucător zonele obţinute din benzile plasate de acesta pe tabla de joc. O zonă va fi
reprezentată prin cele două extremităţi (poziţia de ı̂nceput şi poziţia de sfârşit a zonei).

struct zona { int inc , sf ; } ;

Considerăm că jucătorul 0 este Ana şi jucătorul 1 este Bogdan.


Structurile de date utilizate sunt:

zona Z[2][NRMAX] ; // zonele celor doi jucatori


int nrz[2] ; // numarul de zone pentru fiecare jucator
int p[2] ; // penalizarea fiecarui jucator
int lgmax[2] ; // lungimea maxima a unei zone pentru fiecare jucator

NRMAX este numărul maxim de zone ale unui jucător (se garantează că la finalul jocului
NRMAX nu depăşeşte 5 000, dar pe parcursul jocului poate fi mai mare).
Vom reţine zonele ı̂n ordinea crescătoare a poziţiilor de ı̂nceput.
Vom citi succesiv mutările celor doi jucători. La o mutare verificăm mai ı̂ntâi validitatea
acesteia. Să notăm poz poziţia corespunzătoare mutării curente şi cu cine jucătorul care este la
mutare. Evident, adversarul va fi 1  cine.
Pentru a verifica validitatea vom căuta pe tabla adversarului cea mai mică poziţie (unde)
pentru care poz & Z adversarunde.inc. Zonele fiind sortate, vom face o căutare binară.
Dacă o bandă plasată ı̂n poziţia poz se intersectează cu, sau se lipeşte de, cea de pe poziţia
unde (la dreapta) sau cu cea de pe poziţia unde  1 (la stânga) atunci mutarea nu este validă.
În cazul ı̂n care mutarea este validă, plasăm o bandă pe tabla jucătorului care este la mutare.
CAPITOLUL 1. OJI 2022 1.2. STRIPS 8

Pentru aceasta facem din nou o căutare binară, de data aceasta ı̂n zonele jucătorului care este
la mutare şi determinăm cea mai mică poziţie (unde) pentru care poz & Z cineunde.inc.
Pot apărea următoarele situaţii:

(1) banda poate fi alipită zonei Z cineunde (dacă această zonă există şi poz  L  1 '
Z cineunde.inc  1;
(2) banda poate fi alipită zonei Z cineunde  1 (dacă această zonă există şi poz &
Z cineunde  1.sf  1;
(3) ı̂n urma alipirii zonele Z cineunde  1 şi Z cineunde pot fi şi ele alipite, formând astfel
o singură zonă (le alipim şi eliminăm una dintre ele);
(4) dacă nicio alipire nu este posibilă, inserăm o nouă zonă cu ı̂nceputul poz şi sfârşitul poz  L  1
ı̂n poziţia unde (astfel zonele rămânând sortate).
Eficienţa timp a acestui algoritm este afectată de situaţiile 3 sau 4 (eliminarea, respectiv
inserarea fiind liniară).
Utilizarea unor structuri de date avansate (de exemplu, structura de date set din STL) poate
optimiza acest pas, dar o astfel de implementare depăşeşte nivelul clasei a VIII-a şi nu este necesară
pentru a obţine 100 de puncte.
Este posibilă o implementare bazată pe aceeaşi idee, asociind fiecărei zone culoarea acesteia
şi reţinând zonele sortate după extremitatea iniţială ı̂ntr-un singur vector. Dar ı̂n acest caz
dimensiunea vectorului este mai mare şi timpul necesar pentru eliminare/inserare creşte.

1.2.2 *Cod sursă

1.2.3 *Rezolvare detaliată


Capitolul 2

OJI 2021 - OSEPI

2.1 Cartofi
Problema 1 - Cartofi 100 de puncte
Fermierul Feder cultivă cartofi pe un teren dreptunghiular de lăţime N metri şi lungime M
metri, compartimentat ı̂n N M zone pătratice identice de lungime 1 metru, dispuse alăturat,
câte N pe lăţime (pe N linii, numerotate de la 1 la N ) şi câte M pe lungime (pe M coloane,
numerotate de la 1 la M ).
În fiecare zonă pătratică se află câte o plantă de cartofi. Parcurgând terenul de la prima
linie către ultima, fiecare linie cu număr impar parcurgând-o de la coloana 1 către coloana M ,
iar fiecare linie cu număr par parcurgând-o de la coloana M către coloana 1, fermierul (pasionat
de matematică) a scris numerele cartofilor produşi de fiecare plantă, ı̂n ordinea parcurgerii, şi a
constatat că a obţinut şirul cifrelor unităţilor primilor N M termeni ai şirului Fibonacci (vezi
Figura 1 ı̂n care N 3 şi M 6).

Cerinţe

Scrieţi un program care citeşte numerele N şi M (cu semnificaţia din enunţ), iar apoi determină:

1. numărul plantelor din teren care nu au produs niciun cartof;


2. numărul maxim de cartofi care pot fi produşi de plantele dintr-o suprafat̆ă pătratică din
terenul fermierului;
3. pentru fiecare dintre cele Q perechi de numere A, B  citite, numărul cartofilor produşi de
plantele aflate ı̂n zonele pătratice situate ı̂ntre coloanele cu numerele A şi B, inclusiv acestea.

Date de intrare

Fişierul cartofi.in conţine pe prima linie un număr natural C reprezentând cerinţ,a din prob-
lemă care trebuie rezolvată (1, 2 sau 3). A doua linie a fişierului conţine cele două numere naturale
N şi M , separate printr-un spaţiu, cu semnificaţia din enunţ. Dacă C 3, atunci fişierul va mai
conţine pe a treia linie numărul natural Q, iar pe fiecare linie dintre următoarele Q, câte două
numere naturale separate printr-un spaţiu, reprezentând câte o pereche de numere A, B  dintre
cele Q.

Date de ieşire

9
CAPITOLUL 2. OJI 2021 - OSEPI 2.1. CARTOFI 10

Fişierul cartofi.out va conţine:


ˆ Dacă C 1, pe prima linie un număr natural reprezentând răspunsul la cerinţa 1.
ˆ Dacă C 2, pe prima linie un număr natural reprezentând răspunsul la cerinţa 2.
ˆ Dacă C 3, Q linii, câte o linie pentru fiecare pereche A, B  dintre cele Q. Linia core-
spunzătoare fiecărei perechi A, B  va conţine un număr natural reprezentând numărul
cartofilor produşi de plantele aflate ı̂n zonele pătratice situate ı̂ntre coloanele cu numerele A
şi B, inclusiv aceste valori, reprezentând răspunsul la cerinţa 3.
Restricţii şi precizări
ˆ 2&N & 5 108
3 & M & 10
9
ˆ

ˆ N &M
Q & 10
5
ˆ

ˆ 1&A&B&M
ˆ Pentru cerinţa 1 se acordă 20p, iar pentru cerinţele 2 şi 3 se acordă câte 40p.
ˆ Pentru teste ı̂n valoare de 17 puncte: N & 50, M & 100 şi Q & 20
ˆ Pentru alte teste ı̂n valoare de 24 puncte: N & 100, M & 1000 şi Q & 10000
ˆ Pentru alte teste ı̂n valoare de 31 puncte: N & 1000, M & 10000 şi Q & 100000

Observaţii:
ˆ Şirul Fibonacci este definit astfel: f 1 1, f 2 1 şi fn fn1  fn2 , dacă n ' 3, (n este un
număr natural nenul).
ˆ O suprafaţa pătratică din teren este formată din K K zone pătratice alăturate dispuse câte
K pe linie şi câte K pe coloană, oricare ar fi 1 & K & minrN, M x.

Exemple:

cartofi.in cartofi.out Explicaţii


1 1 Se rezolvă cerinţa 1.
36 N 3, M 6.
Primii N M 18 termeni ai şirului Fibonacci sunt: 1, 1, 2, 3, 5, 8, 13,
21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584.
Astfel, numerele cartofilor produşi de fiecare plantă din teren sunt cele
din Figura 1. În teren există o singură plantă care nu a produs niciun
cartof (cea din linia 3, coloana 3)
2 42 Se rezolvă cerinţa 2.
36 N 3, M 6.
Numerele cartofilor produşi de fiecare plantă din teren sunt cele din
tabelul din Figura 1. Plantele aflate ı̂n suprafaţa pătratică galbenă din
tabelul din Figura 2 au produs cel mai mare număr de cartofi.
3 48 Se rezolvă cerinţa 3.
5 6 64 N 5, M 6, Q 3.
3 43 Tabelul din Figura 3 conţine numerele cartofilor produşi de fiecare plantă
1 2 din teren.
4 6 Suma elementelor cuprinse ı̂ntre coloanele 1, 2, inclusiv 1 şi 2, este 48.
2 3 Suma elementelor cuprinse ı̂ntre coloanele 4, 6, inclusiv 4 şi 6, este 64.
Suma elementelor cuprinse ı̂ntre coloanele 2, 3, inclusiv 2 şi 3, este 43.

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
CAPITOLUL 2. OJI 2021 - OSEPI 2.1. CARTOFI 11

2.1.1 Indicaţii de rezolvare

Propunător: prof. Flavius Boian, Colegiul Naţional ”Spiru Haret”, Târgu Jiu
1.1 Solut, ie cu punctaj parţial
Cerinţa 1 (20 de puncte)
a O soluţie cu punctaj parţial se poate obţine generând şirul cifrelor unităţilor primilor N M
termeni ai şirului Fibonacci, contorizându-se apariţia fiecărei cifre 0. Complexitate: O N M .
Cerinţa 2 (40 de puncte)
a O soluţ ie cu punctaj parţ ial se poate obţine utilizând matricea N  M a numerelor cartofilor
produşi de plante. Se observă că suprafaţa pătratică de sumă maximă trebuie căutată printre
submatricele N  N ale matricei. Astfel, suma numerelor din prima submatrice (formată din
coloanele 1, N ) este egală cu suma valorilor din primele N coloane. Submatricea formată din
coloanele 2, N  1 va avea suma egală cu suma valorilor din coloanele 2, N  1. Această sumă
se poate obţine şi direct, din suma anterioară scăzând suma primei coloane şi adăugând suma
coloanei N  1. Procedăm asemănător pentru celelalte submatrice, ultima fiind formatu a din
ultimele N coloane. Cea mai mare sumă dintre acestea reprezintă răspunsul la cerinţ,a 2.
Pentru a evita calculul repetat al sumelor valorilor din fiecare coloană, putem calcula mai ı̂ntâi
aceste sume ı̂ntr-un vector de sume (SC K  = suma valorilor din coloana K, K 1, 2, ..., M ).
Se observă inutilitatea formării matricei. Calculul sumei fiecărei coloane ı̂n vector se poate
face simulând construirea matricei, adunând ultima cifră a termenului Fibonacci la suma ce ı̂i
corespunde. Complexitate O M .
Cerinţa 3 (40 de puncte)
a O soluţie cu punctaj parţial se poate utilizând matricea, calculând pentru fiecare pereche
A, B  suma elementelor situate ı̂ntre aceste coloane.
a O soluţ ie cu un punctaj parţial mai mare ca anteriorul se obţine utilizând un alt mod
de calculare a sumelor elementelor cuprinse ı̂ntre coloanele A, B  şi constă ı̂n calculul sumelor
parţiale ale vectorului SC intr-un alt vector S cu M componente. Astfel, suma elementelor dintre
coloanele A, B  va fi egală cu S B   S A  1, iar S 0 0. Complexitate O Q M .
1.2 Soluţie cu punctaj maxim
Propusă de stud. Ioan-Cristian Pop (UPB) şi stud. Ioan-Bogdan Iordache (UB)
General
a Ştim că şirul cifrelor unităţilor termenilor din şirul lui Fibonacii este periodic cu perioada
60.
Fie R 60. Pe baza acestei proprietăţi, putem construi strict primele 60 de elemente din acest
şir, iar pe baza acestora putem calcula răspunsul la fiecare cerinţă.
De exemplu, al 500-lea termen din şir va fi egal cu al 20-lea termen.
Cerinţa 1 (20 de puncte)
a Plecând de la observaţia de mai sus, construim vectorul alcătuit din primele 60 de numere
din şir. De aici aflăm că cifra 0 apare de 4 ori ı̂n acest set. Ştiind că există N M ©R seturi
complete, rămâne să determinăm numărul de apariţii ale cifrei 0 ı̂n secvenţa formată din ultimii
N M %R termeni ai vectorului.
De exemplu, pentru N 50 şi M 100, obţinem N M = 5 000. Ştim că vor fi 5000©60 83
seturi complete, iar ı̂n ultimii 20 de termeni, 0 se află o singură dată. Deci, răspunsul va fi
83 4  1 333.
a Complexitate: O R.

Cerinţa 2 (40 de puncte)


a Este suficient să calculăm submatricea din colţul stânga sus (o numim submatricea A, de di-
mensiuni min N, 60 min M, 60 şi să calculăm primele min M  N  1, 60 matrici de dimensiuni
N N , ı̂ncepând de la stânga. Matricea are colţul stânga-sus ı̂n coordonatele 1, 1.
a Ca să construim matricea, calculăm, pentru fiecare poziţie i, j  din matrice, al câtelea
termen din şir este, cu ajutorul formulei:
ˆ i  1 M  j, pentru i impar;

ˆ i1 M  M  j  1, pentru i par.


CAPITOLUL 2. OJI 2021 - OSEPI 2.2. TUNEL 12

De exemplu, pentru N 50, M 100, termenul de pe poziţia i 20, j 30 este al i N 


1  M  j  1 1021-lea termen din şir, care la rândul lui este egal cu primul termen din şir
(şirul fiind periodic cu perioada 60).
a Pentru a construi eficient sumele parţiale pe coloane, determinăm frecvenţa de apariţii a
fiecărui termen din matricea A pe linii.
De exemplu, dacă N ar fi egal cu 460, termenul de pe prima linie se va repeta de 7 ori
( 460  1©7), ı̂n timp ce termenul de pe linia 60 se va repeta de 6 ori ( 460  60©7).
a Pentru a rezolva optim, calculăm inţial suma primei matrice, cu colţul stânga-sus ı̂n 1, 1,
pe baza sumelor parţiale pe coloane. O variantă ar fi să determinăm frecvenţa apariţiei fiecărei
coloane ı̂n această matrice.
De exemplu, pentru N 79, frecvenţa primei coloane este 2, ı̂n timp ce frecvenţa celei de-a
30-a coloane este 1.
Apoi, pentru a calcula suma matricei următoare, scădem suma de pe prima coloană şi adăugm̆
suma de pe coloana următoare (sliding window).
Complexitate O R R.
Cerinţa 3 (40 de puncte)
a O soluţie cu punctaj mediu se poate obţine ı̂n maniera descrisă la cerinţa 2, plecând de la
sumele parţiale de pe coloane. Pentru fiecare pereche de coloane X, Y  determinăm frecvenţa de
apariţie a fiecărei coloane dintre cele 60 şi calculăm suma.
Complexitate: O Q R.
a O soluţie cu punctaj maxim, eficientă, presupune construirea unor sume parţiale pe baza
sumelor parţiale de pe coloane (ideea poate fi aplicată şi la cerinţa 2).
a Fie spj  = suma elementelor din primele j coloane. Apoi, pentru fiecare pereche de coloane
X, Y , aflăm de câte ori se repetă setul de 60 de coloane, şi adaugăm restul de coloane neutilizate.
De exemplu, pentru X 45 şi Y 130 observăm că este un set complet de coloane care se
repetă. Rămâne să adăugăm restul de coloane neutilizate: 45, ..., 60 şi 121, ..., 130. Cu ajutorul
sumelor parţiale, putem calcula imediat aceste sume: sp60  sp44, respectiv sp10  sp0.
a Astfel, raspundem in O 1 la fiecare ı̂ntrebare.
Complexitate finală: O R R  Q (R R provine de la generarea matricei de 60 60, apoi
construirea sumelor parţiale).

2.1.2 Cod sursă

Obs: https://ebooks.infobits.ro/culegere_OJI_2021.pdf

2.1.3 *Rezolvare detaliată

2.2 Tunel
Problema 2 - Tunel 100 de puncte
Tommy este un motan alintat care adoră să se plimbe prin orice tunel. De
aceea, stăpânii lui i-au construit o nouă jucărie, formată din N tuneluri inter-
conectate (etichetate cu numerele distincte de la 1 la N ). Toate tunelurile au
aceeaşi lungime, sunt formate din M elemente unitare identice (numerotate
cu numerele distincte de la 1 la M ) şi au ieşiri la ambele capete. Conectarea
dintre două tuneluri alăturate se face printr-un element unitar numit pasaj.
În exemplul din Figura 1, jucăria este formată din 4 tuneluri, fiecare tunel
fiind format din 9 elemente unitare. Pentru a fi mai provocator, stăpânii motanului plasează ı̂n
ultimul element unitar al ultimului tunel o recompensă.
Motan isteţ, Tommy a ı̂nvăţat deja toate regulile jocului:

ˆ poate intra prin capătul din stânga al oricărui tunel (prin elementul unitar 1);
CAPITOLUL 2. OJI 2021 - OSEPI 2.2. TUNEL 13

ˆ nu trece de mai multe ori prin acelaşi pasaj;


ˆ dacă nu se află lângă un pasaj, continuă să meargă prin tunel către dreapta;
ˆ dacă ajunge la un pasaj, atunci trece prin acesta ı̂n tunelul alăturat;

ˆ dacă ajunge ı̂n ultimul element unitar al tunelului etichetat cu N , atunci Tommy iese din
acest tunel cu recompensă, chiar dacă ar exista un pasaj ce conectează acest ultim element
la ultimul element din tunelul N  1 (vezi Figura 2.b);
ˆ dacă ajunge ı̂n ultimul element unitar al tunelului etichetat cu N  1 şi există un pasaj
care conectează acest element cu ultimul element unitar al tunelului etichetat cu N , atunci
Tommy trece prin acest pasaj ı̂n ultimul element din ultimul tunel, ia recompensa şi iese din
tunel (vezi Figura 2.a). În cazul ı̂n care acest pasaj nu există, Tommy iese din tunelul N  1
fără recompensă;
ˆ dacă ajunge ı̂n ultimul element unitar al unui tunel cu eticheta strict mai mică decât N  1,
atunci Tommy iese din tunel fără recompensă.
Ajutaţi-l pe Tommy să ajungă cât mai repede la recompensă respectând regulile jocului!

Cerinţe

Scrieţi un program care citeşte numerele naturale N , M şi X, iar apoi determină:

1. eticheta tunelului prin care iese Tommy dacă intră prin tunelul cu eticheta X, respectând
regulile jocului;
2. numărul minim L de elemente unitare (ale tunelurilor şi ale pasajelor) prin care Tommy ar
trebui să treacă, respectând regulile jocului, pentru a ajunge la recompensă.

Date de intrare

Fişierul tunel.in conţine pe prima linie un număr natural C reprezentând cerinţa din problemă
care trebuie rezolvată (1 sau 2). A doua linie a fişierului conţine cele trei numere naturale N , M
şi X, separate prin câte un spaţ, iu, cu semnificaţ ia din enunţ. Următoarele N  1 linii descriu
pasajele dintre tuneluri. Prima linie dintre cele N  1 indică pasajele dintre tunelurile etichetate cu
1 şi 2, următoarea linie indică pasajele dintre tunelurile etichetate cu 2 şi 3, ..., ultima dintre cele
N  1 linii indică pasajele dintre tunelurile etichetate cu N  1 şi N . Primul număr din fiecare astfel
de linie reprezintă numărul P de pasaje, iar următoarele P numere distincte, scrise ı̂n ordine
crescătoare, reprezintă poziţ iile elementelor unitare (dintre cele două tuneluri) conectate prin
cele P pasaje.

Date de ieşire

ˆ Dacă C 1, fişierul tunel.out va conţine pe prima linie un număr natural reprezentând


răspunsul la cerinţa 1.

ˆ Dacă C 2, fişierul tunel.out va conţine pe prima linie numărul natural L, reprezentând


răspunsul la cerinţa 2.

Restricţii şi precizări

ˆ 3&N & 1 000


ˆ 4&M & 20 000
CAPITOLUL 2. OJI 2021 - OSEPI 2.2. TUNEL 14

ˆ 1&P &M 2
ˆ Pot exista cel mult 150 000 pasaje care interconectează tunelurile.
ˆ Pot exista pasaje ı̂nvecinate care să conecteze elementele unitare din două tuneluri alăturate
(vezi Figura 1 ı̂n care tunelurile 1 şi 2 sunt interconectate prin pasajele ı̂nvecinate dintre
elementele 6, respectiv 7).
ˆ Primul element unitar din fiecare tunel nu este conectat la niciun pasaj.
ˆ Ultimul element unitar din tunelurile etichetate cu 1, 2, ..., N  2 nu este conectat la niciun
pasaj.

ˆ Oricare element unitar poate fi conectat la cel mult un pasaj.


ˆ Oricare două tuneluri etichetate cu numere consecutive sunt interconectate prin cel puţin
un pasaj.
ˆ Pentru fiecare intrare ı̂ntr-un tunel există traseu către ieşire.

ˆ Pentru fiecare test există cel puţin o intrare ı̂ntr-un tunel prin care Tommy poate ajunge la
ieşirea cu recompensă din tunelul N .
ˆ Pentru cerinţa 1 se acordă 40 de puncte iar pentru cerinţa 2 se acordă 60 de puncte.

Exemple:

tunel.in tunel.out Explicaţii


1 1 Se rezolvă cerinţa 1. N 4, M 9, X 4.
4 9 4 Tommy, intra prin tunelul etichetat cu X 4 şi iese prin tunelul
3 2 46 etichetat cu 1 (vezi Figura 2.d).
2 3 5
3 4 69
2 17 Se rezolvă cerinţa 2. N 4, M 9, X 4.
4 9 4 Figurile 2.a, 2.b, 2.c, 2.d conţin trecerea lui Tommy prin tunele
3 2 46 pentru fiecare din cele patru intrări. Doar 2 dintre aceste treceri
2 3 5 ı̂l duc pe Tommy la recompensă (vezi Figura 2.a şi Figura 2.b).
3 4 69 Dacă Tommy intră prin tunelul 2 atunci el ajunge la recompensă
trecând prin numărul minim L 17 (= min 17, 19) elemente
unitare.

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

2.2.1 Indicaţii de rezolvare

Autor: prof. Cristina Sichim


Problema Tunel este ultima problemă propusă de regretata noastră colegă prof. Cristina Sichim.
Problema a fost selectată pentru etapa naţională a Olimpiadei de Informatică Gimnaziu - 2020,
clasa a VII-a (ONIGim 2020), etapă anulată din cauza pandemiei. Selectarea şi pregătirea acestei
probleme pentru OSEPI 2021 reprezintă un omagiu pe care ı̂l aducem celei care timp de două
CAPITOLUL 2. OJI 2021 - OSEPI 2.2. TUNEL 15

decenii ne-a fost o foarte apreciată şi respectată colegă ı̂n comisiile de organizare a competiţiilor
naţionale şi internaţionale de informatică din ţara noastră.
2.1 Soluţie cu punctaje variate
Soluţia propusă utilizează o matrice binară A de dimensiune 2N  1  M , ı̂n care valorile de
1 din liniile impare reprezintă elementele unitare, iar valorile 1 din liniile pare reprezintă pasajele.
Observaţii: a) umplerea inutilă cu 1 a liniilor din matrice corespunzătoare tunelurilor va mări
timpul de executare, diminuând punctajul; b) simularea deplasării ı̂n matrice se poate face din
element ı̂n element sau din pasaj ı̂n pasaj.
Cerinţa 1 (40 de puncte)

ˆ O soluţie cu punctaj maxim (complexitate O M ) se obţine pornind din elementul A2X 


1,  corespunzător intrării ı̂n tunelul X, simulând deplasarea după regulile date.
ˆ Pentru fiecare intrare, traseul lui Tommy spre ieşire este unic.
ˆ Astfel, din Ai, j , Tommy se deplasează ı̂n: a) Ai  2, j  1 dacă Ai  1, j  1 (există
pasaj ı̂n jos) sau b) Ai  2, j  1 dacă Ai  1, j  1 (există pasaj ı̂n sus) sau c) Ai, j  1.
Doar una din cele 3 situaţii a), b) şi c) poate să apară.
ˆ Exceptând tunelul cu eticheta N  1 (căruia ı̂i corespunde linia 2N  3 din matrice), de-
plasarea se finalizează ı̂n momentul ı̂n care se ajunge ı̂n coloana j M , valoarea indicelui i
reprezentând numărul tunelului prin care Tommy iese.
ˆ În cazul ı̂n care Tommy ajunge ı̂n ultimul element al tunelului cu eticheta N , indiferent de
valoarea A2N  2; M  (adică indiferent de existenţa unui pasaj către tunelul N  1) Tommy
ia recompensa si iese prin tunelul N cu recompensă.
ˆ În cazul ı̂n care Tommy ajunge ı̂n ultimul element din tunelul N  1, dacă A2N  2; M  1
(adică există pasaj către ultimul tunel) atunci Tommy trece prin pasaj, ia recompensa si
iese prin tunelul N . Altfel, va ieşi prin tunelul N  1.

Cerinţa 2 (60 de puncte)

ˆ O soluţie cu punctaj parţial şi complexitate O N M  se obţine folosind algoritmul descris


la cerinţa 1 pentru fiecare dintre cele N tuneluri. În plus, vom contoriza fiecare element
unitar şi fiecare pasaj prin care vom trece. Valoarea acestui contor va reprezenta lungimea
traseului parcurs. Răspunsul la cerinţa 2 va fi valoarea minimă a lungimilor traseelor cu
ieşire prin tunelul N .
ˆ O soluţie cu punctaj maxim se obţine simulând deplasarea de la ieşire către intrare pentru
cele maxim două trasee posibile. Plecând din A2N  1, M , se trece ı̂n A2N  1, M  1
sau ı̂n A2N  3, M  (doar dacă A2N  2, M  1 adică există pasaj ı̂ntre ultimele elemente
ale tunelurilor N şi N  1).

Pentru fiecare dintre aceste aceste trasee vom proceda astfel: din Ai, j  trecem ı̂n a) Ai 
2, j  1 dacă Ai  1, j  1 sau b) Ai  2, j  1 dacă Ai  1, j  1 sau c) Ai, j  1. Doar
una dintre situaţiile a), b) sau c) poate să apară. Astfel, sunt parcurse cel mult două trasee din
cele N posibile (complexitate O M ).
2.2 Soluţie cu punctaj maxim
Propusă de drd. Diana Ghinea (ETH Zürich) şi ş.l. Stelian Ciurea (ULB Sibiu)
Complexitatea soluţiei, atât pentru cerinţa 1 cât şi pentru cerinţa 2 este O M  cu memorie
<
N
suplimentară O N M , sau O M log N  cu memorie suplimentară O i 1 Pi , unde Pi reprezintă
numărul de pasaje din tunelul i.
Cerinţa 1 (40 de puncte)
Soluţia se poate obţine simulând traseul lui Tommy element cu element de la intrarea dată
până la ieşire. Notăm cu T, E  elementul unitar cu numărul E din tunelul cu eticheta T . Orice
traseu conţine O M  elemente ı̂ntrucât fiecare element unitar al oricărui tunel este conectat la cel
mult un pasaj. Pentru fiecare element i, j  j N, M , verificăm dacă există un pasaj spre tunelul
i  1 sau spre tunelul i  1 din elementul i, j . În caz afirmativ, Tommy va merge prin pasajul
corespunzător către elementul i  1, j , respectiv i  1; j . Altfel, Tommy va continua să meargă
către dreapta: fie ı̂n elementul i, j  1, fie iese din pasaj.
Putem verifica dacă există un pasaj din elementul i, j  j N, M  ı̂n mai multe moduri:
CAPITOLUL 2. OJI 2021 - OSEPI 2.2. TUNEL 16

ˆ Reţinem o matrice bool P cu N linii şi M coloane, unde P ij  = true dacă şi numai dacă
există un pasaj din celula i, j  către celula i  1, j . Astfel, putem verifica dacă există un
pasaj ı̂n complexitate O 1, ı̂nsă cu memorie suplimentară O N M .
ˆ Utilizăm structura vector din C++: pentru fiecare coloană j, vector-ul P j  reţine pasajele
de pe coloana j (cel mult O N  pasaje). Putem verifica dacă există un pasaj folosind căutare
N
<
binară, ı̂n complexitate O log N , cu memorie suplimentară O i 1 Pi , unde Pi reprezintă
numărul de pasaje din tunelul i.
ˆ Utilizăm structura set din C++: pentru fiecare coloană j, set-ul P j  reţine pasajele de pe
coloana j (cel mult O N  pasaje). Putem verifica dacă există un pasaj (pentru elementul
i, j , dacă i aparţine set-ului P j ) ı̂n complexitate O log N , cu memorie suplimentară
< N
O i 1 Pi , unde Pi reprezintă numărul de pasaje din tunelul i.
Astfel, utilizând prima modalitate, obţinem complexitatea O M , iar utilizând una dintre
celelalte două modalităţi, obţinem complexitatea O M logN .
Cerinţa 2 (60 de puncte)
O soluţie naivă ar simula traseul lui Tommy pornind din intrarea fiecărui tunel. Fără a lua ı̂n
calcul timpul pentru verificarea pasajelor, o astfel de soluţie ar avea complexitate O N M .
Pentru a obţine o soluţie ı̂n complexitatea de timp dorită, avem nevoie de următoarea observaţie:
Există cel mult două tuneluri din care Tommy poate ajunge la recomensă.
Orice traseu care ajunge la recompensă (ı̂l vom denumi traseu cu succes) fie trece prin elementul
N  1, M  (dacă există un pasaj către N, M ), fie trece prin elementul N, M  1.
Presupunem că există cel puţin trei trasee distincte cu succes. Atunci, două dintre aceste
trasee trec prin acelaşi element N  1, M  sau N, ; M  1. Vom presupune că cele două trasee
trec prin elementul N  1, M  (celălalt caz este analog).
Vom nota aceste trasee cu A al x, 1, al1 , ..., a2 N  1, M , a1 N, M  şi B
¬
bl ¬ x , 1, bl¬ 1 , ..., b2 N  1, M , a1 N, M  (acestee trasee nu includ pasajele).
Din moment ce A şi B sunt două trasee distincte, dar ultimele două elemente ale lui A şi B
sunt comune, există un element C ak bk iC , jC  astfel ı̂ncât ai bi pentru 1 & i & k şi
ak1 j bk1 . Acest lucru este posibil doar dacă unul dintre cele două trasee a trecut aici printr-un
pasaj. Întrucât un element poate fi conectat la maxim un pasaj, doar unul dintre traseele A şi
B poate trece aici printr-un pasaj: presupunem că acest traseu este A şi că ak1 iC 1 , jC 
(analog pentru celelalte cazuri). Atunci, bk1 iC , jC 1  şi, din moment ce aici este un pasaj,
bk1 iC  1, jC  j iC , jC 1  ak1 , ceea ce contrazice modul ı̂n care am ales elementul C şi
demonstrează observaţia noastră.
Astfel, nu este necesar să simulăm toate traseele posibile pentru a obţ ine răspunsul la această
cerinţa: trebuie să simulăm cel mult două trasee.
Adăugăm ı̂ncă o observaţie:
ˆ Există un traseu cu succes ı̂n care Tommy trece prin elementul N, M  1.
ˆ Dacă există un pasaj ı̂ntre N  1, M  şi N, M , atunci există un traseu cu succes ı̂n care
Tommy trece prin elementul N, M  1.
În concluzie, o soluţie pentru această cerinţă va simula ı̂n sens invers deplasarea lui Tommy
ı̂n cel mult două trasee (de la recompensă către intrare) şi va calcula lungimea acestora:
ˆ unicul traseu cu succes ce trece prin celula N, M  1, dacă acest traseu există (ı̂n cazul ı̂n
care există un pasaj ı̂ntre celulele N, M  şi N, M  1);
ˆ unicul traseu cu succes ce trece prin celula N, M  1.
Lungimea unui traseu poate fi calculată ca M  2p, unde p este numărul de pasaje prin care
Tommy trece ı̂n acel traseu.

2.2.2 Cod sursă

Obs: https://ebooks.infobits.ro/culegere_OJI_2021.pdf

2.2.3 *Rezolvare detaliată


Capitolul 3

OJI 2020

3.1 datorii
Problema 1 - datorii 100 de puncte
Într-o ţară ı̂ndepărtată, economia este ı̂n criză. Cea mai mare problemă este lipsa de capital
care creează blocaje financiare. De exemplu, o firmă X poate avea datorii către o firmă Y pe care
nu le poate plăti, deoarece o altă firmă Z are datorii către firma X pe care nu le-a plătit, ş.a.m.d.
Există o listă cu toate datoriile firmelor sub forma următoare:

X %YS
cu semnificaţia ”firma X datorează firmei Y suma S”.
Este posibil ca X să aibă mai multe datorii la firma Y (ı̂n funcţie de contractele derulate
ı̂mpreună) sau chiar ca X să aibă datorii la Y şi Y să aibă datorii la X.

Cerinţe

Cunoscând lista cu datoriile firmelor, scrieţi un program care să rezolve următoarele cerinţe:

1. determină numărul de firme distincte care apar ı̂n această listă;


2. realizează o situaţie financiară a firmelor distincte din această listă, scrise ı̂n ordine lex-
icografică; pentru fiecare firmă se vor determina două valori SD SP, unde SD reprezintă
suma totală a datoriilor pe care fir-ma le are către alte firme, iar SP este totalul sumelor pe
care firma trebuie să le primească de la alte firme.

Date de intrare

Fişierul de intrare datorii.in conţine pe prima linie un număr natural C reprezentând cerinţa
care trebuie să fie rezolvată (1 sau 2). Pe a doua linie se află un număr natural D care reprezintă
numărul de ı̂nregistrări existente ı̂n lista datoriilor firmelor. Pe următoarele D linii sunt descrise
datoriile firmelor, ı̂n forma specificată ı̂n enunţ, câte o datorie pe o linie.

Date de ieşire

Fişierul de ieşire datorii.out va conţine răspunsul la cerinţa C specificată ı̂n fişierul de intrare.
Dacă C 1 fişierul va conţine un număr natural, reprezentând numărul de firme distincte care
apar ı̂n lista menţionată.
Dacă C 2 fişierul va conţine pentru fiecare dintre firmele distincte din lista menţionată câte
un singur triplet de forma X SD SP , unde X este numele firmei, iar SD şi SP au semnificaţia
din enunţ pentru firma X; tripletele vor fi scrise astfel ı̂ncât numele firmelor să apară ı̂n ordine
lexicografică, fiecare triplet pe câte o linie a fişierului, iar X, SD şi SP vor fi separate prin câte
un singur spaţiu.

Restricţii şi precizări

a Există ı̂n total cel mult 6 000 de firme distincte ı̂n lista menţionată de datorii.

17
CAPITOLUL 3. OJI 2020 3.1. DATORII 18

a Numele unei firme este format din maximum 20 de caractere (litere mari şi mici ale alfa-
betului englez, cifre, spaţii); se face distincţie ı̂ntre literele mari şi literele mici ı̂n numele
firmelor; nu există alte restricţii referitoare la numele firmelor.
a Două firme distincte au nume distincte. O firmă nu poate avea datorii la ea ı̂nsăşi.
a În descrierea unei datorii X % Y S  există un singur spaţiu ı̂ntre X şi %, un singur spaţiu
ı̂ntre % şi Y , respectiv un singur spaţiu ı̂ntre Y şi S.
a 1&D & 80 000
a Sumele datorate de firme sunt numere naturale nenule & 106 .
a Dacă X şi Y sunt numele a două firme distincte, iar k (k '
0) este valoarea maximă cu
proprietatea că secvenţa formată din primele k caractere din X este identică cu secvenţa
formată din primele caractere din Y , spunem că X precedă din punct de vedere lexicografic
pe Y dacă X are doar k caractere sau dacă al k  1-lea caracter din X este mai mic decât
al k  1-lea caracter din Y .
a Pentru teste valorând 30 de puncte cerinţa este 1. Pentru teste valorând 60 de puncte cerinţa
este 2. Pentru teste valorând 40 de puncte D &1 000. Pentru teste valorând 45 de puncte
numele firmelor nu conţin spaţii. 10 puncte se acordă din oficiu.

Exemple:

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

3.1.1 Indicaţii de rezolvare


prof. Emanuela Cerchez, Colegiul Naţional ”Emil Racoviţă” Iaşi

Vom citi datoriile firmelor succesiv, linie cu linie, ı̂ntr-un şir de caractere s.
Suntem interesaţi doar de firmele distincte, ca urmare vom reţine evidenţa firmelor distincte
ı̂ntr-un vector F .
O firmă va fi o structură cu 3 câmpuri (un şir de caractere reprezentând numele acesteia şi
două numere naturale SD şi SP , reprezentând totalul datoriilor firmei către alte firme, respectiv
suma totală pe care firma trebuie să o primească de la alte firme).
Vom prelucra şirul s, care reprezintă o datorie, ı̂n scopul de a extrage numele celor două firme
(X şi Y ) şi suma datorată S.
Deoarece numele firmelor pot conţine şi ele cifre, vom parcurge şirul s de la sfârşit către ı̂nceput,
construind suma S, din cifrele ı̂ntâlnite până la primul spaţiu.
Eliminăm din şirul s suma S, prin trunchierea acestuia. şirul s va avea acum forma X ¿ Y .
Separăm şirul s ı̂n două subşiruri (unul care să reprezinte numele firmei X, celălalt numele
firmei Y ), ştiind că cele două nume sunt separate prin ¿.
CAPITOLUL 3. OJI 2020 3.1. DATORII 19

Vom căuta numele firmei X ı̂n vectorul F . Dacă acesta există deja ı̂n vectorul F , doar adăugăm
suma S la câmpul SD al firmei cu numele X. Dacă nu există o firmă cu numele X, o adăugăm ı̂n
vectorul F , iniţializând SD cu S şi SP cu 0.
ı̂n mod similar procedăm cu firma Y : o căutăm ı̂n vectorul F ; dacă există deja adăugăm suma
S la câmpul SP al firmei cu numele Y ; dacă nu există o firmă cu numele Y , o adăugăm la vectorul
F şi iniţializăm câmpul SD cu 0, iar SP cu S.
Căutarea numelui unei firme se poate face secvenţial ı̂n vectorul F , dar ı̂n acest caz se obţine
punctaj parţial, din cauza depăşirii timpului de execuţie.
Observaţie: pentru teste valorând 45 de puncte, numele firmelor nu conţin spaţii şi deci etapa
de prelucrare a şirului s este eliminată, citind separat X, Y , S.
Pentru un algoritm eficient, va fi necesar să facem o căutare binară ı̂n vectorul F . Pentru ca
acest lucru să fie posibil, ar trebui ca, la fiecare pas, vectorul F să fie deja sortat lexiicografic după
numele firmelor.
Pentru aceasta, vom face o sortare prin inserare. Mai exact, de fiecare dată când adăugăm o
nouă firmă ı̂n vectorul F , căutăm locul corect şi o inserăm direct ı̂n poziţia corectă, astfel ı̂ncât F
să rămână sortat.

3.1.2 Cod sursă

Listing 3.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 3. OJI 2020 3.1. DATORII 20

49 ii = strlen(linie) - 1;
50 suma = 0;
51 while ( 48 <= linie[ii] && linie[ii] <= 57)
52 {
53 suma = suma + z * (linie[ii]-48);
54 z *= 10;
55 ii--;
56 }
57
58 ii++;
59 linie[ii-1] = ’\0’;
60 cuv = strchr(linie, ’>’);
61 strncpy(numeFirma1, linie, cuv - linie - 1);
62 numeFirma1[cuv - linie - 1] =’\0’;
63 strcpy(numeFirma2, cuv+2);
64
65 //pun firmele la locul lor
66 poz = apartine(numeFirma1);
67 if(isdigit(numeFirma1[0]))
68 lista = numeFirma1[0] - ’0’;
69 else
70 if(isupper(numeFirma1[0]))
71 lista = 10 + numeFirma1[0] - ’A’;
72 else
73 lista = 36 + numeFirma1[0] - ’a’;
74
75 if(poz)
76 v[lista][poz].SD += suma;
77 else
78 {
79 nrFirme++;
80 nrF[lista]++;
81
82 strcpy(v[lista][nrF[lista]].nume,numeFirma1);
83
84 v[lista][nrF[lista]].SP = 0;
85 v[lista][nrF[lista]].SD = suma;
86
87 //mut firma la locul ei
88 for(j = nrF[lista];
89 j>1 && strcmp(v[lista][j].nume, v[lista][j-1].nume) < 0;
90 j--)
91 tmp = v[lista][j],
92 v[lista][j] = v[lista][j-1],
93 v[lista][j-1] = tmp;
94 }
95
96 poz = apartine(numeFirma2);
97 if(isdigit(numeFirma2[0]))
98 lista = numeFirma2[0] - ’0’;
99 else
100 if(isupper(numeFirma2[0]))
101 lista = 10 + numeFirma2[0] - ’A’;
102 else
103 lista = 36 + numeFirma2[0] - ’a’;
104
105 if(poz)
106 v[lista][poz].SP += suma;
107 else
108 {
109 nrFirme++;
110 nrF[lista]++;
111 strcpy(v[lista][nrF[lista]].nume,numeFirma2);
112 v[lista][nrF[lista]].SP = suma;
113 v[lista][nrF[lista]].SD = 0;
114
115 //mut firma la locul ei
116 for(j = nrF[lista];
117 j>1 && strcmp(v[lista][j].nume, v[lista][j-1].nume) < 0;
118 j--)
119 tmp = v[lista][j],
120 v[lista][j] = v[lista][j-1],
121 v[lista][j-1] = tmp;
122 }
123 }
124
CAPITOLUL 3. OJI 2020 3.1. DATORII 21

125 if( C == 1)
126 {
127 scrie << nrFirme <<’\n’;
128 }
129 else
130 {
131 for(i = 0; i < 62; ++i)
132 if(nrF[i])
133 for(j = 1; j <= nrF[i]; ++j)
134 scrie << v[i][j].nume << " " <<
135 v[i][j].SD << " " <<
136 v[i][j].SP << ’\n’;
137 }
138
139 citeste.close();
140 scrie.close();
141 return 0;
142 }
143
144 int apartine(char *denumire)
145 {
146 int st, dr, mij;
147 int lista;
148
149 if(isdigit(denumire[0]))
150 lista = denumire[0] - ’0’;
151 else
152 if(isupper(denumire[0]))
153 lista = 10 + denumire[0] - ’A’;
154 else
155 lista = 36 + denumire[0] - ’a’;
156
157 st = 1;
158 dr = nrF[lista];
159 while(st <= dr)
160 {
161 mij = (st + dr)/2;
162 if(strcmp(v[lista][mij].nume, denumire) == 0)
163 return mij;
164 else
165 if(strcmp(v[lista][mij].nume, denumire) < 0)
166 st = mij + 1;
167 else
168 dr = mij - 1;
169 }
170
171 return 0;
172 }

Listing 3.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 3. OJI 2020 3.2. TRIUNGHI 22

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 }

3.1.3 *Rezolvare detaliată

3.2 triunghi
Problema 2 - triunghi 100 de puncte
Se consideră A un tablou bidimensional cu n linii, n coloane şi elemente numere naturale. O
zonă triunghiulară a tabloului, reprezentată de tripletul lin, col, k , este o zonă de forma unui
triunghi dreptunghic cu catetele de lungime egală cu ¶k ¶, definită astfel:

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


a pe prima linie a zonei se află elementele Alincol, Alincol  1, ..., Alincol 
k  1;
a pe a doua linie a zonei se află elementele Alin  1col, Alin  1col  1, ...,
Alin  1col  k  2;
a pe a treia linie a zonei se află elementele Alin  2col, Alin  2col  1, ...,
Alin  2col  k  3;
CAPITOLUL 3. OJI 2020 3.2. TRIUNGHI 23

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

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

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


a pe a doua linie a zonei se află elementele Alin  ¶k ¶  2col  1, Alin  ¶k ¶  2col;
a ...
a pe ultima linie a zonei se află 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 semnificaţia din
enunţ.
Pe următoarele n linii se găsesc câte n valori naturale, reprezentând elementele tabloului A.
Pe linia n  2 se află 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 aflate pe aceeaşi linie a fiş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 fi 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ă
2 10 10 2 4 8 maximă (59) este reprezentată de
8 10 3 4 6 6 tripletul (4 4 -4) şi conţine valorile
469719 evienţiate:
6 7 2 2 10 6 (59= 4+
10 4 6 1 10 4 10+2+
3 10+3+4+
413 4+6+9+7).
4 4 -4
6 5 -2

Timp maxim de executare/test: 0.7 secunde


Memorie: total 32 MB din care pentru stivă 16 MB
Dimensiune maximă a sursei: 15 KB
CAPITOLUL 3. OJI 2020 3.2. TRIUNGHI 24

3.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 fiecare ı̂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 fiecare zonă vom reactualiza, dacă e cazul, suma maximă.
Complexitatea algoritmului este O Q ˜ k ˜ k  n ˜ n deoarece, pentru fiecare 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
operaţiilor necesare calculării sumei elementelor dintr-un triunghi.
Vom precalcula un tablou bidimensional Slin, cu următoarea semnificaţie:
Slinij  = suma primelor j elemente de pe linia i
Pe baza tabloului Slin putem afla ı̂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 defini trei tablouri bidimensionale de ordin n: sum, slin şi
sdiag, având semnificaţ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 fi suma elementelor dintr-o zonă determinată astfel:
Dacă ipotenuza triunghiului se găseşte de-a lungul unei diagonale aflate deasupra diagonalei
principale, zona va fi de forma unui trapez dreptunghic determinat de punctele 1, 1, i, 1, i, j ,
1, i  j  1 vezi figura 1.

Figura 1

Dacă ipotenuza triunghiului se găseşte de-a lungul unei diagonale aflate sub diagonala princi-
pală, zona va fi de forma unui pentagon determinat de punctele 1, 1, i, 1, i, j , x, n şi 1, n,
unde x, n este punctul ı̂n care prelungirea ipotenuzei triunghiului intersectează ultima coloană
(vezi figura 2).
CAPITOLUL 3. OJI 2020 3.2. TRIUNGHI 25

Figura 2

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, aflarea 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 figura 3).

Figura 3

Formula de calcul suferă unele modificări ı̂n funcţie de poziţia triunghiului şi de forma zonei.
Dacă triunghiul are k $ 0 (triunghi cu unghiul drept ”ı̂n jos”), suma elementelor sale se
calculează ca fiind diferenţa dintre suma elementelor din pătratul de latură k, având colţul din
dreapta-jos de coordonate i, j  şi suma elementelor triunghiului determinat de tripletul i  ¶k ¶  1,
j  ¶k ¶  1, ¶k ¶ (calculat prin procedeul descris anterior).
Complexitatea algoritmului este O Q  n ˜ n.

3.2.2 Cod sursă


CAPITOLUL 3. OJI 2020 3.2. TRIUNGHI 26

Listing 3.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 p a r i a l e
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 3. OJI 2020 3.2. TRIUNGHI 27

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 3.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 3. OJI 2020 3.2. TRIUNGHI 28

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 }

3.2.3 *Rezolvare detaliată


Capitolul 4

OJI 2019

4.1 cate3cifre
Problema 1 - cate3cifre 90 de
puncte
Gigel, pasionat de numere, ştie că orice număr natural se scrie ı̂ntr-o bază de numeraţie b ca
o succesiune de simboluri care au asociate valori de la 0 la b  1. De exemplu numărul 7, scris
ı̂n baza 10, se scrie ı̂n baza 2ca 111 2 , iar numărul 26732, scris ı̂n baza 10, se scrie ı̂n baza 37 ca
o succesiune de 3 simboluri, primele două având asociată valoarea 19, iar ultimul având asociată
valoarea 18. El a descoperit că există numere care au proprietatea că se scriu, ı̂n exact două baze
diferite, prin exact trei simboluri identice. De exemplu, numărul 931 10 se scrie ı̂n baza 11 ca
777 11 , iar ı̂n baza 30 se scrie 111 30 .

Cerinţe

Fiind dat un număr natural N , să se determine cel mai mare număr natural mai mic sau egal
cu N , care are proprietatea că se scrie ı̂n exact două baze diferite prin exact 3 simboluri identice.
1. Să se scrie numărul determinat
2. Să se scrie cele două baze determinate şi valorile simbolurilor respective.

Date de intrare

Fişierul de intrare cate3cifre.in conţine pe prima linie cerinţa (1 sau 2). Pe linia a doua a
fişierului de intrare se află numărul natural N .

Date de ieşire

Fişierul de ieşire cate3cifre.out va conţine pe prima linie, dacă cerinţa este 1, numărul deter-
minat. Dacă cerinţa este 2, prima şi cea de a doua linie a fişierului de ieşire au aceeaşi structură:
pe fiecare linie se vor scrie, separate printr-un spaţiu, două numere naturale b c, reprezentând baza
şi valoarea simbolului cerut din baza respectivă. Cele două baze se vor afişa ı̂n ordine crescătoare.

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 fiecare test există soluţie.

Exemple

29
CAPITOLUL 4. OJI 2019 4.1. CATE3CIFRE 30

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 ca 111 163
2 37 19
30000 163 1
Tabelul 4.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 fi 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
oficiu.

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


CAPITOLUL 4. OJI 2019 4.2. PARALELE 31

Grad de dificultate: 3

4.1.2 Cod sursă

Listing 4.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 }

4.1.3 *Rezolvare detaliată

4.2 paralele
Problema 2 - paralele 90 de puncte
Avem o matrice de dimensiuni N  M , cu elemente 0 şi 1. Numim segment o secvenţă de cel
puţin două valori 1 aflate una lângă alta, toate pe aceeaşi linie, sau toate pe aceeaşi coloană a
matricei.

Cerinţe

Se cere determinarea numărului de perechi de segmente:


1. aflate pe linii distincte ale matricei;
2. aflate pe coloane distincte ale matricei;
CAPITOLUL 4. OJI 2019 4.2. PARALELE 32

Date de intrare

Fişierul paralele.in conţine pe prima linie, separate prin câte un spaţiu trei valori naturale,
ı̂n ordine: T , N şi M . Dacă T este 1 se rezolvă doar cerinţa 1, iar dacă T este 2 se rezolvă doar
cerinţa 2.
Începând cu linia a doua se află elementele matricei, o linie a matricei pe o linie a fişierului.
Elementele de pe aceeaşi linie se separă prin câte un spaţiu.

Date de ieşire

Fişierul paralele.out conţine pe prima linie un număr natural reprezentând valoarea cerută.

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 fişierul de intrare fiind 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, nefiind 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: fiecare
dintre cele trei segmente de pe prima linie este paralel cu
fiecare 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 fi
avut T 2 rezultatul calculat ar fi trebuit să fie 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 fi 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
oficiu.

4.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).
CAPITOLUL 4. OJI 2019 4.2. PARALELE 33

Timpul de calcul va fi de ordin n*m (de la calculul valorilor din F observam ca


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 pentru
fiecare linie i.

O alta abordare care obtine punctajul maxim este urmatoarea: odata calculat vectoru
cu semnificatia de mai sus, pentru fiecare valoare i dintre 2 si n adunam la soluti
produsul dintre F[i] si suma valorilor F de pe pozitii de la 1 la i-1. Aceasta suma
obtinem folosind tehnica sumelor partiale in vectorul F.

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


punctaje partiale.

Grad de dificultate: 3

4.2.2 Cod sursă

Listing 4.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 }
CAPITOLUL 4. OJI 2019 4.2. PARALELE 34

42 long long peColoane()


43 {
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 4.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 }
CAPITOLUL 4. OJI 2019 4.2. PARALELE 35

30
31 int main ()
32 {
33
34 fin>>t>>n>>m;
35
36 for (i=1;i<=n;i++)
37 {
38 for (j=1;j<=m;j++)
39 {
40 fin>>c;
41 if (n<=500)
42 a[i][j] = c;
43 else
44 A[i][j] = c;
45 }
46 }
47
48 if (t == 2)
49 {
50 for (i=1;i<=n;i++)
51 for (j=1;j<=m;j++)
52 b[j][n-i+1] = a[i][j];
53
54 int aux = n;
55
56 n = m;
57 m = aux;
58 for (i=1;i<=n;i++)
59 {
60 for (j=1;j<=m;j++)
61 a[i][j] = b[i][j];
62 a[i][m+1] = 0;
63 }
64 }
65
66 for (i=1;i<=n;i++)
67 {
68 L = 0;
69 for (j=1;j<=m;j++)
70 {
71 if (getA(i, j))
72 L++;
73 else
74 L = 0;
75
76 if (L >= 2)
77 f[i] += (L-1);
78 }
79 }
80
81 for (i=n-1;i>=1;i--)
82 {
83 sol += f[i] * 1LL * f[i+1];
84 f[i] += f[i+1];
85 }
86
87 fout<<sol;
88 return 0;
89 }

Listing 4.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;
CAPITOLUL 4. OJI 2019 4.2. PARALELE 36

13 int A[50005][22];
14
15 ifstream fin ("paralele.in");
16 ofstream fout("paralele.out");
17
18 long long gauss(int n)
19 {
20 return n*1LL*(n-1)/2;
21 }
22
23 inline int getA(int i, int j)
24 {
25 if (n <= 500)
26 return a[i][j];
27 else
28 return A[i][j];
29 }
30
31 int main ()
32 {
33
34 fin>>t>>n>>m;
35
36 for (i=1;i<=n;i++)
37 {
38 for (j=1;j<=m;j++)
39 {
40 fin>>c;
41 if (n<=500)
42 a[i][j] = c;
43 else
44 A[i][j] = c;
45 }
46 }
47
48 if (t == 2)
49 {
50 for (i=1;i<=n;i++)
51 for (j=1;j<=m;j++)
52 b[j][n-i+1] = a[i][j];
53
54 int aux = n;
55 n = m;
56 m = aux;
57 for (i=1;i<=n;i++)
58 {
59 for (j=1;j<=m;j++)
60 a[i][j] = b[i][j];
61 a[i][m+1] = 0;
62 }
63 }
64
65 for (i=1;i<=n;i++)
66 {
67 for (j=2;j<=m;j++)
68 f[j] = 0;
69
70 L = 0;
71 for (j=1;j<=m+1;j++)
72 if (getA(i,j) == 1)
73 L++;
74 else
75 {
76 if (getA(i,j-1) == 1 && L >= 2)
77 f[L]++;
78 L = 0;
79 }
80
81 long long alter = 0;
82 for (j=2;j<=m;j++)
83 {
84 alter += gauss(j) * f[j];
85 }
86
87 sol -= gauss(alter);
88 alterAll += alter;
CAPITOLUL 4. OJI 2019 4.2. PARALELE 37

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

4.2.3 *Rezolvare detaliată


Capitolul 5

OJI 2018

5.1 Cruce
Problema 1 - Cruce 100 de puncte
Se consideră o matrice pătratică de dimensiune N , conţinând numere naturale. Numim cruce
de lăţime K reuniunea mulţimii tuturor elementelor aflate pe K linii consecutive ale matricei
şi a mulţimii tuturor elementelor aflate pe K coloane consecutive ale matricei. Două elemente
ale matricei se consideră distincte dacă sunt situate pe poziţii distincte ı̂n matrice. Se acceptă
şi forma degenerată a unei cruci, ı̂n formă de T sau L, când una dintre liniile sau coloanele care
formează crucea sunt chiar la marginea matricei. Vom defini valoarea unei cruci ca fiind suma
elementelor din care aceasta este formată.

Cerinţe

Scrieţi un program care, pentru o valoare K dată, determină o cruce de lăţime K a cărei
valoare este maximă şi poziţia ei ı̂n matrice. Această poziţie va fi exprimată prin perechea de
indici reprezentând prima linie din cele K consecutive şi prima coloană din cele K consecutive din
care este formată crucea.

Date de intrare

Fişierul cruce.in conţine pe prima linie numerele N şi K, iar pe următoarele N linii câte N
numere ı̂ntregi reprezentând ı̂n ordine, pe linii, elementele matricei. Numerele de pe aceeaşi linie
sunt separate prin câte un spaţiu.

Date de ieşire

Fişierul cruce.out va conţine trei numere V max L C, separate prin câte un spaţiu,
reprezentând valoarea maximă determinată pentru o cruce de lăţime K, respectiv linia şi coloana
care exprimă poziţia acesteia ı̂n matrice.

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

38
CAPITOLUL 5. OJI 2018 5.1. CRUCE 39

cruce.in cruce.out Explicaţii


52 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
12345 -3 2 2 -2 -1
1 0 -7 1 1 1 23 45
32123 1 0 -7 1 1
3 21 23
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

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

5.1.2 Cod sursă

Listing 5.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
CAPITOLUL 5. OJI 2018 5.1. CRUCE 40

13 int main()
14 {
15 freopen("cruce.in", "r",stdin);
16 freopen("cruce.out","w",stdout);
17
18 scanf("%d%d\n",&N, &K);
19 assert(N && K && N<501 && K<=N);
20
21 for(i=1; i<=N; ++i)
22 for(j=1; j<=N; ++j)
23 {
24 scanf("%d",&S[i][j]);
25 assert(S[i][j]>=-5000 && S[i][j]<=5000);
26 S[i][j]= S[i-1][j]+S[i][j-1]-S[i-1][j-1] + S[i][j];
27 }
28
29 Smax=(-5000)*500*501;
30 for(i=1; i<=N - K + 1; ++i)
31 for(j = 1; j<=N -K + 1; j++)
32 {
33 Sum= S[i+K-1][N] - S[i-1][N] + S[N][j+K-1]-S[N][j-1];
34 Dif = S[i+K-1][j+K-1]-S[i-1][j+K-1]-S[i+K-1][j-1]+S[i-1][j-1];
35 Sum-=Dif;
36 if(Sum>Smax){Smax=Sum; minL=i; minC=j;}
37 }
38
39 printf("%d %d %d \n", Smax, minL, minC);
40
41 return 0;
42 }

Listing 5.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;
CAPITOLUL 5. OJI 2018 5.2. PAL 41

43 }

Listing 5.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 }

5.1.3 *Rezolvare detaliată

5.2 pal
Problema 2 - pal 100 de puncte
Micul Prinţ a ajuns ı̂n ţara numerelor palindrom cu număr impar de cifre unde a primit de la
sfetnicul regelui o listă care conţine N numere naturale, fiecare cu număr impar de cifre. Un număr
este palindrom dacă prima lui cifră este egală cu ultima, a doua cu penultima, ş.a.m.d. Acesta
i-a transmis că regele este foarte bolnav. Odată cu regele, numerele din listă s-au ı̂mbolnăvit şi
ele. Sfetnicul i-a spus că lista corectă poate fi obţinută prin ı̂nlocuirea fiecărui număr din ea cu
cel mai mic palindrom mai mare sau egal cu numărul respectiv.
După ce a urmat recomandarea sfetnicului, Micul Prinţ a constatat că ı̂n lista corectă obţinută
toate palindromurile sunt distincte. Uitându-se mai cu atenţie la palindromurile din această listă,
a observat că există perechi de palindromuri ı̂n care cel mai mic se poate obţine din cel mai mare
prin ştergerea aceluiaşi număr de cifre de la ambele capete. De exemplu pentru perechea 7531357
şi 313 palindromul 313 se obţine din 7531357 prin eliminarea a câte două cifre de la ambele capete
ale sale.
Considerăm un şir de palindromuri din lista corectă şi notăm cu X valoarea maximă a acestui
şir. Vom spune că şirul este magic dacă toate palindromurile din el se pot obţine după metoda
descrisă mai sus, din palindromul de valoare X. Un exemplu de şir magic este 4, 53435, 7534357,
89753435798, presupunând că toate aceste numere se regăsesc ı̂n lista corectă.
CAPITOLUL 5. OJI 2018 5.2. PAL 42

Cerinţe

Scrieţi un program care citeşte numerele din lista primită de la sfetnicul regelui şi afişează:
1) Lista corectă obţinută de Micul Prinţ;
2) Numărul de elemente ale celui mai lung şir magic care se poate obţine din lista corectă;
3) Palindromurile din care este format cel mai lung şir magic, afişate ı̂n ordine crescătoare.
Dacă există mai multe astfel de şiruri ı̂n lista corectă a Micului Prinţ, se va afişa cel ı̂n care ultimul
număr este cel mai mare.

Date de intrare

Fişierul de intrare pal.in conţine pe prima linie numărul natural P , care nu poate avea decât
valorile 1, 24 sau 3 şi indică numărul cerinţei care va fi rezolvată. Pe a doua linie numărul natural
N de numere de pe lista primită de la sfetnicul regelui. Pe a treia linie se află numerele naturale
din lista primită de la sfetnic, separate prin câte un spaţiu.

Date de ieşire

Fişierul de ieşire pal.out va conţine pe prima linie răspunsul la cerinţa rezolvată. Dacă s-a
rezolvat prima cerinţă, fişierul de ieşire va conţine un şir de N numere naturale, separate prin
câte un spaţiu, reprezentând numerele din lista corectă, ı̂n ordinea corespunzătoare listei iniţiale.
Dacă s-a rezolvat cerinţa 2, pe prima linie a fişierului de ieşire se va scrie lungimea celui mai lung
şir magic. Dacă s-a rezolvat cerinţa 3, fişierul de ieşire va conţine numerele determinate şi afişate
conform cerinţei.

Restricţii şi precizări

a 0 $ N & 50000;
a Numerele de pe lista sfetnicului sunt naturale nenule şi fiecare are cel mult 17 cifre;
a Pentru rezolvarea corectă a primei cerinţe se acordă 20 de puncte, pentru rezolvarea corectă
a celei de a doua cerinţe se acordă 20 de puncte, iar pentru rezolvarea corectă a celei de a treia
cerinţe se acordă 50 de puncte.

Exemple

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
Tabelul 5.2: pal

Timp maxim de executare/test: 0.5 secunde


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

5.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.
CAPITOLUL 5. OJI 2018 5.2. PAL 43

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

5.2.2 Cod sursă

Listing 5.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;
CAPITOLUL 5. OJI 2018 5.2. PAL 44

56 for(j=1; j<=nr; ++j)


57 a[i] = a[i] * 10 + cf[j];
58 }
59 }
60
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;
CAPITOLUL 5. OJI 2018 5.2. PAL 45

132 if(cauta(x)) v[++nr] = x;


133 z /= 100;
134 }
135
136 for(i=nr; i; --i)
137 printf("%lld ",v[i]);
138 printf("\n");
139
140 return 0;
141 }

Listing 5.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 }
CAPITOLUL 5. OJI 2018 5.2. PAL 46

63 else
64 a[i]=a[n-i+1];
65
66 if(a[m+1]>9)
67 {
68 r=0;
69 for(i=m+1;i<=n;i++)
70 r+=a[i], a[i]=r%10, r/=10;
71
72 for(i=1;i<=m;i++)
73 a[i]=a[n-i+1];
74 }
75
76 for(i=n;i>=1;i--)
77 x=x*10+a[i];
78
79 return x;
80 }
81
82 int main()
83 {
84 int i, c, j;
85 f>>C>>n;
86
87 for(i=1;i<=n;i++)
88 f>>z, a[i].x=pal(z,a[i].n);
89
90 if(C==1)
91 for(i=1;i<=n;i++)
92 g<<a[i].x<<’ ’;
93 else
94 {
95 sort(a+1,a+n+1, comp);
96
97 p10[0]=1;
98 for(i=1;i<=18;i++)
99 p10[i]=p10[i-1]*10;
100
101 for(i=n;i>=1;i--)
102 {
103 m=1;
104 s[m]=z=a[i].x;
105 c=a[i].n;
106 for(j=1;j<=c/2;j++)
107 if(CB(z/p10[j]%p10[c-2*j]))
108 s[++m]=z/p10[j]%p10[c-2*j];
109
110 if(m>mx)
111 {
112 mx=m;
113 for(j=1;j<=m;j++)
114 sol[j]=s[j];
115 }
116 }
117
118 if(C==2)
119 g<<mx<<’\n’;
120 else
121 for(i=mx;i>=1;i--)
122 g<<sol[i]<<’ ’;
123 }
124
125 return 0;
126 }

5.2.3 *Rezolvare detaliată


Capitolul 6

OJI 2017

6.1 tablou
Problema 1 - tablou 100 de puncte
Se consideră un tablou cu N linii şi N coloane (numerotate de la 1 la N ) care conţine valoarea
1 ı̂n fiecare dintre cele N  N celule. Valorile din tablou pot fi modificate prin aplicarea a două
operaţii codificate astfel:
a L nr, prin care se schimbă simultan toate semnele numerelor din linia cu numărul nr.
a C nr, prin care se schimbă simultan toate semnele numerelor din coloana cu numărul nr.

Cerinţe

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 finalul 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 modifică 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 fişierului de intrare conţine numerele N şi K, separate
printr-un spaţiu, iar următoarele K linii conţin fiecare câte o literă mare (L sau C) şi un număr
nr, separate printr-un spaţiu, reprezentând codificarea uneia dintre cele două operaţii (L nr sau
C nr).
a Dacă p 2 atunci linia a doua a fişierului de intrare conţine numerele N şi Z, separate
printr-un spaţiu.

Date de ieşire

a Dacă p 1, atunci fişierul de ieşire tablou.out conţine pe prima linie un număr natural,
reprezentând numărul valorilor pozitive din tabloul obţinut la finalul executării celor K operaţii
asupra tabloului iniţial (răspunsul la cerinţa 1).
a Dacă p 2, atunci fişierul de ieşire tablou.out conţine pe prima linie un număr natural
reprezentând numărul minim de operaţii L nr sau C nr, care, aplicate tabloului iniţial, ı̂l modifică
astfel ı̂ncât tabloul obţinut să conţină exact Z valori negative (răspunsul la cerinţa 2). Dacă prin
aplicarea de operaţii L nr sau C nr tabloului iniţial nu se poate obţine un tablou cu Z valori
negative, atunci, fişierul va conţine pe prima linie valoarea 0 (zero).

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 oficiu şi câte 45 puncte pentru rezolvarea corectă a fiecărei cerinţe.

47
CAPITOLUL 6. OJI 2017 6.1. TABLOU 48

Exemple
tablou.in tablou.out Explicaţii
1 10 N=4. La finalul aplicării succesiunii de K=4 operaţii, tablou
44 modificat are conţinutul:
L1 -1 1 1 1
L3 -1 1 1 1
C1 1 -1 -1 -1
L1 -1 1 1 1
Astfel, tabloul conţine 10 valori pozitive.
2 3 Sunt necesare minimum 3 operaţii, de exemplu:
35 L3
L1
C1
2 0 Nu există nicio succesiune de operaţii pentru care să se obţină
47 Z=7 valori negative.

Timp maxim de executare/test: 0.1 secunde


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

6.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,,N} pentru care exista un y
din {0,1,2,..,N} si cu proprietatea ca y=(Z-N*x)/(N-2*x) si N2*x

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

6.1.2 Cod sursă

Listing 6.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;
CAPITOLUL 6. OJI 2017 6.1. TABLOU 49

12
13 char x;
14 int i,y,n,v,k,l,c,s,p;
15
16 void solve1()
17 {
18 for(i=1;i<=k;++i)
19 {
20 fin>>x>>y;
21 if(x==’L’)
22 L[y]=L[y]ˆ1;
23 else
24 C[y]=C[y]ˆ1;
25 }
26
27 l=L.count();
28 c=C.count();
29 fout<<n*n-l*(n-c)-c*(n-l);
30 }
31
32 void solve2()
33 {
34 if(k>n*n)
35 {
36 fout<<0;
37 return;
38 }
39
40 for(s=k/n;s<=n;++s)
41 if((n*s-k)%2==0)
42 {
43 p=(n*s-k)/2;
44 y=sqrt(s*s-4*p);
45 if(y*y==s*s-4*p)
46 {
47 fout<<s;
48 return;
49 }
50 }
51
52 fout<<0;
53 }
54
55 int main()
56 {
57 fin>>v>>n>>k;
58
59 if(v==1)
60 solve1();
61 else
62 solve2();
63
64 fin.close();
65 fout.close();
66 return 0;
67 }

Listing 6.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 {
CAPITOLUL 6. OJI 2017 6.1. TABLOU 50

17 fin>>N>>K;
18 char LC;
19 int nr_val_pozitive,nr_val_negative,nr;
20 int i, linimpar=0, colimpar=0;
21
22 for(i=1; i<=K; i++)
23 {
24 fin>>LC>>nr;
25 if(LC==’L’)
26 linie[nr]=(linie[nr]+1)%2;
27 else
28 coloana[nr]=(coloana[nr]+1)%2;
29 }
30
31 for(i=1; i<=N; i++)
32 {
33 linimpar+=linie[i];
34 colimpar+=coloana[i];
35 }
36
37 nr_val_negative=linimpar*(N-colimpar)+(N-linimpar)*colimpar;
38 nr_val_pozitive=N*N - nr_val_negative;
39 fout<<nr_val_pozitive<<endl;
40 }
41
42 void cerinta2()
43 {
44 fin>>N>>Z;
45 int M,linimpar, colimpar,ok=0, i;
46
47 if(Z>N*N)
48 ok=0;
49 else
50 if(Z==N*N)
51 {
52 linimpar=N;
53 colimpar=0;
54 ok=1;
55 }
56 else
57 if(2*Z==N*N)
58 {
59 ok=1;
60 linimpar=N/2;
61 colimpar=0;
62 }
63 else
64 for(linimpar=0; (linimpar<=N) && (ok==0); linimpar++)
65 {
66 if(N-2*linimpar!=0 && (Z-N*linimpar)%(N-2*linimpar)==0)
67 {
68 colimpar=(Z-N*linimpar)/(N-2*linimpar);
69 if(colimpar>=0 && colimpar<=N)
70 {
71 ok=1;
72 break;
73 }
74 }
75 }
76
77 if(ok==0)
78 fout<<0<<endl;
79 else
80 fout<<linimpar+colimpar<<endl;
81 }
82
83 int main()
84 {
85 fin>>P;
86
87 if(P==1)
88 cerinta1();
89 else
90 cerinta2();
91
92 return 0;
CAPITOLUL 6. OJI 2017 6.2. TRIUNGHIURI 51

93 }

6.1.3 *Rezolvare detaliată

6.2 triunghiuri
Problema 2 - triunghiuri 100 de
puncte
Se consideră N puncte din plan, având coordonate numere naturale,
relativ la un reper cartezian XOY , oricare două puncte fiind distincte.

Cerinţe

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ătoarele condiţii: Figura 6.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 fişierul triunghiuri.in, care are următoarea structură:
a Pe prima linie se află numărul p, care indică cerinţa ce trebuie rezolvată (p are valoarea 1
sau 2);
a Pe a doua linie se află numărul natural N , reprezentând numărul punctelor date;
a Pe următoarele N linii se găsesc câte două valori naturale x y, separate prin câte un spaţiu,
reprezentând coordonatele punctelor date.

Date de ieşire

Fişierul triunghiuri.out va avea următoarea structură:


Dacă p 1 se va scrie ı̂n fişier, pe prima linie, numărul maxim de puncte care au aceeaşi
abscisă (cerinţa 1).
Dacă p 2 se va scrie ı̂n fiş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

a3 & N & 100000


a0 & x $ 1000
a 0 & y $ 1000
a Se acordă 10 puncte din oficiu, 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
CAPITOLUL 6. OJI 2017 6.2. TRIUNGHIURI 52

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 fiş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

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. 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).
CAPITOLUL 6. OJI 2017 6.2. TRIUNGHIURI 53

6.2.2 Cod sursă

Listing 6.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
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
CAPITOLUL 6. OJI 2017 6.2. TRIUNGHIURI 54

71 return 0;
72 }

Listing 6.2.2: triCS.cpp


1 ///prof. Cristina Sichim
2 #include <fstream>
3 #include <vector>
4
5 #define MOD 1000003
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 }

6.2.3 *Rezolvare detaliată


Capitolul 7

OJI 2016

7.1 arma
Problema 1 - arma 100 de puncte
În anul 2214 a izbucnit primul război interstelar. Pământul a fost atacat de către n civilizaţii
extraterestre, pe care le vom numerota pentru simplicitate de la 1 la n.
Pentru a se apăra, pământenii au inventat o armă specială ce poate fi ı̂ncărcată cu proiectile de
diferite greutăţi, fabricate dintr-un material special denumit narun. Dacă arma este programată la
p
nivelul p, atunci un proiectil de greutate k va ajunge exact la distanţa k km (k la puterea p) faţă
de Pământ şi dacă ı̂n acel punct se află cartierul general al unui atacator, acesta va fi distrus. De
exemplu, dacă arma este programată la nivelul 2, un proiectil de greutate 10 va distruge cartierul
2
general al extratereştrilor situat la distanţa 10 100 km de Pământ.
Arma poate fi ı̂ncărcată cu proiectile de diferite greutăţi, dar cum narunul este un material
foarte rar şi foarte scump, pământenii vor să folosească proiectile cât mai uşoare pentru a distruge
cartierele generale inamice.

Cerinţe

Cunoscându-se n, numărul atacatorilor, precum şi cele n distanţe până la cartierele generale
ale acestora, să se scrie un program care determină:
1. cantitatea minimă de narun necesară pentru a distruge toate cartierele generale inamice;
2. nivelurile la care trebuie programată arma, pentru a distruge fiecare cartier general inamic
cu o cantitate minimă de narun.

Date de intrare

Fişierul de intrare arma.in conţine pe prima linie un număr natural c reprezentând cerinţa care
trebuie să fie rezolvată (1 sau 2). Pe cea de a doua linie se află numărul natural n, reprezentând
numărul atacatorilor. Pe următoarele n linii se află n numere naturale, câte un număr pe o linie;
pe cea de a i-a linie dintre cele n 1 & i & n se află distanţa faţă de Pământ a cartierului general
al celei de a i-a civilizaţii extraterestre.

Date de ieşire

Dacă cerinţa este c 1, atunci pe prima linie a fişierului arma.out va fi scris un număr
natural reprezentând cantitatea minimă de narun necesară distrugerii tuturor cartierelor generale
inamice.
Dacă cerinţa este c 2, atunci fişierul de ieşire arma.out va conţine n linii. Pe a i-a linie
(1 & i & n) se va scrie nivelul la care trebuie programată arma pentru a distruge cartierul general
al celei de a i-a civilizaţii extraterestre.

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.

55
CAPITOLUL 7. OJI 2016 7.1. ARMA 56

Exemple
arma.in arma.out Explicaţii
1 122 Primul cartier general se poate distruge cu un proiectil de greutate
5 10, programat la nivelul 2, al doilea obiectiv cu un proiectil de
100 greutate 97 programat la nivelul 1, al treilea cu un proiectil de
97 greutate 5 programat la nivelul 4, al patrulea cu un proiectil de
625 greutate 7 programat la nivelul 9, iar ultimul cu un proiectil de
40353607 greutate 3 programat la nivelul 4. Cantitatea minimă de narun
81 necesară este 10+97+5+7+3=122.
2 2 Primul cartier general se poate distruge cu un proiectil de greutate
5 1 10, programat la nivelul 2, al doilea obiectiv cu un proiectil de
100 4 greutate 97 programat la nivelul 1, al treilea cu un proiectil de
97 9 greutate 5 programat la nivelul 4, al patrulea cu un proiectil de
625 4 greutate 7 programat la nivelul 9, iar ultimul cu un proiectil de
40353607 greutate 3 programat la nivelul 4. Nivelurile sunt ı̂n ordine: 2 1 4
81 94

Timp maxim de executare/test: 0.6 secunde


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

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

7.1.2 Cod sursă

Listing 7.1.1: arma.cpp


1 //Sandor Lukacs 100 puncte
2 #include <iostream>
CAPITOLUL 7. OJI 2016 7.1. ARMA 57

3 #include <fstream>
4
5 using namespace std;
6
7 ifstream f("arma.in");
8 ofstream g("arma.out");
9
10 int n,v[10002],pr[50000],nr,c[50000],opt,niv; //pr - sirul numerelor prime
11 int fp[50000]; //fp[i] - retine nivelul maxim la care trebuie programata arma
12 // pt a distuge inamicul i
13 long long s; //cantitatea minima necesara
14
15 void ciur()
16 {
17 long long i,j;
18 nr = 0;
19 pr[++nr] = 2;
20 for(i = 3; i <=48000; i = i + 2)
21 {
22 if(c[i] == 0)
23 {
24 pr[++nr] = i;
25 for(j = i; j*i <= 48000; j = j + 2)
26 c[i*j] = 1;
27 }
28 }
29 }
30
31 int cmmdc(int a, int b)
32 {
33 int r;
34 while(b != 0)
35 {
36 r = a % b;
37 a = b;
38 b = r;
39 }
40 return a;
41 }
42
43 int nivel(int d)
44 {//determin cmmdc al puterilor factorilor primi din descompunerea lui d
45 int i,rez, p;
46 rez = 0;//rezultatul
47
48 for(i = 1; i <= nr && pr[i]*pr[i] <= d;++i)
49 {
50 p = 0;
51 while(d % pr[i] == 0)
52 {//cat timp pr[i] este factor prim
53 p = p + 1;//cresc puterea
54 d = d / pr[i]; //noua valoare
55 }
56
57 if(p != 0)
58 {//daca pr[i] apare in descompunere
59 if(p == 1)
60 return 1; //daca apare la puterea 1 nu mai continui
61 else
62 {
63 rez = cmmdc(rez,p); //retin cmmdc al puterilor
64 }
65 }
66 }
67
68 if(rez == 0 || d != 1)
69 return 1;//daca e numar prim sau mai avem un factor primnivelul e 1
70 else
71 return rez;
72 }
73
74 long long x_la_p(long long x, int p, int d)
75 {
76 long long y = x, rasp = 1;
77 while(p > 1)
78 {
CAPITOLUL 7. OJI 2016 7.1. ARMA 58

79 if(p % 2 == 1)
80 rasp = rasp*y;
81 y = y * y;
82 if(y > d)
83 return d + 1;//daca depasesc d nu mai calculez
84 p = p/2;
85 }
86
87 return rasp*y;
88 }
89
90 int cant_minim(int d, int p)
91 {
92 // pentru a determina cantitatea minima trebuie sa determin
93 // o valoare x, a.i. xˆp = d
94 long long x,ls,ld,y;
95 if(p == 1)
96 return d;//daca e numar prim
97
98 x = d;
99 y = 1;
100 //calculez [sqrt(d)]
101 while(x > y)
102 {
103 x = (x + y) / 2;
104 y = d / x;
105 }
106
107 if(p == 2)
108 return x;//daca e patrat perfect
109
110 ls = 1;
111 ld = x;
112 y = x_la_p(x,p,d);
113 while(y != d)
114 {
115 if(y > d)
116 ld = x;
117
118 if(d > y)
119 ls = x;
120
121 x = ls + (ld - ls)/2;
122 y = x_la_p(x,p,d);// calculez x ˆ p
123 }
124
125 return x;
126 }
127
128 int main()
129 {
130 int i;
131 ciur();
132 f>>opt>>n;
133 for(i = 1; i <= n; ++i)
134 {
135 f>>v[i];
136 niv = nivel(v[i]);
137 fp[i] = niv;
138 }
139
140 if(opt == 1)
141 {
142 s = 0; //initial
143 for(i = 1; i <= n; ++i)
144 s = s + cant_minim(v[i],fp[i]);
145 g<<s;
146 }
147
148 if(opt == 2)
149 {
150 for(i = 1; i <= n; ++i)
151 g<<fp[i]<<"\n";
152 }
153
154 return 0;
CAPITOLUL 7. OJI 2016 7.1. ARMA 59

155 }

Listing 7.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 }
CAPITOLUL 7. OJI 2016 7.1. ARMA 60

72 }
73
74 if (d>1)
75 {
76 fp[nrf]=d;
77 mf[nrf++]=1;
78 }
79
80 p=cmmdc();
81 c=1;
82 for (j=0; j<nrf; j++)
83 c*=putere(fp[j],mf[j]/p);
84 }
85
86 void afisare()
87 {
88 int i;
89 if (cerinta==1)
90 fout<<cant<<’\n’;
91 else
92 for (i=0; i<n; i++)
93 fout<<p[i]<<’\n’;
94 fout.close();
95 }
96
97 int cmmdc()
98 {
99 int d=mf[0], i;
100 for (i=1; i<nrf; i++)
101 d=euclid(d,mf[i]);
102 return d;
103 }
104
105 int euclid(int a, int b)
106 {
107 int r;
108 while (b)
109 {
110 r=a%b;
111 a=b;
112 b=r;
113 }
114
115 return a;
116 }
117
118 int putere(int x, int y)
119 {
120 int p;
121 if (y==0)
122 return 1;
123 p=putere(x,y/2);
124 if (y%2)
125 return x*p*p;
126
127 return p*p;
128 }

Listing 7.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];
CAPITOLUL 7. OJI 2016 7.1. ARMA 61

16 int fp[NRMAXPRIME];
17 int mf[NRMAXPRIME];
18 int p[NMAX];
19 long long int cant;
20
21 void eratostene();
22 void afisare();
23 void determina(int d, int &p, int &c);
24 int cmmdc();
25 int euclid(int a, int b);
26 int putere(int x, int y);
27
28 int main()
29 {
30 int i, d, c;
31 eratostene();
32 fin>>cerinta>>n;
33 for (i=0; i<n; i++)
34 {
35 fin>>d;
36 determina(d,p[i],c);
37 cant+=c;
38 }
39
40 afisare();
41 return 0;
42 }
43
44 void eratostene()
45 {int d, j;
46 for (d=2; d*d<VMAX; d++)
47 if (!ciur[d])
48 for (j=d*d; j<VMAX; j+=d)
49 ciur[j]=1;
50 //transfer intr-un vector numerele prime <=vmax
51 prim[0]=2; nrprim=1;
52 for (d=3; d<VMAX; d+=2)
53 if (!ciur[d]) prim[nrprim++]=d;
54 }
55
56 void determina(int d, int &p, int &c)
57 {int m, j;
58 nrf=0;
59 for (j=0; j<nrprim && prim[j]*prim[j]<=d; j++)
60 {
61 for (m=0; d%prim[j]==0; m++,d/=prim[j]);
62 if (m)
63 {
64 fp[nrf]=prim[j];
65 mf[nrf++]=m;
66 }
67 }
68 if (d>1)
69 {
70 fp[nrf]=d;
71 mf[nrf++]=1;
72 }
73 p=cmmdc();
74 c=1;
75 for (j=0; j<nrf; j++)
76 c*=putere(fp[j],mf[j]/p);
77 }
78
79 void afisare()
80 {int i;
81 if (cerinta==1)
82 fout<<cant<<’\n’;
83 else
84 for (i=0; i<n; i++) fout<<p[i]<<’\n’;
85 fout.close();
86 }
87
88 int cmmdc()
89 {int d=mf[0], i;
90 for (i=1; i<nrf; i++)
91 d=euclid(d,mf[i]);
CAPITOLUL 7. OJI 2016 7.2. KS 62

92 return d;
93 }
94
95 int euclid(int a, int b)
96 {int r;
97 while (b)
98 {
99 r=a%b; a=b; b=r;
100 }
101 return a;
102 }
103
104 int putere(int x, int y)
105 {int p=1;
106 while (y)
107 {
108 if (y%2) {p*=x; y--;}
109 else {x*=x;y/=2;}
110 }
111 return p;
112 }

7.1.3 *Rezolvare detaliată

7.2 ks
Problema 2 - ks 100 de puncte
Ana şi Bogdan au inventat din nou un joc, pe care l-au denumit ks. Pe tabla de joc sunt
plasate pe poziţii consecutive n jetoane, pe fiecare jeton fiind scris un număr natural nenul.
Ana este prima la mutare şi are voie să extragă de pe tablă exact k jetoane situate pe poziţii
consecutive.
Bogdan mută al doilea şi are şi el voie să extragă exact k jetoane, dintre cele rămase pe tablă,
situate de asemenea pe poziţii consecutive.
Punctajul asociat unei mutări este egal cu suma numerelor scrise pe jetoanele extrase la mu-
tarea respectivă.
Scopul Anei este să efectueze mutarea sa astfel ı̂ncât punctajul obţinut de Bogdan să fie cât
mai mic. Considerăm că atât Ana, cât şi Bogdan joacă optim.

Cerinţe

Cunoscând numărul de jetoane de pe tabla de joc, valorile ı̂nscrise pe acestea, precum şi
valoarea k, scrieţi un program care să determine care este cel mai bun punctaj pe care Bogdan ı̂l
poate obţine, ştiind că ambii jucători joacă optim.

Date de intrare

Fişierul de intrare ks.in conţine pe prima linie două numere naturale separate prin spaţiu n
k, având semnificaţia din enunţ. Pe cea de a doua linie se află n valori naturale nenule, separate
prin câte un spaţiu, reprezentând valorile ı̂nscrise pe cele n jetoane, ı̂n ordinea ı̂n care acestea sunt
plasate pe tabla de joc.

Date de ieşire

Fişierul de ieşire ks.out va conţine o singură linie pe care va fi scris un număr natural
reprezentând punctajul maxim pe care ı̂l poate obţine Bogdan la mutarea sa, ştiind că ambii
jucători joacă optim.

Restricţii şi precizări

a 3 & n & 100000


a 1 & k & n©3
a Valorile ı̂nscrise pe jetoane sunt numere naturale nenule & 10 .
9

a După ce Ana extrage jetoanele sale, jetoanele rămase pe tablă ı̂şi vor păstra poziţiile iniţiale.
CAPITOLUL 7. OJI 2016 7.2. KS 63

Exemple

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

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

7.2.2 Cod sursă

Listing 7.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;
CAPITOLUL 7. OJI 2016 7.2. KS 64

13
14 int main()
15 {
16 fin>>n>>k;
17
18 fill(s,s+n+2,gol);
19
20 for(i=1;i<=n;i++)
21 {
22 fin>>a[i];su+=a[i];
23 if(i>=k){su-=a[i-k];s[i-k+1]=su;}
24 }
25
26 st[k]=s[1];
27 for(i=k+1;i<=n;i++)
28 st[i]=max(st[i-1],s[i-k+1]);
29
30 dr[n-k+1]=s[n-k+1];
31 for(i=n-k;i>0;i--)
32 dr[i]=max(dr[i+1],s[i]);
33
34 for(i=2;i<=n-k+1;i++)
35 rez=min(rez,max(st[i-1],dr[i+k]));
36
37 fout<<rez;
38 return 0;
39 }

Listing 7.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 }
CAPITOLUL 7. OJI 2016 7.2. KS 65

46
47 for (i=n; i>n-K+1; i--) maxdr[i]=-1;
48 maxdr[n-K+1]=S[n-K+1];
49 for (i=n-K; i>0; i--)
50 if (S[i]>maxdr[i+1])
51 maxdr[i]=S[i];
52 else
53 maxdr[i]=maxdr[i+1];
54 }
55
56 long long int rezolva()
57 {
58 long long int optim=maxdr[K+1], maxim;
59 int i;
60 for (i=2; i<K; i++)
61 if (optim>maxdr[i+K]) optim=maxdr[i+K];
62
63 for (i=K; i<=n-K+1; i++)
64 {//Ana ia o secventa de K jetoane incepand cu pozitia i
65 maxim=maxst[i-1];
66 if (maxim<maxdr[i+K]) maxim=maxdr[i+K];
67 if (optim>maxim) optim=maxim;
68 }
69
70 for (i=n-K+2; i<=n; i++)
71 if (optim>maxst[i-1]) optim=maxst[i-1];
72
73 return optim;
74 }

7.2.3 *Rezolvare detaliată


Capitolul 8

OJI 2015

8.1 dominant
Problema 1 - dominant 100 de puncte
Considerând un şir de valori binare, numim secvenţă dominantă un set de elemente aflate pe
poziţii consecutive ı̂n şir care are proprietatea că numărul valorilor egale cu 1 este strict mai mare
decât numărul valorilor de 0. De exemplu, ı̂n şirul 1,0,0,0,1,1,0,1,1,1,0,0 o secvenţă dominantă
este 0,1,1 şi o alta, de lungime mai mare, este 0,1,1,0,1,1,1. Secvenţa dominantă maximală este
secvenţa dominantă de lungime maximă. În şirul din exemplu secvenţa dominantă maximală este
1,0,0,0,1,1,0,1,1,1,0 (adică ı̂ntreg şirul, fără ultimul zero).
Cerinţe
Dat un şir de valori binare, să se determine lungimea unei secvenţe dominante maximale
precum şi numărul acestor secvenţe.
Date de intrare
Fişierul dominant.in conţine pe prima linie un număr natural V , iar pe linia a doua şirul de
valori binare, fără spaţii.

Date de ieşire
Fişierul dominant.out va conţine:
a varianta 1: dacă V 1, atunci pe prima linie a fişierului de ieşire va fi un singur număr
natural reprezentând lungimea unei secvenţe dominante maximale.
a varianta 2: dacă V 2, atunci pe prima linie a fişierului de ieşire va fi un singur număr
natural reprezentând numărul secvenţelor dominante maximale.
Restricţii şi precizări
a V " r1, 2x
a Lungimea şirului de valori binare este de cel mult 300 000.
a Pentru toate testele şirul binar va conţine cel puţin o valoare de 1.
a Pentru 60% din punctaj V 1.

Exemple
dominant.in dominant.out Explicaţii
1 100011011100 11 Secvenţa dominantă maximală este 10001101110 şi
are lungimea 11.
2 100011011100 1 Secvenţa dominantă maximală este 10001101110 şi
are lungimea 11. Este o singură secvenţă dominantă
maximală.
1 9 Secvenţa dominantă maximală are lungime 9;
0000110000111 aceasta este 110000111.
2 10000111000 3 Secvenţa dominantă maximală are lungimea 5. Sunt
trei secvenţe dominante maximale: 00111, 01110 şi
11100.

66
CAPITOLUL 8. OJI 2015 8.1. DOMINANT 67

Timp maxim de executare/test: 0.3 secunde


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

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

8.1.2 Cod sursă

Listing 8.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 8. OJI 2015 8.1. DOMINANT 68

24 n = 1;
25 while (fin >> s[n])
26 n++;
27 n--;
28 s[0] = ’*’;
29 fin.close();
30
31 st = x + Nmax;
32 dr = y + Nmax;
33
34 // initializare st si dr
35 for (i = -n; i <= n; ++i)
36 {
37 st[i] = 10000000;
38 dr[i] = -10000000;
39 }
40
41 // calcul st si dr
42 // st[i] = cea mai din stanga pozitie unde apare valoarea i
43 // dr[i] = cea mai din dreapta pozitie unde apare valoarea i
44 st[0] = dr[0] = 0;
45 suma = 0;
46 vmin = 10000000;
47 vmax = -10000000;
48 for (i = 1; s[i]; ++i)
49 {
50 if (s[i] == ’0’)
51 suma--;
52 else
53 suma++;
54
55 st[suma] = min(st[suma], i);
56 dr[suma] = max(dr[suma], i);
57 vmin = min(vmin, suma);
58 vmax = max(vmax, suma);
59 }
60
61 //for (i = vmin; i <= vmax; i++)
62 // cout << i << " : " << st[i] << " " << dr[i] << "\n";
63
64 // lungimea maxima a secventei
65 lgMax = 0;
66 for (i = vmin; i < vmax; i++)
67 lgMax = max(lgMax, dr[i+1] - st[i]);
68
69 // numarul de aparitii ale secventei maximale
70 int s0, s1;
71 s0 = s1 = suma = 0;
72 for (i = 1; i <= lgMax; ++i)
73 if (s[i] == ’0’)
74 s0++;
75 else
76 s1++;
77
78 if (s1 > s0) suma++;
79
80 for (i = lgMax + 1; s[i]; ++i)
81 {
82 if (s[i-lgMax] == ’0’)
83 s0--;
84 else
85 s1--;
86
87 if (s[i] == ’0’)
88 s0++;
89 else
90 s1++;
91
92 if (s1 > s0) suma++;
93 }
94
95 ofstream fout(outFile);
96 if (optiune == 1)
97 fout << lgMax << "\n";
98 else
99 fout << suma << "\n";
CAPITOLUL 8. OJI 2015 8.1. DOMINANT 69

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

Listing 8.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 8. OJI 2015 8.1. DOMINANT 70

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 8.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 8. OJI 2015 8.1. DOMINANT 71

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 8.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 8. OJI 2015 8.1. DOMINANT 72

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 8.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 8. OJI 2015 8.1. DOMINANT 73

31 v[i] = v[i-1] + 1;
32 else
33 v[i] = v[i-1] - 1;
34
35 w[i] = make_pair(v[i], i);
36 }
37
38 sort(w, w+n+1);
39
40 j = 1;
41 for (i=2;i<=n;i++)
42 if (w[i].first != w[j].first)
43 {
44 w[++j] = w[i];
45 } else
46 if (w[i].first == w[j-1].first)
47 w[j] = w[i];
48 else
49 w[++j] = w[i];
50
51 if (w[1].second < w[0].second)
52 {
53 minim1 = w[1].second;
54 minim2 = w[0].second;
55 }
56 else
57 {
58 minim1 = w[0].second;
59 minim2 = w[1].second;
60
61 }
62
63 n = j;
64 maxim = -INF;
65 for (i=2;i<=n;i++)
66 {
67 if (w[i-1].second != minim1)
68 {
69 maxim = max(maxim, w[i].second-minim1);
70 }
71 else
72 {
73 maxim = max(maxim, w[i].second-minim2);
74 }
75
76 if (w[i].second < minim1)
77 {
78 minim2 = minim1;
79 minim1 = w[i].second;
80 }
81 else
82 if (w[i].second < minim2)
83 minim2 = w[i].second;
84 }
85
86 for (i=maxim;i<=m;i++)
87 if (v[i] - v[i-maxim] > 0)
88 nr++;
89
90 if (V == 1)
91 fout<<maxim<<"\n";
92 else
93 fout<<nr<<"\n";
94
95 return 0;
96 }

8.1.3 *Rezolvare detaliată


CAPITOLUL 8. OJI 2015 8.2. PAVARE 74

8.2 pavare
Problema 2 - pavare 100 de puncte
Ca ı̂n mai toate poveştile, Făt-Frumos a căutat o Cosânzeană şi a găsit-o, dar tatăl ei i-a cerut
să-i paveze drumul de lungime N care leagă castelele sale. Dalele cu care va pava drumul au
aceeaşi lăţime (egală cu lăţimea drumului) şi lungimi numere naturale. Fiind un ı̂mpărat cam
sâcâit, acesta doreşte ca pavarea să se facă folosind un număr minim de dale, diferenţa de lungime
ı̂ntre două dale vecine să nu fie mai mare ca 1, iar prima şi ultima dală să fie de lungime 1.
Împăratul nu se mulţumeşte să primească de la Făt-Frumos doar un număr (numărul minim de
dale necesare): el vrea şi posibilitatea de pavare cea mai mică din punct de vedere lexicografic.
Compararea lexicografică a două şiruri de numere este o extensie la numere a comparării
alfabetice a două cuvinte. Astfel, fiind date două şiruri numerice de aceeaşi lungime, A1 , A2 , ...,
Am şi B1 , B2 , ..., Bm , acestea sunt egale dacă şi numai dacă Ai Bi pentru orice i de la 1 la
m. Şirul A este mai mic lexicografic decât şirul B dacă există o valoare k astfel ı̂ncât Ak $ Bk
şi Ai Bi pentru orice i de la 1 la k  1. De exemplu, şirul 3, 5, 4, 1 este mai mare lexicografic
decât şirul 3, 5, 2, 9 pentru că prima poziţie pe care valorile diferă este poziţia 3 (4 % 2), fără a
mai conta valorile aflate după aceasta.

Cerinţe

Cunoscând lungimea drumului, determinaţi numărul minim de dale necesare pavării şi posibil-
itatea de pavare cu număr minim de dale, care este cea mai mică din punct de vedere lexicografic.

Date de intrare

Prima linie a fişierului pavare.in conţine un număr natural V . Linia a doua conţine un număr
natural N ce reprezintă lungimea drumului.

Date de ieşire

Dacă V va avea valoarea 1, ı̂n fişierul pavare.out se va scrie, pe prima linie, doar numărul
minim de dale necesare pavării.
Dacă V va avea valoarea 2, ı̂n fişierul pavare.out se va scrie, pe prima linie, un şir de numere
separate prin câte un spaţiu, ce reprezintă soluţia de pavare a drumului, folosind un număr minim
de dale, care este cea mai mică din punct de vedere lexicografic.

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

8.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
CAPITOLUL 8. OJI 2015 8.2. PAVARE 75

Dist(x) 1 2 4 6 9 12 16 20 25
Dale 1 11 121 1221 12321 123321 1234321 12344321 123454321

Pentru o distanta data n cautam cel mai mic x pentru care Dist(x)>=n.

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

8.2.2 Cod sursă

Listing 8.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;
CAPITOLUL 8. OJI 2015 8.2. PAVARE 76

32
33 // calculez un numar k a.i 1,2,..k,k,k-1,...1 sa fie cel mult N
34 for( K = 1; K*(K+1) <= CN ; ++K);
35 K--;
36 NrDale += 2*K;
37 CN = CN - K*(K+1);
38
39 //incerc sa asez dale cat mai mari care sa acopere distanta ramasa
40 Dala = K+1;
41 while(CN && Dala > 0)
42 {
43 while(CN >= Dala) { CN -= Dala; NrDale++;}
44 Dala--;
45 }
46 OU<<NrDale<<’\n’;
47 }
48 else
49 {
50 //rezolvarea cerintei 2
51 for(i = 1; i < NMAX; ++i) NrApar[i] = 0;
52 CN = N;
53 NrDale = 0;
54
55 // calculez un numar k a.i 1,2,..k,k,k-1,...1 sa fie cel mult N
56 for( K = 1; K*(K+1) <= CN ; ++K);
57 K--;
58 NrDale += 2*K;
59 CN = CN - K*(K+1);
60 for( i = 1; i <= K; ++i) NrApar[i] += 2;
61
62 //incerc sa asez dale cat mai mari care sa acopere distanta ramasa
63 Dala = K+1;
64 while(CN && Dala > 0)
65 {
66 while(CN >= Dala) { CN -= Dala; NrApar[Dala]++; NrDale++;}
67 Dala--;
68 }
69
70 for ( i = 1; i<NMAX; ++i)
71 {
72 if(NrApar[i] % 2 == 1)
73 {
74 for( K = 1; K <= NrApar[i]/2 + 1; ++K) OU<<i<<’ ’;
75 NrApar[i] /= 2;
76 }
77 else
78 {
79 for( K = 1; K <= NrApar[i]/2; ++K) OU<<i<<’ ’;
80 NrApar[i] /= 2;
81 }
82 }
83
84 for ( i = NMAX - 1; i>0; --i)
85 {
86 for( K = 1; K <= NrApar[i]; ++K) OU<<i<<’ ’;
87 }
88 }
89
90 OU.close();
91 return 0;
92 }

Listing 8.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)
CAPITOLUL 8. OJI 2015 8.2. PAVARE 77

12 {
13 int i;
14 if (v[k-1] == 1)
15 {
16 if (k-1 < minim)
17 {
18 minim = k-1;
19 for (i=1;i<=k-1;i++)
20 {
21 vminim[i] = v[i];
22 }
23 }
24 else
25 if (k-1 == minim)
26 {
27 for (i=1;i<=k-1;i++)
28 if (v[i] != vminim[i])
29 break;
30
31 if (v[i] < vminim[i])
32 {
33 for (i=1;i<=k-1;i++)
34 {
35 vminim[i] = v[i];
36 }
37 }
38 }
39 }
40 }
41 else
42 {
43 for (int i=v[k-1]-1;i<=v[k-1]+1;i++)
44 {
45 if (i == 0)
46 continue;
47
48 if (i <= s)
49 {
50 s-=i;
51 v[k] = i;
52 back(k+1);
53 s+=i;
54 }
55 }
56 }
57 }
58
59 ifstream fin ("pavare.in");
60 ofstream fout("pavare.out");
61
62 int main()
63 {
64 fin>>V;
65 fin>>n;
66 minim = INF;
67 v[1] = 1;
68 s = n-1;
69
70 back(2);
71
72 if (V == 1)
73 fout<<minim<<"\n";
74 else
75 {
76 for (i=1;i<=minim;i++)
77 fout<<vminim[i]<<" ";
78 fout<<"\n";
79 }
80 }

Listing 8.2.3: pavareCT.cpp


1 #include <iostream>
2 #include <fstream>
3
CAPITOLUL 8. OJI 2015 8.2. PAVARE 78

4 using namespace std;


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 8.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 8. OJI 2015 8.2. PAVARE 79

Listing 8.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 8.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 8. OJI 2015 8.2. PAVARE 80

12
13 int main()
14 {
15 fin>>V;
16 fin>>x;
17
18 r = (long long)((1 + sqrt(4*x))/2);
19 //cout<<r;
20
21 if (x <= (r*(r-1) + r) )
22 {
23
24 if (V == 1)
25 {
26 fout<<2*r-1<<"\n";
27 return 0;
28 }
29
30 if (x == r*(r-1))
31 {
32 for (i = 1; i<=r-1; i++)
33 fout<<i<<" ";
34
35 for (i=r-1;i>=1;i--)
36 fout<<i<<" ";
37 }
38 else
39 {
40 k = x - (r*(r-1));
41 for (i=1;i<=k;i++)
42 fout<<i<<" ";
43
44 for (i=k;i<=r-1;i++)
45 fout<<i<<" ";
46
47 for (i=r-1;i>=1;i--)
48 fout<<i<<" ";
49 }
50
51 }
52 else
53 {
54 if (V == 1)
55 {
56 fout<<2*r<<"\n";
57 return 0;
58 }
59
60 k = x - (r*(r-1)) - r;
61
62 for (i=1;i<=k;i++)
63 fout<<i<<" ";
64
65 for (i=k;i<=r;i++)
66 fout<<i<<" ";
67
68 for (i=r-1;i>=1;i--)
69 fout<<i<<" ";
70 }
71
72 return 0;
73 }

8.2.3 *Rezolvare detaliată


Capitolul 9

OJI 2014

9.1 arrows
Problema 1 - arrows 100 de
puncte
”Arrows” este un joc care se joacă pe o tablă dreptunghiulară a cărei
suprafaţă este ı̂mpărţită ı̂n N  M celule, aranjate pe N linii şi M coloane.
În fiecare celulă se află o săgeată (sus, jos, stânga sau dreapta), ca ı̂n figura
alăturată.
Când este la mutare, un jucător poate alege o poziţie de start pe care
plasează un jeton, apoi deplasează jetonul la celula ı̂nvecinată ı̂n sensul in-
dicat de săgeată. Deplasarea continuă până când jetonul părăseşte tabla
de joc, caz ı̂n care jucătorul obţine un punctaj egal cu numărul de celule
parcurse de jetonul său.
Există ı̂nsă poziţii de start denumite favorabile, pentru care jetonul nu
Figura 9.1: arrows
va părăsi niciodată tabla de joc. De exemplu, toate poziţiile din figură cu
fundal gri sunt favorabile. Jucătorul care alege o poziţie de start favorabilă obţine un punctaj egal
cu numărul de celule distincte vizitate ı̂nmulţit cu 1000.
Cerinţe
Scrieţi un program care, cunoscând configuraţia tablei de joc, rezolvă una dintre următoarele
cerinţe:
1. determină punctajul pe care ı̂l obţine un jucător care plasează jetonul său pe o poziţie de
start specificată;
2. determină numărul de celule favorabile de pe tabla de joc;
3. determină punctajul maxim pe care jucătorul ı̂l poate obţine la o mutare, alegând convenabil
poziţia de start.
Date de intrare
Fişierul de intrare arrows.in conţine pe prima linie cerinţa care trebuie să fie rezolvată (1, 2 sau
3). Pe a doua linie se află numerele naturale N M , care reprezintă numărul de linii şi respectiv de
coloane de pe tabla de joc. Pe următoarele N linii se află câte M numere din mulţimea r1, 2, 3, 4x
reprezentând săgeţile aflate ı̂n celulele de pe tabla de joc (1 semnificând săgeata la dreapta, 2
săgeata ı̂n sus, 3 săgeata la stânga şi 4 săgeata ı̂n jos). Pe ultima linie sunt scrise numerele
naturale lin col, reprezentând linia şi coloana pe care se află poziţia de start specificată. Valorile
scrise pe aceeaşi linie ı̂n fişierul de intrare sunt separate prin spaţii.
Date de ieşire
Fişierul de ieşire arrows.out va conţine o singură linie pe care va fi scris un număr natural
reprezentând răspunsul pentru cerinţa specificată pe prima linie a fişierului de intrare.
Restricţii şi precizări
a 1 & N, M & 500
a Liniile sunt numerotate de la 1 la N , iar coloanele de la 1 la M .
a Punctaj. Pentru teste valorând 20 de puncte cerinţa este 1. Pentru teste valorând 40 de
puncte cerinţa este 2. Pentru celelalte teste, valorând de asemenea 40 de puncte, cerinţa este 3.

81
CAPITOLUL 9. OJI 2014 9.1. ARROWS 82

Exemple
arrows.in arrows.out Explicaţii
1 2000 Exemplul corespunde tablei de joc din figură.
6 5 Punctajele pentru fiecare poziţie sunt:
3 1 1 4 2 1 14000 14000 14000 1
1 2 4 3 1 15000 14000 14000 14000 1
4 2 1 1 4 16000 14000 14000 14000 14000
1 2 3 3 3 15000 14000 14000 14000 14000
3 1 4 4 4 1 4000 4000 2 2000
2 2 3 4 2 2 4000 4000 1 2000
5 5 Cerinţa este 1: punctajul care se obţine plecând din poziţia de
start aflată pe linia 5 şi coloana 5 este 2000.
2 23 Cerinţa este 2: există 23 de poziţii favorabile.
6 5
3 1 1 4 2
1 2 4 3 1
4 2 1 1 4
1 2 3 3 3
3 1 4 4 4
2 2 3 4 2
5 5
3 16000 Cerinţa este 3: punctajul maxim se poate obţine plasând jetonul
6 5 ı̂n punctul de start de pe linia 3 şi coloana 1.
3 1 1 4 2
1 2 4 3 1
4 2 1 1 4
1 2 3 3 3
3 1 4 4 4
2 2 3 4 2
5 5

Timp maxim de executare/test: 0.5 secunde


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

9.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};
CAPITOLUL 9. OJI 2014 9.1. ARROWS 83

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


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.
CAPITOLUL 9. OJI 2014 9.1. ARROWS 84

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

9.1.2 Cod sursă

Listing 9.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;
CAPITOLUL 9. OJI 2014 9.1. ARROWS 85

56 }
57
58 void citire()
59 {int i, j, x;
60 fin>>cerinta>>n>>m;
61 assert(1<= cerinta && cerinta <=3);
62 assert(0<n && n<501);
63 assert(0<m && m<501);
64
65 for (i=1; i<=n; i++)
66 for (j=1; j<=m; j++)
67 {
68 assert(fin>>x);
69 a[i][j]=x;
70 assert(x>0 && x<5);
71 }
72
73 fin>>xs>>ys;
74 assert(0<xs && xs<=n);
75 assert(0<ys && ys<=m);
76 assert(!(fin>>i));
77 }
78
79 int goxy(int x, int y)
80 {
81 int sum=0, dir, cx=x, cy=y, s;
82
83 while (a[x][y] && !pct[x][y])
84 {pct[x][y]=1; sum++;
85 dir=a[x][y];
86 x+=dl[dir]; y+=dc[dir];
87 }
88
89 //completarea punctajelor in pct
90 if (!a[x][y] || pct[x][y]<0) //pozitie nefavorabila
91 {sum-=pct[x][y]; x=cx, y=cy; s=-sum;
92 while (pct[x][y]==1)
93 {pct[x][y]=s; s++;
94 dir=a[x][y];
95 x+=dl[dir]; y+=dc[dir];
96 }
97
98 return -sum; //pozitie nefavorabila
99 }
100
101 if (pct[x][y]==1) //pozitie favorabila de pe acelasi traseu
102 {
103 //se formeaza un circuit care contine pozitia x,y
104 //toate punctele de pe circuit se marcheaza cu acelasi punctaj
105 s=sum;
106 while (cx!=x || cy!=y)
107 {
108 pct[cx][cy]=s; s--;
109 dir=a[cx][cy];
110 cx+=dl[dir]; cy+=dc[dir];
111 }
112
113 //punctele de pe circuit le marchez cu s
114 while (pct[x][y]==1)
115 {pct[x][y]=s;
116 dir=a[x][y];
117 x+=dl[dir]; y+=dc[dir];
118 }
119 return sum;
120 }
121
122 //pozitie favorabila care conduce catre un circuit
123 s=sum+pct[x][y];
124 while (cx!=x || cy!=y)
125 {
126 pct[cx][cy]=s; s--;
127 dir=a[cx][cy];
128 cx+=dl[dir]; cy+=dc[dir];
129 }
130 return sum+pct[x][y];
131 }
CAPITOLUL 9. OJI 2014 9.1. ARROWS 86

Listing 9.1.2: arrows 100 sofia.cpp


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 }
CAPITOLUL 9. OJI 2014 9.2. TCIF 87

75
76 }
77 else
78 if(ciclu[x][y]==1)
79 {//am ajuns intr-un punct ce face parte dintr-un ciclu det anterior
80 tk=tk+nr-mx;
81 val=t[x][y];
82 nr=nr-mx;
83 l=i;c=j;
84 if(nr*1000+val>Max)
85 Max=nr*1000+val;
86 while(!(l==x&&c==y)){
87 t[l][c]=nr*1000+val;ciclu[l][c]=1;
88 nr--;
89 int k=a[l][c];
90 l=l+dx[k];
91 c=c+dy[k];
92 }
93 }
94 else
95 {
96 val=t[x][y];
97 nr=nr-mx;
98 l=i;c=j;
99 if(nr+val>Max)
100 Max=nr+val;
101 while(!(l==x&&c==y)){
102 t[l][c]=nr+val;
103 nr--;
104 int k=a[l][c];
105 l=l+dx[k];
106 c=c+dy[k];
107 }
108 }
109 }
110 else
111 {
112 l=i;c=j;nr=nr-mx;
113 while(t[l][c]!=-1){
114 t[l][c]=nr--;
115 int k=a[l][c];
116 l=l+dx[k];
117 c=c+dy[k];
118
119 }
120 if(t[i][j]>Max)
121 Max=t[i][j];
122 }
123 }
124 f>>x>>y;
125 if(tip==1){
126 g<<t[x][y]<<’\n’;
127 return 0;
128 }
129 if(tip==2){
130 g<<tk<<’\n’;
131 return 0;
132 }
133 g<<Max<<’\n’;
134 return 0;
135 }

9.1.3 *Rezolvare detaliată

9.2 tcif
Problema 2 - tcif 100 de puncte
Avem la dispoziţie patru numere naturale N , A, B, C, precum şi trei cifre c1, c24, c3 distincte
două câte două.
CAPITOLUL 9. OJI 2014 9.2. TCIF 88

Cerinţe

Să se determine numărul natural minim, strict mai mare decât N , care are exact A cifre c1,
B cifre c2, C cifre c3 şi nu conţine alte cifre.

Date de intrare

Fişierul de intrare tcif.in conţine pe prima linie, separate prin câte un spaţiu, numerele naturale
A B C c1 c2 c3. Pe linia a doua se află numărul natural N .

Date de ieşire

Fişierul de ieşire tcif.out va conţine o singură linie pe care va fi scris cel mai mic număr natural
strict mai mare decât N care conţine exact A cifre c1, exact B cifre c2 şi exact C cifre c3 şi nu
conţine alte cifre.

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


222324 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
231106 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

9.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
CAPITOLUL 9. OJI 2014 9.2. TCIF 89

solutia problemei.

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.

9.2.2 Cod sursă

Listing 9.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";
CAPITOLUL 9. OJI 2014 9.2. TCIF 90

24 return 0;
25 }
26 if (cif[1] >= x && a[1] > 0) return 1;
27 if (cif[2] >= x && a[2] > 0) return 2;
28 return -1;
29 }
30
31 int main()
32 {
33 int i, j, k, nr, gata;
34 char s[Dim];
35
36 // citire
37 ifstream fin(inFile);
38 fin >> a[0] >> a[1] >> a[2] >> cif[0] >> cif[1] >> cif[2];
39 fin >> s;
40 fin.close();
41
42 // init
43 for (n = 0; s[n]; ++n)
44 t[n] = s[n] - ’0’;
45
46 for (j = 0; j < 2; ++j)
47 for (k = j + 1; k < 3; ++k)
48 if (cif[j] > cif[k])
49 {
50 nr = cif[j];
51 cif[j] = cif[k];
52 cif[k] = nr;
53 nr = a[j];
54 a[j] = a[k];
55 a[k] = nr;
56 }
57
58 // rezolvare caz 1 : n < a + b + c
59 if (n < a[0] + a[1] + a[2])
60 {
61 ofstream fout(outFile);
62 if (cif[0] == 0)
63 {
64 fout << cif[1];
65 a[1]--;
66 }
67 for (i = 0; i < 3; ++i)
68 for (j = 1; j <= a[i]; ++j)
69 fout << cif[i];
70 fout << "\n";
71 fout.close();
72 return 0;
73 }
74
75 // caz 2 : n = a + b + c
76 i = 0;
77 gata = 0;
78 while (!gata && (i < n) && ((k = AlegeCifra(t[i])) != -1))
79 {
80 sol[i] = k;
81 a[k]--;
82 if (cif[sol[i]] > t[i]) gata = 1;
83 else i++;
84 }
85 if (gata == 1) // pun restul cifrelor in sol, ordonate crescator
86 {
87 for (k = 0; k < 3; ++k)
88 for (j = 1; j <= a[k]; ++j)
89 sol[++i] = k;
90 }
91 else // ma intorc inapoi
92 {
93 i--;
94 while ((k = AlegeCifra(t[i] + 1)) == -1)
95 {
96 a[sol[i]]++; // pun cifra inapoi
97 i--;
98 }
99 a[sol[i]]++;
CAPITOLUL 9. OJI 2014 9.2. TCIF 91

100 sol[i] = k;
101 a[k]--;
102 for (k = 0; k < 3; ++k)
103 for (j = 1; j <= a[k]; ++j)
104 sol[++i] = k;
105 }
106
107 ofstream fout(outFile);
108 for (i = 0; i < n; ++i)
109 fout << cif[sol[i]];
110
111 fout << "\n";
112 fout.close();
113
114 return 0;
115 }

Listing 9.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]);
CAPITOLUL 9. OJI 2014 9.2. TCIF 92

57 if (poz) nr[cif[poz]]--;
58 else break;
59 }
60 //i indica cea mai din dreapta pozitie care nu apare in cif
61 //caut de la i catre lg prima pozitie care poate fi marita
62 poz=potmari(cn[i]);
63 if (poz)
64 {
65 cn[i]=cif[poz]; nr[cif[poz]]--;
66 }
67 else
68 {i++;
69 while (1)
70 {
71 poz=potmari(cn[i]);
72 if (!poz) {nr[cn[i]]++; i++;}
73 else break;
74 }
75 nr[cn[i]]++; cn[i]=cif[poz]; nr[cif[poz]]--;
76 }
77
78 //completez cn de la i-1 la 0 cu cele mai mici valori posibile
79 for (j=i-1, poz=1; poz<=3; poz++)
80 while (nr[cif[poz]]) {cn[j--]=cif[poz]; nr[cif[poz]]--;}
81
82 //afisez cn
83 for (i=lg-1; i>=0; i--) fout<<cn[i];
84 fout<<’\n’;
85 fout.close();
86 return 0;
87 }
88
89
90 int apare(int x)
91 //caut cifra x in vectorul cif
92 //daca apare, returnez poz pozitia pe care apare
93 {int i;
94 for (i=1; i<=3; i++)
95 if (cif[i]==x && nr[cif[i]]) return i;
96 return 0;
97
98 }
99
100 int potmari(int x)
101 //caut in cif prima cifra >x
102 {int i;
103 for (i=1; i<=3; i++)
104 if (cif[i]>x && nr[cif[i]]) return i;
105 return 0;
106 }

Listing 9.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 {
CAPITOLUL 9. OJI 2014 9.2. TCIF 93

23 if (vf==1 && st[vf]==0) return 0;


24 return 1;
25 }
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 }
CAPITOLUL 9. OJI 2014 9.2. TCIF 94

9.2.3 *Rezolvare detaliată


Capitolul 10

OJI 2013

10.1 maxp
Problema 1 - maxp 100 de puncte
Considerăm un şir de numere a1 , a2 , ..., aN . O secvenţă nevidă ı̂n acest şir este de forma ai
ai1 ... aj , unde i & j. De exemplu, pentru N 4 şi şirul 2 3 4 3, secvenţele nevide sunt: 2, 2
3, 2 3 4, 2 3 4 3, 3, 3 4, 3 4 3, 4, 4 3, 3. Definim puterea unui element ai ca fiind numărul de
secvenţe care-l conţin pe ai şi ı̂n care ai este strict mai mare decât celelalte elemente ale fiecăreia
dintre respectivele secvenţe. Astfel ı̂n şirul 2 3 4 3 puterea elementului a1 este 1 (fiind maxim
doar ı̂n secvenţa formată din el ı̂nsuşi), a elementului a2 este 2 (a2 fiind maxim ı̂n secvenţele 2 3
şi 3), a elementului a3 este 6 (fiind maxim ı̂n secvenţele 2 3 4, 2 3 4 3, 3 4, 3 4 3, 4 şi 4 3), iar a
elementului a4 este 1.

Cerinţe

Scrieţi un program care determină puterea cea mai mare a unui element din şirul dat, precum
şi numărul de elemente din şir care au cea mai mare putere.

Date de intrare

Fişierul maxp.in conţine pe prima linie numărul natural N , iar pe a doua linie, ı̂n ordine,
numerele naturale a1 , a2 , ..., aN separate prin câte un spaţiu.

Date de ieşire

Fişierul maxp.out va conţine pe prima linie un număr natural ce reprezintă puterea cea mai
mare a unui element din şirul dat şi pe a doua linie va conţine un număr natural ce reprezintă
numărul de elemente din şir care au cea mai mare putere.

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:
9345122 1 3 4 5, 3 4 5 1, 3 4 5 1 2, 3 4 5 1 2 2, 4 5, 4 5 1, 4 5 1 2,
4 5 1 2 2, 5, 5 1, 5 1 2, 5 1 2 2, deci puterea lui este 12.
Este singurul element care are această putere, celelalte
elemente având puteri mai mici.
6 3 Elementele din poziţiile 3 şi 4 sunt maxime ı̂n 3 secvenţe,
107726 2 deci puterea lor este 3. Celelalte elemente au puteri mai
mici.

95
CAPITOLUL 10. OJI 2013 10.1. MAXP 96

Timp maxim de executare/test: 0.5 secunde


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

10.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.
CAPITOLUL 10. OJI 2013 10.1. MAXP 97

10.1.2 Cod sursă

Listing 10.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 10.1.2: maxpDP cstdio.cpp


1 //prof.Dan Pracsiu
CAPITOLUL 10. OJI 2013 10.1. MAXP 98

2 #include<cstdio>
3 #include<fstream>
4
5 #define inFile "maxp.in"
6 #define outFile "maxp.out"
7 #define dim 200001
8
9 using namespace std;
10
11 int a[dim], st[dim], dr[dim], q[dim], poz[dim], n;
12
13 int main()
14 {
15 int i, k, x, nrsol;
16 long long p, pmax;
17
18 //citire
19 freopen(inFile, "r", stdin);
20 scanf("%d", &n);
21 for (i = 1; i <= n; i++)
22 scanf("%d", a + i);
23
24 // constructie st
25 k = 0;
26 q[k] = dim + 2;
27 poz[k] = 0;
28 st[k] = 0;
29 for (i = 1; i <= n; i++)
30 {
31 x = a[i];
32 while (q[k] < x) k--;
33 st[i] = i - poz[k] - 1;
34 k++;
35 q[k] = x;
36 poz[k] = i;
37 }
38
39 // constructie dr
40 k = 0;
41 q[k] = dim + 2;
42 poz[k] = n + 1;
43 dr[k] = 0;
44 for (i = n; i >= 1; i--)
45 {
46 x = a[i];
47 while (q[k] < x) k--;
48 dr[i] = poz[k] - i - 1;
49 k++;
50 q[k] = x;
51 poz[k] = i;
52 }
53
54 // calcul
55 nrsol = 1;
56 pmax = (st[1] + 1);
57 pmax *= (dr[1] + 1);
58 for (i = 2; i <= n; i++)
59 {
60 p = (st[i] + 1);
61 p = (p * (dr[i] + 1));
62 if (p > pmax)
63 {
64 pmax = p;
65 nrsol = 1;
66 }
67 else if (p == pmax) nrsol++;
68 }
69
70 ofstream fout(outFile);
71 fout << pmax << "\n" << nrsol << "\n";
72 fout.close();
73
74 return 0;
75 }

Listing 10.1.3: maxpDP fstream.cpp


CAPITOLUL 10. OJI 2013 10.1. MAXP 99

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


2 #include<fstream>
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();
CAPITOLUL 10. OJI 2013 10.1. MAXP 100

76
77 return 0;
78 }

Listing 10.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 10.1.5: maxpPRV.cpp


1 //prof.PIT-RADA IONEL VASILE
2 #include<fstream>
3
CAPITOLUL 10. OJI 2013 10.1. MAXP 101

4 using namespace std;


5
6 int n, a[200004], mx, j3, j2, nr, i;
7 long long s,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 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 10.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 {
CAPITOLUL 10. OJI 2013 10.2. PUNCTE 102

24 j=i-1;
25 while (a[j]<a[i])
26 j = j - st[j] - 1;
27 st[i]=i-j-1;
28 }
29
30 for (i=n;i>=1;i--)
31 {
32 j = i+1;
33 while (a[j]<a[i])
34 j = j + dr[j] + 1;
35 dr[i]=j-i-1;
36 }
37
38 for (i=1;i<=n;i++)
39 {
40 putere = st[i] + dr[i] + 1 + st[i]*dr[i];
41 if (putere > pmax)
42 pmax = putere,ct=1;
43 else
44 if (putere == pmax)
45 ct++;
46 }
47
48 ofstream gg("maxp.out");
49 gg << pmax << "\n" << ct << "\n";
50
51 f.close();
52 gg.close();
53
54 return 0;
55 }

10.1.3 *Rezolvare detaliată

10.2 puncte
Problema 2 - puncte 90 de
puncte
Andrei se descurcă foarte bine la geometrie şi de aceea născoceşte tot felul de jocuri pe care
le testează cu Alexandru, colegul său de bancă. Pentru a pregăti noul joc cu trei niveluri, Andrei
desenează pe o foaie de matematică reperul cartezian xOy şi mai multe puncte distincte. Fiecare
punct desenat are atât abscisa x, cât şi ordonata y, numere ı̂ntregi.
La primul nivel, Alexandru determină numărul maxim de puncte (dintre cele desenate) aflate
pe una dintre axele sistemului cartezian sau pe o dreaptă paralelă cu una dintre cele două axe.
La al doilea nivel, Alexandru consideră toate punctele desenate a căror abscisă x şi ordonată
y verifică cel puţin una dintre relaţiile x y sau x  y 0 şi apoi calculează câte drepte distincte
trec prin cel puţin două dintre aceste puncte.
La al treilea nivel, Alexandru numără şi şterge punctele din 3 ı̂n 3 (primul, al 4-lea, al 7-lea
etc.), ı̂ncepând cu cel mai din stânga punct desenat şi continuând către dreapta. Dacă două sau
mai multe puncte au aceeaşi abscisă, el le numără pe acestea de jos ı̂n sus (ı̂ncepând de la punctul
cu ordonata cea mai mică). Când a ajuns cu număratul la cel mai din dreapta punct continuă cu
cel mai din stânga punct rămas.
Alexandru se opreşte cu numărarea şi ştergerea când rămâne un singur punct desenat pe foaie.
Exemplu:
CAPITOLUL 10. OJI 2013 10.2. PUNCTE 103

Figura 10.1: Puncte

Cerinţe

Scrieţi un program care citeşte numărul natural nenul N , apoi cele 2 ˜ N numere ı̂ntregi ce
reprezintă coordonatele celor N puncte şi determină:
a) N RP , numărul maxim de puncte (dintre cele desenate) aflate pe una dintre axele sistemului
cartezian sau pe o dreaptă paralelă cu una dintre cele două axe;
b) N RD, numărul de drepte distincte care trec prin cel puţin două dintre punctele desenate a
căror abscisa x şi ordonată y verifică cel puţin una dintre relaţiile x y sau x  y 0;
c) XP reprezentând abscisa punctului rămas pe foaie la sfârşitul celui de-al treilea nivel al
jocului.

Date de intrare

Fişierul puncte.in conţine pe prima linie numărul N de puncte, iar pe fiecare dintre
următoarele N linii, câte două numere ı̂ntregi, despărite printr-un spaiu, reprezentând, ı̂n ordine,
abscisa şi ordonata unui punct din plan.

Date de ieşire

Fişierul puncte.out va conţine pe prima linie numărul natural N RP , pe a doua linie numărul
natural N RD, iar pe a treia linie numărul ı̂ntreg ce reprezintă coordonata XP .

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 10.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
CAPITOLUL 10. OJI 2013 10.2. PUNCTE 104

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

10.2.2 Cod sursă

Listing 10.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;
CAPITOLUL 10. OJI 2013 10.2. PUNCTE 105

31 for(i=1;i<=1999;i++)
32 {
33 if(X[i]>maxi)maxi=X[i];
34 if(Y[i]>maxi)maxi=Y[i];
35 }
36 g<<maxi<<’\n’;
37
38 //b) numarul de drepte
39 if(b1==0 && b2==0)nrd=0;
40 else if(b2+nr0>1 && b1+nr0>1) nrd=b1*b2+2;
41 else nrd=b1*b2+1;
42 g<<nrd<<’\n’;
43
44 //c) indicele punctului ramas nemarcat
45 i=1;
46 while(marcate<n-1)
47 {
48 marcate++;poz[i]=0;
49 do{i++;if(i>n) i=1;}while(poz[i]==0);
50 do{i++;if(i>n) i=1;}while(poz[i]==0);
51 do{i++;if(i>n) i=1;}while(poz[i]==0);
52 }
53 nrp=1;while(poz[nrp]==0)nrp++;
54
55 //c) determinare coordonate
56 i=1;
57 while(nrp>0)
58 {
59 while(X[i]==0)i++;
60 nrabs=X[i];
61 while(nrabs>0 && nrp>0)
62 {
63 nrp--;nrabs--;
64 }
65 i++;
66 }
67
68 g<<i-1001<<’\n’;
69 f.close();g.close();
70 return 0;
71 }

Listing 10.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)
CAPITOLUL 10. OJI 2013 10.2. PUNCTE 106

32 b2++;
33
34 x+=1000;
35 y+=1000;
36 X[x]++;
37 Y[y]++;
38 }
39
40 //a
41 i=2000;
42 while(i--)
43 {
44 if(X[i]>m) m=X[i];
45 if(Y[i]>m) m=Y[i];
46 }
47 g<<m<<’\n’;
48
49 //b
50 x=b1*b2;
51 if(b1>1)x++;
52 if(b2>1)x++;
53 x+=(b1==1 && z)+(b2==1 && z);
54 g<<x<<’\n’;
55
56 //c
57 for(i=1;i<2000;i++)
58 if(X[i])
59 {
60 P.h=X[i];
61 P.x=i;
62 v.push_back(P);
63 }
64
65 p=1;// distanta pana la urmatorul punct care se sterge
66 i=0;
67 while(v.size()>1)
68 {
69 if(v[i].h-p>=0)
70 {
71 x=v[i].h;
72 v[i].h=x-1-(x-p)/3;
73
74 if(v[i].h==0)
75 v.erase(v.begin()+i);
76 else
77 i++;
78
79 p=3-(x-p)%3;
80 }
81 else
82 {
83 p=p-v[i].h;
84 i++;
85 }
86
87 if(i==v.size()) i=0;
88 }
89
90 g<<v[0].x-1000<<’\n’;
91 f.close();
92 g.close();
93 return 0;
94 }

Listing 10.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;
CAPITOLUL 10. OJI 2013 10.2. PUNCTE 107

10 bool a[2010][2010];
11
12 struct punct
13 {
14 int x, y;
15 punct *leg;
16 };
17
18 punct *L;
19
20 int main()
21 {
22 int i, j, x, y, maxim, zero;
23 long long drepte;
24 punct *p, *ul;
25
26 // citire
27 ifstream fin(inFile);
28 fin >> n;
29 zero = 0;
30 for (i = 1; i <= n; i++)
31 {
32 fin >> x >> y;
33 ox[1000 + x]++;
34 oy[1000 + y]++;
35 a[1000 + y][1000 + x] = true;
36 if (x == 0 && y == 0) zero = 1;
37 else
38 {
39 if (x == y) b1++;
40 if (x == -y) b2++;
41 }
42 }
43
44 ofstream fout(outFile);
45 // punctul 1
46 maxim = 0;
47 for (i = 0; i <= 2000; i++)
48 if (maxim < ox[i]) maxim = ox[i];
49 for (i = 0; i <= 2000; i++)
50 if (maxim < oy[i]) maxim = oy[i];
51 fout << maxim << "\n";
52
53 // punctul 2
54 drepte = b1;
55 drepte *= b2;
56 if (zero == 1)
57 {
58 if (b1 > 0) drepte++;
59 if (b2 > 0) drepte++;
60 }
61 else
62 {
63 if (b1 > 1) drepte++;
64 if (b2 > 1) drepte++;
65 }
66 fout << drepte << "\n";
67
68 // punctul 3
69 L = ul = NULL;
70 for (j = 0; j <= 2000; j++)
71 for (i = 0; i <= 2000; i++)
72 if (a[i][j])
73 {
74 if (L == NULL)
75 {
76 L = new punct;
77 L->x = i - 1000;
78 L->y = j - 1000;
79 L->leg = NULL;
80 ul = L;
81 }
82 else
83 {
84 p = new punct;
85 p->x = i - 1000;
CAPITOLUL 10. OJI 2013 10.2. PUNCTE 108

86 p->y = j - 1000;
87 p->leg = NULL;
88 ul->leg = p;
89 ul = p;
90 }
91 }
92
93 ul->leg = L;
94 for (i = 1; i < n; i++)
95 {
96 ul->leg = L->leg;
97 delete L;
98 ul = ul->leg->leg;
99 L = ul->leg;
100 }
101 fout << ul->y << "\n";
102 fout.close();
103
104 return 0;
105 }

Listing 10.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++;
CAPITOLUL 10. OJI 2013 10.2. PUNCTE 109

53 }
54
55 nrp=0;
56 for (i=0;i<=2000;i++)
57 {
58 if (lin[i]>nrp)nrp=lin[i];
59 if (col[i]>nrp)nrp=col[i];
60 }
61
62 bis1=bis1-oo;
63 bis2=bis2-oo;
64 nrd=bis1*bis2;
65 if (bis1+oo>1)
66 {
67 nrd++;
68 }
69 if (bis2+oo>1)
70 {
71 nrd++;
72 }
73
74 fout<<nrp<<"\n";
75 fout<<nrd<<"\n";
76 abscisa();
77 fout<<xp-1000;
78
79 fin.close();
80 fout.close();
81 return 0;
82 }

Listing 10.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 {
CAPITOLUL 10. OJI 2013 10.2. PUNCTE 110

43 if (ct==0) return 2;
44 if (ct==1) return 0;
45 return 3;
46 }
47
48 if (n==5)
49 {
50 if (ct==0) return 1;
51 if (ct==1) return 3;
52 return 2;
53 }
54
55 if (n==3)
56 {
57 if (ct==0) return 2;
58 if (ct==1) return 1;
59 return 0;
60 }
61
62 return -1; // ... !!!
63 }
64
65
66 int cmp(punct a, punct b)
67 {
68 if (a.x < b.x)
69 return 1;
70 if (a.x > b.x)
71 return 0;
72 if (a.y < b.y)
73 return 1;
74 return 0;
75 }
76
77 int cmp1(punct a, punct b)
78 {
79 if (a.y < b.y)
80 return 1;
81 else
82 return 0;
83 }
84
85 int main()
86 {
87 f >> n;
88 punct buf;
89 while (f >> buf.x >> buf.y)
90 {
91 v.push_back(buf);
92 ct++;
93 }
94
95 n = v.size();
96 sort(v.begin(),v.end(),cmp1);
97
98 if (v[0].x==0 && v[0].y==0)
99 zero=1;
100 else
101 {
102 if (v[0].x == v[0].y)
103 ct1++;
104 if (v[0].x == -v[0].y)
105 ct2++;
106 }
107
108 ct=1;
109 for (i=1;i<n;i++)
110 {
111 if (v[i].y == v[i-1].y)
112 ct++;
113 else
114 ct=1;
115 if (ct > ctmax)
116 {
117 ctmax = ct;
118 val = v[i].y;
CAPITOLUL 10. OJI 2013 10.2. PUNCTE 111

119 tip = 2;
120 }
121
122 if (v[i].x==0 && v[i].y==0)
123 zero=1;
124 else
125 {
126 if (v[i].x == v[i].y)
127 ct1++;
128 if (v[i].x == -v[i].y)
129 ct2++;
130 }
131 }
132
133 sort(v.begin(),v.end(),cmp);
134
135 ct=1;
136 for (i=1;i<n;i++)
137 {
138 if (v[i].x == v[i-1].x)
139 ct++;
140 else
141 ct=1;
142 if (ct > ctmax)
143 {
144 ctmax = ct;
145 val = v[i].x;
146 tip = 1;
147 }
148 }
149
150 ofstream fout("puncte.out");
151 fout << ctmax << endl;
152 fout << rez2(ct1,ct2,zero) <<endl;
153
154 ct=0;
155
156 for (int k=0;k<30;k++)
157 {
158
159 if (v.size()<=5)
160 {
161 r = v[rez3(v.size(),ct%3)].x;
162 break;
163 }
164
165 for (i=0;i<v.size();i++,ct++)
166 {
167
168 if (ct%3!=0)
169 v2.push_back(v[i]);
170 }
171
172
173 if (v2.size()<=5)
174 {
175 r = v2[rez3(v2.size(),ct%3)].x;
176 break;
177 }
178 v = v2;
179 v2.clear();
180
181 }
182
183 fout << r << endl;
184
185 return 0;
186 }

10.2.3 *Rezolvare detaliată


Capitolul 11

OJI 2012

11.1 deal
Problema 1 - deal 100 de puncte
Vasilică are la grădiniţă N turnuri cu ı̂nălţimile h1 , h2 , ..., hN . Când aşază ı̂n linie nişte turnuri,
cel puţin două, astfel ı̂ncât ı̂nălţimile lor să fie ı̂n ordine crescătoare, Vasilică spune că a construit
un deal. Înălţimea dealului este egală cu ı̂nălţimea celui mai ı̂nalt turn folosit. Iată, de exemplu,
că aşezând ı̂n ordine turnurile cu ı̂nălţimile 2 4 4 7 9 a format un deal cu ı̂nălţimea 9.
Vasilică şi-ar dori să aşeze ı̂n linie cele N turnuri, formând o succesiune de dealuri astfel ı̂ncât
suma ı̂nălţimilor dealurilor formate să fie maximă.

Cerinţe

Scrieţi un program care, cunoscând ı̂nălţimile celor N turnuri, va determina suma ı̂nălţimilor
dealurilor ce se pot forma aşezând ı̂n linie cele N turnuri, maximă posibil.

Date de intrare

Fişierul de intrare deal.in conţine pe prima linie numărul natural N . Pe cea de a doua linie
se află N numere naturale separate prin spaţii, reprezentând ı̂nălţimile celor N turnuri.

Date de ieşire

Fişierul de ieşire deal.out va conţine o singură linie pe care va fi scris un număr natural
reprezentând cerinţa problemei.

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 fi: 2 10 2 5 2 2 7
10 2 2 2 7 5 2 S-au format trei dealuri: 2 10 (cu ı̂nălţimea 10) şi 2 5 (cu
ı̂nălţimea 5) şi 2 2 7 (cu ı̂năţimea 7).

Timp maxim de executare/test: 0.5 secunde


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

112
CAPITOLUL 11. OJI 2012 11.1. DEAL 113

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


CAPITOLUL 11. OJI 2012 11.1. DEAL 114

’’gramada’’ de la stanga la dreapta.

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


de la dreapta la stanga.

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.

11.1.2 Cod sursă

Listing 11.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
CAPITOLUL 11. OJI 2012 11.1. DEAL 115

33 if (w[dr]==0) dr--;
34 else {s=s+dr;w[st]--;w[dr]--;}
35 }
36
37 pdp=(n-w[dr])/2+1-(v[dr]-w[dr]);
38
39 if (w[dr]/2<=pdp) s=s+dr*(w[dr]/2);
40 else s=s+dr*pdp;
41
42 g<<s;
43
44 f.close();
45 g.close();
46
47 return 0;
48 }

Listing 11.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 11.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
CAPITOLUL 11. OJI 2012 11.1. DEAL 116

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
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 {
CAPITOLUL 11. OJI 2012 11.2. OZN 117

84 int j = i;
85 while ((j < n) && (sol[j] <= sol[j + 1])) ++j;
86 if (j-i+1>1) rez += sol[j];
87 i = j;
88 }
89
90 printf("%I64d\n", rez);
91
92 /*
93 for (i=0; i<n; ++i) printf("%d ", sol[i]);
94 printf("\n");
95 for (i=0; i<n; ++i) printf("%d ", a[i]);*/
96
97 return 0;
98 }

11.1.3 *Rezolvare detaliată

11.2 ozn
Problema 2 - ozn 100 de puncte
O invazie de N farfurii zburătoare (denumite uzual OZN) dă bătăi de cap autorităţilor. În
fiecare astfel de OZN se află extratereştri care au ca misiune distrugerea planetei noastre. Radarul
care a detectat invazia are un ecran similar cu planul XOY . Fiecare OZN este reprezentat pe
ecran printr-un segment de dreaptă.
Pentru anihilarea OZN-urilor, autorităţile dispun de K arme laser. Armele sunt poziţionate
pe sol (ilustrat pe ecranul radarului prin axa OX). Fiecare armă emite o rază laser, ilustrată
pe ecran printr-o paralelă cu axa OY . Dacă o rază laser intersectează segmentul de pe ecranul
radarului corespunzător unui OZN, raza va omorı̂ toţi extratereştrii aflaţi ı̂n OZN-ul respectiv.
Din păcate, ı̂n preajmă se află doar un militar specializat ı̂n arme laser, aşa că autorităţile
doresc să ştie exact ce armă trebuie să folosească acesta pentru a distruge cât mai mulţi ex-
tratereştri.
Cerinţe
Ajutaţi autorităţile să determine numărul de extratereştri care pot fi anihilaţi cu fiecare armă
din dotare.
Date de intrare
Fişierul de intrare ozn.in conţine pe prima linie două numere naturale separate prin spaţiu
N K reprezentând numărul de OZN-uri şi respectiv numărul de arme laser. Pe următoarele
N linii sunt descrise cele N OZN-uri, câte unul pe linie. Un OZN este descris prin 5 numere
naturale separate prin câte un spaţiu x1 y1 x2 y2 nr, reprezentând ı̂n ordine coordonatele capetelor
segmentului corespunzător x1 , y1 , x2 , y2 , iar nr - numărul de extratereştri din el. Pe ultima
linie se găsesc K numere naturale a1 a2 a3 ... aK , separate prin câte un spaţiu, reprezentând
coordonatele pe axa OX (abscisele) unde sunt amplasate armele laser.
Date de ieşire
Fişierul de ieşire ozn.out va conţine pe K linii. Pe linia i va fi scris numărul total de ex-
tratereştri care pot fi distruşi cu arma i, considerând armele numerotate ı̂n ordinea ı̂n care acestea
apar ı̂n fişierul de intrare.
Restricţii şi precizări
a 1 & N & 20000
a 1 & K & 20000
a 1 & orice coordonată din fişierul de intrare & 2000000
a 1 & nr & 100, pentru orice OZN
a x1 $ x2 , pentru orice OZN
a Pe ecranul radarului segmentele ce descriu navele se pot intersecta.
a Dacă raza laser trece prin unul dintre capetele unui OZN atunci acesta este distrus.
a Pentru 50% dintre testele de intrare 1 & N ˜ K & 10000000
CAPITOLUL 11. OJI 2012 11.2. OZN 118

Exemple

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

11.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 x1aix2; 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 11. OJI 2012 11.2. OZN 119

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

11.2.2 Cod sursă

Listing 11.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 11.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 11. OJI 2012 11.2. OZN 120

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 11.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 11. OJI 2012 11.2. OZN 121

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 11.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 11. OJI 2012 11.2. OZN 122

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 }

11.2.3 *Rezolvare detaliată


Capitolul 12

OJI 2011

12.1 adunscad
Problema 1 - adunscad 100 de puncte
Considerăm un număr ı̂ntreg N şi un şir de M cifre zecimale nenule. Să se determine dacă
numărul N poate fi rezultatul unei expresii aritmetice simple (fără paranteze), formată exclusiv
din cifrele şirului citit şi din operatorii aritmetici desemnaţi pentru operaţiile de adunare şi scădere
, .

Cerinţe

Scrieţi un program care citeşte numerele N şi M de pe prima linie a fişierului de intrare şi
şirul de M cifre de pe linia următoare şi determină şi afişează expresia găsită sau valoarea 0 ı̂n
cazul ı̂n care nu există soluţie.

Date de intrare

Fişierul de intrare adunscad.in conţine pe prima linie numerele ı̂ntregi N M , separate printr-
un spaţiu, reprezentând valoarea ce trebuie obţinută la evaluarea expresiei şi numărul de cifre din
şir. Linia a doua a fişierului de intrare conţine şirul celor M cifre nenule, separate prin câte un
spaţiu.

Date de ieşire

Fişierul de ieşire adunscad.out va conţine pe prima linie expresia determinată, ı̂n cazul ı̂n
care există soluţie, sau valoarea 0 ı̂n cazul ı̂n care nu există soluţie.

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ă fie 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 afişa o soluţie corectă

Exemple

123
CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 124

adunscad.in adunscad.out Explicaţii


21 4 3+9+1+8 Soluţie corectă utilizând numai operatorul ’+’
3918
-1 4 -1+2+3-5 Soluţie corectă. O altă soluţie corectă este: -1-2-3+5
1235
-7 7 -1-1-1-1-1-1-1 Soluţie corectă utilizând numai operatorul ’-’
1111111
12 3 0 Nu există soluţie
123

Timp maxim de executare/test: 1.0 secunde

12.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 )
CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 125

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

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


CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 126

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.

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


CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 127

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

12.1.2 Cod sursă

Listing 12.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)
CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 128

65 {
66 int i, care;
67
68 Fin = fopen(’’adunscad.in’’, ’’r’’);
69 Fout = fopen(’’adunscad.out’’, ’’w’’);
70
71 fscanf(Fin, ’’%d %d\n’’, &N, &M); //citesc N si M
72 for (i = 1; i <= M; i++) //citesc cifrele din sir
73 fscanf(Fin, ’’%d’’, &cifre[i]);
74 fclose(Fin);
75
76 for (i = 0; i < 31; i++) comb[i] = 0;
77 gasit = 0;
78 while (!gasit && !comb[1]) //cand comb[1] este 1 am terminat
79 {
80 care = OK(); //verific expresia
81 if (care) //este OK
82 {
83 afiseaza(care); //afisez
84 break; //opresc ciclul
85 }
86 i = M; //adun 1 in baza 2
87 while (comb[i]) comb[i--] = 0; //caut primul 0
88 comb[i] = 1; //il fac 1
89 }
90 if (comb[1]) fprintf(Fout, ’’0\n’’);
91
92 fclose(Fout);
93 return 0;
94 }

Listing 12.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 }
CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 129

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 }

Listing 12.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
CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 130

63
64 //C[y]=numar binar cu L biti
65 //daca V[y]=1 cei L biti marcheaza de la st la dr
66 //semnul utilizat de cele L cifre bit=1=>semn=’-’ bit=0=>semn=’+’
67 long *VS,*CS,*VD,*CD;//vectori analogi cu V , C
68 //pentru subsecventele stanga / dreapta
69 long L,LS,LD,i,j;
70 //L=nr total cifre
71 //LS=nr cifre in prima jumatate ( in subsecventa stanga)
72 //LD=nr cifre in a doua jumatate (in subsecventa dreapta)
73 if(st==dr)
74 {
75 V[x[st]]=1;C[x[st]]=0;
76 //daca secventa are o singura cifra atunci
77 //singura valoare generata
78 //codul este 0 adica cifra e luata cu +
79 return;
80 }
81
82 L=dr-st+1;
83 LS=(L+1)/2;VS=init();CS=init();//pregatesc apelul pe prima subsecventa
84 LD=L-LS;VD=init();CD=init();//pregatesc apelul pe a doua subsecventa
85
86 //DIVIDE apel pe cele doua subsecvente
87 //fiecare va returna valorile generate precum si codurile cu care
88 // se genereaza
89 dei(st,(st+dr)/2,VS,CS);
90 dei((st+dr)/2+1,dr,VD,CD);
91
92 //IMPERA Se parcurg toate valorile posibile care pot fi generate
93 // in stanga si in dreapta
94 //OBS pe cele doua subsecvente valoarea maxima generata este de 9 ori
95 // lungimea secventei
96 for(i=0;i<=9*LS;i++)
97 if(VS[i])//DACA VALOAREA i SE OBTINE IN STANGA
98 for(j=0;j<=9*LD;j++)
99 if(VD[j])//DACA VALOAREA j SE OBTINE IN DREAPTA
100 {
101 V[i+j]=1;
102 //VALOAREA i+j>=0 SE OBTINE PE SECVENTA
103 C[i+j]=(CD[j]*p[LS])+CS[i];
104 //CODUL SE OBTINE CONCATENAND CODURILE CU CARE
105 // S-AU OBTINUT i si j
106 if(i>=j)
107 {//DACA VALOAREA STANGA > VALOAREA DREAPTA
108 V[i-j]=1;//DIFERENTA i-j SE OBTINE PE SECVENTA
109 C[i-j]=(p[LD]-CD[j]-1)*p[LS]+CS[i];
110 //CODUL DREAPTA VA FI NEGAT DEOARECE j SE SCADE
111 //DECI TOATE SEMNELE VOR FI SCHIMBATE
112 }
113 else
114 {//ANALOG DACA VALOAREA DREAPTA > VALOAREA STANGA
115 V[j-i]=1;
116 C[j-i]=(CD[j]*p[LS])+(p[LS]-CS[i]-1);
117 //DAR VOM NEGA CODUL STANGA
118 }
119 }
120 //ELIBEREZ MEMORIA PE VECTORII TEMPORAR UTILIZAT PENTRU A GENERA VALORILE
121 //PE SUBSEVCENTE
122 delete VS;delete VD;
123 delete CS;delete CD;
124
125 }
126
127 long *init()
128 {
129 //initializeaza cu 0 un vector temporar alocat dinamic cu zona utila 0-180
130 long *VECT;
131 VECT=new long[185];
132 for(long i=0;i<=182;i++)VECT[i]=0;
133 return VECT;
134 }

Listing 12.1.4: adrian3.cpp


CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 131

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 {
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 12.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
CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 132

12 int main()
13 {
14 freopen("adunscad.in","r",stdin);
15 freopen("adunscad.out","w",stdout);
16
17 scanf("%d%d",&c,&n);
18 for(i=1;i<=n;i++)scanf("%d",&x[i]);
19
20 sol=ver(1,c);
21
22 if(!sol){printf("0\n");return 0;}
23 if(*sol==’+’)sol++;
24 printf("%s\n",sol);
25
26 return 0;
27 }
28
29 char *ver(int p,int v)
30 {
31 char *ret;ret=0;
32 if(p==n)
33 {
34 if(v== x[n]){ret=SOL+2*n-1;*ret=’0’+x[n];ret--;*ret=’+’;}
35 if(v==-x[n]){ret=SOL+2*n-1;*ret=’0’+x[n];ret--;*ret=’-’;}
36 return ret;
37 }
38
39 if(ver(p+1,v-x[p]))
40 {ret=SOL+2*p-1;*ret=’0’+x[p];ret--;*ret=’+’;return ret;}
41
42 if(ver(p+1,v+x[p]))
43 {ret=SOL+2*p-1;*ret=’0’+x[p];ret--;*ret=’-’;return ret;}
44
45 return ret;
46 }

Listing 12.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
CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 133

38 fprintf(Fout, "-");
39 }
40 fprintf(Fout, "%d\n", cifre[M]);
41 }
42 }
43
44 int OK(void)
45 {
46 int i, Sum = 0, Sum_ = 0;
47
48 for (i = 1; i <= M; i++)
49 if (comb[i])
50 Sum -= cifre[i]; //1 este ’-’, 0 este ’+’
51 else
52 Sum += cifre[i];
53 for (i = 1; i <= M; i++) //invers
54 if (comb[i]) //1 este ’+’, 0 este ’-’
55 Sum_ += cifre[i];
56 else
57 Sum_ -= cifre[i];
58
59 if (Sum == N && Sum_ == N) return 3; // daca sumele sunt egale caz = 3,
60 // deci indiferent cum afisez
61 if (Sum == N) return 1; // 1 este ’-’
62 if (Sum_ == N) return 2; // 1 este ’+’
63
64 return 0; //combinatia NU este buna
65 }
66
67 void back(long k)
68 {
69 int care;
70 if (k == M + 1) //am o combinatie de +/- (0/1)
71 {
72 care = OK(); //verific daca furnizeaza valoarea N si in ce mod
73 if (care) //DA, e buna
74 {
75 afiseaza(care); //afiseaza
76 fclose(Fout); //inchide
77 exit(0); //opreste
78 } //NU, nu e buna - nu fa nimic
79 }
80 else //nu am inca o combinatie de +/- (0/1)
81 {
82 comb[k] = 0; back(k+1); //pune pe pozitia curenta 0 (+) si treci
83 //mai departe
84 comb[k] = 1; back(k+1); //la revenire pune 1 (-) si treci mai departe
85 }
86 }
87
88 int main(void)
89 {
90 unsigned long i;
91 Fin = fopen("adunscad.in", "r");
92 Fout = fopen("adunscad.out", "w");
93
94 fscanf(Fin, "%d %d\n", &N, &M);
95 for (i = 1; i <= M; i++)
96 fscanf(Fin, "%d", &cifre[i]);
97 back(2); //cand pozitia 1 din vectorul solutie comb devine 1 am terminat
98 if (!gasit) //nu am gasit nici o combinatie
99 fprintf(Fout, "0\n"); //scrie 0
100 fclose(Fout);
101 return 0;
102 }

Listing 12.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;
CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 134

8
9 int N, M, gasit;
10 int cifre[31], comb[31];
11
12 void afiseaza(void)
13 {
14 int i;
15 gasit = 1;
16 if (comb[1]) fprintf(Fout, "-");
17 for (i = 1; i < M; i++)
18 {
19 fprintf(Fout, "%d", cifre[i]);
20 if (comb[i+1])
21 fprintf(Fout, "-");
22 else
23 fprintf(Fout, "+");
24 }
25 fprintf(Fout, "%d\n", cifre[M]);
26 }
27
28 int OK(void)
29 {
30 int i, Sum = 0;
31
32 for (i = 1; i <= M; i++)
33 if (comb[i])
34 Sum -= cifre[i];
35 else
36 Sum += cifre[i];
37 return (Sum == N);
38 }
39
40 int main(void)
41 {
42 int i;
43
44 Fin = fopen("adunscad.in", "r");
45 Fout = fopen("adunscad.out", "w");
46
47 fscanf(Fin, "%d %d\n", &N, &M); //citesc N si M
48 for (i = 1; i <= M; i++) //citesc cifrele din sir
49 fscanf(Fin, "%d", &cifre[i]);
50 fclose(Fin);
51
52 gasit = 0;
53 while (!gasit && !comb[0])
54 {
55 if (OK())
56 {
57 afiseaza();
58 break;
59 }
60 i = M;
61 while (comb[i]) comb[i--] = 0;
62 comb[i] = 1;
63 }
64
65 if (comb[0]) fprintf(Fout, "0\n");
66
67 fclose(Fout);
68 return 0;
69 }

Listing 12.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)
CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 135

11 {
12 int i,s=0;
13 unsigned long b=1<<(m-1);
14
15 for (i=1; i<=m; ++i,b>>=1)
16 {
17 if (x & b)
18 s-=a[i];
19 else
20 s+=a[i];
21 }
22
23 return (s==n);
24 }
25
26 void afiseaza(unsigned long x)
27 {
28 unsigned long b=1<<(m-1);
29 int i;
30 for (i=1;i<=m;++i,b>>=1)
31 {
32 if (x & b)
33 printf("-");
34 else
35 if (i!=1)
36 printf("+");
37
38 printf("%d", a[i]);
39 }
40
41 printf("\n");
42 }
43
44 int main()
45 {
46 freopen("adunscad.in","r",stdin);
47 freopen("adunscad.out","w",stdout);
48 scanf("%d %d", &n, &m);
49
50 for (i=1;i<=m;++i)
51 {
52 scanf("%d", &a[i]);
53 fin=(fin<<1)+1;
54 }
55
56 for (i=0;i<=fin;++i)
57 if (solutie(i))
58 {
59 afiseaza(i);
60 return 0;
61 }
62
63 printf("0\n");
64 return 0;
65 }

Listing 12.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
CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 136

18 for(i=1;i<=M;i++)
19 if(s[i]==1)
20 E=E+x[i];
21 else
22 E=E-x[i];
23
24 if(E==N)
25 {
26 if(s[1]==2) g<<"-";
27 g<<x[1];
28
29 for(i=2;i<=M;i++)
30 {
31 if(s[i]==1)
32 g<<"+";
33 else
34 g<<"-";
35
36 g<<x[i];
37 }
38
39 g<<’\n’;
40 g.close();
41 exit(0);
42 }
43 }
44
45 void gen(int i)
46 {
47 if(i<=M)
48 {
49 s[i]=1;
50 gen(i+1);
51 s[i]=2;
52 gen(i+1);
53 }
54 else
55 {
56 testare_afisare();
57 }
58 }
59
60 int main()
61 {
62 int i;
63
64 f>>N>>M;
65 for(i=1;i<=M;i++)
66 f>>x[i];
67
68 gen(1);
69
70 g<<0<<’\n’;
71 g.close();
72 return 0;
73 }

Listing 12.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];}
CAPITOLUL 12. OJI 2011 12.1. ADUNSCAD 137

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
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 12.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)
CAPITOLUL 12. OJI 2011 12.2. COMP 138

22 g<<0<<’\n’;
23 else
24 {
25 y=(t-n)/2;
26 a[0]=1;
27 int d=0;
28
29 for(i=1;i<=m &&!a[y];i++)
30 for(k=d;k>=0&&!a[y];k--)
31 if(a[k] &&k+c[i]<=y && !a[k+c[i]])
32 {
33 a[k+c[i]]=i;
34 if(k+c[i]>d) d=k+c[i];
35 }
36
37 if(!a[y])
38 g<<0<<’\n’;
39 else
40 {
41 while(y)
42 {
43 b[a[y]]=1;
44 y-=c[a[y]];
45 }
46
47 if(b[1]) g<<’-’;
48
49 g<<c[1];
50 for(i=2;i<=m;i++)
51 {
52 if(b[i])
53 g<<’-’;
54 else
55 g<<’+’;
56
57 g<<c[i];
58 }
59
60 g<<’\n’;
61 }
62 }
63
64 f.close();
65 g.close();
66 return 0;
67 }

12.1.3 *Rezolvare detaliată

12.2 comp
Problema 2 - comp 100 de puncte
Locuitorii planetei Eudora folosesc o reprezentare mai ciudată a numerelor naturale, astfel că
orice număr natural va fi scris notând câte mii, sute, zeci, respectiv unităţi conţine acesta. De
exemplu, numărul 3207 se poate reprezenta ı̂n mai multe moduri echivalente: 3m2s7u (3 mii 2
sute şi 7 unităţi), 32s0z7u (32 sute 0 zeci şi 7 unităţi), 32s7u, 3207u, etc.
Pentru a compara două numere naturale, eudorienii folosesc semnele $ şi %, acestea având
semnificaţia cunoscută şi pe Terra, iar pentru a calcula suma a două numere naturale utilizează
semnul .
Pentru a testa abilităţile pământenilor ı̂n privinţa lucrului cu numere naturale, eudorienii au
trimis pe Terra un fişier text ce conţine N linii, fiecare linie fiind o comparaţie de forma:
expresie1 % expresie2
sau
expresie1 $ expresie2
Observaţi că o comparaţie este constituită din două expresii separate prin semnul $ sau prin
semnul %.
CAPITOLUL 12. OJI 2011 12.2. COMP 139

O expresie este compusă dintr-un număr natural sau dintr-o sumă de două sau mai multe
numere naturale, toate scrise ı̂n forma eudoriană. Fişierul nu conţine caractere spaţiu.

Cerinţe

Scrieţi un program care determină câte dintre comparaţiile date utilizează semnul $, precum
şi valoarea de adevăr a fiecărei comparaţii dintre cele N date (afişând 0 dacă acea comparaţie e
falsă, respectiv 1 dacă acea comparaţie e adevărată).

Date de intrare

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

Date de ieşire

Fişierul comp.out va conţine pe prima linie un număr natural reprezentând numărul de


comparaţii ı̂n care se utilizează semnul $. Urmează N linii, fiecare 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
fişierul de intrare este falsă, respectiv 1 ı̂n caz contrar.

Restricţii şi precizări

a 0 $ N & 1000
a Numerele din fişier nu depăşesc ı̂n valoare numărul eudorian 1000m1000s1000z1000u.
a Lungimea fiecărei linii din fişier este cel mult 250.
a Punctaj. Dacă numărul de comparaţii care utilizează semnul $ este corect se va acorda 20%
din punctajul pe test. Punctajul integral se acordă pentru rezolvarea corectă a ambelor cerinţe.

Exemple

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

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


CAPITOLUL 12. OJI 2011 12.2. COMP 140

12.2.2 Cod sursă

Listing 12.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;
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
CAPITOLUL 12. OJI 2011 12.2. COMP 141

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 12.2.2: alin.cpp


1 //burtza alin
2 #include <fstream>
3 #include <string.h>
4 #include <ctype.h>
5
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;
CAPITOLUL 12. OJI 2011 12.2. COMP 142

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
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 12.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;
CAPITOLUL 12. OJI 2011 12.2. COMP 143

19 x[i]=comp();
20 }
21 printf("%d\n",cnt);
22 for(int i=1;i<=n;i++)printf("%d\n",x[i]);
23 return 0;
24 }
25
26 int comp()
27 {
28 //evalueaza intreaga expresie
29 //cursorul p este la inceputul expresiei
30 int left,right;
31 char op;
32
33 //evalueaza membrul I
34 left=member();
35
36 //cursorul p ajunge pe semnul de inegalitate
37 op=*p;
38
39 //daca semnul este < se contorizeaza
40 if(op==’<’)cnt++;
41
42 //avanseaza cursorul la inceputul membrului II
43 p++;
44
45 //evalueaza membrul II
46 right=member();
47
48 // decide daca inegalitatea este corecta
49 // si returneaza 1/0 pentru adevarat/fals
50 if(op==’<’&&(left<right))return 1;
51 if(op==’>’&&(left>right))return 1;
52 return 0;
53 }
54
55 int member()
56 {
57 //evalueaza un membru de inegalitate
58 //cursorul p este pozitionat la inceputul membrului
59
60 //initializeaza valoarea membrului cu valoarea termen din membru
61 //la final cursorul va fi in spatele termenului
62
63 int ret=number();
64 while(*p==’+’)
65 {
66 //cat timp intalnim semnul + membrul mai contine termeni
67
68 //avanseaza cursorul pe urmatorul termen
69 p++;
70
71 //evalueaza termenul si il adauga la rezultat
72 ret+=number();
73 }
74
75 //returneaza rezultatul membrului curent
76 return ret;
77 }
78
79 int number()
80 {
81 //evalueaza un numar eudorian
82 //cursorul este pozitionat la inceputul numarului
83 //la final el va fi in spatele numarului
84
85 //initializeaza valoarea cu valoarea primei cifre eudoriene
86 //la final cursorul va fi in spatele cifrei
87 int ret=digit();
88
89 //cat timp cursorul ajunge pe caracter cifra
90 //numarul mai contine cifre eudoriene
91 //deci se adauga cifra la valoarea numarului
92
93 while(isdigit(*p))
94 ret+=digit();
CAPITOLUL 12. OJI 2011 12.2. COMP 144

95
96 //returneaza valoare numar
97 return ret;
98 }
99
100 int digit()
101 {
102 //evalueaza o cifra eudoriana
103 //se parseaza numarul pana intalnim u,z,s sau m
104
105 int ret=0;
106 while(isdigit(*p)){ret*=10;ret+=*p-’0’;p++;};
107
108 // in functie de codul cifrei eudoriene
109 // se multiplica rezultatul cu 1,10,100 sau 1000
110 // si returneaza valoarea
111
112 if(*p==’u’){p++;return ret;}
113 if(*p==’z’){p++;return 10*ret;}
114 if(*p==’s’){p++;return 100*ret;}
115 p++;
116 return 1000*ret;
117 }

Listing 12.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,"+");
CAPITOLUL 12. OJI 2011 12.2. COMP 145

50 while (p)
51 {x=numar(p);
52 sum+=x;
53 p=strtok(NULL, "+");}
54 return sum;
55 }
56
57
58 long int numar (char * s)
59 {
60 long int sum=0;
61 char *p;
62 p=strchr(s,’m’);
63 if (p)
64 {*p=0;
65 sum+=1000*atol(s);
66 s=p+1;}
67 p=strchr(s,’s’);
68 if (p)
69 {*p=0;
70 sum+=100*atol(s);
71 s=p+1;}
72 p=strchr(s,’z’);
73 if (p)
74 {*p=0;
75 sum+=10*atol(s);
76 s=p+1;}
77 p=strchr(s,’u’);
78 if (p)
79 {*p=0;
80 sum+=atol(s);
81 }
82 return sum;
83 }

Listing 12.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;
CAPITOLUL 12. OJI 2011 12.2. COMP 146

39
40 p=strchr(s,’<’);
41 if (!p) p=strchr(s,’>’);
42 strcpy(dr,p+1);
43 sm=*p;
44
45 *p=’\0’;
46 p=strtok(s,"+");
47 while(p)
48 {
49 xs+=numar(p);
50 p=strtok(NULL,"+");
51 }
52
53 p=strtok(dr,"+");
54 while(p)
55 {
56 xd+=numar(p);
57 p=strtok(NULL,"+");
58 }
59
60 if (sm==’>’) return xs>xd;
61 if (sm==’<’) return xs<xd;
62
63 return -1; // ... !!!
64 }
65
66 int main()
67 {
68 f>>n;f.get();
69 for (i=1;i<=n;++i)
70 {
71 f.getline(e,256);
72 g<<eval(e)<<’\n’;
73 }
74 return 0;
75 }

Listing 12.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 }
CAPITOLUL 12. OJI 2011 12.2. COMP 147

Listing 12.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();
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;
CAPITOLUL 12. OJI 2011 12.2. COMP 148

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 }

12.2.3 *Rezolvare detaliată


Capitolul 13

OJI 2010

13.1 cladiri
Problema 1 - cladiri 100 de puncte
Institutul de Fizică a Pământului studiază
efectele unui potenţial cutremur folosind simulări
computerizate. Harta plană a clădirilor de pe un
teritoriu oarecare este reprezentată folosind coor-
donatele GPS ı̂n plan, longitudine şi latitudine,
faţă de un reper considerat de coordonate 0, 0,
ca ı̂n figura alăturată.
Fiecare dintre clădirile aflate pe hartă, au două
coordonate GPS, (Longitudine, Latitudine) şi un
Grad de rezistenţă la cutremure.
Un cutremur se poate produce ı̂n orice punct
de coordonate de pe hartă, numit centrul seismu-
lui şi are o anumită intensitate. Unda de şoc se
propagă sub forma unor pătrate concentrice cu Figura 13.1: cladiri
centrul seismului, numite nivele (nivelul 0 reprezintă centrul seismului, nivelul 1 primul pătrat
concentric, nivelul 2 al doilea pătrat concentric şi aşa mai departe). Intensitatea slăbeşte la fiecare
pătrat concentric cu centrul seismului cu câte o unitate. Clădirile sunt afectate de cutremur doar
dacă gradul lor de rezistenţă la cutremur este mai mic sau egal cu intensitatea cutremurului ı̂n
poziţia clădirii.

Cerinţe

Scrieţi un program care să citească coordonatele centrului seismului şi intensitatea sa ı̂n acel
punct, precum şi coordonatele clădirilor şi gradul lor de rezistenţă la cutremur, şi apoi să deter-
mine:
a) numărul N total de clădiri afectate;
b) numărul M maxim de clădiri afectate pe un nivel;
c) numerele nivelelor cu M clădiri afectate, ı̂n ordinea crescătoare a numerelor acestor nivele.

Date de intrare

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 fiecare dintre următoarele linii, până la sfârşitul fiş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;

149
CAPITOLUL 13. OJI 2010 13.1. CLADIRI 150

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

Restricţii şi precizări

a0 & Long, Lat, Grad, Intensitate & 10000;


a0 $ număr clădiri & 100000;
a ı̂n centrul seismului se pot afla 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 fiecare 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 24 3 şi este atins pe nivelele 2 şi 4.
13 5 1
8 11 4
876
15 4 3
15 9 10
13 10 1
16 8 4
3 3 3 0 Intensitatea cutremurului este 3 şi nu poate afecta cele
1 3 5 0 3 clădiri, deci avem N=0 clădiri afectate, iar maximul
2 4 7 clădirilor afectate pe un nivel este, evident, M=0.
3 2 9

Timp maxim de executare/test: 1.0 secunde

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

13.1.2 Cod sursă

Listing 13.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];
CAPITOLUL 13. OJI 2010 13.1. CLADIRI 151

8
9 long x,y,ic,nivel[10001],nivmax=0;
10
11 // acesta functie testeaza daca o cladire rezista cutremurului
12 void test(long xf,long yf,long grf)
13 {
14 long mx,my,niv;
15 if (xf-x>=0) mx = xf-x;
16 else mx = x-xf;
17 if (yf-y>=0) my = yf-y;
18 else my = y-yf;
19 if (mx>=my) niv=mx;
20 else niv=my;
21 if (niv>nivmax) nivmax=niv;
22 if (grf<=ic-niv) nivel[niv]++;
23 }
24
25 // acesta functie determina numarul total de cladiri distruse
26 long total(vector v,long n)
27 {
28 long s=0,i;
29 for(i=0;i<=n;i++)
30 s+=v[i];
31 return s;
32 }
33
34 // acesta functie determina nivelul pe care se gasesc cele mai multe cladiri
35 // distruse
36 long maxim(vector v,long n)
37 {
38 long max=v[0],i;
39 for(i=1;i<=n;i++)
40 if (v[i]>max) max=v[i];
41 return max;
42 }
43
44 int main()
45 {
46 long xc,yc,gr,i,m,c=0;
47 ifstream f("cladiri.in");
48 ofstream g("cladiri.out");
49
50 // citesc datele cutremurului
51 f>>x>>y>>ic;
52
53 // citesc succesiv datele cladirilor
54 while (f>>xc>>yc>>gr)
55 {
56 test(xc,yc,gr);
57 c++;
58 }
59
60 g<<total(nivel,nivmax)<<"\n";
61
62 m=maxim(nivel,nivmax);g<<m<<"\n";
63
64 // aflu nivelele cu cele mai multe cladiri distruse
65 if (m)
66 for(i=0;i<=nivmax;i++)
67 if (nivel[i]==m) g<<i<<" ";
68
69 g<<"\n";
70
71 f.close();
72 g.close();
73 return 0;
74 }

Listing 13.1.2: sol3.cpp


1 #include <fstream>
2 #include <math.h>
3
4 using namespace std;
5
CAPITOLUL 13. OJI 2010 13.2. SECVENTE 152

6 long v[10001], nr_clad=0;


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 }

13.1.3 *Rezolvare detaliată

13.2 secvente
Problema 2 - secvente 100 de
puncte
Mariei ı̂i plac numerele prime şi puterile numerelor prime. Pornind de la un număr prim p, ea
construieşte noi numere, fiecare număr construit fiind un produs de forma p (y " N, y j 0) sau
y

q p , m " N şi q un număr prim, numindu-le numere p-prime. De exemplu, numerele 2, 3, 4, 5,


m
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 final, pentru fiecare 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, fiecare set fiind format din
numerele n, p, k, cu semnificaţiile din enunţ, şi şirul cu n elemente a1 , a2 , a3 , ..., an , numerele
Mariei. Programul va determina pentru fiecare set de date numărul secvenţelor ce conţin exact k
numere p-prime, precum şi poziţiile de ı̂nceput şi de final ale acestor secvenţe ı̂n şirul din set.
CAPITOLUL 13. OJI 2010 13.2. SECVENTE 153

Date de intrare

Pe prima linie a fişierului secvente.in se află numărul D reprezentând numărul de seturi de


date din fişier. Seturile de date sunt scrise ı̂n fişier pe linii succesive. Pentru fiecare set de date,
prima linie conţine câte trei numere naturale: n (numărul de elemente de pe foaie), p şi k (cu
semnificaţia din enunţ), separate prin câte un spaţiu, iar fiecare 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 celor D seturi de date. Pentru


fiecare soluţie prima linie va conţine un număr x reprezentând numărul de secvenţe ce ı̂ndeplinesc
proprietăţile cerute, iar fiecare 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 final a fiecă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 aflate pe poziţii consecutive ı̂n şirul dat.
CAPITOLUL 13. OJI 2010 13.2. SECVENTE 154

Exemple

secvente.in secvente.out Explicaţii


2 2 Cum D=2, fişierul de intrare conţine două seturi de date.
532 12 Primul set de date: n 5, p 3, k 2 şi a 7, 27, 4, 45, 1.
7 24 Ş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 fişierului de ieşire se va scrie valoarea
357 2, iar pe următoarele două linii, poziţiile de ı̂nceput şi de final
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 fişierul de ieşire, pe cea de-a patra linie, se va scrie
valoarea 0.

Timp maxim de executare/test: 1.0 secunde

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

13.2.2 Cod sursă

Listing 13.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
CAPITOLUL 13. OJI 2010 13.2. SECVENTE 155

9 var f,g:text;
10 t,n,p,k,m,i,x,d:integer; {n-atatea numere de pe foaie}
11 {p-baza componentei putere a numarului p-prim}
12 {k-cate numere prime vor fi in secventele cerute}
13 a:array[1..15000] of integer; {a- contine pozitiile numerelor p-prime}
14 prime: array[1..MAX] of boolean;
15 {prim[i]=true/false ne va spune daca i este numar prim}
16
17 procedure numereprime; {ciurul lui Eratostene}
18 var i,j:integer; {pentru generarea numerelor prime}
19 rad:real;
20 begin
21 rad:=sqrt(MAX);
22 for i:=1 to MAX do {toate numerele le consideram prime la inceput}
23 prime[i]:=true;
24 i:=1; {plec de la inceputul vectorului}
25 while i<=rad do {se lucreaza doar pana la radical din n}
26 begin
27 repeat
28 inc(i)
29 until prime[i]; {caut urmatorul numar prim la rand}
30 j:=i*i; {primul numar care nu mai este prim este patratul lui}
31 while j<=MAX do {apoi plecand de la el}
32 begin
33 prime[j]:=false; {il marchez ca fiind neprim (e multiplu de i)}
34 j:=j+i {si marchez din i in i}
35 end;
36 end;
37 End;
38
39 function pprim(x:integer):boolean;
40 begin
41 if x=1 then pprim:=false {numarul 1 nu e pprim}
42 else
43 begin
44 while x mod p=0 do {simplific cu p}
45 x := x div p;
46 pprim:= prime[x]; {si verific daca e prim}
47 {- de data asta se accepta si 1}
48 end;
49 end;
50
51 begin
52 assign(f,fin);
53 reset(f);
54 assign(g,fout);
55 rewrite(g);
56 numereprime; {generez tabelul numerelor prime cu ciurul lui Eratostene}
57 readln(f,D);
58 for t:=1 to D do
59 begin
60 readln(f,n,p,k);
61 m:=0;
62 for i:=1 to n do
63 begin
64 readln(f,x);
65 if pprim(x) then
66 begin
67 inc(m);
68 a[m]:=i;
69 {am memorat pozitia de aparitie a unui numar p-prim}
70 end;
71 end;
72 if m-k+1<=0 then
73 writeln(g,0)
74 else
75 writeln(g,m-k+1);
76 {din m numere prime pot crea m-k+1 secvente de lungime k}
77
78 for i:=1 to m-k+1 do
79 writeln(g,a[i],’ ’,a[i+k-1]); {astea sunt extremitatile secventelor}
80 end;
81 close(f);
82 close(g);
83 end.
CAPITOLUL 13. OJI 2010 13.2. SECVENTE 156

Listing 13.2.2: BPREPRO.PAS


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)
CAPITOLUL 13. OJI 2010 13.2. SECVENTE 157

75 else
76 writeln(g,m-k+1);
77 {din m numere prime pot crea m-k+1 secvente de lungime k}
78
79 for i:=1 to m-k+1 do
80 writeln(g,a[i],’ ’,a[i+k-1]); {astea sunt extremitatile secventelor}
81 end;
82 close(f);
83 close(g);
84 end.

13.2.3 *Rezolvare detaliată


Part II

ONI - Olimpiada naţională de


informatică

158
Capitolul 14

ONI 2022

14.1 proeminenta
Problema 1 - Proeminenta 100 de puncte
Cea mai cunoscută măsură a unui vârf de munte este altitudinea sa. Vârfuri secundare ale
unui munte ı̂nalt pot avea altitudini considerabile, dar ı̂n general acestea nu sunt atât de relevante.
Din acest motiv, geografii au introdus o nouă măsură pentru un vârf: proeminenţa.
Un profil este o reprezentare a altitudinilor ı̂n puncte succesive, ı̂ntre care ne putem deplasa
la stânga sau dreapta.

În desen avem profilul munţilor Făgăraş, pe care exemplificăm următoarele definiţii:

1. Un vârf este un punct (sau o succesiune de puncte consecutive cu aceeaşi altitudine), pentru
care altitudinile punctelor alăturate ı̂n stânga, respectiv ı̂n dreapta sunt strict mai mici decât
altitudinea vârfului. De exemplu, Negoiu este un vârf (altitudinea sa este 2535m, punctul de
pe profil situat ı̂n stânga sa are altitudinea 2306m, iar punctul de pe profil situat ı̂n dreapta
sa are altitudinea 2030m). Un alt exemplu de vârf este Urlea, fiind reprezentat pe profil
prin două puncte consecutive cu altitudinea 2473m, având ı̂n stânga un punct cu altitudinea
2160m, iar ı̂n dreapta un punct cu altitudinea 1511m.
2. Cea mai adâncă vale dintre două vârfuri este altitudinea minimă a unui punct care se află
ı̂ntre cele două vârfuri. De exemplu, ı̂n desen ı̂ntre Negoiu şi Moldoveanu cea mai adâncă
vale este la 2030m.

3. Proeminenţa unui vârf este diferenţa minimă dintre altitudinea lui şi cea mai adâncă
vale dintre el şi un vârf strict mai ı̂nalt decât el. Dacă nu există un vârf strict mai ı̂nalt,
proeminenţa este altitudinea vârfului. De exemplu, pentru a determina proeminenţa vârfului
Gălăşescu Mare putem considera vârfurile strict mai ı̂nalte (ı̂n ordine de la stânga la dreapta:
Negoiu, Moldoveanu şi Urlea). Dacă plecăm de pe Negoiu, diferenţa dintre cea mai adâncă
vale şi Gălăşescu Mare este 2470-2030=440m. Dacă plecăm de pe Moldoveanu, diferenţa

159
CAPITOLUL 14. ONI 2022 14.1. PROEMINENTA 160

dintre cea mai adâncă vale şi Gălăşescu Mare este 2470-2213=257m. Dacă plecăm de
pe Urlea, diferenţa dintre cea mai adâncă vale şi Gălăşescu Mare este 2470-2160=310m.
Diferenţa minimă este 257m, deci proeminenţa vârfului Gălăşescu Mare este dată de vârful
Moldoveanu.

Cerinţe

Scrieţi un program care, cunoscând configuraţia unui profil, rezolvă următoarele două cerinte:
1. determină numărul de vârfuri existente pe profilul respectiv;
2. determină proeminenţa fiecărui vârf de pe profil.

Date de intrare

Fişierul de intrare proeminenta.in conţine pe prima linie numărul natural C, reprezentând


cerinţa care trebuie să fie rezolvată: 1 sau 2. Pe cea de a doua linie se află numărul natural N ,
reprezentând numărul de puncte din profil. Pe a treia linie se află N numere naturale separate
prin câte un spaţiu a1 , a2 , ..., aN , reprezentând altitudinile celor N puncte de pe profil, ı̂n ordine
de la stânga la dreapta.

Date de ieşire

Fişierul de ieşire proeminenta.out va conţine o singură linie pe care va fi scris răspunsul la


cerinţa C.
Dacă C 1 răspunsul va fi numărul natural Nr, reprezentând numărul de vârfuri.
Dacă C 2 răspunsul va fi o succesiune de Nr valori naturale separate prin câte un singur
spaţiu, reprezentând proeminentele celor Nr vârfuri, ı̂n ordinea din fişierul de intrare.

Restricţii şi precizări

3 & N & 10
6
ˆ

0 & ai $ 2 , pentru 1 & i & N


31
ˆ

ˆ a1 $ a2 şi aN 1 % aN

# Punctaj Restricţii
1 10 Pentru teste valorând 10 de puncte: C 1.
2 30 Pentru teste valorând 30 de puncte: C 2 şi N & 1000.
3 20 Pentru teste valorând 20 de puncte: C 2 şi 1000 $ N ,
iar altitudinile ai sunt distincte două câte două.

Exemple:

proeminenta.in proeminenta.out
1 7
19
1387 1890 2306 2535 2030 2391 2079
2544 2213 2470 2305 2433 2160 2473
2473 1511 1735 1621 1367
2 505 312 2544 257 128 313 224
19
1387 1890 2306 2535 2030 2391 2079
2544 2213 2470 2305 2433 2160 2473
2473 1511 1735 1621 1367

Explicaţii:

Profilul din fişierele de intrare este cel reprezentat ı̂n desen. Cele 7 vârfuri sunt denumite pe desen.
Deşi altitudinile apar ı̂n tabel pe mai multe linii, ele vor fi scrise pe o singură linie ı̂n fişierul de
intrare.
CAPITOLUL 14. ONI 2022 14.1. PROEMINENTA 161

14.1.1 Indicaţii de rezolvare

Propusă de: Lector dr. Paul Diac, Facultatea de Informatică, Universitatea ”Alexandru Ioan Cuza”
Iaşi
Pentru ambele cerinţe trebuie eliminate altitudinile consecutive egale. Acest lucru se poate
face prin citirea altitudinilor ı̂ntr-o variabilă temporară x şi adăugarea ı̂n vector doar a valorilor
diferite de ultima adăugată:

if ( x != a [n-1] ) { a [n++] = x ; }

Pentru cerinţa C 1:
Se parcurg altitudinile 0 $ i $ n  1 şi se numără vârfurile (pe pozi c tia i este un vârf dacă
ai  1 $ ai % ai  1.
Pentru cerinţa C 2:
3
Soluţie O N . O primă soluţie pentru a determina proeminenţa unui vârf de pe poziţia
i este să aplicăm definiţia ı̂n mod direct. Parcurgem orice alt vârf j şi le procesăm pe cele cu
aj  % ai.
Pentru a determina cea mai adâncă vale dintre i şi j, se parcurg poziţiile k dintre min i, j  şi
max i, j  şi se reţine cea mică valoare ak . Proeminenţa vârfului i va fi diferenţa minimă dintre
ai şi valorile ak  obţinute astfel. Soluţia se ı̂ncadrează ı̂n timp pe testele cu N & 1 000.
2
Soluţie O N . După ce fixăm un vârf i pentru care calculăm proeminenţa, putem parcurge
vârfurile j ı̂ncepând de la pozitia i prima dată ı̂n stânga, apoi ı̂n dreapta. Prin aceste parcurgeri
putem determina vârfuri aj  % ai concomitent cu a determina cea mai adâncă vale dintre ele.
Reţinem acestă altitudine minimă ı̂ntr-o variabilă pe care o folosim şi pentru următoarele poziţii
j.
Optimizare: Analizând mai atent definiţia proeminenţei, deducem că pe un profil
proeminenţa unui vârf i va fi dată de primul vârf mai mare decât ai situat ı̂n stânga sa as % ai
(s $ i) sau de primul vârf mai mare decât ai situat ı̂n dreapta sa ad % ai (d % i).
Justificarea constă ı̂n faptul că, dacă ar exista alte vârfuri ak  % ai cu k $ s sau k % d până
la care văile ar fi şi mai adânci, ele nu vor fi relevante, deoarece atunci când se alege minimul dintre
diferenţa ai şi cea mai adâncă vale, ele dau o diferenţă mai mare, care nu modifică minimul.
2
Soluţiile de complexitate O N  se ı̂ncadrează ı̂n timp pe un număr semnificativ mai mare de
teste, ı̂n funcţie de optimizările efectuate.
Soluţie O N . Pentru a proiecta o soluţie de complexitate liniară determinăm mai ı̂ntâi
”proeminenţa la stânga”, parcurgând vârfurile de la stânga către dreapta. Procedăm apoi similar
pentru a determina ”proeminenţa la dreapta”, parcurgând vârfurile de la dreapta cs̆tângaşi la final
afişăm minimul dintre cele două valori obţinute pentru proeminenţă.
Pentru a calcula proeminenţa la stânga, parcurgem vârfurile ı̂n ordinea crescătoare a indicilor
i. Pentru orice vârf i doar cel mai apropiat vârf j din stânga lui i (j $ i), strict mai ı̂nalt decât
vârful i este relevant, prin urmare toate vârfurile dintre acestea j $ k $ i care au altitudini mai
mici ak  $ ai, pot fi ignorate mai departe.
Este deci suficient să reţinem o stivă cu vârfuri montane ı̂n ordinea strict descrescătoare
a ı̂nălţimilor. Pentru fiecare dintre aceste vârfuri, vom reţineşi cea mai adâncă vale situată ı̂n
dreapta vârfului, până la următorul vârf adăugat ı̂n stivă, vale pe care o determinăm ”din mers”.
Rezumând, parcurgem profilul de la stânga la dreapta şi identificăm vârfurile montane (fie ai
altitudinea vârfului montan curent).
Cât timp ı̂n stivă există elemente şi ai ' altitudinea vârfului montan plasat la vârful stivei,
extragem din stivă un element (evident, doar elementul situat la vârful stivei poate fi extras).
Atunci când extragem un element din stivă, actualizăm, dacă este cazul, şi ı̂nălţimea celei mai
adânci văi până la vârful montan curent.
Dacă stiva a devenit vidă, inserăm vârful montan curent ı̂n stivă şi iniţializăm ı̂nălţimea celei
mai adânci văi până la momentul curent cu ai.
Dacă stiva este nevidă, atunci ai $ altitudinea vârfului plasat la vârful stivei. În acest caz:
CAPITOLUL 14. ONI 2022 14.1. PROEMINENTA 162

(1) pentru vârful montan situat ı̂n vârful stivei memorăm cea mai mică altitudine a unei văi
identificate până la vârful montan curent;
(2) determinăm proeminenţa la stânga a vârfului montan curent ca fiind diferenţa dintre a[i] si
altitudinea minimă a unei văi până la vârful montan curent;

(3) reiniţializăm ı̂nălţimea minimă a văii curente cu ai;


(4) inserăm vârful montan curent ı̂n stivă.
Pentru a evita duplicarea codului pentru ”proeminenţa la dreapta”, putem oglindi vectorul şi
repeta aceeaşi procedură.

Listing 14.1.1: proeminenta.cpp


1 /*
2 autor: Paul Diac
3 idee: Iteram toate inaltimile , retinem varfurile in ordine scrict descrescatoare si
4 cele mai adanci vai de dupa ele. Un varf nou adaugat elimina toate varfurile mai mici
5 decat el, deci vom folosi o stiva pentru varfuri. Pentru a evita alte cazuri, la inceput
6 calculam proeminenta la stanga , si apoi la dreapta dupa o oglindire.
7 La final afisam minimul dintre cele doua proeminente calculate.
8 memorie: O(N), timp: O(N), scor: 100
9 */
10 #include<stdio.h>
11 #define NMax 1000005;
12 long int N, C, s;
13 long int a [NMax];
14
15 long int min_( long int i, long int j ) { if ( i < j ) return i; return j; }
16 long int peak[NMax], valley[NMax], top=-1; // peak este stiva de varfuri
17 // valley retine "cele mai adanci vai"
18 long int prom[2][NMax]; // proeminenta la stanga varfului de pe pozita i este prom[0][i]
19 // si la dreapta este prom[0][n-1-i] datorita oglindirii
20
21 void popTop()
22 { // eliminam un varf din stiva, actualizam cea mai adanca vale
23 // din dreapta daca e cazul if ( top < 0 ) { return ; }
24 if( top >= 1 && valley[top-1] > valley[top] )
25 valley[top-1] = valley[top] ;
26 top--;
27 }
28
29 int main()
30 {
31 freopen( "proeminenta.in", "r", stdin ) ;
32 freopen( "proeminenta.out", "w", stdout ) ;
33
34 scanf( "%ld %ld", &C , &N ) ;
35 for( int i = 0; i < N; i++)
36 {
37 scanf( "%ld", &a[i] );
38
39 if( i > 0 && a[i] == a[i-1] )
40 {
41 i--; N--; // eliminam duplicatele
42 }
43 }
44
45 if(C == 1 )
46 {
47 for( int i = 1; i < N-1; i ++)
48 if( a[i-1] < a[i] && a[i] > a [i+1] ) { s++; } // este varf
49
50 printf( "%ld\n", s ) ;
51 return 0 ;
52 }
53
54 for( int d = 0; d <= 1; d++)
55 { // d=0 va fi parcurgerea stanga-dreapta, si d=1
56 // dupa oglindire, adica dreapta-stanga
57 for( int i = 1; i < N-1; i++)
58 {
59 if(a[i-1] < a[i] && a[i] > a[i+1] )
60 { // este varf
CAPITOLUL 14. ONI 2022 14.2. RGB 163

61 prom[d][i] = a[i] ;
62 while( top >= 0 && a[peak[top]] <= a[i] )
63 { // elimim varfurile mai
64 // mici din stiva pe rand
65 popTop() ;
66 }
67
68 if( top >= 0 && a[peak[top]] > a[i] )
69 {
70 prom[d][i] = a[i] - valley[top] ;
71 // am gasit primul varf mai mare din stanga
72 }
73 peak[++top] = i ;
74 valley[top] = a[i] ; // adaugam noul varf
75 }
76
77 if( top >= 0 && valley[top] > a[i] )
78 valley[top] = a [i];
79 }
80
81 for( int i = 0; i < N/2; i++)
82 { // oglindim altitudinile
83 long aux = a[i];
84 a[i] = a[N-1-i];
85 a[N-1-i] = aux ;
86 }
87
88 top = -1;
89 }
90
91 for( int i=1; i < N-1; i++)
92 if( a[i-1] < a[i] && a[i] > a[i+1] )
93 {
94 if( prom[0][i] > prom[1][N-1-i ] )
95 { // retinem maximul in prom[0][i]
96 prom[0][i] = prom[1][N-1-i] ;
97 }
98 }
99
100 for( int i=1; i < N-1; i++)
101 if( prom[0][i] > 0 )
102 printf( "% l d ", prom[0][i] ) ; } // si il afisam
103
104 printf( "\n" );
105 fclose( stdout );
106 return 0 ;
107 }

aaaaaaaaaaa

14.1.2 *Cod sursă

bbbbbbbbbbbb

14.1.3 *Rezolvare detaliată

ccccccccccccc

14.2 rgb
Problema 2 - RGB 100 de puncte
Ionuţ, tânăr programator, se lansează pe piaţa producătorilor de jocuri pe calculator. Jocul
pe care l-a proiectat se numeşte RGB. În joc există N personaje extraterestre. Fiindcă Ionuţ nu
este de acord cu teoria omuleţilor verzi, personajele lui sunt de 3 culori:
ˆ R extratereştri de culoare roşie;
ˆ G extratereştri de culoare verde;
ˆ B extratereştri de culoare albastră.
CAPITOLUL 14. ONI 2022 14.2. RGB 164

Fiecare extraterestru are o anumită putere, exprimată printr-un număr natural impar, puterile
oricăror doi extratereştri fiind diferite.
Pe parcursul jocului fiecare extraterestru va lupta cu fiecare dintre ceilalţi extratereştri.
Rezultatul unei lupte ı̂ntre doi extratereştri depinde de puterea acestora, dar şi de culoarea lor.
Într-o luptă dintre doi extratereştri de aceeaşi culoare, va câştiga cel cu puterea cea mai mare.
Într-o luptă ı̂ntre doi extratereştri de culori diferite, puterile lor se modifică după cum urmează,
iar după modificare lupta o va câştiga extraterestrul cu puterea mai mare:
ˆ Puterea unui extraterestru roşu este dublată dacă adversarul este un extraterestru verde.
ˆ Puterea unui extraterestru verde este dublată dacă adversarul este un extraterestru albastru.
ˆ Puterea unui extraterestru albastru este dublată dacă adversarul este un extraterestru roşu.
După fiecare luptă, puterile extratereştrilor revin la valorile iniţiale, ı̂n caz că s-au modificat.

Cerinţe

Scrieţi un program care, cunoscând culorile şi puterile extratereştrilor, rezolvă următoarele
două cerinte:
1. determină puterea extraterestrului care câştigă cele mai multe lupte; dacă există mai mulţi
astfel de extratereştri, se va afişa puterea minimă;
2. determină pentru fiecare extraterestru numărul de lupte câştigate de acesta.

Date de intrare

Fişierul de intrare rgb.in va conţine pe prima linie numerele naturale C R G şi B, unde C
este cerinţa care trebuie să fie rezolvată (1 sau 2), R reprezintă numărul de extratereştri roşii, G
numărul de extratereştri verzi, iar B numărul de extratereştri albaştri.
Pe cea de a doua linie se află R numere naturale impare ı̂n ordine strict crescătoare,
reprezentând puterile celor R extratereştri roşii.
Pe cea de a treia linie se află G numere naturale impare ı̂n ordine strict crescătoare,
reprezentând puterile celor G extratereştri verzi.
Pe cea de a patra linie se află B numere naturale impare ı̂n ordine strict crescătoare,
reprezentând puterile celor B extratereştri albaştri.
Valorile scrise pe aceeaşi linie sunt separate prin câte un spaţiu.

Date de ieşire

Pentru C 1, fişierul de ieşire rgb.out va conţine o singură linie pe care va fi scrisă puterea
extraterestrului care câştigă cele mai multe lupte; dacă există mai mulţi extratereştri care câştigă
un număr maxim de lupte, se va afişa puterea minimă.
Pentru C 2, fişierul de ieşire rgb.out va conţine trei linii. Pe prima linie se va scrie numărul
de lupte câştigate de fiecare extraterestru roşu. Pe a doua linie, se va scrie numărul de lupte
câştigate de fiecare extraterestru verde. Pe a treia linie, se va scrie numărul de lupte câştigate de
fiecare extraterestru albastru.
Pentru fiecare culoare, valorile vor fi afişate considerând ordinea extratereştrilor din fişierul de
intrare.
Valorile scrise pe aceeaşi linie vor fi separate prin câte un spaţiu.

Restricţii şi precizări

ˆ N R+G+B
ˆ 1 & R, G, B & N  2
ˆ 1 & puterea oricărui extraterestru &2 N  1
CAPITOLUL 14. ONI 2022 14.2. RGB 165

# Punctaj Restricţii
1 21 C 1, iar 1 & N 500 000
2 18 C 2, iar 1 & N & 1 000
3 25 C 2, iar 1 000 $ N & 100 000
4 36 C 2, iar 100 000 $ N & 500 000

Exemple:

rgb.in rgb.out
1122 7
3
17
59
2122 1
3 04
17 23
59

Explicaţii:

Pentru primul exemplu, C 1, deci se va rezolva prima cerinţă. Există un extraterestru roşu,
care are puterea 3, doi extratereştri verzi, având puterile 1, respectiv 7 şi doi extratereştri albaştri
cu puterile 5, respectiv 9.
Extraterestrul cu puterea 7 este singurul care va câştiga cele mai multe lupte (ı̂n cazul acesta,
chiar toate):

ˆ când luptă cu extraterestrul verde cu puterea 1 câştigă, pentru că are puterea mai mare;
ˆ când luptă cu extraterestrul roşu cu puterea 3, acesta ı̂şi va dubla puterea (va avea puterea
6), dar va fi insuficient pentru a câştiga lupta;
ˆ când luptă contra extraterestrului albastru cu puterea 9, va avea puterea dublată (14), prin
urmare va câştiga şi această luptă.

Pentru al doilea exemplu, C 2, deci se va rezolva a doua cerinţă.

ˆ Extraterestrul cu puterea 3 poate câştiga doar o luptă (contra extraterestrului cu puterea 1).
ˆ Extraterestrul cu puterea 1 nu poate câştiga nicio luptă.
ˆ Extraterestrul cu puterea 7 poate câştiga toate luptele (vezi explicaţia de la exemplul prece-
dent).
ˆ Extraterestrul cu puterea 5 poate câştiga două lupte (contra extratereştrilor cu puterile 1 şi 3).
ˆ Extraterestrul cu puterea 9 poate câştiga 3 lupte (contra extratereştrilor cu puterile 1, 3 şi 5).

14.2.1 Indicaţii de rezolvare

Propusă de: stud. Ioan Cristian Pop, Universitatea Politehnica Bucureşti


O observaţie cheie care simplifică implementarea problemei este aceea că este imposibil ca doi
extratereştri să aibă puteri egale ı̂n cadrul unei lupte, toţi având puteri impare distincte. În cadrul
unei lupte ı̂n care extratereştrii au culori diferite, puterea unuia dintre extratereştri va deveni pară
(iar comparaţia va fi par cu impar, deci tot ı̂ntre valori distincte). În plus, puterile extratereştrilor
de aceeaşi culoare sunt ordonate crescător ı̂n fişierul de intrare.
O altă observaţie cheie este că problema se rezolvă similar pentru fiecare culoare, deci rezolvarea
pentru o anumită culoare se poate aplica şi pentru celelalte două culori.
Cerinţa 1 - Soluţie O N . Pentru C 1, este suficient să calculăm câte lupte va câştiga cel
mai puternic extraterestru de fiecare culoare, apoi să afişăm puterea extraterestrului care câştigă
un număr maxim de lupte.
CAPITOLUL 14. ONI 2022 14.3. SUBSIR 166

Pentru a calcula numărul de lupte câştigate de un extraterestru, este necesar să calculăm doar
câte lupte va câştiga luptând cu extratereştrii de celelalte două culori (el fiind cel mai puternic
de culoarea sa va câştiga toate luptele cu extratereştrii având aceeaşi culoare cu el). O atenţie
specială trebuie acordată cazului ı̂n care există mai mulţi extratereştri care câştigă acelaşi număr
maxim de lupte (se va afişa puterea cea mai mică).
2
Cerinţa 2 - Soluţie O N . Pentru C 2, simulăm toate luptele dintre extratereştri şi
reţinem câte lupte câştigă fiecare.
Cerinţa 2 - Soluţie O N log2 N . Fie X şi Y doi extratereştri de culori diferite. Observăm
că, dacă X va câştiga lupta contra lui Y , atunci X va câştiga luptele contra tuturor extratereştrilor
de aceeaşi culoare ca Y , ı̂nsă cu putere mai mică. Prin urmare, pentru fiecare extraterestru, putem
căuta binar care este cel mai puternic extraterestru de fiecare culoare diferită de a sa pe care ı̂l
va ı̂nvinge. O astfel de soluţie poate obţine mai multe puncte ı̂n funcţie de cât de eficientă este
implementarea.
Cerinţa 2-Soluţie O N . Putem optimiza soluţia anterioară. Fie Z următorul extraterestru
de aceeaşi culoare ca X (cu putere mai mare). Atunci, Z va câştiga din start toate luptele pe
care X le va câştiga. Deci, ce trebuie verificat este dacă mai câştigă şi alte lupte ı̂n plus. Să
presupunem că A este cel mai puternic extraterestru de o culoare diferită pe care X ı̂l ı̂nvinge.
Atunci, Z ı̂i va ı̂nvinge pe toţi până la A inclusivşi vom verifica dacă va mai ı̂nvinge şi pe
alţii de aceeaşi culoare, căutând secvenţial, ı̂ncepând după A. Să presupunem că cel mai puternic
extraterestru pe care ı̂l va ı̂nvinge va fi B. Atunci, pentru următorul extraterestru de aceeaşi
culoare ca X şi Z, căutarea va ı̂ncepe după B, şi aşa mai departe. Analog pentru cealaltă culoare
diferită. Tehnica de rezolvare mai este cunoscută drept ”Two pointers”.
De precizat este că citirea poate fi parsată
 (datele citite ca şir de caractere şi apoi transformate
ı̂n numere). Nu este ı̂nsă necesară pentru a obţine 100 de puncte.

14.2.2 *Cod sursă

14.2.3 *Rezolvare detaliată

14.3 subsir
Problema 3 - Subsir 100 de puncte
Fie S S1 , S2 , ..., SN  un şir format din N numere naturale mai mici decât 10 şi x un număr
natural cu p cifre, reprezentat ı̂n baza 10 prin x x1 x2 ...xp . Observaţi că poziţiile din şirul S
sunt numerotate de la stânga la dreapta de la 1 la N , iar cifrele numărului x sunt numerotate de
la stânga la dreapta de la 1 la p (cifra p fiind cifra unităţilor).
Vom spune că x apare ca subşir ı̂n şirul S dacă există poziţiile 1 & j1 $ j2 $ ... $ jp & N
astfel ı̂ncât să avem Sji xi pentru orice 1 & i & p.
De exemplu, pentru S 2, 3, 7, 9, 27 apare ca subşir ı̂n S, dar 93 nu apare ca subşir ı̂n S.
Numim prefix de lungime j al lui S secvenţa S1 , S2 , ..., Sj (secvenţa care ı̂ncepe la poziţia
1 şi se termină la poziţia j, 1 & j & N ).

Cerinţe

Scrieţi un program care, cunoscând şirul S, rezolvă următoarele două cerinţe:


1. fiind date N r perechi de forma x j, determină pentru fiecare pereche dacă numărul x apare
ca subşir ı̂n prefixul de lungime j al lui S şi afişează valoarea 1 ı̂n caz afirmativ, respectiv
valoarea 0 ı̂n caz contrar;
2. fiind date N r perechi de forma a b, determină şi afişează pentru fiecare pereche numărul de
numere naturale din intervalul ı̂nchis a, b care apar ca subşir ı̂n şirul S.

Date de intrare
CAPITOLUL 14. ONI 2022 14.3. SUBSIR 167

Fişierul de intrare subsir.in conţine pe prima linie un număr natural C care reprezintă cerinţa
care urmează a fi rezolvată (1 sau 2).
Pe a doua linie se află numărul natural N .
Pe a treia linie se află N numere naturale mai mici decât 10, S1 S2 ...SN , reprezentând elementele
şirului S.
Pe linia a patra se află un număr natural N r care reprezintă numărul de perechi.
Pe următoarele N r linii se află câte o pereche de numere naturale. Numerele scrise pe aceeaşi
linie ı̂n fişierul de intrare sunt separate prin câte un spaţiu.

Date de ieşire

Fişierul de ieşire subsir.out va conţine N r linii, pe cea de a i-a linie fiind scris rezultatul
pentru cea de a i-a pereche dintre cele N r perechi din fişierul de intrare, conform cerinţei C.

Restricţii şi precizări

1 & N & 10
4
ˆ

1 & N r & 10
4
ˆ

0 & x $ 10
7
ˆ

ˆ 1&j&N
ˆ 0 & a & b $ 107

# Punctaj Restricţii
1 30 Pentru teste valorând 30 de puncte cerinţa este 1.
2 70 Pentru teste valorând 70 de puncte cerinţa este 2.

Exemple:

subsir.in subsir.out
1 1
10 0
5610325231 0
5 1
14 0
12
153 6
153 9
72 8
2 11
10 15
5610325231 0
3
0 20
40 105
81 99

Explicaţii:

Pentru primul exemplu, cerinţa este 1, deci trebuie să verificăm pentru fiecare dintre cele
Nr 5 perechi x j specificate dacă x apare ca subşir ı̂n prefixul de lungime j al lui S.
Prima pereche este 1 4. Numărul 1 apare ca subşir ı̂n prefixul 5 6 1 0, deci se afişează 1.
A doua pereche este 1 2. Numărul 1 nu apare ca subşir ı̂n prefixul 5 6, deci se afişează 0.
A treia pereche este 153 6. Numărul 153 nu apare ı̂n prefixul 5 6 1 0 3 2, deci se va afişa 0.
A patra pereche este 153 9. De data aceasta numărul 153 apare ca subşir ı̂n prefixul 5 6 1 0 3
2 5 2 3, deci se afişează 1.
CAPITOLUL 14. ONI 2022 14.3. SUBSIR 168

A cincea pereche este 72 8. Numărul 72 nu apare ca subşir ı̂n niciun prefix al lui S, deci nici
ı̂n cel de lungime 8 şi se va afişa 0.
Pentru al doilea exemplu cerinţa este 2, deci trebuie să determinăm pentru fiecare dintre cele
Nr 3 perechi a b specificate câte numere naturale din intervalul [a, b] apar ca subşir ı̂n S.
Pentru intervalul [0,20] există 11 numere, acestea fiind 0, 1, 2, 3, 5, 6, 10, 11, 12, 13, 15.
Pentru intervalul [40, 105] există 15 numere, acestea fiind 50, 51, 52, 53, 55, 56, 60, 61, 62, 63,
65, 101, 102, 103, 105.
Pentru intervalul [81,99] răspunsul este 0, deoarece nu există niciun număr ı̂n acest interval
care să fie subşir al lui S.

14.3.1 Indicaţii de rezolvare

Propusă de: prof. Ionel-Vasile Piţ-Rada, Colegiul Naţional ”Traian”, Drobeta-Turnu Severin
Cerinţa 1 Soluţie ”naivă” O N N r. Pentru fiecare pereche x, j , se determină şirul
cifrelor lui x şi apoi, ı̂ncepând cu cifra cea mai semnificativă (cea din stânga) şi continuând spre
cifra cea mai puţin semnificativă (ultima, cea din dreapta), se caută succesiv prima apariţie a cifrei
respective şi când se găseşte această poziţie se continuă căutarea de acolo pentru cifra următoare.
Dacă se depăşeşte poziţia j sau una dintre cifre nu se mai găseşte, atunci se opreşte căutarea.
Cerinţa 2 Soluţie ”naivă” O N alf a. Am notat cu alf a suma lungimilor intervalelor
de interogare, care este cel mult egală cu Nr 10 000 000. Pentru fiecare interval a, b şi pentru
fiecare număr x din interval se aplică ideea de la cerinţa 1, cu limita de căutare j N .
Optimizări. Pentru obţinerea unei performanţe mai bune este util să facem următoarele
precalculări:

(1) Pentru fiecare poziţie 1 & i & N şi pentru fiecare cifră zecimală 0 & cif & 9, determinăm
poz icif  cea mai mică (prima) poziţie dinşirul S pe care apare cifra cif , după poziţia
i sau N  1 ı̂n caz că cifra cif nu mai apare ı̂n S după poziţia i.
Pentru a verifica dacă x apare ca subşir ı̂n prefixul de lungime j al lui S, parcurgem cifrele
lui x de la stânga la dreapta şi construim subşirul preluând poziţiile din poz (dacă cifra
curentă din x este cif , iar poziţia pe care apare precedenta cifra din x este i, atunci poziţia
ı̂n subşir a cifrei cif va fi poz icif . Astfel, numărul de paşi efectuaţi pentru verificarea
unei valori x este cel mult egal cu numărul de cifre din x.
Asemănător, se poate rezolva cerinţa 2, verificând succesiv fiecare valoare din fiecare interval
a, b.

(2) O a doua precalculare foarte utilă este aceea prin care pentru fiecare număr x din intervalul
7
0, 10  se determină minpoz x cea mai mică poziţie din S cu proprietatea că numărul
x apare ca subşir ı̂n prefixul de lungime minpoz x al lui S, respectiv minpoz x N  1
ı̂n cazul ı̂n care x nu apare ca subşir ı̂n S.
Iniţializarea se face prin minpoz x poz 1x, pentru 0 & x & 9, apoi, ı̂n ordine
crescătoare, pentru fiecare număr x cu 10 & x & 10 , vom calcula
7

minpoz[x] = poz [minpoz[x/10]][x%10];

Valorile pentru minpoz se determină astfel cu complexitate liniară.


(3) O a treia precalculare se poate realiza utilizând tehnica sumelor parţiale. Vom determina
nrx numărul de numere din intervalul 0, x care apar ca subşir ı̂n S. Utilizând vectorul
nr, putem determina numărul de valori x din intervalul a, b care apar ca subşir ı̂n S ca
fiind nrb  nra  1 (dacă a % 0) sau nrb (dacă a 0).

După aceste precalculări, la cerinţa 1, pentru fiecare pereche x, j  verificarea are complexitatea
O 1, deci complexitatea totală (pentru cele N r perechi) este O N r.
Analog, la cerinţa 2 pentru fiecare interval a, b numărarea se va realiza tot ı̂n O 1, deci
complexitatea totală (pentru cele N r intervale) va fi O N r.
Problema admite multiple alte abordări, care obţin diferite punctaje.
CAPITOLUL 14. ONI 2022 14.3. SUBSIR 169

14.3.2 *Cod sursă

14.3.3 *Rezolvare detaliată


Capitolul 15

ONI 2021

15.1 Bile
Problema 1 - Bile 100 de puncte
Presupunem că avem două cutii notate A şi B. Cutia A conţine N bile numerotate cu numerele
naturale distincte: 0, 1, 2, ..., N  1. Cutia B este goală.
Spunem că o bilă dintr-o cutie este bila specială a acestei cutii dacă numărul X cu care este
numerotată această bilă este egal cu media aritmetică a numerelor celorlalte bile din cutie.
La un moment dat, cineva mută bila cu numărul K din cutia A ı̂n cutia B.
Vi se cere să alegeţi alte K bile, din cutia A, pe care să le mutaţi ı̂n cutia B astfel ı̂ncât cutia
B să conţină K  1 bile, iar bila cu numărul K să fie bila specială a cutiei B.

Cerinţe

Scrieţi un program care citeşte numerele N şi K, apoi determină:

1. dacă, ı̂nainte să fie mutate bile din cutia A ı̂n cutia B, există o bilă specială ı̂n cutia A; ı̂n caz
afirmativ, programul determină numărul X cu care este numerotată această bilă specială;
2. cel mai mic (ı̂n sens lexicografic) şir strict crescător al numerelor celor K bile care pot fi
mutate din cutia A ı̂n cutia B astfel ı̂ncât bila cu numărul K să fie bila specială a cutiei B;
3. cel mai mare (ı̂n sens lexicografic) şir strict crescător al numerelor celor K bile care pot fi
mutate din cutia A ı̂n cutia B astfel ı̂ncât bila cu numărul K să fie bila specială a cutiei B.

Date de intrare

Fişierul de intrare bile.in conţine pe prima linie trei numere naturale C, N şi K, separate prin
câte un spaţiu. C reprezintă cerinţa care trebuie rezolvată (1, 2 sau 3), iar N şi K au semnificaţia
din enunţ.

Date de ieşire

Fişierul de ieşire bile.out va conţine:

ˆ dacă C 1, pe prima linie, numărul natural X reprezentând numărul bilei speciale din cutia
A sau valoarea -1 dacă cutia A nu conţine o astfel de bilă (reprezentând răspunsul la cerinţa
1);
ˆ dacă C 2, pe prima linie, un şir strict crescător de K numere naturale, separate prin câte
un spaţiu (reprezentând răspunsul la cerinţa 2);
ˆ dacă C 3, pe prima linie, un şir strict crescător de K numere naturale, separate prin câte
un spaţiu (reprezentând răspunsul la cerinţa 3).

Restricţii şi precizări

ˆ N număr natural, 4 & N & 100 000


ˆ K număr natural 2 & K & N ©2
170
CAPITOLUL 15. ONI 2021 15.1. BILE 171

ˆ Şirul y1 , y2 , ..., yK este mai mic ı̂n sens lexicograficc decât şirul z1 , z2 , ..., zK dacă există un
indice p, 1 & p & K, astfel ı̂ncât: y1 z1 , y2 z2 , ..., yp1 zp1 şi yp $ zp
ˆ Pentru cerinţa 1 se acordă 20p, iar pentru fiecare dintre cerinţele 2 şi 3 se acordă câte 40p.

Exemple:

bile.in bile.out Explicaţii


193 4 Se rezolvă cerinţa 1.
N = 9.
Avem 9 bile inscripţionate cu 0, 1, 2, 3, 4, 5, 6, 7, 8.
Bila specială este X 4 deoarece:
X 0  1  2  3  5  6  7  8 8 32 8 4.
183 -1 Se rezolvă cerinţa 1.
N 8.
Se va scrie ı̂n fişierul de ieşire valoarea -1 deoarece cutia A nu conţine
nicio bilă specială.
283 027 Se rezolvă cerinţa 2.
N 8.
Şirurile strict crescătoare ale numerelor bilelor care pot fi mutate ı̂n
cutia B, lângă bila specială K 3, sunt: 0, 2, 7 sau 0, 4, 5 sau 1, 2, 6,
deoarece:
3 0  2  7 3 0  4  5 3 1  2  6 3.
Cel mai mic şir ı̂n sens lexicografic, crescător, este: 0, 2, 7.
383 126 Se rezolvă cerinţa 3.
N 8.
Şirurile strict crescătoare ale numerelor bilelor care pot fi mutate ı̂n
cutia B, lângă bila specială K 3, sunt: 0, 2, 7 sau 0, 4, 5 sau 1, 2, 6,
deoarece:
3 0  2  7 3 0  4  5 3 1  2  6 3.
Cel mai mare şir ı̂n sens lexicografic, crescător, este: 1, 2, 6.

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

15.1.1 Indicaţii de rezolvare

Propunător: Diana Ghinea, Eidgenössische Technische Hochschule (ETH) Zürich


Cerinţa 1 (20 puncte)
N N 1
Notăm cu S suma numerelor bilelor din cutia A: S 0  1  ...  N  1 2
. Fie X
numărul bilei speciale din cutia A. Atunci, X S X © N 1, din care rezultă că X N 1©2.
Astfel, cutia A conţine o bilă specială doar dacă 2¶ N  1, deci dacă N este impar.
Complexitatea soluţiei: O 1.
Cerinţa 2 (40 puncte)
K 2 K 1
2
a Dacă K 
2
&N  1, atunci răspunsul pentru cerinţa 2 este şirul de numere:

2 K  2 K  1
0, 1, 2, 3, . . . , K  2, K 
ÍÒÒ Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò ÒÑ Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ò Ï 2
cele mai mici K-1
numere naturale distincte
K 2 K 1
aDacă K 
2
2
% N  1, notăm soluţia acestei cerinţe cu s.
Iniţializăm si i pentru i 0, 1, ..., K  1 şi notăm cu S suma valorilor iniţiale din s:
S s0  s1  ...  sK  1 K K  1©2.
CAPITOLUL 15. ONI 2021 15.1. BILE 172

2
Pentru ca media aritmetică a şirului s să fie K, trebuie să distribuim diferenţa dif K  S la
elementele din şir. Întrucât dorim să obţinem cea mai mică soluţie din punct de vedere lexicografic,
vom utiliza această diferenţă pentru a maximiza ultimele elemente ale lui s.
Şirul s trebuie să conţină numere distincte mai mici sau egale cu N  1. Astfel, cea mai mare
valoare posibilă pentru sK  1 este N  1, cea mai mare valoare posibilă pentru sK  2 este
N  2, ..., iar cea mai mare valoare posibilă pentru s0 este N  K. Deci, diferenţa dintre valoarea
actuală a oricărui element din s şi valoarea maximă a acelui element este N  K.
Întrucât putem distribui doar diferenţa dif , vom adăuga N  K la ultimele g dif © N  K 
elemente din şir. Rămâne să adăugăm restul rest dif % N  K . Încercăm să adăugăm restul
la elementul sK  g  1. Dacă sK  g  1  rest j K, adăugăm restul astfel:

Altfel, dacă sK  g  1  rest K, atunci ş irul se modifică astfel:

Complexitatea soluţiei: O K .
Cerinţa 3 (40 puncte)
Notăm soluţia acestei cerinţe cu s. Întrucât s trebuie să fie cea mai mare soluţie din punct de
vedere lexicografic, ı̂ncercăm să maximizăm primele elemente din s. Putem construi s astfel:

ˆ K par
Fie M K ©2. Cea mai mare valoare posibilă pentru s0 astfel ı̂ncât media valorilor din
şir să fie K este K 2. Alegem valorile celorlalţi termeni din şir astfel ı̂ncât:
– suma termenilor situaţi pe poziţii simetrice faţă de mijlocul şirului să fie egală cu 2K:
s[0] + s[K-1] = 2K; s[1] + s[K-2] = 2K; ..., s[M-1] + s[M] = 2K.

Deci, pentru K par, obţinem următoarea soluţie.

ˆ K impar
Fie M K ©2. Cea mai mare valoare posibilă pentru s0 astfel ı̂ncât media valorilor din
şir să fie K este [K/2]. Alegem valorile celorlalţi termeni din şir astfel ı̂ncât:
– s0  sK  1 2K  1;
– sM  K  1;
– suma termenilor situaţi pe poziţii simetrice faţă de mijlocul şirului să fie egală cu 2K:
s[1] + s[K-2] = 2K, s[2] + s[K-3] = 2K, ..., [M-1] + s[M+1] = 2K;

Deci, pentru K impar, obţinem următoarea soluţie:

Complexitatea soluţiei: O K .
Observaţii
CAPITOLUL 15. ONI 2021 15.2. SECVENTE 173

ˆ Nu este necesar să se utilizeze un tablou unidimensional pentru a memora termenii lui s.
2
ˆ O soluţie care generează toate modalităţile de a scrie pe K ca o suma de K numere cu
proprietatea cerută va obţine un punctaj parţial.

15.1.2 *Cod sursă

15.1.3 *Rezolvare detaliată

15.2 Secvente
Problema 2 - Secvente 100 de puncte
Se consideră un şir cu N elemente numere ı̂ntregi. Definim următoarele noţiuni:

ˆ secvenţă ı̂n şir = elemente situate pe poziţii consecutive ı̂n şir


ˆ lungimea unei secvenţe = numărul de elemente care o formează
ˆ suma unei secvenţe = suma elementelor care o formează
ˆ secvenţa nebanală = secvenţa de lungime cel puţin egală cu 2
ˆ N -secvenţă = secvenţ ă a cărei sumă este divizibilă cu N (secvenţa poate fi şi banală)
ˆ N -secvenţă nebanală = secvenţă nebanală a cărei sumă este divizibilă cu N .

Cerinţe

Scrieţi un program care să citească numărul natural N şi apoi şirul de N elemente. Programul
determină:

1. numărul de N -secvenţe nebanale din şir;


2. cea mai mare lungime a unei N -secvenţe din şir;
3. cea mai mare sumă a unei N -secvenţe din şir.

Date de intrare

Fişierul de intrare secvente.in conţine pe prima linie două numere naturale C şi N , separate
printr-un singur spaţiu, C reprezentând cerinţa care trebuie rezolvată (1, 2 sau 3). A doua linie a
fişierului conţine cele N elemente numere ı̂ntregi, separate prin câte un spaţiu.

Date de ieşire

Fişierul de ieşire secvente.out va conţine:

ˆ dacă C 1, pe prima linie, un număr natural reprezentând numărul de N -secvenţe nebanale


din şir (răspunsul la cerinţa 1);
ˆ dacă C 2, pe prima linie, un număr natural reprezentând cea mai mare lungime a unei
N -secvenţe din şir (răspunsul la cerinţa 2);
ˆ dacă C 3, pe prima linie, un număr natural reprezentând cea mai mare sumă a unei
N -secvenţe din şir (răspunsul la cerinţa 3).

Restricţii şi precizări

ˆ N număr natural, 2 & N & 100 000


9 9
ˆ elementele şirului sunt numere ı̂ntregi din intervalul ı̂nchis 10 , 10 
ˆ ı̂n şirul de numere dat există cel puţin o N -secvenţa a cărei sumă este un număr natural
CAPITOLUL 15. ONI 2021 15.2. SECVENTE 174

ˆ numărul ı̂ntreg negativ X este divizibil cu numărul natural nenul N dacă restul ı̂mpărţirii
modulului lui X la N este 0 (de exemplu, X 30 este divizibil cu N 6, iar X 45 nu
este divizibil cu N 6).
ˆ Pentru fiecare dintre cerinţele 1 şi 2 se acordă 30p, iar pentru cerinţa 3 se acordă 40p.

Exemple:

secvente.in secvente.out Explicaţii


1 10 8 Se rezolvă cerinţa 1.
-9 -3 4 -10 -1 -16 18 18 -10 50 Şirul are N 10 elemente ı̂ntregi:
-9, -3, 4, -10, -1, -16, 18, 18, -10, 50. În Figura1
sunt marcate cele 8 N -secvenţe nebanale.
2 10 9 Se rezolvă cerinţa 2.
-9 -3 4 -10 -1 -16 18 18 -10 50 Şirul are N 10 elemente ı̂ntregi:
-9, -3, 4, -10, -1, -16, 18, 18, -10, 50
Cea mai lungă dintre aceste secvenţe este
N -secvenţa: -3, 4, -10, -1, -16, 18, 18, -10, 50.
Lugimea acestei N -secvenţe este 9.
3 10 60 Se rezolvă cerinţa 3.
-9 -3 4 -10 -1 -16 18 18 -10 50 Şirul are N 10 elemente ı̂ntregi:
-9, -3, 4, -10, -1, -16, 18, 18, -10, 50
Suma maximă a unei N -secvenţe este 60
(suma N -secvenţei: -16, 18, 18, -10, 50).

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

15.2.1 Indicaţii de rezolvare

Propunător: Stelian Ciurea, Universitatea ”Lucian Blaga” din Sibiu


O soluţie, cu complexitate O N  se poate obţine astfel: odată cu citirea elementelor şirului
v 1, v 2, ..., v N  se construiesc sumele parţiale S k  v 1  v 2  ...  v k  S i  1  v k . Se
va utiliza un vector de frecvenţă Rest  a valorilor resturilor ı̂mpărţirilor acestor sume parţiale la
N . La fiecare rest negativ se va aduna N , rezultând un rest pozitiv din mulţimea r0, 1, 2, ..., N  1x.
Cerinţa 1 (30 puncte)
Se observă că orice N -secvenţă se poate construi cu elementele aflate ı̂ntre oricare două poziţii
cu sumele cu acelaşi rest. Numărul de secvenţe pentru un rest dat este egal cu numărul de perechi
pe care ı̂l putem forma cu poziţiile care au dat sume având acel rest (dacă avem x poziţii, numărul
de perechi este x x  1©2. Facem suma acestor valori pentru fiecare rest iar din această sumă
scădem numărul de secvenţe banale care este egal cu numărul de valori din şirul v care sunt
multiplii ai numărului N .
Cerinţa 2 (30 puncte)
Pentru fiecare valoare distinctă a resturilor se va memora poziţia p1 a primei apariţii a unei
sume cu acest rest şi poziţia p2 a ultimei apariţii a unei sume cu acest rest. Astfel, numerele din
CAPITOLUL 15. ONI 2021 15.3. VALOARE 175

şir situate ı̂ntre p1 (exclusiv) şi p2 (inclusiv) formează o N -secvenţa de lungime p2  p1 care este
maximală relativ la valoarea restului respectiv. Căutăm apoi maximul dintre aceste lungimi.
Cerinţa 3 (40 puncte)
În timp ce se construiesc cei doi vectori (vectorul sumelor parţiale şi vectorul frecvenţelor
de apariţie) se păstrează pentru fiecare rest posibil poziťia unde ı̂ncepe o potenţială secvenţă de
sumă maximă. Pentru fiecare element din şirul dat se calculează suma maximă a secvenţei care
se termină ı̂n el şi se compară cu o variabilă ı̂n care memorăm suma maximă (iniţializată cu 0
deoarece se garantează că există cel puţin o N -secvenţă de sumă pozitivă).
Pentru fiecare element v k  din şir (k 1, 2, ..., N ) vom proceda astfel:
ˆ se calculează suma parţială S k  S k  1  v k ;
ˆ se calculează restul r S k %N ;
ˆ dacă r $ 0 atunci r r  N;
ˆ Restr Restr  1;
ˆ dacă v k %N 0 atunci Smax max Smax, v k ;
ˆ dacă S k %N 0 atunci Smax max Smax, S k ;
ˆ dacă Restr 1 (adică, r apare pentru prima dată) atunci SumaRestr sk 
ˆ altfel
– dacă S k   SumaRestr $ 0 (adică suma N -secvenţei curente este negativă), atunci
SumaRestr S k ;
– Smax max Smax, S k   SumaRestk 
La final, se va afişa Smax.

15.2.2 *Cod sursă

15.2.3 *Rezolvare detaliată

15.3 Valoare
Problema 3 - Valoare 100 de puncte
Valoarea unei litere este dată de numărul ei de ordine ı̂n alfabet (A are valoarea 1, B are
valoarea 2, C are valoarea 3, etc.).
Un text special este format din una sau mai multe litere mari ale alfabetului englez şi nu conţine
alte tipuri de caractere.
Valoarea unui text special o definim ca fiind egală cu suma valorilor literelor care ı̂l compun.
De exemplu, textul special ABAC are valoarea 7 (7=1+2+1+3).
Un text special poate fi transcris ı̂ntr-o formă restrânsă prin utilizarea perechilor de
paranteze şi a numerelor naturale. De exemplu, transcrierea ı̂n forma restrânsă a textului
ABACABACEECDCDE este textul ABAC 2E2 CD2E deoarece secvenţa ABAC apare de
două ori consecutiv, litera E are două apariţii consecutive şi la fel secvenţa de litere CD.
Transcrierea ı̂n formă restrânsă a unui text special se realizează prin ı̂nlocuirea fiecărei secvenţe
care are apariţii consecutive ı̂n text cu secvenţa scrisă ı̂ntre o pereche de paranteze rotunde urmată
apoi de numărul de repetiţii. De exemplu, textul special ABABAB se transcrie ı̂n forma restrânsă
ı̂n AB 3.
Dacă, după transcriere, textul ı̂n formă restrânsă conţine secvenţe care au apariţii con-
secutive ı̂n text atunci şi acesta se va transcrie ı̂n formă restrânsă. De exemplu, textul
special ABABCABABC se poate transcrie ı̂ntâi ı̂n forma restrânsă ı̂n ABABC 2 sau ı̂n
AB 2C AB 2C iar apoi ı̂n AB 2C 2.
Dacă textul special nu conţine nicio secvenţă cu apariţii consecutive ı̂n text, atunci forma
restrânsă a textului este identică cu forma iniţială. De exemplu, textul special ABAC are forma
restrânsă ABAC care este identică cu acest text special.
CAPITOLUL 15. ONI 2021 15.3. VALOARE 176

Cerinţe

Scrieţi un program care citeşte un şir de caractere S reprezentând forma restrânsă a unui text
special, şi apoi determină:

1. numărul literelor distincte care apar ı̂n textul special;

2. suma numerelor care apar ı̂n forma restrânsă a textului special;


3. valoarea textului special dat ı̂n forma restrânsă.

Date de intrare

Fişierul de intrare valoare.in conţine:

ˆ pe prima linie, un număr natural C (1, 2 sau 3) reprezentând cerinţa care trebuie rezolvată;

ˆ pe a doua linie, şirul de caractere S reprezentând forma restrânsă a unui text special.

Date de ieşire

Fişierul de ieşire valoare.out va conţine:

ˆ dacă C 1, pe prima linie, un număr natural reprezentând numărul literelor distincte care
apar ı̂n textul special (răspunsul la cerinţa 1);
ˆ dacă C 2, pe prima linie, un număr natural reprezentând suma numerelor care apar ı̂n
forma restrânsă a textului special (răspunsul la cerinţa 2);
ˆ dacă C 3, pe prima linie, un număr natural reprezentând valoarea textului special dat ı̂n
forma restrânsă (răspunsul la cerinţa 3).

Restricţii şi precizări

ˆ Şirul de caractere S ce reprezintă forma restrânsă a unui text special poate avea cel mult
1000 de caractere (litere mari ale alfabetului englez, perechi de paranteze rotunde, cifre).
ˆ Numerele naturale care apar ı̂n forma restrânsă a unui text special sunt nenule şi aparţin
intervalului ı̂nchis 2, 9999.

ˆ Valoarea unui text special este un număr natural format din cel mult 12 cifre.
ˆ Pentru cerinţa 1 se acordă 10p, pentru cerinţa 2 se acordă 20p, iar pentru cerinţa 3 se acordă
70p.
ˆ Pentru teste ı̂n valoare de 10p, forma restrânsă a textului special este identică cu textul
special şi cerinţa este 3.

Exemple:

valoare.in valoare.out Explicaţii


1 5 Se rezolvă cerinţa 1.
(ABAC)2E2(CD)2E Forma restrânsă a textului special este (ABAC)2E2(CD)2E.
În textul special apar doar literele A, B, C, D şi E.
2 6 Se rezolvă cerinţa 2.
(ABAC)2E2(CD)2E Forma restrânsă a textului special este (ABAC)2E2(CD)2E.
În textul special apare doar numărul 2, de 3 ori. Astfel suma
este 6 (=2+2+2).
CAPITOLUL 15. ONI 2021 15.3. VALOARE 177

3 43 Se rezolvă cerinţa 3.
(ABAC)2E2(CD)2E Forma restrânsă (ABAC)2E2(CD)2E reprezintă transcrierea
textului special ABACABACEECDCDE. În textul special
apar 4 de A, 2 de B, 4 de C, 2 de D şi 3 de E. Astfel, valoarea
textului special este egală cu 43 (=4*1+2*2+4*3+2*4+3*5).

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

15.3.1 Indicaţii de rezolvare

Propunător: Raluca Costineanu, Colegiul Naţional ”Ştefan cel Mare”, Suceava


O soluţie se poate obţine astfel:
Cerinţa 1 (10 puncte)

ˆ folosim un vector caracteristic pentru a identifica literele distincte care apar ı̂n textul special.
ˆ Complexitate O n, unde n este lungimea textului

Cerinţa 2 (20 puncte)

ˆ se parcurge şirul pentru a identifica secvenţele formate doar din cifre, fiecare secvenţă se
transformă ı̂n valoare numerică şi se adună la suma generală
ˆ Complexitate O n, unde n este lungimea textului

Cerinţa 3 (70 puncte)

ˆ folosim o stivă (simulată cu un vector) ı̂n care vom adăuga pe rând elemente după cum
urmează:
– dacă ı̂n text urmează o paranteză deschisă adăugăm pe stivă un element care să
marcheze acest lucru, de exemplu o valoare negativă (pentru a şti că aici ı̂ncepe repetiţia
unei secvenţe)
– dacă ı̂n text urmează o literă atunci adăugăm pe stivă valoarea ei
– dacă urmează o paranteză ı̂nchisă atunci sumăm şi eliminăm elementele de pe stivă,
pânla prima paranteză deschisă pe care o ı̂ntâlnim, şi ı̂nlocuim paranteza cu valoarea
sumei calculate (pentru a păstra pe stivă valoarea pe care o are textul cuprins ı̂ntre
paranteze)
– dacă ı̂ntâlnim un număr atunci valoarea din vârful stivei se ı̂nmulţeşte cu numărul şi
este ı̂nlocuită de acest produs (valoarea din vârful stivei reprezintă valoarea secvenţei
de text care trebuie repetat de număr ori)

ˆ valoarea textului este dată de suma elementelor rămase la sfârşit pe stivă.


ˆ Complexitate O n, unde n este lungimea textului

Cerinţa 3 - soluţie alternativă


Propusă de prof. Stelian Ciurea (ULB Sibiu)

ˆ folosim o funcţie recursivă ı̂n care parsăm şirul:


– caracterele alfanumerice le tratăm ı̂n acelaşi mod ca ı̂n descrierea precedentă;
– când ı̂ntâlnim o paranteză deschisă apelăm recursiv funcţia;
– când ı̂ntâlnim o paranteză ı̂nchisă returnăm valoarea calculată ı̂n funcţie.
CAPITOLUL 15. ONI 2021 15.3. VALOARE 178

– rezultatul returnat de primul apel al acestei funcţii reprezintă rezultatul problemei.


Ideea de rezolvare este similară celei din descrierea precedentă cu singura diferent̆a că
folosim stiva sistemului prin mecanismul de apel şi revenire ı̂n cazul funcţiilor recursive
şi nu o stivă implementată efectiv ı̂n program.
ˆ folosim funcţii de eliminare şi inserare ı̂n şir (foarte simplu de utilizat dacă folosim string-uri)
după cum urmează:
– determinăm poziţia primei ”)” din şir;
– apoi parcurgem şirul ı̂n sens invers şi determinăm poziţia parantezei ”(” pereche;
– construim un subşir cu elementele din şir aflate ı̂ntre aceste două poziţii;
– evaluăm acest subşir (care deci nu conţine decât litere şi cifre);
– ı̂nmulţim rezultatul obţinut cu valoarea numărului aflat după ”)”;
– stergem din şirul iniţial subşirul evaluat (cel cuprins ı̂ntre paranteze şi numărul aflat
după ”)”;
– construim un şir format din litera A (deoarece A are valoarea 1) urmată de valoarea
obţinută ı̂n pasul precedent;
– inserăm acest şir ı̂n poziţia de unde am efectuat eliminarea;
– reluăm aceşti paşi până am eliminat toate parantezele din şir;
– evaluăm şirul astfel obţinut care nu mai conţine decât litere şi cifre;
– algoritmul se incadrează ı̂n timp pentru toate testele chiar dacă parcurge ı̂n mod repetat
şirul dat.
– pentru a ilustra algoritmul arătăm transformările prin care trece şirul dat ca exemplu
ı̂n enunţul problemei:
(ABAC)2E2(CD)2E: prima pereche de paranteze este (ABAC), calculăm A+B+A+C
= 7, ı̂nmulţim cu 2, obţinem 14; eliminăm subşirul (ABCD)2 şi ı̂n locul lui inserăm
A14; rezuultă şirul
A14E2(CD)2E: analog (CD): C+D = 7 ı̂nmulţim cu 2, obţinem 14, eliminăm (CD)2,
inserăm A14, rezultă şirul
A14E2A14E : la acesta calculăm prin parsare 1 14  5 2  1 14  5 43

15.3.2 *Cod sursă

15.3.3 *Rezolvare detaliată


Capitolul 16

ONI 2020 - suspendat !!!

... ... ... ...

179
Capitolul 17

ONI 2019

17.1 criptografie - ONI 2019


Problema 1 - criptografie 100 de
puncte
Zedd a descoperit frumuseţea aplicaţiilor din domeniul criptografiei. Astfel, el şi-a activat
abilităţile de hacker şi s-a lovit de următoarea problemă: fiind 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 fiecare literă poate să apară de maximum k ori. Două
secvenţe sunt considerate distincte dacă diferă fie prin poziţia de ı̂nceput, fie prin cea de final.
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 lexicografic
(alfabetic).

Date de intrare

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

Date de ieşire

Fişierul de ieşire criptografie.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 aflate pe poziţii consecutive ı̂ntr-un şir.
a 0 $ n & 105
a 0$k&n
a Pentru teste ı̂n valoare de 67 de puncte C 1, iar pentru 33 de puncte C 2
a Pentru teste ı̂n valoare de 17 puncte se garantează C 1, k 1 şi n & 100
a Pentru teste ı̂n valoare de alte 17 puncte se garantează C 1 şi n & 1000
a Pentru cerinţa 2 se garantează că valoarea lui k este 1.

Exemple

180
CAPITOLUL 17. ONI 2019 17.1. CRIPTOGRAFIE - ONI 2019 181

criptografie.in criptografie.out Explicaţii


1 8 Pentru textul dat, variantele care ar putea fi obţinute
14 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 fi formate este 8.
2 acb Lungimea maximă a unei secvenţe de elemente dis-
16 tincte este 3. Sunt trei astfel de secvenţe. Prima din
abacba punct de vedere alfabetic este acb.

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: criptografie.cpp, criptografie.c sau criptografie.pas va fi salvată ı̂n folderul care are drept
nume ID-ul tău.

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

17.1.2 Cod sursă

Listing 17.1.1: criptografie.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
CAPITOLUL 17. ONI 2019 17.2. DREPT - ONI 2019 182

23 p = 1;
24 strcpy(smaxim, "zz");
25 for (i=1;i<=n;i++)
26 {
27 ch = s[i]-’a’;
28 f[ ch ] ++;
29 while ( f[ch] > k )
30 {
31 f[ s[p]-’a’ ]--;
32 p++;
33 }
34 if (c == 1)
35 sol += i-p+1;
36 else
37 {
38 if (i-p+1 > maxim)
39 {
40 maxim = i-p+1;
41 strncpy(smaxim, s+p, maxim);
42 }
43 else
44 if (i-p+1 == maxim)
45 if (strncmp(smaxim, s+p, maxim) > 0)
46 strncpy(smaxim, s+p, maxim);
47 }
48 }
49 if (c == 1)
50 fout<<sol;
51 else
52 {
53 smaxim[maxim] = 0;
54 fout<<smaxim;
55 }
56 return 0;
57 }

17.1.3 *Rezolvare detaliată

17.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 fiind orizontale sau verticale, prima latură enumerată fiind 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 fiecare dintre ele, dacă reprezintă un poligon drept.
2. ştiind că şirurile date reprezintă poligoane drepte, să se determine aria fiecă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 fiecare test. Pe prima
linie corespunzătoare unui test se află un număr natural n, iar pe a doua linie un şir de n numere
ı̂ntregi, separate prin câte un spaţiu.

Date de ieşire
CAPITOLUL 17. ONI 2019 17.2. DREPT - ONI 2019 183

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 fiecare şir rezultatul este 1, dacă acesta reprezintă
un poligon drept, sau 0 altfel. Dacă C 2 pentru fiecare şir rezultatul este aria poligonului drept
corespunzător.

Restricţii şi precizări

a 1 & T & 10
a 4 & n & 100
a Pentru teste ı̂n valoare de 45 de puncte C 1, pentru restul de 55 de puncte C 2
a Pentru teste ı̂n valoare de 30 de puncte C 1 şi numerele ce descriu figura sunt ı̂ntregi
nenule aparţinând intervalului 100, 100
a Pentru celelalte 15 puncte şi C 1 numerele ce descriu figura sunt ı̂ntregi nenule aparţinând
intervalului 109, 109
a Pentru teste ı̂n valoare de 33 de puncte se garantează că avem C 2 şi intersecţia dintre
orice orizontală cu poligonul este fie vidă, fie formată dintr-un singur segment.
a Pentu testele ı̂n care valoarea lui C este 2, numerele din şirul ce descrie poligonul sunt ı̂ntregi
nenule din intervalul 100, 100 şi se garantează că aceste şiruri reprezintă poligoane drepte.
a Într-un poligon drept laturile nu au puncte comune, exceptând capetele laturilor adiacente.
a Prima şi ultima latură ale unui poligon drept sunt perpendiculare.

Exemple

drept.in drept.out
12 10
8
5 3 -3 -1 2 -1 -4 -1
8
-2 1 3 1 -4 -3 2 1
22 91
8
5 3 -3 -1 2 -1 -4 -1
4
1 1 -1 -1

Explicaţii:

Figura 17.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 fi salvată ı̂n folderul care are drept nume ID-ul tău.

17.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
CAPITOLUL 17. ONI 2019 17.2. DREPT - ONI 2019 184

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

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.

17.2.2 Cod sursă

Listing 17.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 }
CAPITOLUL 17. ONI 2019 17.2. DREPT - ONI 2019 185

37
38 int main ()
39 {
40 ifstream fin ("drept.in");
41 ofstream fout("drept.out");
42
43 fin>>C>>t;
44
45 for (;t--;)
46 {
47 fin>>n;
48 xmin = xmax = ymin = ymax = 0;
49 v[0].x = 0;
50 v[0].y = 0;
51
52 for (i=1;i<=n;i++)
53 {
54 fin>>d;
55 if (i%2 == 1)
56 {
57 v[i].x = v[i-1].x + d;
58 v[i].y = v[i-1].y;
59 }
60 else
61 {
62 v[i].x = v[i-1].x;
63 v[i].y = v[i-1].y + d;
64 }
65
66 xmin = min(xmin, v[i].x);
67 xmax = max(xmax, v[i].x);
68 ymin = min(ymin, v[i].y);
69 ymax = max(ymax, v[i].y);
70 }
71
72 poligon = 1;
73 if (v[n].x != 0 || v[n].y != 0)
74 poligon = 0;
75
76 for (i=0;i<n-1;i++)
77 {
78 for (j=i+2;j<n;j++)
79 {
80 if (i == 0 && j == n-1)
81 continue;
82
83 if (i%2 == j%2)
84 {
85 /// paralele
86 if (punctPeSegment(i, j))
87 poligon = 0;
88 if (punctPeSegment(i+1, j))
89 poligon = 0;
90 if (punctPeSegment(j, i))
91 poligon = 0;
92 if (punctPeSegment(j+1, i))
93 poligon = 0;
94 }
95 else
96 if (i%2 == 0)
97 if (intersectieSegmente(v[i], v[i+1], v[j], v[j+1]))
98 poligon = 0;
99 else
100 if (intersectieSegmente(v[j], v[j+1], v[i], v[i+1]))
101 poligon = 0;
102 }
103 }
104
105 if (C!=1)
106 {
107 aria = 0;
108 for (i=xmin+1;i<=xmax;i++)
109 for (j=ymin+1;j<=ymax;j++)
110 {
111 jos = 0;
112 stanga = 0;
CAPITOLUL 17. ONI 2019 17.3. LUMINI - ONI 2019 186

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 {
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 }

17.2.3 *Rezolvare detaliată

17.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 fiecare dintre cele L ˜ L celule
se află 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 aflate pe aceeaşi paralelă cu diagonala secundară cu elementul specificat şi pe paralelele
cu diagonala secundară aflate 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 finalul furtunii
să se indice care este starea anumitor faruri, aflate la coordonate precizate de pe insulă.

Date de intrare

Prima linie a fişierului lumini.in conţine un numărul natural L, cu semnificaţia de mai sus.
Pe linia a doua se află un număr natural F , reprezentând numărul de fulgere.
CAPITOLUL 17. ONI 2019 17.3. LUMINI - ONI 2019 187

Pe următoarele F linii se află date despre câte un fulger, ı̂n ordinea ı̂n care acestea apar.
Primul număr de pe fiecare linie este tipul de fulger (1 sau 2 sau 3). Dacă acest număr este 1
sau 2, se mai află pe această linie un spaţiu şi ı̂ncă un număr. Acesta reprezintă linia pe care
loveşte fulgerul (dacă linia din fişier are la ı̂nceput 1), respectiv coloana pe care loveşte fulgerul
(dacă linia respectivă din fişier are la ı̂nceput 2). Dacă este vorba despre fulger de tip 3, pe acea
linie se mai află două numere, reprezentând linia şi coloana elementului de pe insulă unde loveşte
fulgerul, separate prin spaţiu.
Linia următoare conţine un număr Q. Pe următoarele Q linii se află câte două numere (separate
prin spaţiu) reprezentând linia şi coloana unui far de pe insulă pentru care se doreşte aflarea stării
după furtună.
Date de ieşire
Fişierul de ieşire lumini.out conţine pe o linie, separate prin câte un spaţiu, Q numere,
reprezentând rezultatele interogărilor ı̂n ordinea ı̂n care acestea apar ı̂n fişierul de intrare. Dacă
farul corespunzător este aprins se va scrie valoarea 1, iar dacă este stins se va scrie valoarea 0.
Restricţii şi precizări
a 2 & L & 2000000000
a 1 & F & 1000
a Se garantează că la precizarea fulgerelor linia ı̂ncepe cu 1, 2 sau 3, iar celelalte valori de pe
liniile respective sunt cuprinse ı̂ntre 1 şi L inclusiv.
a 1 & Q & 100000
a Se garantează că pentru fiecare far a cărei stare trebuie aflată linia şi coloana sunt cuprinse
ı̂ntre 1 şi L inclusiv.
a Pentru 24 de puncte, L & 100, F & 100 şi apar doar fulgere de tipul 1;
a Pentru alte 18 puncte, L & 100, F & 100;
a Pentru alte 12 puncte, L & 10000 şi apar doar fulgere de tipul 1;
a Pentru alte 8 puncte, L & 10000;
a Pentru alte 13 puncte, L & 1000000;
a Pentru restul de 25 de puncte nu sunt restricţii suplimentare.

Exemple

lumini.in lumini.out Explicaţii


4 10010 Iniţial starea becurilor de pe insulă poate fi reprezentată astfel:
3 1010
1 2 0101
3 31 1010
2 3 0101
5 După aplicarea primului fulger starea becurilor devine
1 1 1010
2 4 1010
3 2 0101
4 2 1010
4 4 După aplicarea celui de-al doilea fulger starea becurilor devine
1001
1101
1010
0101
După aplicarea celui de-al treilea starea becurilor devine
1010
1110
1001
0110

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 fi salvată ı̂n folderul care are drept nume ID-ul
tău.
CAPITOLUL 17. ONI 2019 17.3. LUMINI - ONI 2019 188

17.3.1 Indicaţii de rezolvare


prof. Marius Nicoli

Solutia 1

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.

17.3.2 Cod sursă

Listing 17.3.1: lumini.cpp


1 #include <bits/stdc++.h>
CAPITOLUL 17. ONI 2019 17.3. LUMINI - ONI 2019 189

2
3 using namespace std;
4
5 ifstream f("lumini.in");
6 ofstream g("lumini.out");
7
8 #define L 1010
9
10 struct fulger
11 {
12 long long poz;
13 int fr;
14 } lin[L], col[L], diag[2*L];
15
16 int nLin, nCol, nDiag, n;
17
18 int cauta(fulger a[], int n, long long v)
19 {
20 int s=1, d=n, m;
21 while(s<=d)
22 {
23 m=(s+d)/2;
24 if(a[m].poz==v)return m;
25 else if(v<a[m].poz)d=m-1;
26 else s=m+1;
27 }
28 return s;
29 }
30
31 void insereaza(fulger a[], int &n, long long v)
32 {
33 if(n==0)
34 {
35 a[++n].poz=v;
36 a[n].fr=1;
37 return;
38 }
39
40 int pozitie=cauta(a,n,v), i;
41
42 if(pozitie<=n && a[pozitie].poz==v)a[pozitie].fr++;
43 else
44 {
45 i=n;
46 while(i>0 && a[i].poz>v) a[i+1]=a[i--];
47 a[i+1].poz=v;
48 a[i+1].fr=1;
49 n++;
50 }
51 }
52
53 int main()
54 {
55 f>>n;
56 int x, y, z, i, Q;
57 f>>Q;
58 for(i=1; i<=Q; i++)
59 {
60 f>>x;
61 if(x==1)f>>y,
62 insereaza(lin, nLin, y);
63 else
64 if(x==2)
65 f>>y, insereaza(col, nCol, y);
66 else
67 f>>y>>z, insereaza(diag, nDiag, 1LL*y+1LL*z-1);
68 }
69
70 lin[nLin+1].poz = col[nCol+1].poz = diag[nDiag+1].poz=n+1;
71
72 for(i=2; i<=nLin+1; i++) lin[i].fr+=lin[i-1].fr;
73 for(i=1; i<=nCol+1; i++) col[i].fr+=col[i-1].fr;
74 for(i=1; i<=nDiag+1; i++) diag[i].fr+=diag[i-1].fr;
75
76 f>>Q;
77 for(i=1; i<=Q; i++)
CAPITOLUL 17. ONI 2019 17.3. LUMINI - ONI 2019 190

78 {
79 f>>x>>y;
80 int far=1-(x%2+y%2)%2;
81
82 int p=cauta(lin,nLin, x);
83 if(lin[p].poz>x) p--;
84 if(lin[p].fr%2) far=!far;
85
86 p=cauta(col,nCol, y);
87 if(col[p].poz>y) p--;
88 if(col[p].fr%2) far=!far;
89
90 p=cauta(diag,nDiag, 1LL*x+1LL*y-1);
91 if(diag[p].poz>1LL*x+1LL*y-1) p--;
92 if(diag[p].fr%2) far=!far;
93 g<<far<<’ ’;
94 }
95
96 return 0;
97 }

17.3.3 *Rezolvare detaliată


Capitolul 18

ONI 2018

18.1 gene
Problema 1 - gene 100 de puncte
Gigel este curios să afle ı̂n ce zonă a ţării au trăit cei mai mulţi dintre strămoşii săi. El reuşeşte
să adune informaţii despre structura genetică a persoanelor din diferite părţi ale ţării şi speră că,
prin compararea cu propria structură genetică, să identifice o zonă pătratică ı̂n care au trăit cei
mai mulţi dintre strămoşii săi.
Structura genetică a unei persoane este reprezentată sub forma unei secvenţe cu cel mult 20 de
caractere (litere mici ale alfabetului englez). O persoană poate fi considerată strămoş a lui Gigel
dacă gradul de similaritate dintre secvenţa corespunzătoare persoanei respective şi cea a lui Gigel
este mai mare strict decât un număr K, cunoscut.
Gradul de similaritate dintre două secvenţe este reprezentat de numărul de caractere comune
celor două secvenţe. De exemplu pentru secvenţele abcdabd şi acbdaad gradul de similaritate este
6 (2 caractere a, 2 caractere d, 1 caracter b, 1 caracter c).
Gigel reprezintă harta ţării sub forma unui tablou bidimensional cu N linii şi M coloane ı̂n
care fiecare element reprezintă structura genetică a unei persoane din zona respectivă.

Cerinţe

Cunoscând N , M , K, structura genetică pentru Gigel şi reprezentarea hărţii identificată 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 fi 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 fi rezolvată.
Pe a doua linie numerele naturale N , M şi K, separate prin câte un spaţiu, cu semnificaţia de
mai sus.
Pe a treia linie se află structura genetică a lui Gigel, iar pe următoarele N ˜ M linii câte o
secvenţă de maximum 20 de litere mici ale alfabetului englez, care reprezintă structurile genetice
ale persoanelor din ţară, ı̂n ordinea parcurgerii hărţii pe linii.

Date de ieşire

Fişierul de ieşire gene.out va conţine răspunsul la cerinţa rezolvată.


Dacă s-a rezolvat prima cerinţă, fişierul de ieşire va conţine, pe linii separate, datele de iden-
tificare 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 fiş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 identificat
pe hartă.

191
CAPITOLUL 18. ONI 2018 18.1. GENE 192

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 afişa
ı̂n ordine lexicografică 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 afiş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
333 3 2 ccgtabd Pentru secvenţele din hartă gradele de similaritate sunt:
acgt aaacctg - grad 4
aaacctg abrcg - grad 3
abrcg saasdfs - grad 1
saasdfs ecctg - grad 3
ecctg abnctt - grad 3
abnctt agtatggt - grad 3
agtatggt aaa - grad 1
aaa ccgtabd - grad 4
ccgtabd bbbb - grad 0
bbbb deci maximul este 4 şi apare pentru două dintre secvenţe
2 232 Pentru harta dată, gradele de similaritate cu secvenţa acgt
442 sunt, ı̂n ordine: 2 3 1 4 2 1 3 4 1 2 4 4 3 3 0 1.
acgt Deci reprezentarea hărţii ı̂n formă bidimensională este:
aec 2314
ctg 21 34
abvf 12 44
acgtaaa 3301
bbbbttg Cea mai mare zonă pătratică, ı̂n care toate gradele sunt mai
saa mari strict decât 2 are latura 2 şi ı̂ncepe la linia 2, coloana 3.
acgec
actgt
abvf
nctt
cagtatggt
acgtaaa
bbabttg
saatg
sdfs
fhgj

Timp maxim de executare/test: 0.5 secunde


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

18.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 fiecărui caracter ı̂n fiecare din cele două şiruri.
Gradul de similaritate ı̂l obţinem ı̂nsumând numărul minim de apariţii pentru fiecare caracter.
CAPITOLUL 18. ONI 2018 18.2. JOCXZERO 193

Pentru determinarea zonei pătratice de dimensiune maximă ı̂n care toate valorile gradelor de
similaritate depăşesc pragul k, construim o matrice a cu n linii şi m coloane ı̂n care fiecare element
are valoarea 1 dacă gradul de similaritate a persoanei din zona respectivă depăşeşte pragul k sau
0 ı̂n caz contrar.
Determinăm apoi, ı̂n O n ˜ m, pentru fiecare poziţie i, j  latura maximă a pătratului din
care face parte elementul şi respectă restricţia ca toate elemetele corespunzătoare din pătrat să
fie mai mari egale 1, ı̂n modul următor: dacă elementul curent este 1 atunci am putea extinde
pătratul anterior determinat, deci ai, j  min ai  1, j  1, ai  1, j , ai, j  1  1, altfel
latura va rămne 0.
Latura maximă a unui pătrat va fi valoarea maximă din matricea a.

18.1.2 *Cod sursă

18.1.3 *Rezolvare detaliată

18.2 jocxzero
Problema 2 - jocxzero 100 de puncte
Pe o foaie dintr-un caiet de matematică de dimensiune N  M
X X X X 0
(N numărul de linii şi M numărul de coloane) sunt completate toate
pătrăţelele cu X sau 0. Pentru un număr natural K dat, numim şir 0 X X X 0
corect, o secvenţă de K elemente consecutive pe linie, coloană sau di- 0 0 X 0 0
agonale care au aceeaşi valoare (X sau 0). Două pătrăţele de pe foaie
sunt vecine pe aceeaşi diagonală dacă au un singur colţ comun. 0 0 0 X X
Exemplu din figura alăturată, pentru care N 4, M 5, K 3 conţine 6 şiruri corecte de X
şi 5 şiruri corecte de 0.

Cerinţe

1. Se dau numerele naturale N , M şi K şi o foaie de matematică plină cu X şi 0. Determinaţi
câte şiruri corecte de X şi câte şiruri corecte de 0 se găsesc pe foaia dată.
2. Se dau Q ı̂ntrebări de forma A B, ı̂n care A este caracterul X sau 0 şi B este un număr
natural. Determinaţi ı̂n câte moduri putem tăia foaia de matematică vertical pentru a obţine ı̂n
subtabloul din partea stângă exact B şiruri corecte de A. Foia se poate tăia ı̂n M  1 variante:
după prima coloană, a doua coloană, după a treia coloană, ş.a.m.d, până după penultima coloană.

Date de intrare

Fişierul de intrare jocxzero.in conţine pe prima linie un număr natural P reprezentând cerinţa
din problemă care trebuie rezolvată.
Dacă P 1 atunci pe a doua linie se găsesc ı̂n ordine numerele naturale N , M şi K, separate
prin câte un spaţiu, apoi pe următoarele N linii câte M caractere de X sau 0 reprezentând foaia
dată.
Dacă P 2 atunci pe a doua linie se găsesc ı̂n ordine numerele naturale N , M şi K, separate
prin câte un spaţiu, apoi pe următoarele N linii câte M caractere de X sau 0 reprezentând foaia
dată.
Pe linia N  3 se găseşte numărul natural Q. Pe următoarele Q linii se găsesc câte un caracter
A şi un număr natural B despărţite prin un spaţiu.

Date de ieşire

Dacă P 1 atunci fişierul de ieşire jocxzero.in conţine pe o singură linie două numere naturale
separate printr-un spaţiu, reprezentând, ı̂n ordine, numărul de şiruri corecte de X şi numărul de
şiruri corecte de 0.
Dacă P 2 atunci fişierul de ieşire jocxzero.out conţine pe Q linii, câte un număr natural
reprezentând răspunsul la ı̂ntrebarea corespunzătoare din fişierul de intrare.

Restricţii şi precizări


CAPITOLUL 18. ONI 2018 18.2. JOCXZERO 194

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 fiş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 65 Pe prima linie sunt 2 şiruri corecte de X, pe a doua un şir
453 corect de X, pe diagonală avem 2 şiruri corecte de X şi unul
XXXX0 pe verticală.
0XXX0 Pe ultima linie avem un şir corect de 0, pe prima coloana avem
00X00 un şir corect de 0, pe ultima coloană avem un şir corect de 0,
000XX pe diagonală mai avem 2 şiruri corecte de 0.
2 2 Putem tăia vertical după prima coloană, după a doua, după
453 0 a treia şi după a patra coloană. Dacă tăiem după prima şi a
XXXX0 doua obţinem un singur şir corect de 0.
0XXX0 Indiferent pe unde tăiem nu putem avea un subtablou cu un
00X00 singur şir corect de X.
000X0
2
01
X1

Timp maxim de executare/test: 0.7 secunde


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

18.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 fiecare 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 fiecare
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 N M  Qlog M  pentru punctul 2.

18.2.2 *Cod sursă


CAPITOLUL 18. ONI 2018 18.3. LASER 195

18.2.3 *Rezolvare detaliată

18.3 laser
Problema 3 - laser 100 de puncte
Considerăm N segmente ı̂n plan identificate prin coordonatele extremităţilor lor. Toate seg-
mentele sunt ı̂nchise, adică fiecare conţine şi cele două puncte considerate extremităţile sale.
Presupunem că ı̂n punctul O 0, 0 care este originea sistemul de axe ortogonale XOY , se află un
laser care poate transmite câte un fascicul de lumină ı̂n orice punct cu ordonata pozitivă (' 0).
Fasciculul poate fi reprezentat ı̂n plan, ca o semidreaptă cu extremitatea ı̂n originea axelor. Totuşi,
dacă fasciculul de lumină ı̂ntâlneşte un segment, acesta ı̂l obturează, adică ı̂l ı̂mpiedică să treacă
mai departe de acesta. Considerăm că fiecare segment are asociat un număr care reprezintă un
cost pentru desenarea lui ı̂n plan.

Cerinţe

Determinaţi costul total minim al segmentelor care pot fi alese pentru a obtura orice fascicul
de lumină care ar pleca din origine către un punct cu ordonata pozitivă.

Date de intrare

Fişierul de intrare laser.in conţine pe prima linie numărul natural N de segmente.


Pe următoarele N linii se află câte cinci numere ı̂ntregi x1 y1 x2 y2 cost, separate prin câte
un spaţiu.
Primele patru numere reprezintă coordonatele extremităţilor fiecărui segment, pentru fiecare
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

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


a
La evaluare se vor folosi fişiere de intrare ı̂n valoare de 30 de puncte care au pentru toate
a
segmentele costul egal cu 1

Exemple
laser.in laser.out Explicaţii
4 4 S-au ales segmentele de cost total minim
23502 [(-5, 0), (-2,4)], [(-4, 4), (2,3)] şi [(2, 3), (5,0)].
2 3 -4 4 1 Segmentele [(-5, 0), (-2,4)] şi [(-14, 1), (6,0)]
-2 4 -5 0 1 obturează orice fascicul dar are cost total mai mare
6 0 -14 1 8
4 3 S-au ales segmentele
-1 3 1 3 1 [(-2, 0), (-1,1)];
-2 0 -1 1 1 [(-1, 1), (1,1)] ;
20111 [(1, 1), (2,0)].
1 1 -1 1 1
3 -1 Nu există segmente care să respecte cerinţele.
-1 3 1 3 1
-2 0 -1 1 4
20115
CAPITOLUL 18. ONI 2018 18.3. LASER 196

Timp maxim de executare/test: 1.0 secunde


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

18.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 fi ordonate antitrigono-
metric (ı̂n sensul acelor de ceasornic), adică pantaunghi1 % pantaunghi2.
Toate cele N segmente vor fi reţinute ı̂ntr-un vector A. Toate segmentele se vor ordona
descrescător după pantaunghi1.
Pentru determinarea costului minim, ne aflăm ı̂n faţa problemei de a selecta submulţimea de
cost minim de segmente din cele N cu proprietatea că obturează orice fascicul de lumina (către
un punct cu ordonata pozitivă). Vom folosi vectorul auxiliar dp, ı̂n care vom reţine, pentru fiecare
segment i:
dpi - costul minim al unei subşir care ı̂l are ca utim element pe segmentul i şi care obturează
orice fascicul de lumină cu panta unghiul format cu axa OX, mai mare sau egal decât pantaunghi2.
Printre două segmente P , Q (pantaunghi1P % pantaunghi1Q ) nu trece niciun fascicul de
lumină dacă şi numai dacă pantaunghi2P pantaunghi1Q
Pentru a determina dpi, vom traversa toate segmentele j (1 & j & i1) a.ı̂. printre segmentele
i şi j nu trece niciun fascicul de lumină şi vom retine min dpj . Dacă nu există vom avea
dpi inf.

18.3.2 *Cod sursă

18.3.3 *Rezolvare detaliată


Capitolul 19

ONI 2017

19.1 boats
Problema 1 - boats 100 de puncte
Pe o foaie de matematică cu N pătrăţele orizontale (pe aceeaşi linie) şi M pătrăţele verticale
(pe aceeaşi coloană), Alex a pictat nave.
Definim o navă linie (L) ca un set de pătrăţele umbrite, consecutive pe un rând al foii de
matematică.
Definim o navă coloană (C) ca un set de pătrăţele umbrite consecutive pe o coloană a foii de
matematică.
Dimensiunea unei nave este egală cu numărul de pătrăţele din care este formată. O navă
formată dintr un singur pătrăţel nu este nici linie, nici coloană. Navele pot avea diferite dimensiuni.
Două nave diferite nu se ating pe laturi sau colţuri, nu se suprapun şi nu au pătrăţele comune. Pe
foaia de matematică sunt pictate doar nave de cele 3 tipuri: navă linie (L), navă coloană (C) sau
navă pătrăţel.

Cerinţe

Cunoscându-se M , N şi pictura lui Alex, scrieţi un program care să determine:
1. Numărul de nave formate doar dintr-un singur pătrăţel;
2. Numărul de nave linie şi numărul de nave coloană, precum şi dimensiunile acestora.

Date de intrare

Fişierul de intrare boats.in conţine pe prima linie un număr natural P reprezentând cerinţa
care trebuie să fie rezolvată 1 sau 2 ). Pe cea de a doua linie fişierul conţine două numere ı̂ntregi,
separate printr-un spaţiu, reprezentând valorile M şi N din enunţ. Pe următoarele M linii se află
câte N valori egale cu 0 sau 1, separate prin câte un spaţiu, 0 dacă pătrăţelul nu face parte dintr-o
navă, 1 ı̂n cazul ı̂n care pătrăţelul este o parte a unei nave).

Date de ieşire

Dacă cerinţa este P 1, atunci pe prima linie a fişierului boats.out va fi scris un număr
natural reprezentând numărul de nave formate dintr un singur pătrăţel.
Dacă cerinţa este P 2, aunci ı̂n fişierul boats.out vor fi scrise pe câte o linie, separate prin
câte un spaţiu, trei valori: caracterul L urmat de numerele d şi c, ı̂n ordine crescătoare după
valoarea d, unde d reprezintă lungimea navei (numărul de pătrăţele) iar c numărul de nave linie
de lungime d. Apoi, pe fiecare dintre liniile următoare vor fi scrise, separate prin câte un spaţiu,
caracterul C urmat de două numere: d şi c, ı̂n ordine crescătoare după d, unde d reprezintă
lungimea navei (numărul de pătrăţele) şi c numărul de nave coloană de lungime d.

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.

197
CAPITOLUL 19. ONI 2017 19.1. BOATS 198

Exemple:
boats.in boats.out Explicaţii
1 Se rezolvă doar cerinţa 1.
Există o singură navă formatată dintr-
un singur pătrăţel

L21 Se rezolvă doar cerinţa 2.


L51 Există 3 nave linie: o navă linie de
L61 lungime 2, o navă linie de lungime 5 şi
C32 o navă linie de lungime 6.
C41 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

19.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 tabloului
lungime vertical  numărul de nave coloană de lungime j.
Parcurgând matricea determinăm secvenţele de 1 (adică părţi ale navelor). Iniţial parcurgem
din punctul curent către EST pe aceeaşi linie (pentru determinarea navelor linie), apoi, din punctul
curent către SUD (pentru determinarea navelor coloană). Dacă găsim o parte a unei nave marcăm
poziţia curentă din matricea iniţială cu 0, semn că am adăugat valoarea 1 la contorul pentru
lungime şi pentru a evita posibilitatea de a o repeta.
Dacă nu se găsesc valori egale cu 1 ı̂n alte direcţii la est sau sud, ı̂nseamnă că nava este alcătuită
dintr-un singur pătrăţel.

19.1.2 Cod sursă

Listing 19.1.1: boatsAB.cpp


1 //Alin Burta
2 #include <fstream>
CAPITOLUL 19. ONI 2017 19.1. BOATS 199

3
4 #define FIN "boats.in"
5 #define FOU "boats.out"
6 #define Mmax 1002
7 #define Nmax 1002
8
9 short Old[Nmax] ={0}, New[Nmax] ={0}, Nxt[Nmax]={0};
10 short Oriz[Nmax]={0}, Vert[Nmax]={0};
11 int M, N, P;
12
13 using namespace std;
14
15 void Vcopy(short A[], short B[], int n)
16 {
17 int i;
18 for(i=1; i<=n; ++i) A[i] = B[i];
19 }
20
21 int main()
22 {
23 ifstream fin(FIN);
24 ofstream fou(FOU);
25 int i, j, NrUnu, Dim;
26 bool Este;
27 //citesc datele
28 fin >> P >> M >> N;
29 if( P == 1)
30 {
31 NrUnu = 0;
32 for(j=1; j <= N; ++j) fin >> Old[j];
33 for(j=1; j <= N; ++j) fin >> New[j];
34
35 //calculez pentru prima linie
36 for(j=1; j <= N; ++j)
37 if(Old[j] == 1 && Old[j-1] + Old[j+1] + New[j-1] + New[j] + New[j+1] == 0)
38 NrUnu++;
39
40 for(i=3; i <= M; ++i)
41 {
42 for(j=1; j <= N; ++j) fin >> Nxt[j];
43 for(j=1; j <= N; ++j)
44 if(New[j] == 1 &&
45 New[j-1] + New[j+1] + Old[j-1] + Old[j] + Old[j+1] +
46 Nxt[j-1] + Nxt[j] + Nxt[j+1] == 0)
47 NrUnu++;
48 Vcopy(Old, New, N);
49 Vcopy(New, Nxt, N);
50 }
51
52 //calculez pentru ultima linie
53 for(j=1; j <= N; ++j)
54 if(New[j] == 1 &&
55 New[j-1] + New[j+1] + Old[j-1] + Old[j] + Old[j+1] == 0)
56 NrUnu++;
57 //scriu rezultatul
58 fou << NrUnu << ’\n’;
59 }
60 else
61 {
62 for(j=1; j <= N; ++j) fin >> Old[j];
63
64 //pentru prima linie
65 Este = false;
66 for(j=2; j <= N; ++j)
67 if(Old[j] && Old[j-1])
68 if( ! Este) Este = true, Dim = 1, Old[j-1] = 0;
69 else Dim++, Old[j-1] = 0;
70 else
71 if( Este) Oriz[ Dim+1 ]++, Este = false, Old[j-1] = 0;
72
73 if( Este) Oriz[ Dim+1 ]++, Old[N] = 0;
74
75 for(i=2; i <= M; ++i)
76 {
77 for(j=1; j <= N; ++j) fin >> Nxt[j];
78 //caut nave orizontale
CAPITOLUL 19. ONI 2017 19.1. BOATS 200

79 Este = false;
80 for(j=2; j <= N; ++j)
81 if(Nxt[j] && Nxt[j-1])
82 if( ! Este) Este = true, Dim = 1, New[j-1] = Nxt[j-1] = 0;
83 else Dim++, New[j-1] = Nxt[j-1] = 0;
84 else
85 if( Este) Oriz[ Dim+1 ]++, Este = false, New[j-1] = Nxt[j-1] = 0;
86 else New[j] = 0;
87 if( Este) Oriz[ Dim+1 ]++, New[N] = Nxt[N] = 0;
88
89 //caut nave verticale sau orizontale de dim. 1
90 for(j=1; j <= N; ++j)
91 if(Nxt[j] == 0)
92 {
93 if(Old[j])
94 if(Old[j] == 1) Oriz[1]++, New[j] = 0;
95 else Vert[ Old[j] ]++, New[j] = 0;
96 }
97 else
98 New[j] = Old[j] + 1;
99 Vcopy(Old, New, N);
100 }
101
102 //pentru ultima linie
103 for(j=1; j <= N; ++j)
104 {
105 if(Old[j])
106 if(Old[j] == 1) Oriz[1]++;
107 else Vert[ Old[j] ]++;
108 }
109
110 //scriu datele de iesire
111 for(i=2; i <= Nmax - 2; ++i)
112 if(Oriz[i]) fou<<’L’<<’ ’<<i<<’ ’<<Oriz[i]<<’\n’;
113 for(i=2; i <= Nmax - 2; ++i)
114 if(Vert[i]) fou<<’C’<<’ ’<<i<<’ ’<<Vert[i]<<’\n’;
115 }
116
117 fin.close();
118 fou.close();
119 return 0;
120 }

Listing 19.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 }
CAPITOLUL 19. ONI 2017 19.1. BOATS 201

31 }
32
33 void sol()
34 {
35 int i,j,nr;
36 for(i=1;i<=n;i++)
37 for(j=1;j<=m;j++)
38 if(a[i][j])
39 { nr=1;
40 if(a[i+1][j]==0)
41 { j++;
42 while(a[i][j]==1)
43 {a[i][j]=0; j++;nr++;}
44 oriz[nr]++;j--;
45 }
46 else
47 { int k=i+1;
48 while(a[k][j]==1)
49 {a[k][j]=0; k++;nr++;}
50 vert[nr]++; }
51 ///
52 ///cout<<nr<<endl;
53 ;
54 }
55 if(p==1)g<<oriz[1];
56 else{
57 for(i=2;i<=m;i++)
58 if(oriz[i])g<<"L "<<i<<" "<<oriz[i]<<endl;
59 for(i=2;i<=n;i++)
60 if(vert[i])g<<"C "<<i<<" "<<vert[i]<<endl;
61 }
62 }
63
64 int main()
65 {
66 citire();
67 sol();
68 ///scrie();
69 return 0;
70 }

Listing 19.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;
CAPITOLUL 19. ONI 2017 19.1. BOATS 202

33 while(v[k][j]==1) {
34 v[k][j]=0;
35 k--;
36 lungime++;
37 }
38 v[k][j]=0;
39 lungime_vertical[lungime].numar++;
40 }
41 else if(v[i+1][j]==1) {
42 v[i][j]=0;
43 int lungime=1,k;
44 if(v[i+1][j]==1) {
45 k=i+1;
46 while(v[k][j]==1) {
47 v[k][j]=0;
48 k++;
49 lungime++;
50 }
51 v[i+1][j]=0;
52 lungime_vertical[lungime].numar++;
53 }
54 }
55 else if(v[i][j+1]==1) {//aceeasi linie,stanga
56 v[i][j]=0;
57 int lungime=1,k;
58 if(v[i][j+1]==1) {
59 k=j+1;
60 while(v[i][k]==1) {
61 v[i][k]=0;
62 k++;
63 lungime++;
64 }
65 v[i][k]=0;
66 lungime_orizontal[lungime].numar++;
67 }
68 }
69 else if(v[i][j-1]==1) {//aceeasi linie,dreapta
70 v[i][j]=0;
71 int lungime=1,k;
72 if(v[i][j-1]==1) {
73 k=j-1;
74 while(v[i][k]==1) {
75 v[i][k]==0;
76 k--;
77 lungime++;
78 }
79 v[i][j-1]=0;
80 lungime_orizontal[lungime].numar++;
81 }
82 }
83 else singur_patratel++;
84 }
85 }
86 }
87
88 void afisare() {
89 if(p==1) g<<singur_patratel<<endl;
90 else if(p==2)
91 {for(int i=2;i<=1001;i++)
92 if(lungime_orizontal[i].numar!=0)
93 g<<"L "<<i<<" "<<lungime_orizontal[i].numar<<endl;
94
95 for(int j=2;j<=1001;j++)
96 if(lungime_vertical[j].numar!=0)
97 g<<"C "<<j<<" "<<lungime_vertical[j].numar<<endl;
98 }
99 }
100
101 int main()
102 {
103 citire();
104 determinare_nave();
105 afisare();
106 return 0;
107 }
CAPITOLUL 19. ONI 2017 19.2. DOILAN 203

19.1.3 *Rezolvare detaliată

19.2 doilan
Problema 2 - doilan 100 de puncte
Fie n un număr natural nenul.
Se construieşte mulţimea M a tuturor numerelor formate din exact n cifre, numere formate
doar cu cifrele 1 şi 2.

Cerinţe

Scrieţi un program care citeşte numărul natural n şi apoi determină cel mai mic număr natural
n
x din mulţimea M cu proprietatea că x este divizibil cu 2 .

Date de intrare

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 fişierului de
ieşire doilan.out

Timp maxim de executare/test: 0.2 secunde


Memorie: total 2 MB
Dimensiune maximă a sursei: 5 KB

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


nenul).
...
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 19. ONI 2017 19.3. Z 204

19.2.2 Cod sursă

Listing 19.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 }

19.2.3 *Rezolvare detaliată

19.3 z
Problema 3 - z 100 de puncte
Magazinul de jocuri a lansat cea mai recentă versiune a jocului Z, pentru
a-i ajuta pe elevii din clasa a VIII-a să ı̂nţeleagă mai bine modul de identifi-
care a coordonatelor unui punct din plan, ı̂ntr-un sistem de axe ortogonale.
Numim semn Z ı̂n planul xOy figura obţinută cu ajutorul a 4 puncte
distincte A xA , yA , B xB , yB , C xC , yC , D xD , yD , unite ca ı̂n fig.1, ı̂n
care xA xC , xB xD , yA yB , yC yD .
CAPITOLUL 19. ONI 2017 19.3. Z 205

Pe ecran este afiş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 fiecare punct şi să traseze un segment care să unească punctul (cu
excepţia primului punct marcat) cu cel marcat anterior.
În exemplul din fig.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 afişate succesiv pe ecran) şi coordonatele celor n puncte
din plan, să se scrie un program care determină:
1. Numărul de treceri prin originea sistemului de coordonate.
2. Numărul maxim al semnelor Z distincte, formate cu puncte marcate.

Date de intrare

Fişierul de intrare z.in conţine pe prima linie un număr natural P reprezentând cerinţa care
trebuie să fie rezolvată (1 sau 2).
Pe cea de a doua linie se află numărul natural n, reprezentând numărul punctelor afişate
succesiv pe ecran.
Pe următoarele n linii se află câte două numere ı̂ntregi, x şi y, separate printr-un spaţiu,
reprezentând coordonatele unui punct x, y  din plan, ı̂n ordinea apariţiei pe ecran.

Date de ieşire

Dacă cerinţa este P 1, atunci pe prima linie a fişierului z.out va fi scris un număr natural
reprezentând numărul de treceri prin originea sistemului de coordonate.
Dacă cerinţa este P 2, atunci fişierul de ieşire z.out va conţine numărul maxim al semnelor
Z distincte, formate cu puncte marcate.

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


CAPITOLUL 19. ONI 2017 19.3. Z 206

1 2 A trecut de două ori prin originea sistemului de coordonate.


14
-8 4
-5 6
-2 6
16
-2 2
-5 -2
-5 2
12
-5 -2
1 -2
-1 2
26
-1 2
1 -2
2 3 S-au format 3 semne Z, reprezentate ı̂n figurile 3, 4 şi 5.
14
-8 4
-5 6
-2 6
16
-2 2
-5 -2
-5 2
12
-5 -2
1 -2
-1 2
26
-1 2
1 -2

Timp maxim de executare/test: 1.0 secunde


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

19.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 verifică pentru
fiecare segment trasat, care nu are capete ı̂n origine, dacă acesta conţine originea sistemului de
coordonate.
2) Pentru a determina numărul semnelor Z, reţinem, pentru fiecare ordonată Y : mulţimea
punctelor marcate, distincte, cu ordonata Y şi intervalele orizontale  x1, Y , x2, Y .
Se calculează reuninea intervalelor orizontale, pentru fiecare ordonată Y .
Se memorează segmentele oblice SO care ar putea forma un semn Z şi se realizează reuniunea
segmentelor oblice.
Pentru fiecare segment oblic astfel determinat, se identifică mulţimea perechilor de puncte
marcate care se află pe acest segment şi se verifică, pentru fiecare diagonală determinată de
punctele perechii, dacă determină un semn Z.

19.3.2 Cod sursă


CAPITOLUL 19. ONI 2017 19.3. Z 207

Listing 19.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
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;
CAPITOLUL 19. ONI 2017 19.3. Z 208

75 S[y][0].st=k;
76 }
77 }
78
79 void adaugaSegmentOblic(short xs,short ys,short xj,short yj)
80 {
81 short i,j=0;
82 for(short i=1;i<=nro;++i)
83 if(( xj>=SO[i].xj && xj<=SO[i].xs || xj<=SO[i].xj && SO[i].xj<=xs)
84 && peAceeasiDreapta(SO[i].xj,SO[i].yj, xj,yj, SO[i].xs,SO[i].ys)
85 && peAceeasiDreapta(SO[i].xj,SO[i].yj, xs,ys, SO[i].xs,SO[i].ys))
86 {
87 xj=min(xj,SO[i].xj);
88 yj=min(yj,SO[i].yj);
89 xs=max(xs,SO[i].xs);
90 ys=max(ys,SO[i].ys);
91 }
92 else
93 SO[++j]=SO[i];
94 ++j;
95 SO[j].xj=xj;
96 SO[j].yj=yj;
97 SO[j].xs=xs;
98 SO[j].ys=ys;
99 nro=j;
100 }
101
102 int uniteOrizontal(short x1, short x2, short y)
103 {
104 if(!P[y][x1]) return 0;
105 if(!P[y][x2]) return 0;
106
107 int i=1;
108 while(i<=S[y][0].st &&S[y][i].dr<x1)
109 i++;
110 if(i<=S[y][0].st && x2 <= S[y][i].dr)
111 return 1;
112
113 return 0;
114 }
115
116 int ZZ(short i)
117 {
118 short nrp=0,y,x,z=0,j;
119 for(y=SO[i].ys;y>=SO[i].yj;--y)
120 if(S[y][0].st &&
121 (y-SO[i].yj)*(SO[i].xs-SO[i].xj)%(SO[i].ys-SO[i].yj)==0)
122 {
123 x=(y-SO[i].yj)*(SO[i].xs-SO[i].xj)/(SO[i].ys-SO[i].yj)+SO[i].xj;
124 if(P[y][x])
125 {
126 ++nrp;
127 p[nrp].x=x;
128 p[nrp].y=y;
129 }
130 }
131
132 for(i=1;i<nrp;++i)
133 for(j=i+1;j<=nrp;++j)
134 if(uniteOrizontal(p[j].x,p[i].x,p[i].y) &&
135 uniteOrizontal(p[j].x, p[i].x, p[j].y))
136 ++z;
137 return z;
138 }
139
140 void numaraZ()
141 {
142 short i;
143 unsigned long long nrz=0;
144 for(i=1;i<=nro;++i)
145 nrz+=ZZ(i);
146 fout<<nrz<<’\n’;
147 }
148
149 int main()
150 {
CAPITOLUL 19. ONI 2017 19.3. Z 209

151 fin>>k>>n>>x>>y;
152 if(k==1)
153 { xa=x;ya=y;z=0;
154 for(i=2;i<=n;++i)
155 {
156 fin>>x>>y;
157 if(xa*x<0 && ya*y<0 && xa*(y-ya)==ya*(x-xa) ||
158 xa*x<0 && ya==0 && y==0 || ya*y<0 && xa==0 && x==0)
159 ++z;
160
161 xa=x;ya=y;
162 }
163 fout<<z<<’\n’;
164 }
165 else
166 {
167 x+=1000;
168 y+=1000;
169 ymax=ymin=y;
170 P[y][x]=true;
171 xa=x;ya=y;
172
173 for(i=2;i<=n;++i)
174 {
175 fin>>x>>y;
176 x+=1000;
177 y+=1000;
178
179 if(y>ymax)
180 ymax=y;
181 else
182 if(y<ymin)
183 ymin=y;
184
185 P[y][x]=true;
186 if(y==ya && x!=xa)
187 adaugaSegmentOrizontal(y,xa,x);
188 else
189 if(xa>x && ya>y)
190 adaugaSegmentOblic(xa,ya,x,y);
191 else
192 if(x>xa && y>ya)
193 adaugaSegmentOblic( x,y,xa,ya);
194 xa=x;ya=y;
195 }
196
197 reuniuneIntervale();
198 numaraZ();
199 }
200
201 fin.close();
202 fout.close();
203
204 return 0;
205 }

19.3.3 *Rezolvare detaliată


Capitolul 20

ONI 2016

20.1 cercetasi
Problema 1 - cercetasi 100 de puncte
Un grup de N cercetaşi, numerotaţi de la 1 la N , se află ı̂n tabără la munte. Pentru ei,
organizatorii au pregătit N scaune, de asemenea numerotate de la 1 la N , aşezate ı̂n cerc, astfel
ı̂ncât fiecare cercetaş să aibă locul său (locul cercetaşului i este pe scaunul i, 1 & i & N ).
Pentru desfăşurarea următoarei activităţi, organizatorii au decis ca M dintre cercetaşi să prez-
inte diferite exerciţii. Numărul M este egal cu cea mai mare putere a lui 2 cu proprietatea că
numărul N de cercetaşi aflaţi ı̂n tabără se poate scrie ca sumă de M numere consecutive ı̂n
mulţimea numerelor impare. Cei M cercetaşi care vor prezenta sunt cei numerotaţi cu numerele
impare consecutive a căror sumă este N . De exemplu, dacă N 8, atunci M este 2, iar exerciţiile
vor fi prezentate de cercetaşii numerotaţi cu 3, respectiv cu 5.
Din joacă, micii cercetaşi s-au aşezat pe scaune la ı̂ntâmplare. Organizatorii au nevoie pentru a
desfăşura activitatea ca cel puţin cei M cercetaşi care vor prezenta exerciţiile să se afle pe locurile
lor. Pentru aceasta, o parte dintre cercetaşi trebuie să-şi schimbe locul şi organizatorii invită
micii cercetaşi să participe la jocul numit ”Mutare”. Acest joc se desfăşoară astfel: unul dintre
cercetaşii care nu se află pe locul lor se ridică şi merge ı̂n interiorul cercului. Cercetaşul numerotat
cu numărul scaunului rămas liber ı̂şi va ocupa locul, iar locul ocupat de el anterior rămâne astfel
liber. Jocul continuă până când scaunul cercetaşului aflat ı̂n interiorul cercului se eliberează şi el
se aşază pe locul său.

Cerinţe

Fiind dat numărul N , precum şi ordinea ı̂n care s-au aşezat cercetaşii pe scaunele numerotate
de la 1 la N , scrieţi un program care să determine:
a numărul M de cercetaşi care vor prezenta exerciţii ı̂n cadrul activităţii;
a numerele de identificare ale celor M cercetaşi care vor prezenta exerciţiile, ı̂n ordine strict
crescătoare;
a numărul minim de cercetaşi care ı̂şi vor schimba locul, astfel ı̂ncât toţi cei M cercetaşi care
vor prezenta exerciţiile să se afle pe locurile lor.

Date de intrare

Fişierul de intrare cercetasi.in conţine pe prima linie numărul natural N cu semnificaţia din
enunţ. Pe a doua linie, se află N numere naturale distincte din mulţimea r1, 2, ..., N x, separate
prin spaţii, reprezentând ordinea ı̂n care s-au aşezat cei N cercetaşi pe scaunele numerotate de la
1 la N .

Date de ieşire

Fişierul de ieşire cercetasi.out va conţine 3 linii. Pe prima linie se va scrie un singur număr
natural repre-zentând numărul M de cercetaşi care vor prezenta exerciţiile. Pe a doua linie se vor
scrie M numere naturale, ı̂n ordine strict crescătoare, separate prin câte un spaţiu, reprezentând
cercetaşii care vor prezenta exerciţiile. Pe a treia linie se va scrie un număr natural, reprezentând
numărul minim de cercetaşi care ı̂şi vor schimba locul.

210
CAPITOLUL 20. ONI 2016 20.1. CERCETASI 211

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 afiş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 fi prezentate
23415867 35 de cercetaşii numerotaţi cu 3, respectiv cu 5.
4 Cercetaşul cu numărul 3 nu se află pe locul său şi va trece ı̂n
interiorul cercului, astfel scaunul cu numărul 2 rămâne liber.
Cercetaşul cu numărul 2 ı̂şi ocupă locul şi rămâne liber scaunul
cu numărul 1.
Cercetaşul cu numărul 1 ı̂şi ocupă locul şi rămâne liber scaunul
cu numărul 4.
Cercetaşul cu numărul 4 ı̂şi ocupă locul şi rămâne liber scaunul
cu numărul 3 şi astfel cercetaşul aflat ı̂n interiorul cercului se
poate aşeza pe locul său. In cadrul acestui joc ”Mutare” şi-au
schimbat locul 4 cercetaşi. Cum cercetaşul cu numărul 5 se
află deja pe locul său, numărul de cercetaşi care ı̂şi schimbă
locul rămâne 4.

Timp maxim de executare/test: 0.1 secunde


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

20.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
identificare 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 afle 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 fi numărul de termeni)
M
a câtul ı̂mparţirii lui N la 2 trebuie să fie număr par
M
a câtul ı̂mparţirii lui N la 2 trebuie să fie mai mare decât numărul de termeni din descom-
punere - 1 (astfel ı̂ncât primul termen să fie 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 fiecare 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 afle 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;
CAPITOLUL 20. ONI 2016 20.1. CERCETASI 212

a ı̂n coloana 2 reţinem poziţia iniţială a cercetaşului cu numărul p.


Pentru fiecare dintre cei M cercetaşi, ı̂n cazul ı̂n care nu se află pe locul lor, efectuăm
schimbările jocului ”Mutare”, actualizând tabloul caracteristic şi numărul de cercetaşi care ı̂şi
schimbă locul.

20.1.2 Cod sursă

Listing 20.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 20.1.2: cercetasi2 RBalasa.cpp


CAPITOLUL 20. ONI 2016 20.1. CERCETASI 213

1 #include <fstream>
2
3 using namespace std;
4
5 #define Nmax 10002
6
7 ifstream fin ("cercetasi.in");
8 ofstream fout ("cercetasi.out");
9
10 int n,ok[Nmax][3];
11
12 int mutare(int poz)
13 {
14 int k=0,aux;
15 do
16 {
17 k++;
18 aux=ok[poz][2];
19 ok[poz][2]=poz;
20 ok[poz][1]=1;
21 poz=aux;
22 } while (ok[poz][2]!=poz);
23
24 return k;
25 }
26
27 void calcul(int n, int &k, int &x)
28 {
29 k=1;
30 x=n;
31 if(n%4==0)
32 {
33 while(n%(k*2)==0 && n/(k*2)%2==0 && n/(k*2)-(k*2-1)>0)
34 k=k*2;
35 x=n/k-(k-1);
36 }
37 }
38
39 int main()
40 {
41 int m,i,nr=0,x,cx;
42 fin>>n;
43
44 calcul(n,m,cx);
45 x=cx;
46 fout<<m<<"\n";
47
48 for(i=1;i<=m;i++)
49 {
50 fout<<x<<" ";
51 ok[x][0]=1;
52 x=x+2;
53 }
54 fout<<"\n";
55
56 for(i=1;i<=n;i++)
57 {
58 fin>>x;
59 if(x==i)
60 ok[x][1]=1;
61 if(ok[x][0]==1&&x==i)
62 nr++;
63 ok[x][2]=i;
64 }
65
66 if(nr==m)
67 fout<<"0\n";
68 else
69 {
70 nr=0;
71 for(i=1;i<=m;i++)
72 {
73 if(ok[cx][0]==1 && ok[cx][1]==0)
74 nr=nr+mutare(cx);
75
CAPITOLUL 20. ONI 2016 20.2. FARMA 214

76 cx=cx+2;
77 }
78
79 fout<<nr<<"\n";
80 }
81
82 return 0;
83 }

20.1.3 *Rezolvare detaliată

20.2 farma
Problema 2 - farma 100 de puncte
Noile reguli din sistemul sanitar cer ca medicii să nu prescrie pe reţete un anumit medicament,
ci să menţioneze substanţa activă. Reţeta este formată din n prescripţii, câte una pentru fiecare
substanţă activă prescrisă.
Farmacista de la care cumpăr medicamentele mi-a făcut o listă ı̂n care pentru fiecare substanţă
activă de pe reţetă sunt trecute medicamentele care conţin substanţa activă respectivă, precum
şi preţul pastilelor prescrise din medicamentul respectiv, sub forma următoare:
substanţa activă: medicament1 preţ1 , medicament2 preţ2 , ..., medicamentk preţk

Din păcate, ı̂ntre anumite medicamente există incompatibilităţi şi ca urmare ele nu pot fi
administrate simultan, deoarece ar produce reacţii adverse. De aceea, farmacista mea mi-a dat
şi o listă de incompatibilităţi, ı̂n listă fiind specificate perechi de medicamente incompatibile, sub
forma:
medicament1 /medicament2
Când cumpăr reţeta, eu trebuie să iau câte un medicament pentru fiecare substanţă activă
prescrisă de medic şi să am grijă să nu cumpăr medicamente care sunt incompatibile. Desigur, voi
cumpăra pastilele prescrise pentru tratamentul complet.
Cerinţe
Cunoscând lista pe care mi-a dat-o farmacista, precum şi incompatibilităţile dintre medica-
mente, scrieţi un program care să determine:
1. câte medicamente am la dispoziţie pentru fiecare substanţă activă;
2. suma minimă pe care trebuie să o cheltui pentru a cumpăra reţeta.
Date de intrare
Fişierul de intrare farma.in conţine pe prima linie numărul natural c, reprezentând cerinţa
pe care trebuie să o rezolvăm (1 sau 2).
Pe a doua linie se află numărul natural n, reprezentând numărul de substanţe active prescrise
de medic.
Pe următoarele n linii se află lista pe care mi-a dat-o farmacista, pe fiecare linie fiind specificată
o substanţă activă, urmată de medicamentele care conţin această substanţă şi preţurile lor, sub
forma precizată ı̂n enunţ.
Pe următoarea linie se află un număr natural m, reprezentând numărul de perechi de medica-
mente existente ı̂n lista de incompatibilităţi.
Pe următoarele m linii sunt scrise perechile de medicamente incompatibile, câte o pereche pe
o linie, sub forma precizată ı̂n enunţ.
Date de ieşire
Dacă cerinţa este 1, fişierul de ieşire farma.out va conţine n linii, pe linia i (1 & i & n) fiind
scris numărul de medicamente disponibile pentru substanţa activă descrisă pe linia i  1 ı̂n fişierul
de intrare.
Dacă cerinţa este 2, fişierul de ieşire farma.out va conţine o singură linie pe care va fi scris un
număr natural reprezentând suma minimă pe care trebuie să o plătesc pentru a cumpăra reţeta,
ı̂n condiţiile descrise ı̂n enunţ.
CAPITOLUL 20. ONI 2016 20.2. FARMA 215

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 fiecare substanţă activă există cel mult 9 medicamente care să conţină substanţa
activă respectivă.
a În fiecare pereche din lista de incompatibilităţi se află medicamente care conţin substanţe
active diferite.
a În lista de medicamente corespunzătoare fiecărei substanţe active pot exista oricâte spaţii,
dar lungimea oricărei linii nu depăşeşte 700 de caractere.
a Pentru datele de test există ı̂ntotdeauna soluţie.
a Pentru teste valorând 10% din punctaj cerinţa este 1.

Exemple
farma.in farma.out Explicaţii
1 4 Există 4 medicamente pen-
3 3 tru prima substanţă activă, 3
metformin:siofor 10,glibomet 30,bidiab 60,gliformin 10 3 medicamente pentru cea de a
ibuprofen:nurofen 24,advil 35, ibusinus 9 doua şi tot 3 medicamente pen-
diclofenac : diclac 28 , voltaren 50, cambia 102 tru cea de a treia.
0
2 67 Plătim suma minimă 67 dacă
3 vom cumpăra glibomet (preţ
metformin:siofor 10,glibomet 30,bidiab 60,gliformin 10 30), ibusinus (preţ 9) diclac
ibuprofen:nurofen 24,advil 35, ibusinus 9 (preţ 28).
diclofenac : diclac 28 , voltaren 50, cambia 102 Observaţi că oricare două
5 medicamente cumpărate sunt
siofor/diclac compatibile.
gliformin/diclac
ibusinus/siofor
ibusinus/voltaren
bidiab/diclac

Timp maxim de executare/test: 0.1 secunde


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

20.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.
Să numerotăm substanţele active de la 1 la n (denumirile acestora sunt irelevante ı̂n problemă).
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 fi abordată ı̂n moduri diferite:
CAPITOLUL 20. ONI 2016 20.2. FARMA 216

1. Generăm toate elementele produsului cartezian printr-un algoritm de tip succesor. Pentru
fiecare element generat, verificăm dacă medicamentele sunt compatibile două câte două. În caz
afirmativ, comparăm suma necesară pentru soluţia curentă cu suma minimă şi o reţinem dacă este
cazul.
Această abordare obţine 65 de puncte (sursa f arma gen)
2. Pentru a optimiza generarea observăm că pe poziţia i ı̂n soluţie nu are rost să selectăm un
medicament care este incompatibil cu cel puţin unul deja selectate pe poziţiile 1, 2, ..., i  1.
Putem genera soluţiile ţinând cont de această observaţie utilizând o stivă, implementată ca un
vector sol cu n elemente.
La pasul curent i, trebuie să selectăm un element care poate fi pus ı̂n soluţie pe poziţia i.
Dacă i % n, soluţia este completă, comparăm suma necesară pentru cumpărarea ei cu suma
minimă şi o reţin dacă este cazul.
Dacă i & n, atunci incrementăm soli (plasăm un nou element pe poziţia i ı̂n soluţie).
Apar următoarele cazuri posibile:
soli % lg i (ı̂n acest caz am epuizat valorile posibile pentru poziţia i, cobor pe stivă revenind
la poziţia precedentă (i–).
soli & lg i, iar valoarea soli curentă este compatibilă cu toate valorile sol1, sol2, ...,
soli  1, atunci urcăm un nivel pe stivă (i++) şi iniţializăm soli cu 0.
soli & lg i, dar valoarea soli curentă nu este compatibilă cu valorile sol1, sol2, ...,
soli  1; ı̂n acest caz rămân pe stivă la acelaşi nivel i, urmând la pasul următor să incrementăm
valoarea curentă.
Această generare este implementată ı̂n f arma ok slow şi obţine 75 de puncte
3. Putem optimiza generarea ı̂n continuare făcând următoarea observaţie. Dacă suma necesară
cumpărării medicamentelor ı̂n soluţia parţial generată depăşeşte deja suma minimă curentă, aban-
donăm generarea cu valoarea curentă şi revenim la pasul precedent, pentru că e clar că această
soluţie nu va fi convenabilă.
Pentru a obţine mai rapid o sumă mai mică, vom sorta crescător după preţ medicamentele din
lista fiecărei substanţe active.
Această soluţie obţine 100 de puncte (f arma ok).
O implementare recursivă pentru această idee este concisă şi eficientă (f arma rec) şi obţine
deasemenea 100 de puncte, dar depăşeşte nivelul clasei a VIIII-a.
Sunt posibile şi abordări Greedy care obţin punctaje parţiale. De exemplu dacă nu ţinem
cont de incompatibilităţi şi pentru fiecare substanţă activă selectăm medicamentul cel mai ieftin
obţinem 10 puncte pentru cerinţa 2.
Dacă abordăm Greedy ţinând cont de incompatibilităţi (adică mă opresc la prima soluţie care
ı̂ndeplineşte condiţia de compatibilitate, evident, după sortarea medicamentelor crescător după
preţ) se obţin 20 de puncte la cerinţa 2.
În toate soluţiile prezentate este necesară verificarea rapidă a compatibilităţii medica-
mentelor selectate ı̂n soluţie. Pentru această propunem următoarea codificare a medicamentelor.
Substanţele active sunt numerotate de la 1 la n (n $ 10), iar medicamentele din lista substanţei
active i de la 1 la lg i (lg i $ 10). Identificăm ı̂n mod unic un medicament printr-un număr de
două cifre format din numărul substanţei active şi numărul de ordine al medicamentului ı̂n lista
de medicamente a substanţei active.
Pentru a reţine relaţiile de incompatibilitate ı̂ntre medicamente utilizăm o matrice d100100 .
dc1c2 dc2c1 1, dacă medicamentele care au numerele de identificare c1 şi c2 sunt
incompatibile, respectiv 0 ı̂n caz contrar.
Atenţie! Codificarea medicamentelor se va face după sortarea acestora după preţ, pentru că la
sortare medicamentele ı̂şi schimbă poziţiile ı̂n lista de medicamente.

20.2.2 Cod sursă

Listing 20.2.1: farma ok.cpp


1 //Em. Cerchez
2 #include <fstream>
CAPITOLUL 20. ONI 2016 20.2. FARMA 217

3 #include <cstring>
4
5 #define LGMAX 1004
6 #define INF 1000000000
7
8 using namespace std;
9
10 ifstream fin("farma.in");
11 ofstream fout("farma.out");
12
13 struct med
14 {
15 char nume[31];
16 int pret;
17 };
18
19 int suma, smin=INF;
20 int sol[10];
21 int solmin[10];
22 med a[10][10];
23 int n, cerinta;
24 int lg[10];
25 char s[LGMAX];
26 bool d[100][100];
27
28 void citire();
29 void sortare();
30 void generare();
31 int verif(int vf);
32 int nr(char *s);
33 int cauta(char * s);
34
35 int main()
36 { int i;
37 citire();
38 if (cerinta==1)
39 for (i=1; i<=n; i++) fout<<lg[i]<<’\n’;
40 else
41 {
42 generare();
43 fout<<smin<<’\n’;
44 //for (i=1; i<=n; i++)
45 // fout<<a[i][solmin[i]].nume<<’ ’<<a[i][solmin[i]].pret<<’\n’;
46 }
47 fout.close();
48 return 0;
49 }
50
51 void citire()
52 {char c, *p;
53 int i, poz1, poz2, m;
54 fin>>cerinta>>n;
55 fin.get(c);
56 for (i=1; i<=n; i++)
57 {
58 fin.getline(s,LGMAX);
59 p=strtok(s,":");
60 do
61 {
62 p=strtok(NULL,", ");
63 if (!p) break;
64 strcpy(a[i][++lg[i]].nume,p);
65 p=strtok(NULL,", ");
66 a[i][lg[i]].pret=nr(p);
67 }
68 while (1);
69 }
70 sortare();
71 fin>>m;
72 fin.get(c);
73 for (i=1; i<=m; i++)
74 {fin.getline(s,LGMAX);
75 p=strchr(s,’/’);
76 *p=NULL;
77 poz1=cauta(s);
78 poz2=cauta(p+1);
CAPITOLUL 20. ONI 2016 20.2. FARMA 218

79 d[poz1][poz2]=d[poz2][poz1]=1;
80 }
81 }
82
83 void sortare()
84 //sortez medicamentele de pe fiecare linie crescator dupa pret
85 {int i, j, sch;
86 med aux;
87 for (i=1; i<=n; i++)
88 {
89 do
90 {
91 sch=0;
92 for (j=1; j<lg[i]; j++)
93 if (a[i][j].pret>a[i][j+1].pret)
94 {
95 aux=a[i][j]; a[i][j]=a[i][j+1]; a[i][j+1]=aux;
96 sch=1;
97 }
98 }
99 while (sch);
100 }
101 }
102
103 void generare()
104 {int vf=1, i;
105 sol[1]=0;
106 while (vf>0)
107 {
108 if (vf>n) //solutie completa
109 { if (suma<smin)
110 {smin=suma;
111 for (i=1; i<=n; i++) solmin[i]=sol[i];
112 }
113 vf--;
114 }
115 else
116 {sol[vf]++; //incrementez pozitia curenta
117 if (sol[vf]>lg[vf])
118 {suma=suma-a[vf][sol[vf]-1].pret; sol[vf--]=0;}
119 else
120 if (sol[vf]>1)
121 {suma=suma-a[vf][sol[vf]-1].pret+a[vf][sol[vf]].pret;
122 if (suma>=smin)
123 {suma-= a[vf][sol[vf]].pret; sol[vf--]=0; }
124 else
125 if (verif(vf)) sol[++vf]=0;
126 }
127 else
128 {suma+=a[vf][1].pret;
129 if (suma>=smin)
130 {suma-= a[vf][1].pret; sol[vf--]=0; }
131 else
132 if (verif(vf)) sol[++vf]=0;
133 }
134 }
135 }
136 }
137
138 int verif(int vf)
139 { int i, pvf=vf*10+sol[vf];
140 for (i=1; i<vf; i++)
141 if (d[pvf][i*10+sol[i]]) return 0;
142 return 1;
143 }
144
145 int nr(char *s)
146 {int i, rez;
147 for (rez=i=0; s[i]; i++)
148 rez=rez*10+s[i]-’0’;
149 return rez;
150 }
151
152 int cauta(char * s)
153 {int i, j;
154 for (i=1; i<=n; i++)
CAPITOLUL 20. ONI 2016 20.2. FARMA 219

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 20.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 20. ONI 2016 20.2. FARMA 220

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 20.2.3: lucia farma.cpp


CAPITOLUL 20. ONI 2016 20.2. FARMA 221

1 //Lucia Miron
2 #include <fstream>
3 #include<cstring>
4
5 using namespace std;
6
7 ifstream fin("farma.in");
8 ofstream fout("farma.out");
9
10 int a[10][10], b[100][100],sp,smin,x[10],n,m,cer,i;
11 char med[10][1001];
12
13 void determina(char s[1001], int&l,int &c)
14 {
15 int i, pas,nr;
16 char ss[1001],*p;
17 for(i=1;i<=n;i++)
18 {
19 strcpy(ss,med[i]);
20 if(strstr(ss,s))
21 {
22 nr=0;pas=1;
23 p=strtok(ss," ,");
24 while(p)
25 {
26 if(pas==1)nr++;
27 if(strcmp(p,s)==0)
28 {
29 l=i,c=nr;return;
30 }
31 pas=3-pas;
32 p=strtok(NULL," ,");
33 }
34 }
35 }
36 }
37
38 void citire()
39 {
40 int i,k,j,pas,l,cc,l1,c1;
41 char c;
42 char s[1001],*p,s1[1001];
43
44 fin>>cer>>n;
45 fin.get();
46
47 for(i=1;i<=n;i++)
48 {
49 fin.getline(s,1001);
50 p=strchr(s,’:’);
51 strcpy(s,p+1);
52 strcpy(med[i],s);
53 p=strtok(s," ,");pas=1;j=0;
54 while(p)
55 {
56 if(pas==2)
57 {
58 j++;a[i][j]=0;
59 for(k=0;p[k];k++)
60 a[i][j]=a[i][j]*10+(p[k]-’0’);
61 }
62 pas=3-pas;
63 p=strtok(NULL," ,");
64 }
65 a[i][0]=j;
66 }
67
68 if(cer==2)
69 {
70 fin>>m;
71 for(i=1;i<=m;i++)
72 {
73 fin>>s;
74 p=strchr(s,’/’);
75 s1[0]=’\0’;
76 strncat(s1,s,p-s);
CAPITOLUL 20. ONI 2016 20.3. STELE 222

77 strcpy(s,p+1);
78 determina(s,l,cc);
79 determina(s1,l1,c1);
80 b[l*10+cc][l1*10+c1]=b[l1*10+c1][l*10+cc]=1;
81 }
82 }
83
84 }
85
86 int valid(int k)
87 {
88 int i;
89 for(i=1;i<k;i++)
90 if(b[k*10+x[k]][i*10+x[i]]==1)
91 return 0;
92 return 1;
93 }
94
95 void bkt(int k)
96 {
97 int i,j;
98 if(k>n)
99 {
100 if(sp<smin)smin=sp;
101 }
102 else
103 for(i=1;i<=a[k][0];i++)
104 {
105 x[k]=i;
106 sp=sp+a[k][i];
107 if(valid(k)&&sp<smin)
108 bkt(k+1);
109 sp=sp-a[k][i];
110 }
111 }
112
113 int main()
114 {
115 citire();
116
117 if(cer==1)
118 {
119 for(i=1;i<=n;i++) fout<<a[i][0]<<’\n’;
120 }
121 else
122 {
123 smin=100000;
124 bkt(1);
125 fout<<smin;
126 }
127
128 return 0;
129 }

20.2.3 *Rezolvare detaliată

20.3 stele
Problema 3 - stele 100 de puncte
Pasionată de astronomie, Teodora doreşte să ţină evidenţa numărului de stele din galaxii.
Pentru a face lucrurile mai interesante, ea codifică aceste numere ı̂ntr-un sistem propriu, trans-
formându-le ı̂ntr-o ı̂nşiruire de litere şi cifre după algoritmul următor:
26
- notează fiecare putere a lui 2, strict mai mică decât 2 , cu o literă a alfabetului, astfel:

- reprezintă fiecare număr ca un şir de cifre şi litere obţinut din scrierea acelui număr ca sumă
CAPITOLUL 20. ONI 2016 20.3. STELE 223

de puteri ale lui 2; dacă o putere este folosită de mai multe ori ı̂n descompunerea numărului atunci
ea va fi precedată ı̂n şir de numărul de utilizări.
Un număr poate fi reprezentat astfel ı̂n mai multe moduri. De exemplu, pentru numărul 100
printre variantele de reprezentare avem:
2 5 6
100 = cfg = 2  2  2 = 4+32+64 = 100
0 1 2 3 4 5
100 = 2ab2cde2f = 2 ˜ 2  2  2 ˜ 2  2  2  2 ˜ 2 = 2*1+2+2*4+8+16+2*32 = 100
1 2 6
100 = 16bcg = 16 ˜ 2  2  2 = 16*2+4+64 = 100

Cerinţe

Scrieţi un program care rezolvă următoarele cerinţe:


1. cunoscând s numărul de stele dintr-o galaxie, determină o reprezentare codificată 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 codificată, reprezentând
numărul de stele din fiecare 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 află un număr natural s, ce reprezintă numărul care
trebuie codificat.
Dacă cerinţa este 2, pe a doua linie se află un număr natural g reprezentând numărul de galaxii,
iar pe următoarele g linii câte un şir de caractere reprezentând numărul de stele dintr-o galaxie,
codificat folosind algoritmul descris mai sus.

Date de ieşire

Fişierul de ieşire stele.out va conţine o singură linie pe care va fi scris un şir de litere mici
distincte, ordonate alfabetic, reprezentând scrierea codificată a numărului s (dacă cerinţa este 1)
sau un număr natural ı̂n scriere zecimală ce reprezintă numărul total de stele din cele g galaxii
(dacă cerinţa este 2).

Restricţii şi precizări

1&s&2 1
26
a
a 1 & g & 1000
a Reprezentările codificate din fişierul de intrare pot avea maximum 420 caractere.
a Numărul care poate apărea ı̂n faţa unei litere poate avea maximum 15 cifre.
a Pentru teste valorând 30% din punctaj cerinţa este 1.
a Pentru teste corespunzătoare cerinţei 2 valorând 20% din punctaj valoarea obţinută nu
18
depăşeşte 10 .

Exemple
stele.in stele.out Explicaţii
1 cfg Cerinţa este 1. Reprezentarea numărului 100 care respectă
100 cerinţa este:
2 5 6
cfg=2  2  2 =4+32+64=100
2 6320 Cerinţa este 2 şi avem 5 numere:
5 2a7g = 450
2a7g 17b5d14g = 970
17b5d14g 100a2000b = 4100
100a2000b 7e15f = 592
7e15f 2d6f = 208
2d6f Suma lor este: 450 + 970 + 4100 + 592 + 208 = 6320

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 20. ONI 2016 20.3. STELE 224

20.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,
şi s $ 2 , vom determina direct scrierea numărului. Pentru aceasta, putem lucra ı̂n două moduri:
26

I. cât timp s % 0 determinăm cea mai mare putere a lui 2 mai mică sau egală cu s, o reţinem
şi o scădem din s; la final afişăm codificarea puterilor reţinute ı̂n ordine inversă.
II. Folosim scrierea ı̂n baza 2 a numărului s, afişând acele puteri ale lui 2 corespunzători biţilor
egali cu 1. (30 de puncte)
Cerinţa 2 - 70 de puncte
Varianta 1 - folosind un vector de numărare calculăm numărul de utilizări ale fiecărei litere
ı̂n scrierea celor g numere şi determinăm suma numerelor. Este necesară utilizarea operaţiilor
cu numere mari, deoarece rezultatul poate depăşi 18 cifre. Tot punctaj maxim se obţine dacă
transformăm fiecare număr ı̂n baza 10 şi adunăm numerele obţinute, folosind operaţii cu numere
mari. (70 de puncte)
Varianta 2 - transformăm fiecare numar ı̂n scriere zecimală şi determinăm suma lor, fără a
utiliza operaţii cu numere mari. (20 de puncte)

20.3.2 Cod sursă

Listing 20.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 20. ONI 2016 20.3. STELE 225

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 20.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 20. ONI 2016 20.3. STELE 226

28 i=0;j--;
29 while(i<j)
30 x=s[i], s[i]=s[j], s[j]=x, i++, j--;
31 }
32
33 void numarare(char s[])
34 {
35 int i=0;
36 long long n=0;
37
38 if(s[0]>=’a’ && s[0]<’z’)
39 nr[s[i]-’a’]+=1, i=1;
40
41 for(;s[i];i++)
42 if(s[i]>=’0’ && s[i]<=’9’)
43 n=n*10+s[i]-’0’;
44 else
45 if(s[i-1]>=’a’ && s[i-1]<=’z’)
46 nr[s[i]-’a’]+=1;
47 else
48 nr[s[i]-’a’]+=n, n=0;
49 }
50
51 void prod(long long nr, long long v, int rez[])
52 {
53 int i;
54 long long t=0;
55
56 rez[0]=0;
57 while(nr)
58 rez[++rez[0]]=nr%10, nr/=10;
59
60 for(i=1; i<=rez[0] || t; i++)
61 t+=rez[i]*v, rez[i]=t%10, t/=10;
62
63 rez[0]=i-1;
64 }
65
66 void add(int s[], int rez[])
67 {
68 int i;
69 long long t=0;
70
71 for(i=1;i<=s[0] || i<=rez[0] || t; i++, t/=10)
72 t+=s[i]+rez[i], s[i]=t%10;
73
74 s[0]=i-1;
75 }
76
77 void suma()
78 {
79 int s[101]={0}, rez[101], i, j;
80
81 s[0]=1;
82 s[1]=0;
83 for(i=0;i<26;i++)
84 if(nr[i])
85 {
86 for(j=0;j<100;j++) rez[j]=0;
87 prod(nr[i], v[i], rez);
88 add(s, rez);
89 }
90
91 for(i=s[0];i>=1;i--)
92 fout<<s[i];
93 fout<<’\n’;
94 }
95
96 int main()
97 {
98 int n, C, i, p=1;
99 char s[500];
100
101 for(i=0;i<26;i++)
102 v[i]=p,p*=2;
103
CAPITOLUL 20. ONI 2016 20.3. STELE 227

104 fin>>C>>n;
105 if(C==1)
106 cerinta1(n, s), fout<<s<<’\n’;
107 else
108 {
109 for(i=1;i<=n;i++)
110 fin>>s, numarare(s);
111 suma();
112 }
113
114 fin.close();
115 fout.close();
116 return 0;
117 }

20.3.3 *Rezolvare detaliată


Capitolul 21

ONI 2015

21.1 magic
Problema 1 - magic 100 de puncte
Pentru obţinerea Pietrei Filosofale, un alchimist a preparat un elixir folosind un creuzet de
capacitate C, ı̂n care a turnat picături de metal topit, ı̂ntr-o ordine bine stabilită, ı̂n N etape.
Numărul de picături turnate ı̂ntr-o etapă este cuprins ı̂ntre 0 şi C  1, iar procesul ı̂ncepe când ı̂n
creuzet s-a turnat prima picătură (ı̂n prima etapă numărul de picături turnate este nenul).
Picăturile se adună ı̂n creuzet una câte una şi, de fiecare dată când acesta se umple complet,
alchimistul rosteşte o formulă magică, provocând transformarea ı̂ntregului conţinut ı̂ntr-o singură
picătură, apoi continuă procesul.
O reţetă de obţinere a elixirului se exprimă printr-un şir de N numere, reprezentând numărul
de picături turnate ı̂n cele N etape.
De exemplu, aplicând reţeta 5 6 1 0, cu un creuzet de capacitate C 7, ı̂n cele N 4 etape
procesul este:
- etapa 1: se toarnă 5 picături;
- etapa a 2-a: se toarnă 6 picături, astfel: după primele 2 picături se umple creuzetul (5+2=7)
şi deci se rosteşte formula magică, ı̂n creuzet rămânând o picătură; se continuă cu celelalte 4
picături; la finalul etapei ı̂n creuzet sunt 5 picături (1+4=5);
- etapa a 3-a: se toarnă o picătură; la finalul etapei ı̂n creuzet sunt 6 picături (5+1=6);
- etapa a 4-a: se toarnă 0 picături; după ultima etapă creuzetul conţine 6 picături (6+0=6).
O reţetă care corespunde Pietrei Filosofale trebuie să conducă, la finalul aplicării ei, la obţinerea
unei singure picături, chintesenţa metalelor amestecate. Bineı̂nţeles, sunt mai multe astfel de
reţete.
Fiind un tip responsabil, alchimistul a lăsat posterităţii un set de tratate, care cuprind toate
aceste reţete. El a scris pe fiecare pagină câte o reţetă, astfel ı̂ncât niciuna să nu se repete ı̂n
cadrul ı̂ntregii lucrări. Pe vremea aceea erau meşteri pricepuţi, care fabricau tratate de dimensiuni
corespunzătoare, ı̂ncât fiecare pagină să poată cuprinde o reţetă ca a noastră, oricât de lungă ar
fi ea. Fiecare tratat are P pagini şi doar după ce completează toate cele P pagini ale unui tratat,
alchimistul ı̂ncepe un nou tratat.

Cerinţe

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 semnificaţia din enunţ.

Date de ieşire

Fişierul magic.out conţine un număr natural reprezentând numărul de reţete publicate ı̂n
ultimul tratat.

Restricţii şi precizări

228
CAPITOLUL 21. ONI 2015 21.1. MAGIC 229

1 $ C & 10 ; 2 & N & 10 ; 1 & P & 10 , numere naturale;


7 7 7
a
a pentru 30% dintre teste, C & 10 şi N $ 10, iar pentru 70% dintre teste, N & 104 .

Exemple
magic.in magic.out Explicaţii
423 1 Creuzetul are capacitatea C 4, sunt N 2 etape de aplicare
a fiecărei reţete, tratatele au câte P 3 pagini. Reţetele apli-
cate ı̂n două etape, ı̂n urma fiecărui astfel de proces rămânând
ı̂n creuzet câte o singură picătură, sunt:
1 0; 1 3; 3 1; 2 2.
Pentru acestea sunt necesare două tratate, primul conţinând
trei reţete, iar al doilea (ultimul) o singură reţetă.

Timp maxim de executare/test: 0.1 secunde


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

21.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 fiecare etapă reprezintă câte o cifră a
numărului).
Dacă se scriu toate numerele de N cifre ı̂n baza de numeraţie C ı̂n ordine
crescătoare/lexicografică, cifrele de control ale acestora vor forma un şir de secvenţe de numere
consecutive, fiecare secvenţă fiind de forma 1, 2, ... C  1, unde termenii unei secvenţe sunt cifrele
de control determinate (C  1 numere consecutive).
N
Numărul total de valori de maximum N cifre, scrise ı̂n baza de numeraţie C, este C .
N 1
Numărul total de valori de maximum N  1 cifre, scrise ı̂n baza de numeraţie C, este C .
Numărul total de valori de N cifre, scrise ı̂n baza de numeraţie C, este
N N 1 N 1
C C C C  1.
N 1
C C 1 N 1
Astfel, numărul total de secvenţe de numere consecutive este C 1
C .
Întrucât cifra 1 apare o singură dată ı̂n fiecare astfel de secvenţă, ı̂nseamnă că numărul total de
N 1 N 1
reţete care respectă cerinţa este chiar C . Numărul cerut este (C modulo P ), dacă această
valoare este nenulă, sau P , ı̂n caz contrar (ultimul tratat este complet, deci el va conţine chiar P
reţete).
Pentru obţinerea punctajului maxim, algoritmul de determinare a numărului cerut are o com-
plexitate logaritmică.

21.1.2 Cod sursă

Listing 21.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
CAPITOLUL 21. ONI 2015 21.1. MAGIC 230

15 fin>>C>>N>>P;
16 //P = 1000;
17 // D[i][j] = cate siruri de lungime maxim i se termina in j
18
19 for (j=1;j<C;j++)
20 {
21 D[0][j] = 1;
22 S[0][j] = (S[0][j-1] + D[0][j]) % P;
23 }
24
25 c = 1;
26 for (i=2;i<=N;i++)
27 {
28 for (j=1;j<C;j++)
29 {
30 D[c][j] = D[1-c][j];
31 if (j == 1)
32 {
33 D[c][j] = D[c][j] + S[1-c][C-1];
34 D[c][j] %= P;
35 }
36 else
37 {
38 D[c][j] = D[c][j] + S[1-c][C-1];
39 D[c][j] %= P;
40 }
41
42 S[c][j] = (S[c][j-1] + D[c][j]) % P;
43 }
44 c = 1 - c;
45 }
46
47 if (D[1-c][1] == 0)
48 fout<<P<<"\n";
49 else
50 fout<<D[1-c][1]<<"\n";
51
52 return 0;
53 }

Listing 21.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
CAPITOLUL 21. ONI 2015 21.1. MAGIC 231

34 mag=mag+reteta[i];
35 return mag;
36 }
37
38 int main()
39 {
40 long i;
41 long long nr;
42 in>>C>>N>>P;
43
44 reteta[0]=1;
45 nr=0;
46 do
47 {
48 if(calculeazaMagic()==1)
49 nr=(nr+1)%P;
50 } while(increment()==0);
51
52 if(nr==0)
53 nr=P;
54
55 out<<nr;
56 return 0;
57 }

Listing 21.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 21.1.4: sursaNumarareBKT.cpp


1 #include <iostream>
CAPITOLUL 21. ONI 2015 21.1. MAGIC 232

2 #include <fstream>
3
4 using namespace std;
5
6 ifstream in("magic.in");
7 ofstream out("magic.out");
8
9 long sol[100000];
10 long ultima[100000];
11 long C,N,P;
12 long long nr;
13
14
15 void bkt(long k, long mag)
16 {
17 long transport;
18 for(long i=0;i<=C-1;i++)
19 {
20 sol[k]=i;
21 transport=i+mag;
22 if(transport==C)
23 transport=1;
24 else
25 if(transport>C)
26 transport=1+transport-C;
27
28 if(k==N-1)
29 {
30 if(transport==1)
31 nr=(nr+1)%P;
32 }
33 else bkt(k+1, transport);
34 }
35 }
36
37 int main()
38 {
39 long i;
40 in>>C>>N>>P;
41
42 nr=0;
43 for(i=1;i<C;i++)
44 {
45 sol[0]=i;
46 bkt(1,i);
47 }
48
49 if(nr==0)
50 nr=P;
51
52 out<<nr;
53 return 0;
54 }

Listing 21.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;
CAPITOLUL 21. ONI 2015 21.2. RESTAURARE 233

20 while(N!=0)
21 {
22 if(N%2==1)
23 {
24 nr=(nr*C)%P;
25 if(nr==0)
26 nr=P;
27 }
28 C=(C*C)%P;
29 if(C==0)
30 C=P;
31 N/=2;
32 }
33
34 out<<nr;
35 return 0;
36 }

Listing 21.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 }

21.1.3 *Rezolvare detaliată

21.2 restaurare
Problema 2 - restaurare 100 de puncte
După descoperirea ruinelor unei cetăţi medievale, arheologii au hotărât restaurarea acesteia,
ı̂ncepând cu zidul principal. Acesta este format din N piloni, fiecare cu lăţimea de 1 metru, aşezaţi
unul lângă altul (lipiţi). Se cunoaşte ı̂nălţimea, ı̂n metri, a fiecărui pilon dar, din păcate, nu toţi
mai sunt acum la acelaşi nivel.
Pentru restaurarea zidului, arheologii dispun de cărămizi care au lăţimea de câte 1 metru
şi lungimi variabile, exprimate ı̂n metri. Sunt disponibile oricâte cărămizi, de oricare lungime.
Considerăm că toate cărămizile disponibile şi toţi pilonii care alcătuiesc zidul au aceeaşi grosime,
de 1 metru.
Restaurarea constă ı̂n două etape:
- ı̂n prima etapă, toţi pilonii cu ı̂nălţimea mai mare sau egală cu H se retează, aducându-se
astfel la ı̂nălţimea H, ceilalţi, mai scunzi, păstrându-şi ı̂nălţimea iniţială;
- ı̂n a doua etapă se aduc toţi pilonii la aceeaşi ı̂nălţime, umplându-se golurile dintre ei cu
cărămizi, astfel ı̂ncât zidul să devină compact; din motive neı̂nţelese, arheologii vor aşeza cărămizile
”culcate”, fiecare dintre acestea ocupând, eventual, spaţiul aflat deasupra mai multor piloni.
CAPITOLUL 21. ONI 2015 21.2. RESTAURARE 234

Arheologii au analizat situaţia, independent, pentru Q valori posibile ale lui H.

Cerinţe

Pentru fiecare dintre cele Q valori alese pentru ı̂nălţimea H, se cere să se determine numărul
minim de cărămizi necesare restaurării zidului, independent, pornind de la ı̂nălţimile iniţiale ale
pilonilor.

Date de intrare

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 fiecare dintre ı̂nălţimile H, ı̂n ordinea ı̂n care acestea apar
ı̂n fişierul de intrare.

Restricţii şi precizări

a 1 & N & 100 000;


a ı̂nălţimea fiecă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 N1000, 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,
43242 4 a zidului deci nu mai este necesară nicio cărămidă,
3 2 pentru H=4, sunt necesare 4 cărămizi, zidul
143 având, după restaurare structura din fig. a,
iar pentru H=3, sunt necesare 2 cărămizi,
zidul având, după restaurare structura din fig.
b.

Timp maxim de executare/test: 0.6 secunde


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

21.2.1 Indicaţii de rezolvare

prof. Marius NICOLI, Colegiul Naţional ”Fraţii Buzeşti”, Craiova


CAPITOLUL 21. ONI 2015 21.2. RESTAURARE 235

Considerăm că umplem golurile din zid de sus ı̂n jos. Aşadar, calculăm valoarea maximă
(notată M ax) dintre ı̂nălţimile pilonilor.
Pentru a obţine datele cerute este necesară sortarea ı̂nălţimilor iniţiale ale pilonilor.
La implementare, considerăm că pornim cu K M ax  1 şi secv 1. ı̂n paralel, ı̂ntr-un vector
U vom seta la 0 poziţiile ce nu vor mai face parte din secvenţe goale (iniţial toate valorile din U
sunt 1, reprezentând secvenţa iniţială, aflată deasupra zidului). Observăm că o valoare a lui U
odată setată la 0 va rămâne cu această valoare până la finalul algoritmului.
Analizând o ı̂nălţime K (la care ajungem parcurgı̂nd ı̂nălţimile ı̂n ordine descrescătoare),
trebuie să cunoaştem câte secvenţe ”goale” avem la acea ı̂nălţime (secv). ı̂n funcţie de ı̂nălţimea
pilonului curent (considerat pe o poziţie notată p), vom actualiza valoarea variabilei secv astfel:
dacă U p  1 0 şi U p  1 0, secv scade cu 1 (dispare secvenţa curentă),
iar dacă U p  1 1 şi U p  1 1, secv creşte cu 1 (spargem o secvenţă ı̂n două).
Evident, U p va deveni 0. Astfel, când trecem de la o ı̂nălţime la următoarea ştim câte
secvenţe goale sunt la acea ı̂nălţime precum şi diferenţa de ı̂nălţime şi astfel calculăm numărul
necesar de cărămizi.
Procesând ı̂n ordine descrescătoare ı̂nălţimile la care se retează pilonii, vom putea calcula
valoarea cerută pentru fiecare dintre ele odată cu aplicarea algoritmului prezentat mai sus.
Algoritmul descris anterior are complexitate ı̂n timp liniară.

21.2.2 Cod sursă

Listing 21.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 {
CAPITOLUL 21. ONI 2015 21.2. RESTAURARE 236

44 x[ f[v[i] ] ]= v[i];
45 y[ f[v[i] ] ]= i;
46 f[ v[i] ]--;
47 }
48
49 for (i=1;i<=n;i++)
50 u[i] = 1;
51 secv = 1;
52
53 last = 0;
54 for (i=1;i<=n;i++)
55 {
56 if (x[i] != x[i-1])
57 {
58 sol += secv * (x[i]-last);
59 //
60 A[++d] = make_pair( maxim-x[i]+2, secv * (x[i]-last) );
61 //
62 last = x[i];
63
64 }
65 p = y[i];
66
67 u[p] = 0;
68 if (u[p-1] == 1 && u[p+1] == 1)
69 secv++;
70 if (u[p-1] == 0 && u[p+1] == 0)
71 secv--;
72 }
73
74 p = d;
75 for (i=1;i<=maxim;i++)
76 {
77 if (A[p].first == i)
78 {
79 crt = A[p].second / (A[p-1].first - A[p].first);
80 p--;
81 }
82 S[i] = S[i-1] + crt;
83 }
84
85 fin>>m;
86 for (i=1;i<=m;i++)
87 {
88 fin>>h;
89 fout<<S[h]<<"\n";
90 }
91 /*
92 for (i=1;i<=maxim;i++)
93 cout<<i<<" "<<S[i]<<"\n";
94
95 cout<<"\n";
96 for (i=1;i<=d;i++)
97 cout<<A[i].first<<" "<<A[i].second<<"\n";
98 */
99 // fout<<sol-1<<"\n";
100
101 return 0;
102 }

Listing 21.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];
CAPITOLUL 21. ONI 2015 21.2. RESTAURARE 237

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");
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);
CAPITOLUL 21. ONI 2015 21.2. RESTAURARE 238

90 fclose(fout);
91 return 0;
92 }

Listing 21.2.3: restaurareBrut1.cpp


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 21.2.4: restaurareLT.cpp


1 #include <iostream>
CAPITOLUL 21. ONI 2015 21.3. SORT2DIST 239

2 #include <fstream>
3
4 using namespace std;
5
6 int n, maxim, crt, ant,i, v[100010], h, m;
7
8 long long nr;
9
10 int main()
11 {
12 ifstream in ("restaurare.in");
13 ofstream out("restaurare.out");
14
15 in>>n;
16 for (i=1;i<=n;i++)
17 in>>v[i];
18
19 in>>m;
20 for (;m--;)
21 {
22 in>>h;
23 ant = v[1];
24 if (ant > h)
25 ant = h;
26 maxim=ant;
27 nr = 0;
28 for (i=2;i<=n;i++)
29 {
30 crt = v[i];
31 if (crt > h)
32 crt = h;
33
34 if (crt<ant)
35 nr=nr+ant-crt;
36 else
37 if(crt>maxim)
38 {
39 nr=nr+crt-maxim;
40 maxim=crt;
41 }
42 ant=crt;
43 }
44 out<<nr<<"\n";
45 }
46 return 0;
47 }

21.2.3 *Rezolvare detaliată

21.3 sort2dist
Problema 3 - sort2dist 100 de puncte
Jocul pe care ı̂l joaca Robo atunci când se plictiseşte este un joc inteligent pentru roboţei.
Pe ecranul tabletei lui roboţeşti, sunt N căsuţe de formă pătrată, cu latura egală cu 1. Căsuţele
sunt aşezate pe un rând, una lângă alta, fiind etichetate, ı̂n această ordine, cu numere de la 1 la
N . Fiecare căsuţă conţine câte un număr natural, identificatorul câte unuia dintre prietenii săi,
roboţei, ca şi el. Identificatorii se pot repeta.
Robo poate interschimba conţinutul a două căsuţe, numai dacă distanţa
dintre centrele acestora pe orizontală este egală cu distanţa dintre braţele
sale; distanţa, pe orizontală, dintre centrele a două căsuţe etichetate cu i,
respectiv cu j, este j  i (1 & i $ j & N ).
El ı̂şi poate fixa ı̂n orice moment distanţa dintre braţe la 1 sau ı̂şi poate dubla distanţa curentă
dintre braţe, de oricâte ori este necesar, fără a depăşi valoarea N  1. Astfel, distanţa dintre braţele
sale poate fi 1, apoi, prin dublare, 2, apoi, prin dublare 4, apoi, prin dublare 8 etc. La ı̂nceputul
jocului, distanţa dintre braţele lui Robo este 1. De fiecare dată când consideră convenabilă distanţa
dintre braţe, realizează o interschimbare.
CAPITOLUL 21. ONI 2015 21.3. SORT2DIST 240

Cerinţe
Se cere ca Robo să aşeze identificatorii ı̂n căsuţe ı̂n ordine crescătoare, prin maximum 12 500
interschimbări de tipul celei precizate mai sus.
Date de intrare
Fişierul sort2dist.in conţine:
- pe prima linie numărul natural N , cu semnificaţia din enunţ;
- pe următoarele N linii, N numere, reprezentând, ı̂n această ordine, identificatorii conţinuţi
ı̂n căsuţele tabletei (identificatorul de pe linia i este conţinut de căsuţa i  1).
Date de ieşire
Fişierul sort2dist.out conţine:
- pe prima linie un număr natural M , reprezentând numărul de interschimbări realizate de
Robo (nu neapărat numărul minim de interschimbări necesare);
- pe fiecare dintre următoarele M linii (doar dacă M este nenul), câte două numere naturale,
separate prin câte un spaţiu, reprezentând etichetele căsuţelor al căror conţinut s-a interschimbat,
ı̂n ordinea realizării acestor interschimbări.
Restricţii şi precizări
a 1 & N & 1000;
a identificatorii sunt numere naturale de maximum 30 de cifre;
a pentru 25% din punctaj, fişierele de test conţin numere cu maximum 18 cifre;
a pentru 25% din punctaj, N & 100.

Exemple

sort2dist.in sort2dist.out Explicaţii


4 2 Tableta are 4 căsuţe, conţinând, ı̂n această ordine, identifica-
5 24 torii (5,7,6,2).
7 21 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 fiind 2), identificatorii din căsuţe fiind acum (5, 2,
6, 7);
- s-a interschimbat conţinutul căsuţelor 1 şi 2 (distanţa dintre
centrele lor fiind 1), identificatorii din căsuţe fiind acum (2, 5,
6, 7), ordonaţi crescător.
Tabelul 21.1: sort2dist

Timp maxim de executare/test: 0.4 secunde


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

21.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ă identificăm mai ı̂ntâi poziţia max-
imului din tot şirul (fie aceasta p). Pentru a duce această valoare maximă pe ultima poziţie
(n), calculăm valoarea n  p ş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şadar, 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, identificâ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
aflate pe poziţii vecine, adică la o distanţă egală cu 1 (2 ).
CAPITOLUL 21. ONI 2015 21.3. SORT2DIST 241

21.3.2 Cod sursă

Listing 21.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 21. ONI 2015 21.3. SORT2DIST 242

Listing 21.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 21.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 21. ONI 2015 21.3. SORT2DIST 243

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 21.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 21. ONI 2015 21.3. SORT2DIST 244

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 21.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 21. ONI 2015 21.3. SORT2DIST 245

27 return 1;
28 }
29
30 void schimba(int x, int y)
31 {
32 sol.push_back( make_pair(x, y) );
33 int aux = v[x];
34 v[x] = v[y];
35 v[y] = aux;
36 }
37
38 int main()
39 {
40
41 fin>>n;
42 for (i=1;i<=n;i++)
43 {
44 fin>>s[i];
45 strcpy(w[i], s[i]);
46
47 // v[i] = i;
48 }
49
50 for (i=1;i<n;i++)
51 for (j=i+1;j<=n;j++)
52 {
53 if (myStrcmp(w[i], w[j]) > 0)
54 {
55 strcpy(aux, w[i]);
56 strcpy(w[i], w[j]);
57 strcpy(w[j], aux);
58 }
59 }
60
61 k = 1;
62 for (i=2;i<=n;i++)
63 {
64 if (myStrcmp(w[i], w[k]) != 0)
65 {
66 k++;
67 strcpy(w[k], w[i]);
68 }
69 }
70
71 for (i=1;i<=n;i++)
72 {
73 st = 1;
74 dr = k;
75 while (st <= dr)
76 {
77 mid = (st + dr)/2;
78 if (myStrcmp(s[i], w[mid]) == 0)
79 {
80 v[i] = mid;
81 break;
82 }
83
84 if (myStrcmp(s[i], w[mid]) < 0)
85 dr = mid - 1;
86 else
87 st = mid + 1;
88 }
89 }
90
91 for (i=n;i>=2;i--)
92 {
93 m = v[1];
94 p = 1;
95 for (j=2;j<=i;j++)
96 if (v[j] > m)
97 {
98 m = v[j];
99 p = j;
100 }
101
102 if (p != i)
CAPITOLUL 21. ONI 2015 21.3. SORT2DIST 246

103 {
104 d = i-p;
105
106 putere = 1;
107 last = p;
108 while (d!=0)
109 {
110 if (d%2 == 1)
111 {
112 schimba(last+putere, last);
113 last += putere;
114 }
115 d = d/2;
116 putere = putere*2;
117 }
118 }
119 }
120
121 fout<<sol.size()<<"\n";
122 for (i=0;i<sol.size();i++)
123 {
124 fout<<sol[i].first<<" "<<sol[i].second<<"\n";
125 }
126
127 return 0;
128 }

Listing 21.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 21. ONI 2015 21.3. SORT2DIST 247

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 21.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 21. ONI 2015 21.3. SORT2DIST 248

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 21.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 21. ONI 2015 21.3. SORT2DIST 249

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 21.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 21. ONI 2015 21.3. SORT2DIST 250

66 }
67
68 return 0;
69 }

Listing 21.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 21. ONI 2015 21.3. SORT2DIST 251

21.3.3 *Rezolvare detaliată


Capitolul 22

ONI 2014

22.1 cifre
Problema 1 - cifre 100 de puncte
Maia tocmai a ı̂nvăţat la şcoală să facă adunări cu numere naturale având mai multe cifre.
Pentru că ı̂i place foarte mult matematica s-a apucat să scrie pe o foaie multe numere naturale,
cu una sau mai multe cifre, şi a ı̂nceput să le adune.
După o vreme s-a cam plictisit şi s-a gândit să afle cea mai mare sumă ce s-ar putea obţine
dacă s-ar schimba ı̂ntre ele cifrele numerelor de pe foaie. Are ı̂nsă o singură dorinţă: după ce
schimbă cifrele ı̂ntre ele să rămână acelaşi număr de numere cu o cifră, acelaşi număr de numere
cu două cifre şi aşa mai departe.

Cerinţe

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

Date de ieşire

Fişierul de ieşire cifre.out va conţine pe prima linie un număr natural S reprezentând suma
maximă obţinută. Pe următoarele n linii vor fi scrise n numere naturale, câte un număr pe o
linie, reprezentând un şir de numere pentru care se obţine suma maximă, respectând restricţiile
din enunţ.

Restricţii şi precizări

a 2 & n & 100 000


Numerele din şirul iniţial sunt numere naturale & 2  1
30
a
a Numerele din şirul afişat nu vor conţine zerouri nesemnificative.
a Dacă există mai multe şiruri pentru care se obţine suma maximă conform restricţiilor din
enunţ, se va afişa oricare dintre acestea.
a Pentru afişarea corectă a sumei maxime se acordă 40% din punctaj, punctajul integral
obţinându-se pentru rezolvarea corectă a ambelor cerinţe.

Exemple

252
CAPITOLUL 22. ONI 2014 22.1. CIFRE 253

cifre.in cifre.out Explicaţii


8 14280 Se observă că atât ı̂n şirul iniţial, cât şi ı̂n cel final 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 afişat conţin ı̂n total aceleaşi
50 20 cifre ca numerele din şirul din fiş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

22.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 cifre. Completăm cifra de ordin m  1 a
acestor numere cu cele mai mari cifre disponibile (folosind vectorului nr)
Numerele de m  1 cifre sunt memorate ı̂n ax  1, ax  2, ..., ax  nrm  1
Vom completa acum numerele cifra de ordin m  2 a numerelor a0, ..., ax  nrm  1
ş.a.m.d.
Completarea cifrelor de un anumit rang se va face de la dreapta la stânga, pentru a evita să
punem un 0 pe cea mai semnificativă poziţie a unui număr.
Varianta 2.
Vom construi pe rând fiecare număr ı̂ncepând cu cele având cele mai multe cifre. Pentru a
face acest lucru, mai construim două tablouri suplimentare:
a[i][j][k] = numărul de numere de j cifre care conţin pe poziţia i cifra k
b[i][j] = numărul de numere cu j cifre care nu au ı̂ncă completată cifra de pe poziţia i
Pentru exemplul de mai sus obţinem:

a[0][1][0]=2
a[0][2][0]=3
a[0][3][0]=1
a[0][4][0]=2

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 22. ONI 2014 22.1. CIFRE 254

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-
ficativă 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, verific
ş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 fi memorate ı̂n ordinea crescătoare a numărului de cifre.
Pasul 1
Citesc numerele succesiv şi contorizez ı̂n vectorul nrc numărul de cifre folosite, respectiv ı̂n
vectorul nr numărul de numere pentru fiecare lungime posibilă.
Pasul 2.
Construiesc un vector auxiliar s
int s[LGMAX];
s[1]=1; for (i=2; i<LGMAX; i++) s[i]=s[i-1]+nr[i-1];
Numerele de i cifre sunt plasate ı̂n v de la si la si  nri  1
Pasul 3
Plasez zerourile necesare ı̂n numere de o cifră (acestea sunt singurele numere care pot să ı̂nceapă
cu cifra 0):
for (i=1; i<=nr[1] && nrc[0]; i++) {nrc[0]--; lg[i]=1;}
Plasez celelalte zerouri \ˆin mod echilibrat, p\ˆan\u a le epuizez
for (i=1; i<LGMAX-1; i++) //plasez zerouri pe pozitia i
{if (!nrc[0]) break;
for (j=s[i+1]; j<=n && nrc[0]; j++) lg[j]++, nrc[0]--;
}
CAPITOLUL 22. ONI 2014 22.1. CIFRE 255

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 final 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]; }

22.1.2 Cod sursă

Listing 22.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 22. ONI 2014 22.1. CIFRE 256

53 if (b<m)
54 m=b;
55 a=a-m;
56 b=b-m;
57 return m;
58 }
59
60 void distrib()
61 {
62 int i,j,k,p,q,mx=0;
63
64 for (i=0;i<=m;i++)
65 for (j=i+1;j<=m;j++)
66 b[i][j]=t[j];
67
68 // 0 la cifra unitatilor
69 k=0;
70 for (j=1;j<=m && nr[0]>0;j++)
71 a[0][j][0]=minim(b[0][j],nr[0]);
72
73 // 0 la cifra de pe pozitia i (i=1, cifra zecilor, i=2 cifra sutelor ect)
74 // ATENTIE! numerele de q cifre nu pot avea pe pozitia q-1 cifra 0
75 for (i=1;i<=m && nr[0]>0;i++)
76 for (j=i+2;j<=m;j++)
77 a[i][j][0]=minim(b[i][j],nr[0]);
78
79 // distribuim celelalte cifre (cifrele diferite de 0)
80 for (k=1;k<=9;k++) // cifra
81 for (i=0;i<m && nr[k]>0;i++) // pozitia
82 for (j=i+1;j<=m && nr[k]>0;j++) // nr de cifre
83 {
84 q=minim(b[i][j],nr[k]);
85 if (t>0)
86 {
87 a[i][j][k]=q;
88 sum[i]+=q*k; // cifra k apare de q
89 // ori pe pozitia i
90 // => k*q pe pozitia i in suma
91 p=i; // verificam transportul
92 while (sum[p]>9)
93 {
94 q=sum[p]/10;
95 sum[p]=sum[p]%10;
96 p++;
97 sum[p]+=q;
98 if (p>mx) // mx numarul de cifre din suma
99 mx=p;
100 }
101 }
102 }
103 for (i=mx;i>=0;i--)
104 fout<<sum[i];
105 fout<<"\n";
106 }
107
108 void constr()
109 {
110 int i,j,c,nr,p=0;
111 for (j=m;j>=1;j--) // numarul de cifre
112 while (t[j]>0) // numarul de numere cu j cifre
113 {
114 nr=0;
115 for (i=j-1;i>=0;i--)
116 {
117 c=9;
118 while (a[i][j][c]==0)
119 c--;
120 nr=nr*10+c;
121 a[i][j][c]--;
122 }
123 fout<<nr<<’\n’;
124 p=1;
125 t[j]--;
126 }
127 }
128
CAPITOLUL 22. ONI 2014 22.1. CIFRE 257

129 int main()


130 {
131 citire();
132 distrib();
133 constr();
134 return 0;
135 }

Listing 22.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 22. ONI 2014 22.1. CIFRE 258

66 return 0;
67 }

Listing 22.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 22. ONI 2014 22.1. CIFRE 259

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 22.1.4: cifre sofia.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 22. ONI 2014 22.2. SOLITAR 260

46 if(t<n)
47 t++;
48 else
49 t=1;
50 }
51
52 for(i=1;i<=9;i++)
53 while(cif[i]>0)
54 {
55 if(a[t]<nr[t])
56 {
57 x[t]=p[t]*i+x[t];
58 p[t]*=10;
59 cif[i]--;
60 a[t]++;
61 }
62
63 if(t<n)
64 t++;
65 else
66 t=1;
67 }
68
69 long long s=0;
70 for(i=1;i<=n;i++)
71 s=s+x[i];
72
73 g<<s<<’\n’;
74 for(i=1;i<=n;i++)
75 g<<x[i]<<"\n";
76
77 return 0;
78 }

22.1.3 *Rezolvare detaliată

22.2 solitar
Problema 2 - solitar 100 de puncte
Se consideră un joc de cărţi cu un număr nelimitat de coloane. Iniţial,
pe prima coloană există, ı̂ntr-o ordine oarecare, N cărţi cu numere distincte
din mulţimea r1, 2, ..., N x, următoarele coloane fiind vide (fără cărţi). Numim
secvenţă de la sfârşitul coloanei ultima sau ultimele două sau ultimele trei etc.
cărţi din coloană care au scrise pe ele numere consecutive ı̂n ordine crescătoare,
considerate de jos ı̂n sus.
De exemplu, ı̂n figurile 1 şi 2 sunt reprezentate două astfel de coloane cu câte
6 cărţi având numere ı̂ntre 1 şi 6. În figura 1, secvenţa de la sfârşitul coloanei este
formată doar din cartea 1. În figura 2, secvenţa de la sfârşitul coloanei este formată din cărţile 3,
4 şi 5. Se observă că ı̂n coloana din figura 1 mai există o secvenţă formată din cărţile 2, 3 şi 4,
dar aceasta nu este la sfârşitul coloanei.
Operaţiile permise ale jocului sunt:
A. mutarea secvenţei de cărţi de la sfârşitul unei coloane pe o coloană nouă, dacă acea coloană
este vidă (nu conţine nicio carte);
B. mutarea secvenţei de cărţi de la sfârşitul unei coloane la sfârşitul altei coloane cu cărţi, doar
dacă secvenţa mutată formează o secvenţă de numere consecutive cu cele de pe cartea sau cărţile
aflate la sfârşitul coloanei respective.
Se doreşte ca, printr-un număr minim de operaţii permise, să se obţină pe una dintre coloane
toate numerele de la 1 la N , ı̂n ordine crescătoare, considerate de jos ı̂n sus.
De exemplu, de la configuraţia iniţială din figura 2 se va obţine, printr-o operaţie A, configuraţia
1 de mai jos. Apoi, printr-o operaţie B, se obţine configuraţia 2, printr-o nouă operaţie B se obţine
configuraţia 3, apoi se mută secvenţa 2, 3, 4, 5, 6 pe o coloană vidă (operaţia A), apoi se mută
secvenţa 1 peste secvenţa 2, 3, 4, 5, 6 (operaţia B) şi se obţine, pe coloana a doua, configuraţia
finală cerută.
CAPITOLUL 22. ONI 2014 22.2. SOLITAR 261

Cerinţe

Cunoscând valoarea lui N , precum şi valorile cărţilor de pe prima coloană, să se determine
numărul minim de operaţii prin care se poate obţine secvenţa 1, 2, ..., N pe una dintre coloane.

Date de intrare

Fişierul solitar.in conţine pe prima linie numărul natural N şi pe linia următoare N numere
naturale distincte din mulţimea r1, 2, ..., N x, separate prin câte un spaţiu, date ı̂n ordinea de pe
coloană, de sus ı̂n jos.

Date de ieşire

Fişierul solitar.out va conţine o singură linie pe care va fi scris un număr natural M


reprezentâ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
162543

Timp maxim de executare/test: 0.2 secunde


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

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


3
Soluţie O 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ă.
2
Soluţie O 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 nemaifiind 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ă identifică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 22. ONI 2014 22.2. SOLITAR 262

22.2.2 Cod sursă

Listing 22.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 22. ONI 2014 22.3. TDREPT 263

Listing 22.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 }

22.2.3 *Rezolvare detaliată

22.3 tdrept
Problema 3 - tdrept 100 de puncte
Se consideră N puncte de coordonate ı̂ntregi ı̂n sistemul de coordonate cartezian.

Cerinţe

Scrieţi un program care determină numărul de triunghiuri dreptunghice având vârfurile plasate
ı̂n 3 dintre punctele date şi catetele respectiv paralele cu axele de coordonate.

Date de intrare
CAPITOLUL 22. ONI 2014 22.3. TDREPT 264

Fişierul de intrare tdrept.in conţine pe prima linie numărul natural N , care reprezintă
numărul de puncte.
Pe următoarele N linii se află câte două numere naturale x y, separate prin spaţiu, reprezentând
coordonatele carteziene ale celor N puncte (abscisa şi ordonata).

Date de ieşire

Fişierul de ieşire tdrept.out va conţine o singură linie pe care va fi scris un număr natural
reprezentând numărul de triunghiuri dreptunghice care respectă condiţiile din enunţ. Deoarece
numărul de soluţii poate fi foarte mare, rezultatul va fi afişat modulo 666013 (adică restul ı̂mpărţirii
rezultatului la 666013).

Restricţii şi precizări

a 3 & N & 100 000


a 0 & x, y & 100 000
a Cele N puncte din fişierul de intrare sunt distincte două câte două.

Exemple
tdrept.in tdrept.out Explicaţii
8 5
11 Triunghiurile
14 dreptunghice
10 8 formate
41 sunt:
91
55 (1,1) (1,4) (4,1)
74 (1,1) (9,1) (1,4)
75 (5,5) (7,4) (7,5)
(1,4) (7,4) (7,5)
(1,1) (1,4) (7,4)

Timp maxim de executare/test: 0.3 secunde


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

22.3.1 Indicaţii de rezolvare

prof. Emanuela Cerchez, Colegiul Naţional ”Emil Racoviţă” Iaşi

3
Soluţie O N  - 15 puncte
Construim toate tripletele de N puncte distincte şi verificăm dacă cele 3 puncte reprezintă
vârfurile unui triunghi dreptunghic cu laturile respectiv paralele cu axele.
2
Soluţie O N logN  - 38 puncte
Sortez punctele crescător după abscisă. Dacă sunt mai multe puncte cu aceeaşi abscisă, vor fi
sortate crescător după ordonată.
Aleg toate perechile de puncte şi consider că segmentul format de punctele respective este
ipotenuza unui triunghi dreptunghic. Pentru a verifica dacă acest lucru se ı̂ntâmplă, caut binar ı̂n
vectorul sortat de puncte dacă există cele două puncte care ar putea fi al treilea vârf al triunghiului
dreptunghic respectiv.
Soluţie O N logN  - 65 puncte
Sortăm punctele după abscisă, iar ı̂n alt vector sortăm punctele după ordonată.
Parcurgem abscisele şi numărăm perechile de puncte cu aceeaşi abscisă. Pentru fiecare dintre
acestea, căutăm binar ordonatele lor ı̂n al doilea vector, contorizând triunghiurile dreptunghice
care se formează.
Soluţie O N  - 100 puncte
Pentru fiecare abscisă contorizez numărul de puncte având abscisa respectivă
CAPITOLUL 22. ONI 2014 22.3. TDREPT 265

nrxi = numărul de puncte având abscisa i


În mod similar, pentru fiecare 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 fi egal cu nrxxp  1 ˜ nry yp  1 (am scăzut 1 pentru a nu contoriza
ca triunghi şi punctul ı̂nsuşi).

22.3.2 Cod sursă

Listing 22.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;
CAPITOLUL 22. ONI 2014 22.3. TDREPT 266

58 }
59 if (dr<n+1 && a.x==p[dr].x && a.y==p[dr].y) return 1;
60 return 0;
61 }

Listing 22.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 22.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];
CAPITOLUL 22. ONI 2014 22.3. TDREPT 267

18
19 int n, nr;
20
21 void citire();
22 int verif (int, int, int);
23
24 int main()
25 {int i, j, k;
26 citire();
27 for (i=1; i<n-1; i++)
28 for (j=i+1; j<n; j++)
29 for (k=j+1; k<=n; k++)
30 nr=(nr+verif(i,j,k))%MOD;
31 fout<<nr<<’\n’;
32 fout.close();
33 return 0;
34 }
35
36 void citire()
37 {int i;
38 fin>>n;
39 for (i=1; i<=n; i++)
40 fin>>p[i].x>>p[i].y;
41 }
42
43 int verif(int i, int j, int k)
44 {
45 if (p[i].x==p[j].x && (p[j].y==p[k].y || p[i].y==p[k].y))
46 return 1;
47 if (p[i].x==p[k].x && (p[j].y==p[k].y || p[i].y==p[j].y))
48 return 1;
49 if (p[k].x==p[j].x && (p[j].y==p[i].y || p[i].y==p[k].y))
50 return 1;
51 return 0;
52 }

Listing 22.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();
CAPITOLUL 22. ONI 2014 22.3. TDREPT 268

38
39 sort (px+1, px+n+1,cmpx);
40 sort (py+1, py+n+1,cmpy);
41
42 poz=cautbin(px[1].y);
43 for (nr1=0; px[1].y==py[poz].y; nr1++, poz++);
44
45 lg=1; v[lg]=nr1-1;
46 for (i=2; i<=n+1; i++)
47 {
48 poz=cautbin(px[i].y);
49 for (nr1=0; px[i].y==py[poz].y; nr1++, poz++);
50 if (px[i].x==px[i-1].x)
51 v[++lg]=nr1-1;
52 else
53 {
54 for (j=1; j<lg; j++)
55 for (k=j+1; k<=lg; k++)
56 nr=(nr+v[j]+v[k])%MOD;
57 lg=1; v[lg]=nr1-1;
58 }
59 }
60
61 fout<<nr<<’\n’;
62 fout.close();
63 return 0;
64 }
65
66 void citire()
67 {int i;
68 fin>>n;
69 for (i=1; i<=n; i++)
70 {fin>>px[i].x>>px[i].y;
71 py[i]=px[i];}
72 }
73
74 int cautbin(int a)
75 {int st=0, dr=n+1, mij;
76 while (dr-st>1)
77 {
78 mij=(st+dr)/2;
79 if (py[mij].y<a) st=mij;
80 else dr=mij;
81 }
82 if (dr<n+1 && a==py[dr].y) return dr;
83 return 0;
84 }

Listing 22.3.5: tdrept n sofia.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;
CAPITOLUL 22. ONI 2014 22.3. TDREPT 269

26 for(i=1;i<=n;i++)
27 {
28 sol+=1ll*(x[v[i].x]-1)*(y[v[i].y]-1)%666013 ;
29 if(sol>=666013)
30 sol-=666013;
31 }
32
33 g<<sol;
34 return 0;
35 }

Listing 22.3.6: tdrept n2logn sofia.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;
CAPITOLUL 22. ONI 2014 22.3. TDREPT 270

63 }
64
65 g<<sol;
66 return 0;
67 }

22.3.3 *Rezolvare detaliată


Capitolul 23

ONI 2013

23.1 amestec
Problema 1 - amestec 100 de puncte
Se consideră un amestec de două substanţe, ale căror molecule se notează cu 0, respectiv 1,
reprezentat ca o matrice pătratică cu n linii şi n coloane.
În vederea separării celor două substanţe, asupra amestecului se aplică succesiv o serie de k
forţe magnetice, caracterizate de următoarele trei mărimi:
a durata aplicării forţei, notată cu di (1 & i & k) exprimată ı̂n secunde;
a poziţia aplicării forţei, notată cu pi {’N’, ’S’, ’E’, ’V’}, 1 & i & k, ce reprezintă unul dintre
cele patru puncte cardinale (Nord, Sud, Est, Vest);
a tipul moleculelor (0 sau 1) asupra cărora acţionează forţa, notat cu mi , 1 & i & k.

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 semnificaţia de mai sus. Pe fiecare din următoarele n linii se găseşte câte un
şir de n caractere 0 sau 1. Pe fiecare dintre următoarele k linii se găsesc câte 3 valori, după cum
urmează: un număr natural di , un caracter pi (’N’, ’S’, ’E’, ’V’) şi un număr natural mi , 1 & i & k,
având semnificaţia de mai sus, neseparate prin spaţiu.

Date de ieşire

Fişierul de ieşire amestec.out va conţine matricea amestecului final. Pe fiecare din cele n linii
ale fişierului de ieşire se va scrie câte un şir de n caractere 0 sau 1, neseparate prin spaţiu.

271
CAPITOLUL 23. ONI 2013 23.2. EOLIENE 272

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
32 111 Matricea moleculelor are 3 linii şi 3 coloane.
011 110 Se aplică un număr de k 2 forţe, prima cu durata de 1
101 100 secundă, spre nord, şi care atrage moleculele de tip 1, şi a
110 doua cu durata de 2 secunde, spre est, ce atrage moleculele de
1N1 tip 0. După aplicarea celor 2 forţe, moleculele se vor reaşeza
2E0 conform matricei alăturate.

Timp maxim de executare/test: 0.5 secunde


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

23.1.1 *Indicaţii de rezolvare

23.1.2 *Cod sursă

23.1.3 *Rezolvare detaliată

23.2 eoliene
Problema 2 - eoliene 100 de puncte
Primarul oraşului Oradea intenţionează să instaleze N turbine eoliene cu câte
trei pale (imaginea alăturată) pentru a produce ecologic, cu costuri minime, energia
electrică necesară locuitorilor oraşului. Conform planului primarului, cele N turbine
eoliene (numerotate cu 1, 2, 3, ..., N ) vor fi montate ı̂n linie dreaptă, paralel cu
şoseaua care leagă Oradea de Băile Felix, la distanţe nu neapărat egale unele de
altele. Prima turbină se va instala la distanţa D1 faţă de Oradea, a doua la distanţa
D2 faţă de Oradea, ..., a N -a turbină la distanţă DN faţă de Oradea. Palele turbinelor
sunt poziţionate, ı̂n acelaşi plan, paralel cu şoseaua. Sub acţiunea vântului, palele
turbinelor se rotesc ı̂n jurul nacelei (imaginile următoare), vitezele de rotaţie putând fi diferite de
la o turbină la alta.

Primarul a achiziţionat turbinele şi a angajat echipa inginerului Eol pentru a le construi
fundaţiile şi pentru a le instala. După construirea fundaţiilor, ı̂nainte de instalare, inginerul
Eol a studiat turbinele şi a constatat că:
a turbina 1 are cele trei pale identice de lungime L1 , turbina 2 are cele trei pale identice de
lungime L2 , ..., turbina N are cele trei pale identice de lungime LN iar lungimile L1 , L2 , ..., LN nu
sunt toate egale, o parte dintre turbine având palele cu lungimi diferite faţă de celelalte turbine;
a pilonii celor N turbine sunt identici;
CAPITOLUL 23. ONI 2013 23.2. EOLIENE 273

a dacă vor instala turbinele conform planului, atunci pot fi turbine care ı̂şi pot lovi palele ı̂n
timpul rotirii şi astfel se vor strica.
În concluzie, inginerul Eol va trebui să determine numărul minim M de turbine care pot
fi eliminate din planul primarului, astfel ı̂ncât oricare două turbine dintre cele rămase să nu-şi
lovească palele ı̂n timpul funcţionării (palele a două turbine se lovesc dacă se ating chiar şi ı̂ntr-un
punct), orice valori ar avea vitezele lor de rotaţie.

Cerinţe

Scrieţi un program care să citească numerele naturale N , D1 , D2 , ..., DN , L1 , L2 , ..., LN (cu
semnificaţia din enunţ) şi să determine numărul minim M de turbine ce pot fi eliminate din
planul primarului astfel ı̂ncât oricare două turbine alăturate din cele rămase să nu-şi lovească
palele ı̂n timpul funcţionării.

Date de intrare

Fişierul de intrare eoliene.in conţine pe prima linie numărul natural N . A doua linie conţine
cele N numere naturale D1 , D2 , ..., DN separate prin câte un spaţiu. A treia linie conţine cele N
numere naturale L1 , L2 , ..., LN , separate prin câte un spaţiu, cu semnificaţia din enunţ.

Date de ieşire

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
1554522

Explicaţii:
Sunt N 7 turbine. ı̂n planul primarului ele figurează astfel:
turbine 1 2 3 4 5 6 7
distanţe D1 27 D2 9 D3 28 D4 37 D5 3 D6 54 D7 50
lungime pale L1 1 L2 5 L3 5 L4 4 L5 5 L6 2 L7 2
Palele perechilor de turbine (2,5), (1,3), (3,4) şi (6,7) se vor lovi.Astfel, se vor elimina minimum
M 3 turbine (turbinele 2, 3 şi 6 sau 2,3 şi 7 sau 5,3 şi 6 sau 5,3 şi 7).

Timp maxim de executare/test: 0.5 secunde


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

23.2.1 *Indicaţii de rezolvare

23.2.2 *Cod sursă

23.2.3 *Rezolvare detaliată


CAPITOLUL 23. ONI 2013 23.3. SUDOKU1 274

23.3 sudoku1
Problema 3 - sudoku1 100 de puncte
Numim tablou Sudoku o matrice cu n  n elemente ce conţine doar cifrele 1, 2 şi 3 astfel ı̂ncât
ı̂n fiecare pătrat format din 2  2 elemente alăturate să existe toate cele 3 cifre şi oricare două
elemente alăturate pe linie sau pe coloană să fie distincte.
Fiecărui tablou Sudoku i se asociază un număr obţinut prin scrierea
cifrelor ı̂n ordine, ı̂ncepând cu prima linie.
De exemplu, tabloul Sudoku din imaginea alăturată are asociat numărul:
2132132132213211321321321.
Se defineşte şirul S n ca fiind un şir ordonat, format din toate tablourile
Sudoku cu n  n elemente, {s1 , s2 , s3 , ...} . Pentru orice pereche si , sj 
din S n cu i $ j, numărul asociat tabloului Sudoku si este strict mai mic
decât numărul asociat tabloului Sudoku sj .
Pentru n 2, şirul S 2 conţine, ı̂n ordine, tablourile Sudoku:

Cerinţe

Date fiind două numere naturale n şi k să se determine:


a) numărul tablourilor Sudoku din şirul S n;
b) tabloul Sudoku aflat 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 aflat 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 fişierul de ieşire există
un răspuns pentru prima cerinţă, indiferent de corectitudinea acestuia.

Exemple
sudoku1.in sudoku1.out Explicaţii
26 12 şirul S(2) conţine 12 tablouri Sudoku, pe poziţia 6 ı̂n şir
21 aflându-se tabloul:
32 21
32

Timp maxim de executare/test: 0.3 secunde


Memorie: total 4 MB din care pentru stivă 4 MB
Dimensiune maximă a sursei: 15 KB
CAPITOLUL 23. ONI 2013 23.3. SUDOKU1 275

23.3.1 *Indicaţii de rezolvare

23.3.2 *Cod sursă

23.3.3 *Rezolvare detaliată


Capitolul 24

ONI 2012

24.1 alune
Problema 1 - alune 100 de puncte
Chip şi Dale s-au plictisit de jocurile de până acum şi au hotărât că este
timpul să ı̂mbine culesul alunelor cu un joc care să le stimuleze inteligenţa.
Chip propune: ”eu pun alunele culese de mine ı̂ntr-un şir de C scorburi, iar
tu pui alunele culese de tine ı̂ntr-un alt şir, de D scorburi”.
Dale a ascultat, a fost de acord şi a propus ca jocul să continue astfel:
”dacă la ı̂mpărţirea numărului de alune din prima scorbură a şirului meu
la numărul de alune din fiecare scorbură a şirului tău se obţine acelaşi rest,
atunci consider că scorbura mea este umplută corect şi scriu pe hârtie cifra 1, altfel o consider
umplută incorect şi scriu cifra 0. Verific apoi, aplicând aceeaşi regulă, dacă a doua scorbură din
şirul meu este umplută corect, adică dacă la ı̂mpărţirea numărului de alune din aceasta la numărul
de alune din fiecare scorbură din şirul tău, se obţine acelaşi rest. Notez pe hârtie, ı̂n continuare,
rezultatul verificării (0 sau 1). ı̂ncheiem jocul atunci când terminăm de verificat, după această
regulă, toate cele D scorburi ale mele.”

Cerinţe

Scrieţi un program care citeşte din fişierul alune.in numerele naturale nenule C şi D şi numărul
de alune din fiecare scorbură din şirul lui Chip, respectiv al lui Dale. Programul determină şirul
de cifre notat de Dale pe hârtie.

Date de intrare

Fişierul alune.in conţine pe prima linie cele două numere naturale, C şi D, pe a doua linie
C numere naturale, reprezentând numărul de alune din fiecare scorbură a lui Chip, iar pe a treia
linie D numere naturale, reprezentând numărul de alune din fiecare scorbură a lui Dale. Toate
numerele situate pe aceeaşi linie a fişierului sunt separate prin câte un spaţiu.

Date de ieşire

Fişierul alune.out conţine o singură linie pe care se află şirul determinat. Cifrele din acest şir
nu sunt separate prin spaţii.

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 fiş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 fişierului de intrare, sunt
numere naturale din intervalul [0, 2 000 000 000].

Exemple

276
CAPITOLUL 24. ONI 2012 24.1. ALUNE 277

alune.in alune.out Explicaţii


32 01 Prima scorbură a lui Dale este umplută incorect, deoarece resturile
345 ı̂mpărţirii lui 8 la numerele 3, 4 şi 5 sunt diferite, deci rezultatul
82 verifică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 verifică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

24.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 fişierului
de intrare, notat cu M . Dau acelaşi rest la ı̂mpărţirea la toate numerele de pe această linie
doar acele numere de pe linia a treia care sunt de forma M ˜ k  r, pentru care r este cuprins
ı̂ntre 0 şi M in  1 (unde M in este minimul valorilor de pe a doua linie a fişierului de intrare).
Trebuie acordată atenţie detaliilor de implementare (de exemplu se observă că nu este necesară
calcularea celui mai mic multiplu comun dacă acesta depăşeşte valorile reprezentabile pe 64 de
biţi). Complexitatea ı̂n timp unei soluţii pe această idee este O C ˜ log V M AX   D şi permite
obţinerea a 100 de puncte.

24.1.2 Cod sursă

Listing 24.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++)
CAPITOLUL 24. ONI 2012 24.1. ALUNE 278

38 {
39 fscanf(f,"%d",&X);
40 m = minim(m, X);
41 if (depaseste)
42 continue;
43 mc = mc / cmmdc(mc, X) * X;
44 if (mc > MAX)
45 {
46 depaseste = 1;
47 }
48 }
49
50 for (;Q;--Q)
51 {
52 ok = 1;
53 fscanf(f,"%d",&q);
54 if (depaseste)
55 {
56 if (q >= m)
57 {
58 ok = 0;
59 }
60 }
61 else
62 {
63 if (q % mc >= m)
64 {
65 ok = 0;
66 }
67 }
68 fprintf(g,"%d",ok);
69 }
70
71 fprintf(g,"\n");
72 fclose(g);
73 fclose(f);
74 return 0;
75 }

Listing 24.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;
CAPITOLUL 24. ONI 2012 24.1. ALUNE 279

35 g<<ok;
36 }
37 }
38
39 g<<’\n’;
40 f.close();
41 g.close();
42 return 0;
43 }

Listing 24.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 24.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];
CAPITOLUL 24. ONI 2012 24.1. ALUNE 280

11 int N, Q, a, i, j, ok, r, s;
12
13
14 int main()
15 {
16 FILE *f = fopen("alune.in","r");
17 FILE *g = fopen("alune.out","w");
18
19 fscanf(f,"%d %d",&N, &Q);
20 for (i=1;i<=N;i++)
21 fscanf(f,"%d",&A[i]);
22
23 for (i=1;i<=Q;i++)
24 fscanf(f,"%d",&B[i]);
25
26 sort(A+1, A+N+1);
27
28 a = 1;
29 for (i=2;i<=N;i++)
30 if(A[i] != A[a])
31 {
32 A[++a] = A[i];
33 }
34
35 for (i=1;i<=Q;i++)
36 {
37 ok = 1;
38 r = B[i]%A[1];
39 for (j=2;j<=a;j++)
40 if(B[i]%A[j] != r)
41 {
42 ok = 0;
43 break;
44 }
45 S[s++] = (ok == 1 ? ’1’ : ’0’);
46 }
47
48 S[s] = 0;
49 fprintf(g,"%s\n",S);
50 fclose(f);
51 return 0;
52 }

Listing 24.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
CAPITOLUL 24. ONI 2012 24.1. ALUNE 281

31
32 int main()
33 {
34 FILE *f = fopen("alune.in","r");
35 FILE *g = fopen("alune.out","w");
36
37 fscanf(f,"%d %d",&N, &Q);
38 for (i=1;i<=N;i++)
39 fscanf(f,"%d",&A[i]);
40 for (i=1;i<=Q;i++)
41 {
42 fscanf(f,"%d",&B[i].q);
43 B[i].p = i;
44 }
45
46 sort(A+1, A+N+1);
47
48 a = 1;
49 for (i=2;i<=N;i++)
50 if(A[i] != A[a])
51 {
52 A[++a] = A[i];
53 }
54
55 sort(B+1, B+Q+1, cmp1);
56
57
58 for (i=1;i<=Q;i++)
59 {
60 if (i!=1 && B[i].q == B[i-1].q)
61 {
62 B[i].v = B[i-1].v;
63 continue;
64 }
65 ok = 1;
66 r = B[i].q%A[1];
67 for (j=2;j<=a;j++)
68 if(B[i].q%A[j] != r)
69 {
70 ok = 0;
71 break;
72 }
73 B[i].v = ok;
74 }
75
76 sort(B+1, B+Q+1, cmp2);
77
78 for (i=1;i<=Q;i++)
79 {
80 S[s++] = (B[i].v == 1 ? ’1’ : ’0’);
81 }
82
83 S[s] = 0;
84 fprintf(g,"%s\n", S);
85
86 fclose(g);
87 fclose(f);
88 return 0;
89 }

Listing 24.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;
CAPITOLUL 24. ONI 2012 24.1. ALUNE 282

14
15 int main()
16 {
17 FILE *f = fopen("alune.in","r");
18 FILE *g = fopen("alune.out","w");
19
20 fscanf(f,"%d %d\n",&N, &Q);
21
22 fgets(buff, 1000000, f);
23 p = buff;
24
25 for (i=1;i<N;i++)
26 {
27 u = strchr(p, ’ ’);
28 A[i] = atoi(p);
29 p = u+1;
30 }
31 A[N] = atoi(p);
32
33 fgets(buff, 1000000, f);
34 p = buff;
35
36 for (i=1;i<Q;i++)
37 {
38 u = strchr(p, ’ ’);
39 B[i] = atoi(p);
40 p = u+1;
41 }
42 B[Q] = atoi(p);
43
44 for (i=1;i<=Q;i++)
45 fscanf(f,"%d",&B[i]);
46
47 sort(A+1, A+N+1);
48
49 a = 1;
50 for (i=2;i<=N;i++)
51 if(A[i] != A[a])
52 {
53 A[++a] = A[i];
54 }
55
56 for (i=1;i<=Q;i++)
57 {
58 ok = 1;
59 r = B[i]%A[1];
60 for (j=2;j<=a;j++)
61 if(B[i]%A[j] != r)
62 {
63 ok = 0;
64 break;
65 }
66 S[s++] = (ok == 1 ? ’1’ : ’0’);
67 }
68
69 S[s] = 0;
70 fprintf(g,"%s\n",S);
71 fclose(f);
72 return 0;
73 }

Listing 24.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)
CAPITOLUL 24. ONI 2012 24.1. ALUNE 283

13 {
14 r = a % b;
15 a = b;
16 b = r;
17 }
18 return a;
19 }
20
21 int main()
22 {
23 freopen("alune.in","r",stdin);
24 freopen("alune.out","w",stdout);
25
26 scanf("%d %d",&N,&Q);
27 cmmmc = 1LL;
28 minim = INF;
29 for (i=1; i<=N; ++i)
30 {
31 scanf("%d",&x);
32 if (x < minim)
33 minim = x;
34 cmmmc = 1LL * x * cmmmc / cmmdc(x, cmmmc);
35 if (cmmmc > INF)
36 cmmmc = INF;
37 }
38
39 for (i=1; i<=Q; ++i)
40 {
41 scanf("%d",&x);
42 if (x % cmmmc < minim)
43 printf("1");
44 else
45 printf("0");
46 }
47
48 printf("\n");
49 return 0;
50 }

Listing 24.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");
CAPITOLUL 24. ONI 2012 24.2. CUBURI 284

35 ofstream g("alune.out");
36
37 // fscanf(f,"%d %d",&N, &Q);
38 f >> N >> Q;
39
40 // fscanf(f,"%d",&X);
41 f>>X;
42
43 mc = m = X;
44 depaseste = 0;
45 for (i = 2; i<=N; i++)
46 {
47 // fscanf(f,"%d",&X);
48 f>>X;
49 m = minim(m, X);
50 if (depaseste)
51 continue;
52 mc = mc / cmmdc(mc, X) * X;
53 if (mc > MAX)
54 {
55 depaseste = 1;
56 }
57 }
58
59 for (;Q;--Q)
60 {
61 ok = 1;
62 // fscanf(f,"%d",&q);
63 f>>q;
64 if (depaseste)
65 {
66 if (q >= m)
67 {
68 ok = 0;
69 }
70 }
71 else
72 {
73 if (q % mc >= m)
74 {
75 ok = 0;
76 }
77 }
78 // fprintf(g,"%d",ok);
79 g<<ok;
80 }
81
82 // fprintf(g,"\n");
83 g<<"\n";
84 g.close();
85 f.close();
86 // fclose(g);
87 // fclose(f);
88 return 0;
89 }

24.1.3 *Rezolvare detaliată

24.2 cuburi
Problema 2 - cuburi 100 de puncte
Ionuţ a ı̂nvăţat la şcoală să lucreze cu numere mari. El are la dispoziţie un şir de N numere
naturale nenule. Din fiecare număr el şterge exact trei cifre, fără să schimbe ordinea cifrelor
rămase, astfel ı̂ncât să obţină cel mai mic număr natural nenul posibil.
De exemplu, din numărul 20731049 se
obţine numărul 20049, iar din numărul 13004
se obţine numărul 10. Înlocuind fiecare număr
citit cu numărul obţinut prin operaţia de mai
CAPITOLUL 24. ONI 2012 24.2. CUBURI 285

sus, Ionuţ obţine un nou şir şi scrie termenii


acestuia pe feţele unor cuburi astfel: primele şase numere din şir le scrie pe primul cub şi ı̂l
notează 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 figura 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 fiecare linie a fişierului numerele sunt separate prin câte
un spaţiu.

Date de ieşire

Fişierul cuburi.out are două linii: prima linie conţine numărul de piramide complete care
au fost construite, iar a doua linie conţine toate numerele scrise pe cuburile ce formează primele
K piramide. Numerele sunt scrise separate prin câte un spaţiu, ı̂n ordinea apariţiei lor ı̂n şirul
nou obţinut.

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 fiş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 insuficient, numerele nu apar
26080 90280 scrise pe exact două linii, ca ı̂n fiş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
CAPITOLUL 24. ONI 2012 24.2. CUBURI 286

24.2.1 Indicaţii de rezolvare

prof. Daniela Tarasă - C.N. ”Gheorghe Vrânceanu” Bacău

Pentru determinarea numerelor obţinute prin modificare, se poate proceda ı̂n mai multe mod-
uri.
1. Presupunem că au rămas q cifre de şters, iar ultima cifră adăugată stă pe poziţia last ı̂n
vectorul ce reţine cifrele iniţiale ale numărului. Următoarea cifră adăugată la numărul nou, se va
afla pe una din pozitiile din intervalul last  1...last  q  1. Se caută cifra minimă situată pe aceste
poziţii, iar ı̂n caz de egalitate se alege prima. Notam poziţia acestei cifre cu minim. Toate cifrele
de la last  1 la minim  1 vor fi şterse, cu alte cuvinte, rămân de şters q  minim  last  1.
Noua ultimă poziţie devine minim, deci la pasul următor last minim.
Se tratează separat cazul primei cifre care se alege ca cifră minimă nenulă din primele 4 ale
numarului.
2. Folosim o stivă ı̂n care introducem pe rând cifrele numărului ı̂n ordine, de la stânga la
dreapta. Cât timp cifra curentă este mai mică decât cea din vârful stivei, se scoate o cifră din
stivă. Apoi, cifra curentă se plasează ı̂n vârful stivei. Odată ce s-au scos 3 cifre din stivă, cifrele
numărului care au rămas neanalizate se vor pune ı̂n stivă. Numerele rămase ı̂n stivă reprezintă
soluţia. Trebuie acordată atenţie detaliilor de implementare ı̂n vederea evitării soluţiilor ce ı̂ncep
cu 0.
3. Construim toate numerele ce se pot obţine prin eliminarea ı̂n toate modurile a 3 cifre.
Acest lucru ı̂l putem realiza fie cu un algoritm gen backtracking (de generare a combinărilor de
3 elemente dintr-o mulţime cu C elemente - unde C reprezintă şirul cifrelor numărului), fie prin
fixarea lor pe poziţii ı̂n toate modurile posibile prin 3 foruri.
Pentru a determina câte piramide complete a construit se observă că numărul cuburilor din
fiecare piramidă determină un şir format din primele pătrate perfecte ı̂ncepând cu 1, sau se poate
utiliza formula an an1  2 ˜ n  1 plecând de la a1 1.
Pentru ultima cerinţă se vor fi afişa primele k k  1 2k  1 numere obţinute după modificări.

24.2.2 Cod sursă

Listing 24.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)
CAPITOLUL 24. ONI 2012 24.2. CUBURI 287

32 {
33 Min=v[in];imin=in;
34 for(i=in;i<=sf;i++)
35 if(v[i]<Min)
36 {
37 Min=v[i];
38 imin=i;
39 }
40 nr=nr*10+Min;
41 in=imin+1;sf++;
42 }
43 return nr;
44 }
45
46 int main()
47 {
48 int x,nrc,n,nrpir=0,i,k,j=1,nr=0,s=0;
49 f>>n>>k;
50
51 for(i=1;i<=k;i++)
52 nr=nr+i*i*6;
53
54 for(i=1;i<=n;i++)
55 {
56 f>>x;
57 nrc=(int)log10(x)+1;
58 if(i%6==0 && (sqrt(i/6)==(int)sqrt(i/6)))
59 {
60 s=s+i;
61 if(s<=n)
62 nrpir++;
63 }
64
65 if(i<=nr)
66 w[j++]=nrmin(x,nrc);
67 }
68
69 g<<nrpir<<’\n’;
70 for(i=1;i<j;i++)
71 g<<w[i]<<’ ’;
72 g<<’\n’;
73
74 f.close();
75 g.close();
76 return 0;
77 }

Listing 24.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;
CAPITOLUL 24. ONI 2012 24.2. CUBURI 288

27 }
28
29 int nr=Min;
30 in=imin+1;
31 sf=5;
32 while(sf<=nrc)
33 {
34 Min=v[in];imin=in;
35 for(i=in;i<=sf;i++)
36 if(v[i]<Min)
37 {
38 Min=v[i];
39 imin=i;
40 }
41 nr=nr*10+Min;
42 in=imin+1;sf++;
43 }
44 return nr;
45 }
46
47
48 int main()
49 {
50 freopen("cuburi.in","r",stdin);
51 freopen("cuburi.out","w",stdout);
52
53 int x,nrc,n,nrpir=0,i,k,j=1,nr=0,s=0;
54
55 scanf("%d %d",&n, &k);
56 // f>>n>>k;
57 for(i=1;i<=k;i++)
58 nr=nr+i*i*6;
59
60 for(i=1;i<=n;i++)
61 {
62 // f>>x;
63 scanf("%d",&x);
64 nrc=(int)log10(x)+1;
65
66 if(i%6==0 && (sqrt(i/6)==(int)sqrt(i/6)))
67 {
68 s=s+i;
69 if(s<=n)
70 nrpir++;
71 }
72 if(i<=nr) w[j++]=nrmin(x,nrc);
73 }
74
75 printf("%d\n",nrpir);
76 // g<<nrpir<<’\n’;
77 for(i=1;i<j;i++)
78 printf("%d ",w[i]);
79 // g<<w[i]<<’ ’;
80 // g<<’\n’;
81 printf("\n");
82
83 return 0;
84 }

Listing 24.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
CAPITOLUL 24. ONI 2012 24.2. CUBURI 289

15 int f(int *, int);


16
17 FILE *fin = fopen("cuburi.in","r");
18 FILE *fout = fopen("cuburi.out","w");
19
20 int main()
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;
CAPITOLUL 24. ONI 2012 24.2. CUBURI 290

91 for (p=c;p>=1;p--)
92 if (p!=i && p!=j && p!=k)
93 nr = nr * 10 + C[p];
94 if (nr < mn)
95 mn = nr;
96 }
97 }
98 return mn;
99 }

Listing 24.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 }
CAPITOLUL 24. ONI 2012 24.2. CUBURI 291

64
65 N/=6;
66 i = 1;
67 while (i*i <= N)
68 {
69 N -= i*i;
70 ++i;
71 }
72
73 printf("%d\n", i-1);
74
75 int fete = K * (K+1) * (2*K+1);
76
77 for (i=1; i< fete; ++i)
78 printf("%d ", A[i]);
79 printf("%d\n", A[fete]);
80
81 return 0;
82 }

Listing 24.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()
CAPITOLUL 24. ONI 2012 24.2. CUBURI 292

54 {
55 freopen("cuburi.in","r",stdin);
56 freopen("cuburi.out","w",stdout);
57
58 scanf("%d %d\n",&N,&K);
59 for (i=1; i<=N; ++i)
60 {
61 scanf("%d", &x);
62 A[i] = g(x);
63 }
64
65 N/=6;
66 i = 1;
67 while (i*i <= N)
68 {
69 N -= i*i;
70 ++i;
71 }
72
73 printf("%d\n", i-1);
74
75 int fete = K * (K+1) * (2*K+1);
76
77 for (i=1; i< fete; ++i)
78 printf("%d ", A[i]);
79 printf("%d ", A[fete]);
80 printf("10\n");
81 return 0;
82 }

Listing 24.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;
CAPITOLUL 24. ONI 2012 24.2. CUBURI 293

44 }
45 }
46
47 int main()
48 {
49 long z[100000]={0},w,u,k,x,y;
50 long n,m,i,j,nrp,nrc,p,v;
51
52 FILE *f,*g;
53 f=fopen("cuburi.in","r");
54 g=fopen("cuburi.out","w");
55
56 j=u=0;
57 fscanf(f,"%d %d",&n,&k);
58 m=k*(k+1)*(2*k+1);
59 for(i=1;i<=m;i++)
60 {
61 fscanf(f,"%ld",&x);
62 transf(x,y);
63 z[i]=y;
64 }
65
66 nrp=0;
67 v=0;
68 p=0;
69 do
70 {
71 p++;
72 w=6*p*p;
73 if(v+w<=n)
74 {
75 nrp++;
76 v=v+w;
77 }
78 else
79 break;
80 } while(v<=n);
81
82 fprintf(g,"%d\n",nrp);
83
84 for(i=1;i<m;i++)
85 fprintf(g,"%ld ",z[i]);
86
87 fprintf(g,"%ld\n",z[m]);
88
89 return 0;
90 }

Listing 24.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++)
CAPITOLUL 24. ONI 2012 24.2. CUBURI 294

26 {
27 fscanf(fin,"%d",&V[i]);
28
29 X = V[i];
30 c = 0;
31 while (X)
32 {
33 C[++c] = X%10;
34 X/=10;
35 }
36 memcpy(D, C, sizeof(C));
37 d = c;
38
39 mi = INF;
40 for (k = 0; k<=3; k++)
41 {
42 if (k == 3)
43 mi = minim(mi, f(C, c, 3));
44 else
45 if (k == 2)
46 mi = minim(mi, f(C, c-1, 2));
47 else
48 if (k == 1)
49 mi = minim(mi, f(C, c-2, 1));
50 else
51 mi = minim(mi, f(C, c-3, 0));
52
53 memcpy(C, D, sizeof(C));
54 c = d;
55 }
56
57 W[i] = mi;
58 }
59 fclose(fin);
60
61 M = N;
62 sum = 0;
63 for (B = 1;;B+=2)
64 {
65 cc += B*6;
66 sum += cc;
67 if (sum <= M)
68 {
69 if (K == B/2 + 1)
70 sm = sum;
71 cub++;
72 }
73 else
74 {
75 sum -= cc;
76 break;
77 }
78 }
79
80 fprintf(fout,"%d\n",cub);
81 for (i=1;i<=sm;i++)
82 fprintf(fout,"%d ",W[i]);
83
84 return 0;
85 }
86
87 int f(int *C, int c, int k)
88 {
89 int v = c; int i;
90 for (i=c-1;i>=1;i--)
91 {
92 while (k>0 && C[i]<C[v] && v<=c)
93 {
94 if (C[i] == 0 && v == c)
95 break;
96 v++;
97 k--;
98 }
99 C[--v] = C[i];
100 }
101
CAPITOLUL 24. ONI 2012 24.3. OPTIM 295

102 while (k)


103 {
104 v++;
105 k--;
106 }
107
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 }

24.2.3 *Rezolvare detaliată

24.3 optim
Problema 3 - optim 100 de puncte
Gigel primea de la mama lui, ca temă, o foaie pe care era scris un şir
de N numere ı̂ntregi. Singurul calcul pe care ştia să ı̂l facă până acum era
suma tuturor numerelor. Pentru aceasta el plasa N  1 semne de adunare,
+, ı̂ntre numerele aflate pe poziţii consecutive ı̂n şir şi calcula astfel suma
acestor numere. ı̂ntre timp a crescut şi a ı̂nvăţat şi operaţia de ı̂nmulţire
pentru care foloseşte semnul *. Din şirul celor N  1 semne de adunare, ı̂i
trece prin minte să ı̂nlocuiască K semne + cu K semne *.
Îşi dă seama că tema se complică, deoarece ı̂nmulţirile trebuie efectuate ı̂naintea adunărilor,
dar nu se dă bătut şi duce calculul până la capăt.
Cerinţe
Scrieţi un program care să determine valoarea minimă pe care o poate obţine şi valoarea
maximă pe care o poate obţine după ı̂nlocuirea menţionată.
Date de intrare
Fişierul de intrare optim.in conţine pe prima linie numerele naturale N şi K, separate printr-
un spaţiu, reprezentând numărul de numere ı̂ntregi din şir, respectiv numărul de operaţii de
ı̂nmulţire ce vor fi efectuate. Pe cea de a doua linie se află N numere ı̂ntregi separate prin câte un
spaţiu, x1 x2 ...xN , reprezentând numerele din şir.
Date de ieşire
Fişierul de ieşire optim.out va conţine pe o singură linie, separate printr-un spaţiu, ı̂n ordine
crescătoare, cele două valori cerute.
Restricţii şi precizări
2 & N & 30;
a
0 & K & 9; K $ N ;
a
a 8 & xi & 8, 1 & i & N ;
a Dacă fişierul de ieşire conţine exact două numere, dar doar unul este corect, se obţine 40%
din punctajul acordat testului respectiv.

Exemple
optim.in optim.out Explicaţii
6 3 2 0 3 -1 7 -31 86 2 * 0 + 3 * (-1) + 7 * (-4) = -31
-4 2 + 0 + 3 * (-1) * 7 * (-4) = 86
Timp maxim de executare/test: 1.0 secunde
Memorie: total 2 MB din care pentru stivă 1 MB
Dimensiune maximă a sursei: 5 KB
CAPITOLUL 24. ONI 2012 24.3. OPTIM 296

24.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, identificarea celor care au K elemente, plasarea sem-
nului * faţa acestora şi calcularea valorii corespunzătoare acestei configuraţii
- aboradarea prin programare dinamică.
1. Prima implementare presupune generarea submultimilor folosind vectorii caracteristici (sau
reprezentarea pe biti), si pentru fiecare submulţime de K elemente se calculează valoare core-
spunză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 fiecare 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 fiecărei submulţimi.
La fiecare 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 fi 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 final 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 operaţii
de ı̂nmulţire.
Pentru construcţia fiecă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

24.3.2 Cod sursă

Listing 24.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 24. ONI 2012 24.3. OPTIM 297

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 24.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 24. ONI 2012 24.3. OPTIM 298

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 24.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 24. ONI 2012 24.3. OPTIM 299

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 24.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 24. ONI 2012 24.3. OPTIM 300

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 24.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 24. ONI 2012 24.3. OPTIM 301

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 24.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 24. ONI 2012 24.3. OPTIM 302

28 {
29 if (k == K+1)
30 {
31 sol();
32 return;
33 }
34
35 for (int i = X[k-1]+1;i<N;i++)
36 {
37 X[k] = i;
38
39 if (k == 1)
40 {
41 s = 1;
42 S[s].lung = 1;
43 S[s].start = i;
44 }
45 else
46 if (X[k] == X[k-1]+1)
47 {
48 S[s].lung++;
49 }
50 else
51 {
52 Rez += PP[ S[s].start ][ S[s].lung + 1];
53 Rez -= SP[ S[s].start + S[s].lung] - SP[ S[s].start - 1 ];
54
55 S[++s].lung = 1;
56 S[s].start = i;
57 }
58
59 back(k+1);
60
61 if (k == 1)
62 {
63 s = 1;
64 S[s].lung = 1;
65 S[s].start = i;
66 }
67 else
68 if (X[k] == X[k-1]+1)
69 {
70 S[s].lung--;
71 }
72 else
73 {
74 s--;
75 Rez -= PP[ S[s].start ][ S[s].lung + 1];
76 Rez += SP[S[s].start+S[s].lung] - SP[S[s].start-1];
77 }
78 }
79 }
80
81 int main()
82 {
83 FILE *f = fopen("optim.in","r");
84 FILE *g = fopen("optim.out","w");
85
86 fscanf(f,"%d %d",&N, &K);
87 for (i=1;i<=N;i++)
88 {
89 fscanf(f,"%d",&V[i]);
90 SP[i] = SP[i-1] + V[i];
91 }
92
93 Rez = SP[N];
94
95 //PP[i][j] = produsul numerelor din
96 // secventa care incepe pe pozitia i si are lungimea j
97 for (i=1;i<=N;i++)
98 {
99 PP[i][1] = V[i];
100 for (j=2;j<=K+1;j++)
101 {
102 if (i+j-1 > N)
103 break;
CAPITOLUL 24. ONI 2012 24.3. OPTIM 303

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 24.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 24. ONI 2012 24.3. OPTIM 304

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 24.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 24. ONI 2012 24.3. OPTIM 305

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 }

24.3.3 *Rezolvare detaliată


Capitolul 25

ONI 2011

25.1 butoane
Problema 1 - butoane 100 de puncte
Echipa SG1 se află ı̂n faţa unei noi provocări. Un dispozitiv antic are un sistem foarte ciudat
prin care poate fi pus ı̂n funcţiune. Dispozitivul are n butoane numerotate de la stânga la dreapta
de la 1 la n. Pe fiecare buton se găseşte un număr natural. Suma tuturor numerelor de pe butoane
este divizibilă cu n.
S-a constatat că la atingerea butoanelor din capete (butonul 1 şi butonul n) numărul scris pe
acestea scade cu o unitate, iar numărul de pe butonul vecin creşte cu o unitate. Dacă se atinge
unul dintre celelalte butoane (cele numerotate cu 2, 3, ..., n  1) numărul corespunzător scade cu
două unităţi, iar cele corespunzătoare vecinilor cresc cu câte o unitate. Dispozitivul va fi pus ı̂n
funcţiune dacă toate cele n numere devin egale.
Ajuţati echipa SG1 să pună dispozitivul ı̂n funcţiune folosind un număr minim de atingeri ale
butoanelor.
Cerinţe
Cunoscându-se n, numărul de butoane, precum şi cele n numere naturale scrise iniţial pe
butoane să se stabilească de câte ori trebuie atins fiecare buton astfel ı̂ncât dispozitivul să fie
pornit astfel ı̂ncât numărul total de atingeri să fie minim.
Date de intrare
Fişierul de intrare butoane.in conţine pe prima linie numărul natural n, reprezentând numărul
de butoane. Pe cea de-a doua linie se află n numere naturale, separate prin câte un spaţiu,
reprezentând ı̂n ordine valorile ı̂nscrise iniţial pe cele n butoane.
Date de ieşire
Fişierul de ieşire butoane.out va conţine n linii. Pe linia i (1 & i & n) se va afişa un număr
natural reprezentând numărul de atingeri ale butonului i.
Restricţii şi precizări
a 3 & n & 1000
a Numerele ı̂nscrise iniţial pe cele n butoane sunt numere naturale mai mici sau egale cu 100.
Suma celor n numere este divizibilă cu n.
a Numărul de atingeri ale oricărui buton va fi & 2 000 000 000 (două miliarde).
a Punctaj. Dacă programul afişează o soluţie care determină deschiderea dispozitivului cu
număr minim de atingeri, obţine integral punctajul pentru testul respectiv. Dacă numărul de
atingeri nu este minim, dar soluţia afişată determină deschiderea dispozitivului, se obţine 30% din
punctaj.

Exemple
butoane.in butoane.out Explicaţii
3 0
10 11 12 1
2

306
CAPITOLUL 25. ONI 2011 25.1. BUTOANE 307

Timp maxim de executare/test: 1.0 secunde

25.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 fixat la numerele
corespunzătoare soluţiei optime. Această observaţie se justifică prin faptul că orice soluţie poate
fi 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 final, pe fiecare 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 U P pot fi aplicate simultan de un numar de ori folosind ”SMENUL
LUI MARS” de modificare, ı̂n timp 1, a sumei unei secvenţe, cu o valoare constantă.
Pasul 3. Deoarece este posibil ca soluţia să nu fie optimă vom determina numărul minim de
apăsări obţinute ı̂n pasul 2. Scăzând această valoare din soluţia eventual neoptimă se va obţine
soluţia optimă.

Soluţie 2 - Soluţie optimă. Complexitatea O n


Justifică ş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 aflate 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
CAPITOLUL 25. ONI 2011 25.2. MACHETA 308

- ecuaţia 4 este verificată.

În plus, printr-o analiză atentă a sistemului se observă că diferenţele a  b, b  c, c  d pot fi
bine determinate şi valorile b, c, d sunt obţinute din a modificat cu constante unic determinate
din datele de intrare. Ideea e analogă pentru oricâte butoane.
Algoritm
Notez x[ ] = şirul celor n numere din fişierul de intrare şi sol[ ] şirul soluţie.

Se alege sol[1] = 0
Se calculează
sol[2] = M + sol[1] - x[1]
sol[k] = M - sol[k-2] + 2 sol[k-1] - x[k-1] pentru k &3
Deoarece este posibil ca prin alegerea lui sol[1] = 0 să obţinem sol[k] ¡ 0 (imposibil, deoarece
numărul de atingeri nu poate fi negativ) va trebui să translatăm soluţia prin scăderea valorii
V = min sol[k]

Soluţia 3. - Metoda Greedy (30 puncte)


La fiecare pas se atinge un buton de valoare maximă. Procedeul se repetă până la egalizarea
tuturor butoanelor.
Complexitate: O n ˜ numar total atingeri

25.1.2 *Cod sursă

25.1.3 *Rezolvare detaliată

25.2 macheta
Problema 2 - macheta 100 de puncte
Cristi, participant la ONIGIM 2011, este pasionat de machete. El a realizat la scară macheta
campusului ı̂n care se desfăşoară olimpiada. ı̂n macheta lui sunt modelate N clădiri, numerotate
de la 1 la N , sub forma unor paralelipipede dreptunghice.
Privind macheta de sus, evident, toate clădirile sunt vizibile. Mai mult, asociind un sistem
de coordonate cartezian, cu originea ı̂n colţul stânga-jos al vederii de sus a machetei, axa OX
pe latura sudică (cea de jos) cu sensul către Est, iar axa OY pe latura vestică (cea din stânga)
cu sensul către Nord, observăm că vederea de sus a fiecărei clădiri este un dreptunghi cu laturile
respectiv paralele cu axele. Prin urmare, vederea de sus a unei clădiri poate fi specificată prin 4
valori x y Lx Ly cu semnificaţia: x abscisa, respectiv y ordonata colţului stânga-jos al vederii de
sus a clădirii; Lx lungimea laturilor paralele cu OX, respectiv Ly lungimea laturilor paralele cu
OY .
După ce a analizat macheta privind-o de sus, identificând astfel toate clădirile, Cristi priveşte
macheta perpendicular dinspre laterala sudică (adică priveşte perpendicular pe laterala machetei
pe care se află axa OX). Privind astfel macheta nu mai sunt vizibile toate cele N clădiri.

Cerinţe

Scrieţi un program care, cunoscând vederea de sus a machetei şi ı̂nălţimile clădirilor, să deter-
mine ce clădiri sunt vizibile privind macheta dinspre laterala sudică.

Date de intrare

Fişierul de intrare macheta.in conţine pe prima linie numărul natural N , reprezentând


numă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 specificată prin cinci numere naturale x y Lx Ly H, separate
prin câte un spaţiu, unde x y Lx Ly specifică vederea de sus a clădirii, iar H ı̂nălţimea acesteia.

Date de ieşire
CAPITOLUL 25. ONI 2011 25.2. MACHETA 309

Fişierul de ieşire macheta.out va conţine o singură linie pe care se vor scrie ı̂n ordine
crescătoare numerele clădirilor vizibile privind macheta dinspre laterala sudică.

Restricţii şi precizări

a2 & N & 100


a0 & x, y, Lx, Ly, H & 1000. Pentru 50% dintre teste 0 & x, y, Lx, Ly, H & 250
a Se garantează că ı̂n fiş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 1235
1 6 9 1 8
9 2 1 3 10
1 1 7 1 8
1 3 3 1 6
5 3 3 1 9

Timp maxim de executare/test: 1.0 secunde

25.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 şi ı̂n caz de egalitate după y
- După sortare parcurgem vectorul şi pentru fiecare 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ă, modificâ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
- Afişă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 fiind ”proiectate” pe un
”ecran”. Deci este suficient 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ă fie ”puse pe
ecran” de la cea mai depărtată clădire spre cea mai apropiată. Prin parcurgerea ”ecranului” sunt
identificate 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 fi 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 fiecare zonă ı̂n parte.

Soluţia 3 - prof. Panaete Adrian


CAPITOLUL 25. ONI 2011 25.3. SPORT 310

Prima dată normalizez coordonatele existente astfel ı̂ncât atât pe direcţia orizontală (pe
direcţia OX) cât şi pe verticală (pe ı̂nălţime) coordonatele să fie ı̂nlocuite cu valori consecutive
ı̂ncepând cu valoarea 1.
Ideea prezintă valoarea ca ı̂n acest moment fără să fi modificat problema ı̂n esenţă fiecare zona
pătrată cu latura de o unitate de lungime poate fi reprezentată ı̂ntr-o matrice de dimnesiuni 200
pe orizontală şi 100 pe verticală.
Aceste zone pătrate de latură 1 corespund ı̂n reprezentarea reală unor zone create printr-un
caroiaj creat prin toate laturile orizontale şi verticale ale clădirilor aşa cum se văd ele dinspre sud.
În aceste condiţii şi cu modificarea la scară a desenului problema revine la inscriptionarea
clădirilor ı̂n matrice luate din spate spre ı̂n faţă (pe direcţia Oy) - adică o clădire stă mai ı̂n spate
ı̂n orizontul vizual dacă are y spate ymax mai mare.
Este necesară deci şi o sortare a clădirilor ı̂n ordinea descrescătoare a valorilor Y max Y  LY .
Un exemplu de normalizare: să spunem că luate de la stânga la dreapta pe Ox extremităţile
clădirilor (nu contează dacă sunt cele din stânga sau cele din sau dreapta) iau următoarele valori
x:

2 12 23 112 1002 1331 <- coordonatele reale (cel mult 200, cate doua de cladire)
| | | | | | REZULTA
V V V V V V
1 2 3 4 5 6 <- coordonatele normalizate (coordonate cu valori
intre 1 si 200)
Similar se pot normaliza şi ı̂nălţimile. Rezultând ı̂nălţimi reduse ı̂ntre 1 şi 100.

25.2.2 *Cod sursă

25.2.3 *Rezolvare detaliată

25.3 sport
Problema 3 - sport 100 de puncte
Profesorul nostru de sport este bun prieten cu profesorul de matematică. Din acest motiv
la ora de sport inventează tot felul de probleme şi apoi ı̂i cere profesorului de matematică să le
rezolve.
Azi la ora de sport participă N elevi, care poartă tricouri cu numerele 1, 2, ..., N . La ı̂nceputul
orei, cei N elevi se aşează ı̂n rând ı̂n ordinea p1 p2 ... pN  (adică elevul cu tricoul p1 se
aşează pe poziţia 1 ı̂n rând, elevul cu tricoul p2 stă pe poziţia 2, etc., poziţiile ı̂n rând fiind
numerotate de la 1 la N de la stânga la dreapta). Profesorul de sport spune aşa: ”La comanda
mea schimbaţi locurile astfel: pe poziţia i să se aşeze elevul care acum stă pe poziţia ppi
(pentru fiecare 1iN )”.
De exemplu, dacă N=6 şi iniţial elevii s-au aşezat astfel: 3 1 4 2 6 5
După prima comandă: 4 3 2 1 5 6
Observaţi că pe poziţia 1 se află elevul 3 iar pe poziţia 3 se află elevul 4. După prima comandă
pe poziţia 1 va ajunge elevul pp1 p3 4. Pe poziţia 2 se află elevul 1, iar pe poziţia 1 se
află elevul 3. După prima comandă pe poziţia 2 va ajunge elevul pp2 p1 3 ... După a
doua comandă: 2 4 1 3 6 5
Observaţi că ı̂n configuraţia obţinută după prima comandă pe poziţia 1 stătea elevul 4 deci
după ı̂ncă o comandă va ajunge pe poziţia 1 elevul p4, adică elevul 2. Pe poziţia 2 stătea elevul
3, deci după ı̂ncă o comandă va ajunge pe poziţia 2 elevul p3, adică elevul 4 etc.
După a treia comandă se obţine configuraţia 1 2 3 4 5 6
Iar după a patra comandă se revine la configuraţia iniţială.
CAPITOLUL 25. ONI 2011 25.3. SPORT 311

Profesorul de sport ı̂l ı̂ntreabă pe profesorul de matematică: care este numărul minim de
comenzi pe care trebuie să le dau astfel ı̂ncât elevii să revină ı̂n configuraţia iniţială? şi care ar
fi cea mai mică configuraţie iniţială (considerând ordinea lexicografică) pentru care este necesar
acelaşi număr minim de comenzi pentru a reveni la configuraţia iniţială.

Cerinţe

Scrieţi un program care să ı̂l ajute pe profesorul de matematică să răspundă la cele două
ı̂ntrebări ale profesorului de sport.

Date de intrare

Fişierul de intrare sport.in conţine pe prima linie un număr natural N reprezentând numărul
de elevi. Pe cea de a doua linie se află N valori distincte cuprinse ı̂ntre 1 şi N reprezentând
configuraţia iniţială a elevilor.

Date de ieşire

Fişierul de ieşire sport.out va conţine două linii. Pe prima linie va fi scris un număr natural
reprezentând numărul minim de comenzi ce trebuie date astfel ı̂ncât elevii să revină la configuraţia
iniţială. Pe cea de a doua linie vor fi scrise N valori distincte cuprinse ı̂ntre 1 şi N reprezentând cea
mai mică configuraţie iniţială (considerând ordinea lexicografică) pentru care este necesar acelaşi
număr minim de comenzi pentru a reveni la configuraţia iniţială.

Restricţii şi precizări

a 1 & N & 500


a Valorile scrise pe aceeaşi linie ı̂n fişierul de intrare, respectiv ı̂n fişierul de ieşire sunt separate
prin spaţii.
a Spunem că o configuratie p p1 , p2 , ..., pN  este mai mică din punct de vedere lexicografic
decât o altă configuraţ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ă fie 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
314265 124563

Timp maxim de executare/test: 1.0 secunde

25.3.1 Indicaţii de rezolvare

prof. Emanuela Cerchez - C. N. ”Emil Racoviţă” Iaşi

În limbaj matematic problema poate fi 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 per-
mutarea identică 1 2 ... n).
Prima cerinţă constă ı̂n a determina gradul permutării date.
Orice permutare poate fi 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 .
CAPITOLUL 25. ONI 2011 25.3. SPORT 312

Deoarece:
cmmmc a, b a ˜ b©cmmdc a, b.
Vom alege lungimile ciclurilor ı̂n permutarea pe care o construim astfel:
a a a
n 1  1  ...  1  p1 1  p2 2  ...  pmm
Odată determinate lungimile ciclurilor permutării, pentru a obţine prima permutare de grad
maxim ı̂n ordine lexicografică vom construi permutarea astfel:
1. Considerăm că lg1 & lg2 & ... & lgk
2. Pentru ca permutarea să fie minimă din punct de vedere lexicografic, ciclul 1 va conţine
elementele 1, 2, ..., lg1 , pe care le vom plasa ı̂n permutare ı̂n ordinea:
2 3... lg1 1
Dacă lg1 1, atunci p1 1.
Ciclul al doilea va conţine elementele lg1  1, ..., lg1  lg2 , pe care le plasăm ı̂n permutare ı̂n
ordinea:
lg1  2, lg1  3, ..., lg1  lg2 , lg1  1.
Dacă lg2 1, atunci p2 2.
etc.

25.3.2 *Cod sursă

25.3.3 *Rezolvare detaliată


Capitolul 26

ONI 2010

26.1 fractie
Problema 1 - fractie 100 de puncte
Gigel a ı̂nvăţat să lucreze cu fracţii zecimale neperiodice, periodice simple, respectiv periodice
mixte şi să transforme o fracţie zecimală ı̂n fracţie ordinară. El ştie că există fracţii zecimale şi
fracţii ordinare echivalente.
Gigel are de transformat o fracţie zecimală ı̂n fracţie ordinară scriind numitorul fracţiei ı̂n una
din următoarele două forme:
1. o cifră 1 care poate fi urmată sau nu de 0-uri;
2. una sau mai multe cifre de 9 urmate eventual de 0-uri.
Pot exista mai multe soluţii, din care o va alege pe cea cu număr minim de cifre la numitor.

Pentru fiecare din cele două exemple, oricare ar fi fracţia zecimală dată, Gigel - elev silitor -
va alege fracţia ordinară echivalentă ı̂ngroşată.

Cerinţe

Scrieţi un program care citeşte o fracţie zecimală strict pozitivă şi afişează numărătorul şi
numitorul unei fracţii ordinare echivalente, având numitorul ı̂n una din formele 1 sau 2 şi număr
minim de cifre.

Date de intrare

Fişierul fractie.in conţine pe o singură linie un şir de maxim 80 de caractere reprezentând


fracţia zecimală. Caracterele pot fi: 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%.

313
CAPITOLUL 26. ONI 2010 26.1. FRACTIE 314

Exemple
fractie.in fractie.out Explicaţii
0,3(754754) 3751
9990 3751
0, 3 754754 0, 3 754
9990

6,230000 623
100 623
6, 230000 6, 23
100

Timp maxim de executare/test: 1.0 secunde

26.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 nesemnificative de la partea fractionară;
a numărătorul este data de intrare din care eliminăm virgula zecimală, iar numitorul este
format din 1 urmat de atâtea 0-uri câte cifre au fost după virgula zecimală
3. Dacă avem paranteze urmărim, dacă este cazul, reducerea părţii fracţionare:
a 0,(1212121212)=0,(12);
a 0,123(523)=0,1(235).

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ă sub-
k
unitară 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.

26.1.2 Cod sursă

Listing 26.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);
CAPITOLUL 26. ONI 2010 26.1. FRACTIE 315

16
17 strcpy(p,p+1);
18
19 y=x;
20 while(*p!=’(’)y++,p++;
21
22 strcpy(p,p+1);
23
24 z=y;
25 while(*p!=’)’)z++,p++;
26 strcpy(p,p+1);
27 }
28
29 void elim(char s[])
30 {char *p,*q,*r,c;
31 int x, i;
32
33 p=strchr(s,’(’)-1;
34 q=strchr(s,’)’)-1;
35
36 while(*p==*q){c=*p;*p=*(p+1);*(p+1)=c;strcpy(q, q+1);p--;q--;}
37
38 p=strchr(s,’(’)+1;
39 q=strchr(s,’)’);
40 x=q-p;
41
42 for(i=1;i<=x/2;i++)
43 if (x%i==0)
44 {r=p+i;
45 while(!strncmp(p,r,i)) {r+=i;}
46 if(*r==’)’) {strcpy(p+i,r);i=x;}
47 }
48 }
49
50 int main()
51 {char s[81],*p,*q,*r,a[81];
52 int x,y,z,i;
53
54 f>>s;
55 if(!(p=strchr(s,’,’)))
56 {g<<s<<’\\n’<<1<<’\\n’;g.close();f.close();return 0;}
57
58 while(s[strlen(s)-1]==’0’) s[strlen(s)-1]=0;
59
60 if(!(q=strchr(s,’(’)))
61 {if(s[strlen(s)-1]==’,’)
62 {s[strlen(s)-1]=0;g<<s<<’\\n’<<1<<’\\n’;}
63 else
64 {strcpy(p,p+1);
65 r=s;while(*r==’0’)r++;
66 g<<r<<’\\n’<<1;
67 while(*p){g<<0;p++;}
68 g<<’\\n’;}
69
70 g.close();f.close();return 0;
71 }
72 else {elim(s);
73 p=strchr(s,’,’);q=strchr(s,’(’);
74 if(p+1==q\&\&s[0]==’0’)
75 {s[strlen(s)-1]=’\\0’;
76 g<<s+3<<’\\n’;
77 p+=2;
78 while(*p){g<<9;p++;}
79 g<<’\\n’;
80 }
81 else{norm(s,x,y,z);
82 strncpy(a,s,y);a[y]=0;
83 p=s+z-1;q=a+y-1;i=0;
84 while(q>a)
85 {if(*p<*q+i){*p=*p+’0’+10-*q-i;i=1;}
86 else {*p=*p+’0’-*q-i;i=0;}
87 p--;q--;
88 }
89 if(*p<*q+i){*p=*p+’0’+10-*q-i;i=1;}
90 else {*p=*p+’0’-*q-i;i=0;}
91 p--;
CAPITOLUL 26. ONI 2010 26.2. NEURONI 316

92 if(i)
93 {while(*p==’0’)*p=’9’,p--;
94 (*p)--;
95 }
96 i=0;while(s[i]==’0’)i++;
97 g<<s+i<<’\\n’;
98 z-=y;
99 for(i=0;i<z;i++)g<<9;
100 y-=x;
101 for(i=0;i<y;i++)g<<0;
102 g<<’\\n’;
103 }
104 }
105
106 f.close();
107 g.close();
108 return 0;
109 }

26.1.3 *Rezolvare detaliată

26.2 neuroni
Problema 2 - neuroni 100 de puncte
Cercetătorii neurologi au identificat ı̂n retina umană o zonă de neu-
roni bipolari, ce au exact două ramificaţii, aranjaţi ı̂ntr-o structură
piramidală. Structura de neuroni este dispusă pe n niveluri astfel ı̂ncât
pe un nivel k există k neuroni (k 1, 2, ..., n). S-a constatat că un neu-
ron din această structură poate transmite impulsurile nervoase numai
către cei doi neuroni, corespunzători celor două ramificaţii, aşezaţi pe
nivelul următor.
ı̂n momentul receptării primului impuls de către un neuron din reţea, acesta transmite mai
departe impulsul astfel: dacă se află pe un nivel par, către neuronul din stânga, iar dacă se află
pe un nivel impar către neuronul din dreapta, de pe nivelul următor.
Transmiterea impulsurilor ı̂ntre neuroni funcţionează alternativ. Astfel, după ce un impuls a
fost transmis către neuronul aflat pe ramificaţia din stânga, următorul impuls va fi transmis către
neuronul aflat pe ramificaţia din dreapta şi invers.
Neuronii de pe ultimul nivel al structurii, numiţi şi neuroni receptori, primesc impulsurile din
această reţea. Toate impulsurile provin de la neuronul aflat pe nivelul 1.

Cerinţe

Cunoscând numărul n de niveluri pe care sunt dispuşi neuronii şi numărul m de impulsuri ce
sunt transmise ı̂n reţea, scrieţi un program care să determine numărul de impulsuri receptate de
fiecare neuron de pe nivelul n.

Date de intrare

Prima linie a fişierului de intrare neuroni.in conţine cele două numere naturale n şi m separate
printr-un spaţiu, având semnificaţia de mai sus.

Date de ieşire

Prima linie a fişierului de ieşire neuroni.out va conţine numărul de impulsuri receptate de


fiecare 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 Ramificaţiile ı̂ngroşate din figură arată direcţia de transmitere a primului impuls.
CAPITOLUL 26. ONI 2010 26.2. NEURONI 317

Exemple
neuroni.in neuroni.out Explicaţii
35 131 Traseul celor 5 impulsuri şi numărul de impulsuri recepţionat
pe nivelul 3 va fi următorul:
a 1 : dreapta - stânga (0,1,0)
a 2 : stânga - stânga (1,1,0)
a 3 : dreapta - dreapta (1,1,1)
a 4 : stânga - dreapta (1,2,1)
a 5: dreapta - stânga (1,3,1)

Timp maxim de executare/test: 1.0 secunde

26.2.1 Indicaţii de rezolvare

prof. Ciprian Cheşcă - Gr. Şc. ”C. Neniţescu” Buzău

Dificultatea problemei constă mai ales ı̂n găsirea unui model matematic şi a unei structuri de
date care să permită implementarea problemei.
Astfel o primă abordare se poate face cu ajutorul unui vector care are n ˜ n  1©2 elemente
şi care ı̂n prima parte a sa (n n  1©2 elemente) să reţină sensul de transmitere a impulsurilor
(0 pentru stânga şi 1 pentru dreapta).
Ultima parte a vectorului (de la n n  1©2  1 până la n n  1©2) reţine numărul de impulsuri
ce ajung pe ultimul nivel.
Utilizarea acestui vector se face după ce ı̂n prealabil am notat nodurile din structura de neuroni
cu 1, 2, 3, ..., n n  1©2 de sus ı̂n jos şi de la stânga la dreapta.
Pentru parcurgerea structurii se va folosi şi un vector ı̂n care să reţinem nivelul pe care se
găseşte fiecare neuron.
A doua abordare se poate face cu ajutorul unei matrice cu n  n linii şi coloane şi din care să
utilizăm doar zona de sub diagonala principală. În acest caz nu mai este nevoie de utilizarea unui
vector de nivele iar matricea va reţine tot 0 sau 1 conform sensului de deplasare.
Prima structura permite utilizarea mai eficientă a memoriei ı̂n timp ce a doua structură este
mai rapidă dar utilizează ineficient memoria.

26.2.2 Cod sursă

Listing 26.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 {
CAPITOLUL 26. ONI 2010 26.2. NEURONI 318

25 i=1;j=1;
26 while(i<=n-1)
27 if (mat[i][j]==0) {mat[i][j]=1;i++;}
28 else {mat[i][j]=0;i++;j++;}
29
30 mat[n][j]++;
31 }
32
33 for(i=1;i<=n;i++)
34 g<<mat[n][i]<<" ";
35
36 g<<"\n";
37
38 f.close();
39 g.close();
40 return 0;
41 }

Listing 26.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 }

26.2.3 *Rezolvare detaliată


CAPITOLUL 26. ONI 2010 26.3. RAZE 319

26.3 raze
Problema 3 - raze 100 de puncte
Harta digitală a câmpului de luptă este memorată ı̂ntr-un tablou bidimensional cu N linii,
M coloane şi elemente din mulţimea {0,1}. Valoarea 0 reprezintă o poziţie liberă, iar valoarea 1
reprezintă o poziţie ocupată de un obstacol. În fiecare element aflat pe conturul tabloului, adică
pe prima linie, prima coloana, ultima linie şi ultima coloană, se află obiective inamice. Pe conturul
tabloului se găsesc numai elemente nule.
În interiorul tabloului (elementele care nu se află pe contur), ı̂ntr-o poziţie liberă, trebuie plasat
un soldat. Scopul său este să anihileze cât mai multe obiective inamice. Din păcate, el deţine
o armă laser cu care poate executa doar un singur atac. La lansarea atacului, se trimit 4 raze,
câte una ı̂n fiecare dintre cele 4 direcţii diagonale. O rază poate merge până la ı̂ntâlnirea unui
obstacol (ı̂n acest caz se opreşte şi nu va avea nici un efect) sau până ajunge pe contur (ı̂n acest
caz distruge obiectivul inamic respectiv).
Cerinţe
Scrieţi un program care determină numărul maxim de obiective inamice, notat cu K, ce pot
fi distruse ı̂n urma unui atac, precum şi numărul poziţiilor ı̂n care putem plasa soldatul pentru a
distruge K obiective inamice.
Date de intrare
Fişierul text raze.in are următoarea structură:
a Pe prima linie se găseşte numărul natural T, reprezentând numărul seturilor de date de
intrare.
a Pentru fiecare set de date de intrare:
` Pe prima linie a setului se află numerele naturale N şi M, separate printr-un spaţiu,
reprezentând numărul liniilor, respectiv numărul coloanelor tabloului;
` Pe următoarele N linii ale setului de date se află câte M numere naturale din mulţimea
0,1, separate prin câte un spaţiu, reprezentând forma digitală a hărţii câmpului de
luptă.
Date de ieşire
Fişierul text raze.out va conţine T linii, corespunzătoare celor T seturi de date de intrare. Pe
fiecare linie se vor tipări două numere naturale K şi P , separate printr-un spaţiu, reprezentând
numărul maxim de obiective inamice distruse ı̂n atac, respectiv numărul poziţiilor din care se pot
distruge K obiective inamice.
Restricţii şi precizări
a 1 & T & 80
a 3 & N, M & 135
a Se garantează că există cel puţin un obiectiv inamic ce poate fi anihilat pentru fiecare set de
date de intrare.

Exemple
raze.in raze.out Explicaţii
2 41 În fişier se găsesc 2 seturi de date de intrare.
46 32 În primul set de date se pot anihila maximum 4 obiective inamice,
00 0000 poziţionı̂nd soldatul ı̂n linia 2 şi coloana 2.
0 01110 În al doilea set de date se pot anihila maximum 3 obiective inam-
00 0000 ice, poziţionând soldatul ı̂n elementul de pe linia 3 şi coloana 2
000 000 sau ı̂n elementul din linia 3 şi coloana 6.
47
0000000
011111
0
0 0000 0
0
00 00 00
0
CAPITOLUL 26. ONI 2010 26.3. RAZE 320

Timp maxim de executare/test: 1.0 secunde

26.3.1 Indicaţii de rezolvare

prof. Marius Nicoli - C.N. ”Fraţii Buzeşti”, Craiova


3
O n  pentru 50 puncte
Se parcurge matricea (fără contur) şi, pentru fiecare element, se parcurge ı̂n fiecare 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.
2
O n  pentru 100 puncte
Fiecare obiectiv de pe conturul matricei poate fi 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 fiecare element,
se parcurge ı̂n cele 2 direcţii numai pe elemente 0, incrementându-se valoarea din elementele atinse.
La final se parcurg elementele calculate şi se determină cele două valori cerute.
2
O n  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 N V 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 fiecare element dacă atinge conturul pe fiecare dintre direcţii.
Pentru fiecare poziţie a matricei se vor numără direcţiile din care se poate anihila un obiectiv.
La final se parcurge matricea cu valorile calculate si se determină valorile cerute.
Observaţie: direcţiile N V şi N E, respectiv SV şi SE se pot prelucra ı̂n paralel.

26.3.2 Cod sursă

Listing 26.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;
CAPITOLUL 26. ONI 2010 26.3. RAZE 321

34 for (d=0;d<=3;d++)
35 for (ii = i, jj = j; ii>=0 && ii<=N && jj>=0 &&
36 jj<=M && A[ii][jj] == 0; ii+=DI[d],jj+=DJ[d])
37 B[ii][jj]++;
38 i = N;
39 for (d=0;d<=3;d++)
40 for (ii = i, jj = j; ii>=0 && ii<=N && jj>=0 &&
41 jj<=M && A[ii][jj] == 0; ii+=DI[d],jj+=DJ[d])
42 B[ii][jj]++;
43 }
44
45 for (i=2;i<N;i++)
46 { //coloanele
47 j = 1;
48 for (d=0;d<=3;d++)
49 for (ii = i, jj = j; ii>=0 && ii<=N && jj>=0 &&
50 jj<=M && A[ii][jj] == 0; ii+=DI[d],jj+=DJ[d])
51 B[ii][jj]++;
52 j = M;
53 for (d=0;d<=3;d++)
54 for (ii = i, jj = j; ii>=0 && ii<=N && jj>=0 &&
55 jj<=M && A[ii][jj] == 0; ii+=DI[d],jj+=DJ[d])
56 B[ii][jj]++;
57 }
58
59 max = 0;
60 nMax = 0;
61 for (i=2;i<N;i++)
62 for (j=2;j<M;j++)
63 if (B[i][j] > max)
64 {
65 max = B[i][j];
66 nMax = 1;
67 }
68 else
69 if (B[i][j] == max)
70 nMax++;
71
72 fprintf(g,"%d %u\n",max,nMax);
73 }
74
75 fclose(g);
76 fclose(f);
77 return 0;
78 }

Listing 26.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)
CAPITOLUL 26. ONI 2010 26.3. RAZE 322

28 {
29 fprintf(g,"%u %u\n",4,(M-2)*(N-2));
30 continue;
31 }
32
33 max = 0;
34 for (i=2;i<N;i++)
35 for (j=2;j<M;j++)
36 {
37 k = 0;
38 if (A[i][j] == 0)
39 {
40 for (ii = i+1, jj = j+1;
41 A[ii][jj] == 0 && ii!=N && jj!=M;
42 ii++,jj++);
43
44 if (A[ii][jj] == 0)
45 k++;
46
47 for (ii = i-1, jj = j-1;
48 A[ii][jj] == 0 && ii!=0 && jj!=0;
49 ii--,jj--);
50
51 if (A[ii][jj] == 0)
52 k++;
53
54 for (ii = i-1, jj = j+1;
55 A[ii][jj] == 0 && ii!=0 && jj!=M;
56 ii--,jj++);
57
58 if (A[ii][jj] == 0)
59 k++;
60
61 for (ii = i+1, jj = j-1;
62 A[ii][jj] == 0 && ii!=N && jj!=0;
63 ii++,jj--);
64
65 if (A[ii][jj] == 0)
66 k++;
67 }
68 /*
69 for (d = 0; d<=3; d++) {
70 for (ii = i, jj = j;
71 ii!=0 && ii!=N && jj!=0 && jj!=M && A[ii][jj] == 0;
72 ii+=DI[d],jj+=DJ[d]);
73 if (A[ii][jj] == 0)
74 k++;
75 }
76 */
77 if (k>max)
78 {
79 max = k;
80 nMax = 1;
81 }
82 else
83 if (k==max)
84 nMax++;
85 }
86
87 fprintf(g,"%d %u\n",max,nMax);
88 }
89
90 fclose(f);
91 fclose(g);
92 return 0;
93 }

26.3.3 *Rezolvare detaliată


323
Appendix A

”Instalare” C++

Ca să putem ”lucra” cu C++ avem nevoie de

ˆ un compilator pentru C++, şi


ˆ un IDE (Integrated Development Enviroment) pentru C++.

A.1 Kit OJI 2017


Poate că cel mai uşor este să se descarce fişierul
http://www.cnlr.ro/resurse/download/Kit_OJI_2017.rar
https://cdn.kilonova.ro/p/WXbRLG/Kit_OJI_2017.rar
http://olimpiada.info/oji2018/Kit_OJI_2017.rar
https://www.liis.ro/Documents/download/Kit_OJI_2017.rar
folosit de către elevi la şcoală şi la olimpiade.

Fişierele din arhivă sunt:

Figura A.1: Fişierele din Kit OJI 2017

Se lansează ı̂n execuţie fişierul OJIkit 2017.exe.

Instalarea este foarte uşoară (este de tipul ”next -> next -> ... -> next”) iar pe in-
ternet sunt multe site-uri de ajutor. De exemplu:

https://www.pbinfo.ro/?pagina=intrebari-afisare&id=26
https://www.youtube.com/watch?v=CLkWRvAwLO8
https://infoas.ro/lectie/112/tutorial-instalare-codeblocks-usor-introducere-in-in
https://www.competentedigitale.ro/c/oji2019/Kit_OJI_2017.rar

Există numeroase alternative la CodeBlocks: Dev-C++, Microsoft Visual Studio, Eclipse,


NetBeans, CodeLite, CLion, KDevelop, etc.

324
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 325

Kitul Kit_OJI_2017 se instalează implicit pe C:¯OJI¯


26
t IDE-ul Code::Blocks ,
t compilatorul pentru C: gcc.exe,
27
t compilatorul pentru C++: g++.exe
t make.exe
t gdb.exe
t şi... altele!

La sfârşitul instalării apar pe ecran două link-uri:

Figura A.2: CodeBlocks & C++ Reference

Figura A.3: Ce conţine C:¯ OJI ¯

A.1.1 Code::Blocks
Pentru versiuni mai noi, de Code::Blocks şi compilatoare, se poate accesa site-ul
http://www.codeblocks.org/downloads/binaries
de unde se poate descărca, de exemplu, codeblocks-20.03mingw-setup.exe.
Versiuni mai vechi, dar foarte bune, se pot descărca de la adresa
26
Code::Blocks este un IDE (integrated development environment) pentru C/C++, un fel de Notepad ... mai
sofisticat, cu multe butoane care lansează ı̂n execuţie diverse programe, de exeplu g++.exe
27
g++.exe este compilatorul de C++, programul care va verifica dacă instrucţiunile noastre sunt ok sau nu ...

şi care ne va supăra mereu cu erorile pe care ni le arată ... ... Este “o mică artă” să ı̂nţelegem mesajele de
eroare pe care le vedem pe ecran şi să fim ı̂n stare să “depanăm” programele noastre!
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 326

http://www.codeblocks.org/downloads/source/5
Mai precis:
https://sourceforge.net/projects/codeblocks/files/Binaries/20.03/
http://sourceforge.net/projects/codeblocks/files/Binaries/17.12
http://sourceforge.net/projects/codeblocks/files/Binaries/16.01

A.1.2 Folder de lucru

Figura A.4: Folder de lucru

De preferat este să avem cel puţin două partiţii: C:¯, D:¯, ... şi să “lăsăm ı̂n pace” partiţia C:¯
pentru sistemul de operare şi programele instalate!

În figura de mai sus se poate observa că pe D:¯ există un folder de lucru pentru programele ı̂n
C++, folder care se numeşte Programe C++. Aici vom salva toate programele C++ pe care
le vom scrie, eventual organizate pe mai multe subfoldere.

Acum, să intrăm ı̂n folderul Programe C++.

Click-dreapta cu mouse-ul şi selectăm “New -¿ Text document” ca ı̂n figura următoare.
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 327

Figura A.5: New -¿ Text document

Figura A.6: Schimbare nume fişier şi nume extensie fişier


APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 328

Figura A.7: Confirmare schimbare extensie ı̂n .cpp

Figura A.8: Pregătit pentru Code::Blocks

Dacă vom executa două click-uri pe numele fşierului p01.cpp sau un click pentru a marca
fişierul şi apoi un ¡Enter¿, se va declanşa Code::Blocks cu p01.cpp ı̂n fereastra de editare şi, ce
este şi mai important, cu Programe C++ ca folder curent de lucru pentru Code::Blocks.
Adică, aici vor apărea toate fişiere generate de Code::Blocks pentru p01.cpp.

A.1.3 Utilizare Code::Blocks

Figura A.9: Pregătit pentru a scrie cod de program C++ ı̂n Code::Blocks
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 329

Figura A.10: Primul cod de program C++ ı̂n Code::Blocks

Figura A.11: Build - compilare


APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 330

Figura A.12: 0 error(s), 0 warning(s)

Figura A.13: Run - execuţie


APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 331

Figura A.14: Executat corect: a făcut “nimic”

A.1.4 Setări Code::Blocks


De preferat este să lăsăm setările implicite (sunt stabilite totuşi de nişte specialişti!) dar, dacă
vrem, putem să umblăm şi noi prin setări!

Figura A.15: Settings  % Compiler


APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 332

Figura A.16: Toolchain executables

Figura A.17: Unde sunt acele programe


APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 333

A.1.5 Multe surse ı̂n Code Blocks


Settings Environment: pe calculatorul meu setările sunt:

Figura A.18: Multe surse ı̂n Code Blocks - setări

Dacă avem fişierele p01.cpp, ..., p05.cpp, ı̂n folderul nostru de lucru, şi facem dublu-click pe
fiecare ... vor apărea toate ...

Figura A.19: Multe surse in Code Blocks - exemple


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 334

A.2 winlibs
A.2.1 GCC şi MinGW-w64 pentru Windows
Se descarcă de la
http://winlibs.com/#download-release
unul dintre fişierele:

ˆ winlibs-x86_64-posix-seh-gcc-10.2.0-llvm-11.0.0-mingw-w64-8.0.0-r
3.7z dimensiune fişier = 148 MB
ˆ winlibs-x86_64-posix-seh-gcc-10.2.0-llvm-11.0.0-mingw-w64-8.0.0-r
3.zip dimensiune fişier = 324 MB

ˆ winlibs-x86_64-posix-seh-gcc-10.2.0-mingw-w64-8.0.0-r3.7z dimensiune
fişier = 52.1 MB
ˆ winlibs-x86_64-posix-seh-gcc-10.2.0-mingw-w64-8.0.0-r3.zip dimensi-
une fişier = 141 MB

Se dezarhivează şi se mută folderul mingw64 pe C: sau D: sau ...


Eu l-am dezarhivat pe cel mai mic şi l-am pus pe D:

Figura A.20: mingw64 pe D:

A.2.2 PATH
Trebuie pusă ı̂n PATH calea pentru D:\mingw64\bin\. În “Type here to search” scrieţi: path
şi apoi click pe “Edit the system environment variables”, ca ı̂n figura următoare:
APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 335

Figura A.21: search path

Apare fereastra “System properties”:

Figura A.22: System properties –¿ Advanced

Fereastra este poziţionată pe tab-ul “Advanced”. Se selectează “Environment Variables”.


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 336

Figura A.23: Environment Variables

Se selecteaza “Path” şi click pe “Edit”. Apare fereastra “Edit Environment Variables”

Figura A.24: Edit Environment Variables –¿ New

Se selectează “New”, se scrie calea D:¯mingw64¯bin şi se finalizează cu click pe “OK”,


“OK”, ..., “OK” până la sfârşit!
Se verifică calea şi versiunea pentru “gcc”:

ˆ C:¯path
ˆ C:¯gcc –version (Atentie! sunt 2 caractere - consecutive)
APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 337

Figura A.25: Calea şi versiunea pentru gcc

Dacă totul este OK atunci se trece la instalarea IDE-ului preferat (Integrated Development
28
Environment ), de exemplu Code::Blocks 20.03 (sau Eclipse, Visual Studio Code, Dev C++,
NetBeans, şi altele).

Observatie: Pentru Windows 11

"Setting the path and variables in Windows 11

In the System > About window,


click the Advanced system settings link
at the bottom of the Device specifications section.

In the System Properties window,


click the Advanced tab,
then click the Environment Variables button
near the bottom of that tab."

A.2.3 CodeBlocks
CodeBlocks se poate descărca de la http://www.codeblocks.org/downloads/26. Sunt
mai multe variante dar eu am descărcat numai codeblocks-20.03-setup.exe care are 35.7 MB.
La instalare am lăsat totul implicit, deci ... Next, Next, ..., Next până la sfârşit!
La prima lansare ı̂n execuţie este necesară setarea locaţiei compilatorului de C++ (gcc-ul pe
care tocmai l-am instalat!).
28
https://en.wikipedia.org/wiki/Integrated_development_environment
https://ro.wikipedia.org/wiki/Mediu_de_dezvoltare
APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 338

Figura A.26: Settings –¿ Compiler

Figura A.27: Toolchain executables –¿ Auto-detect


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 339

Figura A.28: New –¿ Text Document

Figura A.29: New text Document.txt

Figura A.30: Schimbare nume şi extensie


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 340

Figura A.31: Moore apps

Figura A.32: Look for another app


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 341

Figura A.33: Cale pentru codeblocks.exe

Figura A.34: Selectare codeblocks.exe


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 342

Figura A.35: Editare test01.cpp

Figura A.36: Compilare test01


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 343

Figura A.37: Mesaje după compilare

Figura A.38: Execuţie test01


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 344

Figura A.39: Rezultat execuţie test01

Figura A.40: Fişiere apărute după compilare!

Figura A.41: Creare test02.cpp gol! + ¡dublu click¿ sau ¡Enter¿


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 345

Figura A.42: Lista programelor de utilizat

Figura A.43: Selectare Code::Blocks IDE pentru fişierele .cpp

Figura A.44: Editare+Compilare+Execuţie pentru test02


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 346

Figura A.45: Selectare tab ce conţine test01.cpp


Appendix B

Exponenţiere rapidă

B.1 Analogie baza 2 cu baza 10

Figura B.1: Analogie B2 cu B10

În figura B.1 este considerat numărul nr 234 care ı̂n baza 2 se scrie sub forma 11101010 (celula
C10).
Pe R3 (rândul 3) este arătat ce se ı̂ntâmplă cu nr atunci când se fac ı̂mpărţiri succesive prin
baza 10: se ”şterge” ulima cifră (care este egală cu nr%10 adică ultima cifră este de fapt restul
ı̂mpărţirii lui nr la baza 10).
Pe R10 (rândul 10) este arătat ce se ı̂ntâmplă cu nr atunci când se fac ı̂mpărţiri succesive prin
baza 2: se ”şterge” ulima cifră (care este egală cu nr%2 adică ultima cifră este de fapt restul
ı̂mpărţirii lui nr la baza 2, la fel cum se ı̂ntâmplă ı̂n baza 10).
Observaţia 1. Cifrele se obţin ı̂n ordine inversă. Prima cifră obţinută ca rest este de fapt ultima
cifră din număr (vezi R4 şi R11).
Pe R6 şi R16 sunt precizate puterile bazei.
Valoarea numărului nr este suma produselor dintre cifre şi puterile corespunzătoare:
ˆ ı̂n baza 10: 4*1 + 3*10 + 2*100 = 234 (R3 şi R6)
ˆ ı̂n baza 2: 0*1 + 1*2 + 0*4 + 1*8 + 0*16 + 1*32 + 1*64 + 1*128 = 234 (R13 şi R16)

347
APPENDIX B. EXPONENŢIERE RAPIDĂ B.2. NOTAŢII, RELAŢII ŞI FORMULE 348

B.2 Notaţii, relaţii şi formule


234 1˜1281˜641˜320˜161˜80˜41˜20˜1
a a
1˜128 1˜64 1˜32 0˜16 1˜8 0˜4 1˜2 0˜1
a ˜ a ˜ a ˜ a ˜ a ˜ a ˜ a ˜ a
128 1 64 1 32 1 16 0 8 1 4 0 2 1 1 0
a  ˜ a  ˜ a  ˜ a  ˜ a  ˜ a  ˜ a  ˜ a  .
k
2
Dacă notăm ak a atunci
234 1 1 1 0 1 0 1 0
a a7  ˜ a6  ˜ a5  ˜ a4  ˜ a3  ˜ a2  ˜ a1  ˜ a0  .
2
Şirul a0 , a1 , a2 , ... se obţine uşor prin ridicări la putere ak ak1 .

2 2 2 4 2 8 2 16
a0 a a1 a0 a  a2 a1 a  a3 a2 a  a4 a3 a 
2 32 2 64 2 128
a5 a4 a  a6 a5 a  a7 a6 a 

Dacă notăm ek ”exponentul (puterea)” la care apare ak ı̂n produs, atunci putem observa
uşor că şirul e0 , e1 , e2 , e3 , ... se obţine prin ı̂mpărţiri succesive ale lui n prin 2 şi preluı̂nd restul
rezultatului. Pentru a folmaliza acest lucru vom considera şirul n0 , n1 , n2 , n3 , ... definit astfel:

n0 n,
w
nk nk1 ©2 (câtul ı̂mpărţirii!) k '1
Folosind aceste notaţii, putem scrie:

ek nk %2 , (restul ı̂mpărţirii!) k '0


ek 1 0
Dacă notăm şi produsul parţial pk ak  ˜ ... ˜ a1  ˜ a0 
obţinem ı̂n final relaţiile:

~
„n0 n;
„
„
„
„a0 a;
„
„
„
„
„
„e n0 %2 " r0, 1x;
„ 0
„
„
„ 1 ˜ a0 , dacă e0 1;
„
„p0
e
a00 sau, altfel scris: p0 w
„
„
„
„ 1, dacă e0 0;
„
„
„
‚
„ (B.2.1)
„
„
„
„
„nk nk1 ©2 k ' 1; nk1 j 0 desigur ! ... dar şi nk1 j 1 
„
„
„ ak1 , k ' 1;
2
„
„ak
„
„
„
„
„ek nk %2 " r0, 1x, k ' 0;
„
„
„ pk1 ˜ ak , k ' 0, dacă ek 1; ak ak modifică produsul pk1 
1
„
„
„
„p w
„ k k ' 0, dacă ek 0; ak 1 nu modifică produsul pk1 
0
€ pk1 ,

B.3 Pregătire pentru scrierea codului!


Relaţia nk nk1 ©2 ı̂nseamnă că nnou nvechi ©2 (sunt explicaţii pentru ı̂ncepători ... nu pentru
avansaţi!) şi se poate programa sub forma n=n/2;
2 2
Relaţia ak ak1 ı̂nseamnă că anou avechi şi se poate programa sub forma a=a*a;
Relaţia pk pk1 ˜ ak ı̂nseamnă că pnou pvechi ˜ anou şi se poate programa sub forma p=p*a;
Relaţia pk pk1 ı̂nseamnă că pnou pvechi şi se poate programa sub forma p=p; care
ı̂nseamnă că nu se schimbă p, deci ... mai bine nu mai scriem nicio instrucţiune!
APPENDIX B. EXPONENŢIERE RAPIDĂ B.4. CODUL 349

B.4 Codul
Codul pentru relaţiile (B.2.1) devine:

Listing B.4.1: exponentiere rapida1.cpp


1 #include<iostream> // actualizare p dupa actualizare a si n
2
3 using namespace std;
4
5 int exponentiere_rapida(int a, int n) // p=aˆn
6 {
7 int nk1, nk;
8 int ak1, ak;
9 int ek1, ek;
10 int pk1, pk;
11
12 int k=0;
13 nk1=n;
14 ak1=a;
15 ek1=nk1%2;
16 if(ek1==1)
17 pk1=ak1;
18 else
19 pk1=1;
20
21 while(nk1>1)
22 {
23 k++;
24 nk=nk1/2;
25 ak=ak1*ak1;
26 ek=nk%2;
27 if(ek==1)
28 pk=pk1*ak;
29 else
30 pk=pk1;
31
32 // devin valori "vechi" inainte de noua actualizare
33 nk1=nk;
34 ak1=ak;
35 ek1=ek;
36 pk1=pk;
37 }
38
39 return pk;
40 }
41
42 int main()
43 {
44 int a=2;
45 //int n=234; // aˆn = prea mare !!!
46 //int n=30;
47 //int n=6; // par: 2ˆ6 = 64 ... usor de verificat!
48 int n=5; // impar: 2ˆ5 = 32 ... usor de verificat!
49
50 int rez=exponentiere_rapida(a,n);
51
52 cout<<a<<"ˆ"<<n<<" = "<<rez<<"\n";
53
54 return 0;
55 }
56 /*
57 2ˆ30 = 1073741824
58
59 Process returned 0 (0x0) execution time : 0.076 s
60 Press any key to continue.
61 */

Observaţia 2. În acest cod actualizarea lui p se face după actualizările pentru a şi n.
Observaţia 3. În codul următor actualizarea lui p se face ı̂naintea actualizărilor pentru a şi n,
corespunzător relaţiilor (B.4.2).
APPENDIX B. EXPONENŢIERE RAPIDĂ B.4. CODUL 350

Listing B.4.2: exponentiere rapida2.cpp


1 #include<iostream> // actualizare p inainte de actualizare a si n
2
3 using namespace std;
4
5 int exponentiere_rapida(int a, int n) // p=aˆn
6 {
7 int nk1, nk;
8 int ak1, ak;
9 int ek1, ek;
10 int pk1, pk;
11
12 nk1=n;
13 ak1=a;
14 pk1=1;
15
16 while(nk1>0)
17 {
18 ek=nk1%2;
19 if(ek==1)
20 pk=pk1*ak1;
21 else
22 pk=pk1;
23 ak=ak1*ak1;
24 nk=nk1/2;
25
26 // devin valori "vechi" inainte de noua actualizare
27 nk1=nk;
28 ak1=ak;
29 pk1=pk;
30 }
31
32 return pk;
33 }
34
35 int main()
36 {
37 int a=2;
38 //int n=234; // aˆn = prea mare !!!
39 //int n=30;
40 int n=6; // par: 2ˆ6 = 64 ... usor de verificat!
41 //int n=5; // impar: 2ˆ5 = 32 ... usor de verificat!
42
43 int rez=exponentiere_rapida(a,n);
44
45 cout<<a<<"ˆ"<<n<<" = "<<rez<<"\n";
46
47 return 0;
48 }
49 /*
50 2ˆ30 = 1073741824
51
52 Process returned 0 (0x0) execution time : 0.076 s
53 Press any key to continue.
54 */

~
„iniţializări:
„
„
„
„
„
„p1 1;
„
„
„
„n1 n;
„
„
„
„
„
„a1 a;
„
„
„
„
„calcul pentru k ' 0:
„
„e
‚
„ k nk1 %2 " r0, 1x; k ' 0 (B.4.2)
„
„ pk1 ˜ ak1 , k ' 0, dacă ek 1; ak1
1
„
„ ak1 modifică produsul pk1 
„
„ k w
p
„ k ' 0, dacă ek 0; ak1
0
„
„ pk1 , 1 nu modifică produsul pk1 
„
„
„actualizări k ' 0:
„
„
„
„
„a ak1 , k ' 0;
2
„
„
„
„
k
„nk nk1 ©2 k ' 0; şi nk1 j 0 desigur ! 
„
€
APPENDIX B. EXPONENŢIERE RAPIDĂ B.4. CODUL 351

Observaţia 4. Instrucţiunile care sunt ”ı̂n plus” se pot elimina. Codul următor arată acest lucru:

Listing B.4.3: exponentiere rapida3.cpp


1 #include<iostream> // actualizare p inainte de actualizare a si n
2 // ... si simplificarea codului ...
3
4 using namespace std;
5
6 int exponentiere_rapida(int a, int n) // p=aˆn
7 {
8 int nk;
9 int ak;
10 int ek;
11 int pk;
12
13 nk=n;
14 ak=a;
15 pk=1;
16
17 while(nk>0)
18 {
19 ek=nk%2;
20 if(ek==1)
21 pk=pk*ak;
22 else
23 pk=pk; // nu are rost ... !!!
24 ak=ak*ak;
25 nk=nk/2;
26 }
27
28 return pk;
29 }
30
31 int main()
32 {
33 int a=2;
34 //int n=234; // aˆn = prea mare !!!
35 //int n=30;
36 int n=6; // par: 2ˆ6 = 64 ... usor de verificat!
37 //int n=5; // impar: 2ˆ5 = 32 ... usor de verificat!
38
39 int rez=exponentiere_rapida(a,n);
40
41 cout<<a<<"ˆ"<<n<<" = "<<rez<<"\n";
42
43 return 0;
44 }
45 /*
46 2ˆ30 = 1073741824
47
48 Process returned 0 (0x0) execution time : 0.076 s
49 Press any key to continue.
50 */

Observaţia 5. Produsul poate deveni foarte mare şi din cauza asta se cere rezultatul modulo un
număr prim. Codul următor arată acest lucru:

Listing B.4.4: exponentiere rapida MOD.cpp


1 #include<iostream>
2
3 using namespace std;
4
5 int MOD=1000;
6 int nropr; // numar operatii (inmultiri) la exponentiere rapida
7
8 int exponentiere_rapida(int a, int n) // p=aˆn
9 {
10 int p = 1;
11 while(n>0)
12 {
13 if(n % 2 == 1) p = (p * a)%MOD;
APPENDIX B. EXPONENŢIERE RAPIDĂ B.5. CHIAR ESTE RAPIDĂ? 352

14 a = (a * a)%MOD;
15 n = n / 2;
16 nropr=nropr+6; // n%2, p*a, a*a, (a * a)%MOD si n/2
17 }
18 return p;
19 }
20
21 int main()
22 {
23 int a=1234;
24 int n=1e+9; // 10ˆ9
25 int rezn; // rezultat exponentiere naiva
26
27 rezn=exponentiere_rapida(a,n);
28
29 cout<<"rezr = "<<rezn<<" nropr = "<<nropr<<"\n";
30
31 return 0;
32 }
33 /*
34 rezr = 376 nropr = 180
35
36 Process returned 0 (0x0) execution time : 0.021 s
37 Press any key to continue.
38 */

B.5 Chiar este rapidă?


De ce se numeşte ”rapidă”?
1 000 000 000
Să presupunem că vrem să calculăm 1234 şi pentru că rezultatul are foarte multe
cifre ... ne vom mulţumi, la fiecare calcul, cu ultimele 3 cifre!
Aceasta este metoda ”naivă”:

Listing B.5.1: exponentiere naiva MOD.cpp


1 #include<iostream>
2
3 using namespace std;
4
5 int MOD=1000;
6 int nropn=0; // numar operatii (inmultiri) la exponentiere naiva
7
8 int exponentiere_naiva(int a, int n) // p=aˆn
9 {
10 int p = 1;
11 for(int k=1; k<=n; k++)
12 {
13 p=(p*a)%MOD;
14 nropn=nropn+1;
15 }
16 return p;
17 }
18
19 int main()
20 {
21 int a=1234;
22 int n=1e+9; // 10ˆ9
23 int rezn; // rezultat exponentiere naiva
24
25 rezn=exponentiere_naiva(a,n);
26
27 cout<<"rezn = "<<rezn<<" nropn = "<<nropn<<"\n";
28
29 return 0;
30 }
31 /*
32 rezn = 376 nropn = 2000000000
33
34 Process returned 0 (0x0) execution time : 21.239 s
35 Press any key to continue.
36 */
APPENDIX B. EXPONENŢIERE RAPIDĂ B.6. REZUMAT INTUITIV! 353

Iar aceasta este metoda ”rapidă”:

Listing B.5.2: exponentiere rapida MOD.cpp


1 #include<iostream>
2
3 using namespace std;
4
5 int MOD=1000;
6 int nropr; // numar operatii (inmultiri) la exponentiere rapida
7
8 int exponentiere_rapida(int a, int n) // p=aˆn
9 {
10 int p = 1;
11 while(n>0)
12 {
13 if(n % 2 == 1) p = (p * a)%MOD;
14 a = (a * a)%MOD;
15 n = n / 2;
16 nropr=nropr+6; // n%2, p*a, ..%MODa*a, (a * a) ... %MOD si n/2
17 }
18 return p;
19 }
20
21 int main()
22 {
23 int a=1234;
24 int n=1e+9; // 10ˆ9
25 int rezn; // rezultat exponentiere naiva
26
27 rezn=exponentiere_rapida(a,n);
28
29 cout<<"rezr = "<<rezn<<" nropr = "<<nropr<<"\n";
30
31 return 0;
32 }
33 /*
34 rezr = 376 nropr = 180
35
36 Process returned 0 (0x0) execution time : 0.021 s
37 Press any key to continue.*/

Numărul de operaţii:
ˆ cu metoda naivă acest număr este 2 000 000 000
ˆ cu metoda rapidă este 180.

Timpul de execuţie (pe calculatorul pe care am testat eu!):


ˆ cu metoda naivă este 21.239 secunde
ˆ cu metoda rapidă este 0.021 secunde
deci ... cam de 1000 de ori mai rapidă!

Iar ca număr de operaţii ... una este să faci 2 miliarde de operaţii şi alta este să faci
numai 180 de operaţii de acelaşi tip (de fapt sunt numai 30 de paşi dar la fiecare pas se fac 5
sau 6 operaţii aritmetice)!

B.6 Rezumat intuitiv!


Revenind la relaţia
234 128 1 64 1 32 1 16 0 8 1 4 0 2 1 1 0
a a  ˜ a  ˜ a  ˜ a  ˜ a  ˜ a  ˜ a  ˜ a 

să privim cu atenţie ce este aici!


Putem calcula uşor acest produs de la dreapta spre stânga!
2 4 8 16 32 64 128
Secvenţa a , a , a , a , a , a , a se poate genera cu instrucţiunea a=a*a;
Puterile expresiilor (marcate cu roşu, cele care sunt ı̂ntre paranteze) se obţin (tot de la dreapta
spre stânga) prin ı̂mpărţiri succesive ale lui n la 2 şi preluând restul. Dacă restul este 0 atunci
0
puterea respectivă este 0 iar ... 1 ... deci, nu mai apare ı̂n produs!
APPENDIX B. EXPONENŢIERE RAPIDĂ B.6. REZUMAT INTUITIV! 354

Asta este tot!


Deci, secvenţa de cod este:

Listing B.6.1: secventa cod.cpp


1 int p = 1;
2 while(n>0)
3 {
4 if(n % 2 == 1) p = (p * a);
5 a = a * a;
6 n = n / 2;
7 }

29
şi nu mai trebuie ”atâtea formule matematice”!

29
Este o glumă!
Index

%I64d, 117 get(), 21, 146, 148, 220, 222


şir periodic, 11 get(linie,500), 21
200 000 000 000 000 000, 64 getline, 22, 146, 148, 219, 220, 222
gets, 93
algoritm de tip succesor, 128, 216 grad al permutării, 311
atoi, 282 Greedy, 216
atol, 145
I.d.k.:
backtracking, 93, 136, 222, 286 I don’t know who the author is., v
backtracking nerecursiv, 137 implementare recursivă, 216
backtracking recursiv, 131, 132 inducţie matematică, 203
baza de numeraţie, 229 isdigit, 21
bitset, 49, 236 isupper, 21
bool, 219
lexicografic, 170
brute-force, 24
Liceul Militar Dimitrie Cantemir, vi
bubble sort, 240
lungime ciclu ı̂n permutare, 312
buff, 282
bytes, 309 make pair, 236, 243
matrice binară, 15
căutare binară, 7, 16, 19, 157, 166, 188, 194, memcpy, 295
264 memset, 71, 72, 321
cel mai mic multiplu comun, 277 metoda Greedy, 308
char, 219, 220, 222 model matematic, 317
ciclu ı̂n permutare, 311
cifra de control, 229 new, 108, 130
CINOR, vi normalizare, 310
ciur, 59 NULL, 145, 146, 219, 220, 222
cmmdc, 60, 278, 283, 312
observaţie cheie, 165
cmmmc, 283, 311
oglindire vector, 162
codificare, 216
operaţii cu numere mari, 224
complexitate logaritmică, 229
ordine lexicografică, 192, 229, 311, 312
condiţii de continuare, 296
count, 49 pair, 73, 236, 242, 243
cumulare, 4 first, 73
make pair, 73
delete, 108, 130 second, 73
descompunere ı̂n factori primi, 311 parsare, 166, 177
divide et impera, 130 parsarea expresiilor, 144
permutare, 311
eratostene, 60
precalculare, 168
euclid, 60
preprocesare, 157
fgets, 282 produsului cartezian, 215
first, 236, 242, 243 programare dinamică, 129, 296
frecvenţa de apariţii, 12 push back, 242, 243
funcţie recursivă, 177 putere, 60, 62
reprezentarea pe biti, 296
generarea combinărilor, 286 reverse, 45
generarea submultimilor, 296
get, 219, 220 scrierea ı̂n baza 2, 224, 240

355
INDEX INDEX 356

second, 236, 242, 243 structură de date, 317


sens invers, 16 sume parţiale, 11, 168, 174
set, 8, 16
size(), 242, 243 tehnica Greedy, 309
sliding window, 12 tip boolean, 309
SMENUL LUI MARS, 307 tip record, 309
soluţie naivă, 16, 168 trage cu tunul, iv
sort, 45, 46, 122, 280, 281 trim, 220
sortare, 216, 235, 264, 309, 310 trucul lui Mars, 188
sortare prin inserare, 19 two pointers, 166
sortare prin selecţie, 240
sqrt, 49 Universitatea Ovidius, vi
stivă, 161, 177, 216, 286 unsigned long long, 46
stiva sistemului, 178
STL, 8 vector, 7, 16, 54, 97, 106, 111, 168, 236, 242,
strategie Greedy, 253 243
strchr, 21, 145, 146, 219, 222, 282 begin(), 54, 97, 106, 111
strcmp, 21, 220, 222 clear(), 111
strcpy, 21, 146, 182, 219, 220, 222 end(), 54, 97, 111
string-uri, 178 erase, 106
strlen, 21, 73, 93, 146 iterator, 54
strncat, 222 push back, 54, 97, 106, 111
strncmp, 182 reverse, 97
strncpy, 21, 182 size(), 54, 106, 111
strstr, 222 sort, 111
strtok, 145, 146, 219, 220, 222 vector caracteristic, 177, 296, 309
struct, 21, 46, 106, 108, 111, 122, 186, 190, vector de frecvenţă, 174, 192
209, 219, 309 vector de prezenţă, 261
structură, 18 vector de sume, 11
Bibliografie

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

357
BIBLIOGRAFIE BIBLIOGRAFIE 358

[26] Odăgescu, I., Smeureanu, I., Ştefănescu, I.; Programarea avansată a calculatoarelor personale,
Ed. Militară, Bucureşti 1993
[27] Odăgescu, I.; Metode şi tehnici de programare, Ed. Computer Lobris Agora, Cluj, 1998
[28] Popescu Anastasiu, D.; Puncte de articulaţie şi punţi ı̂n grafuri, Gazeta de Informatică nr.
5/1993
[29] Răbâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire
/2007/Info/Lista_probleme_2000-2007.pdf
[30] Răbâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire
/2007/Info/Rezolvari_C09.pdf

[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 Peda-
gogică, Bucureşti, 1982
[37] Tudor, S.; Informatică - profilul 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 Filosofie 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 Pub-
lishing 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 BIBLIOGRAFIE 359

[55] *** - https://vdocumente.com/algoritmi-i-structuri-de-date.html


[56] *** - https://pdfslide.net/documents/algoritmi-si-structuri-de-dat
e-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
[83] *** - https://cs.pub.ro/images/NewsLabsImages/Teste-admitere-informa
tica-UPB-2020.pdf
Lista autorilor
problemelor şi indicaţiilor

Adrian Panaete, 124, 307, 309 Gheorghe Dodescu, vi


Alin Burţa, 24, 52
Alina Pintescu, 181 Ilie Vieru, 125
Ana Intuneric, 104 Ioan Cristian Pop, 165
Ioan-Bogdan Iordache, 11
Carmen Mincă, 48, 203 Ioan-Cristian Pop, 11
Carmen Popescu, 253 Ion Văduva, vi
Ciprian Cheşcă, 317 Ionel-Vasile Piţ-Rada, 168
Constantin Tudor, vi
Cristina Sichim, 14, 206 Liliana Chira, 198
Livia Ţoca, 229
Dan Octavian Dumitraşcu, 194
Marinel Şerban, 30, 124, 309
Dan Pracsiu, 67, 88, 96
Marius Nicoli, 32, 188, 234, 277, 320
Dana Lica, 42, 118, 196, 296
Miron Lucia, 309
Daniel Popa, 74
Daniela Tarasă, 286 Nistor Moţ, 4, 39, 183
Diana Ghinea, 15, 171
Paul Diac, 161
Emanuela Cerchez, 7, 18, 63, 82, 113, 215,
264, 311 Raluca Costineanu, 177, 192, 224
Rodica Pintea, 261
Filonela Rodica Bălaşa, 211
Flavius Boian, 11 Sandor Lukacs, 56
Stelian Ciurea, 15, 174, 177
Genoiu Nicolae, 125 Szabo Zoltan, 314

360
What’s the next?
ORNL’s Frontier First to Break the Exaflop Ceiling

H T T P S :// E N . W I K I P E D I A . O R G / W I K I /F R O N T I E R _( S U P E R C O M P U T E R )#/ M E D I A /F I L E :F R O N T I E R _S U P E R C O M P U T E R _(2). J P G

Over time
the following steps
will lead you to the value
you seek for yourself
now!

S-ar putea să vă placă și