Sunteți pe pagina 1din 431

Informatică

Olimpiada - cls7

2023-10
PROBLEME DE INFORMATICĂ
date la olimpiade

OJI + ONI
ı̂n

**** 2023 2022 2021 2020


2019 2018 2017 2016 2015
2014 2013 2012 2011 2010

Dacă ştii să rezolvi problemele date ı̂n ultimii 10-20 de ani
atunci vei şti să rezolvi problemele care se vor da ı̂n acest an!

... draft (ciornă) ...


*** Nobody is perfect ***

Adrian Răbâea, Ph.D.

https://www.scribd.com/user/552245048/Adi-Rabaea (sprijin financiar![ 19 ])

13:15 happy birthday 2023-10-10


Figura 1: Descompunerea canonică a lui f

”O imagine valorează
cât 1000 de cuvinte!” 1

x1 x1
x3 x3
x2 x2
elevii f = aleg ”şeful” elevi
ı̂n clasă ı̂n clasă
fac echipe echipei
y1 y1
y3 y3
y2 y2

p : proiecţia canonică (se fac listele) i : injecţia canonică


(şefii revin)
(ı̂n clasă)
xr x1

liste cu elevi f¯ = predau lista şefi de echipe


”şefului” echipei Im f 
yr y1

Figura 2: Un desen ... i ` f¯ ` p f

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

I would like to dedicate this book ...

a ”to myself” ...


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

a to people impacted by the book


a to my nephew Adam.

( in ascending order! )

2
https://www.femalefirst.co.uk/books/carol-lynne-fighter-1034048.html
3
https://otiliaromea.bandcamp.com/track/dor-de-el
4
https://en.wikipedia.org/wiki/To_be,_or_not_to_be
5
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!.
6
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
7 8
dificilă! :-) Vezi, de exemplu, IOI2020 şi IOI2019 , IOI2015 . (Numai GCC a fost mereu!)
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
9
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
10
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!).


6
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’ ...
7
IOI2019 şi IOI2020 au a permis utilizarea limbajelor de programare C++ şi Java
8
IOI2015 a permis utilizarea limbajelor de programare C++, Java, Pascal, Python şi Ruby
9
Vezi cele 5 secunde pentru Timp maxim de executare/test din problema ”avârcolaci” - ONI2014 clasa a 11-a
10
https://en.wikipedia.org/wiki/Computer

iii
În perioada 2017-2020 cele mai puternice calculatoare din lume au fost: ı̂n noiembrie 2017
11
ı̂n China, ı̂n noiembrie 2019 ı̂n SUA şi ı̂n iunie 2020 ı̂n Japonia. În iunie 2022 ”Frontier”
12
depăşeşte pragul ”exascale”! (1.102 Exaflop/s). ”Peta” a fost depăşit ı̂n 2008, ”tera” ı̂n 1997,
13
”giga” ı̂n 1972. . Pentru ce a fost mai ı̂nainte, vezi https://en.wikipedia.org/wiki/Li
st_of_fastest_computers.
14
O mică observaţie: ı̂n 2017 a fost prima ediţie a olimpiadei EJOI ı̂n Bulgaria şi ... tot
15
ı̂n Bulgaria a fost şi prima ediţie a olimpiadei IOI ı̂n 1989. Dar ... prima ediţie a olimpiadei
16
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ă ”ı̂ncepătorilor” interesaţi de aceste teme! Nu se
adresează ”avansaţilor” şi nici cârcotaşilor! Sunt evidente sursele
”de pe net” (şi locurile ı̂n care au fost folosite); cred că nu sunt
necesare ”ghilimele anti-plagiat” şi precizări la fiecare pas!
Şi un ultim gând: criticile şi sfaturile sunt utile dacă au valoare
reală! Dacă sunt numai aşa ... cum critică lumea la un meci de
fotbal ... sau cum, pe bancă ı̂n parc, unul ”ı̂şi dă cu părerea”
despre rezolvarea problemelor economice ale ţării ... atunci ... !!!
17
”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-Rabaea
https://www.scribd.com/user/552245048/Adi-Rabaea
https://drive.google.com/drive/folders/1hC5PZuslCdS95sl37SW46H-qy59GRDGZ

Bistriţa, 10th October 2023 Adrian Răbâea

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

”I want to thank God most of all because


without God
I wouldn’t be able to do any of this.” 18

and

I thank everyone for their support 19

and/or
suggestions!

18
I.d.k.: ”I don’t know who the author is.”
19
donation/donaţie:
. name: RABAEA AUREL ADRIAN, IBAN: RO22BRDE060SV11538970600, SWIFT: BRDEROBUXXX

v
Despre autor

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

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


email: adrian1803@gmail.com skype: adrian.r53
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://dmi.cunbm.utcluj.ro/
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;
21
Titlul tezei: Algoritmi paraleli şi aplicaţii pe maşini virtual paralele
22
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
23
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ă
24
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-
25
matică, Departamentul de Informatică
26
- (1992-1979) Centrul de Informatică şi organizare CINOR, Bucureşti
27
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)
20
https://dmi.cunbm.utcluj.ro/?page_id=2
21
http://opac.biblioteca.ase.ro/opac/bibliographic_view/149021
22
http://www.ionivan.ro/2015-PERSONALITATI/Dodescu.htm
23
http://old.fmi.unibuc.ro/ro/prezentare/promotii/promotia1978informatica_10ani/
24
https://ro.wikipedia.org/wiki/Ion_V%C4%83duva
25
https://fmi.univ-ovidius.ro/
26
https://www.cinor.ro/index.htm
27
https://www.cantemircml.ro/

vi
Dhawan Sanjeev, Kulvinder Singh, Eduard-Marius Craciun, Adrian Răbâea, Amit Batra;
Next-Cart Recommendation by Utilizing Personalized Item Frequency Information in Online Web
Portals, Neural Processing Letters, 2023; https://doi.org/10.1007/s11063-023-11207-2

Sanjeev Dhawan, Kulvinder Singh, Adrian Răbâea, Amit Batra;


ImprovedGCN: An efficient and accurate recommendation system employing lightweight graph con-
volutional networks in social media; Electronic Commerce Research and Applications, 2022, Vol.
55, Pages 101191-101207; DOI: https://doi.org/10.1016/j.elerap.2022.101191

Sanjeev Dhawan, Kulvinder Singh, Adrian Răbâea, Amit Batra;


Session centered Recommendation Utilizing Future Contexts in Social Media; Analele Ştiinţifice
ale Universităţii Ovidius Constanţa - Seria Matematică, Vol. 29 (3), 2021, 91-104; DOI:
https://doi.org/10.2478/auom-2021-0036

E.M. Craciun, A. Răbâea, S. Das;


Cracks Interaction in a Pre-Stressed and Pre-Polarized Piezoelectric Material;
Journal of Mechanics, 36(2), 2020, 177-182; DOI: https://doi.org/10.1017/jmech.2019.57
https://www.cambridge.org/core/journals/journal-of-mechanics/article/abs/cracks-interaction-
in-a-prestressed-and-prepolarized-piezoelectric-material/3938BF0AD79D5BE6B81BC4FB6BE80208

EM Craciun, A Răbâea, MF Popa, CI Mihailov;


Crack propagation in the human bone. Mode I of fracture; Analele Ştiinţifice ale Univ. Ovidius
Constanţa - Seria Matematică, Vol. 26(2), 2018, 59-70; DOI: https://doi.org/10.2478/auom-2018-
0018

EM Craciun, M Marin, A Răbâea;


Anti-plane crack in human bone. I. Mathematical modelling; Analele Ştiinţifice ale Univer-
sităţii Ovidius Constanţa - Seria Matematică, Volume 26, Issue 1, 2018, Pages 81-90; DOI:
https://doi.org/10.2478/auom-2018-0004

T. Sadowski, E. M. Craciun, A. Răbâea, L. Marsavina;


Mathematical modeling of three equal collinear cracks in an orthotropic solid; Meccanica 51, 329-
339 (2016); https://doi.org/10.1007/s11012-015-0254-5

E.M. Craciun, T. Sadowski, A. Răbâea;


Stress concentration in an anisotropic body with three equal collinear cracks in Mode II of frac-
ture. I. Analytical study; ZAMM Journal of Applied Mathematics and Mechanics / Zeitschrift
fr Angewandte Mathematik und Mechanik, Vol. 94 (9); September 2014, Pages 721-729;
https://doi.org/10.1002/zamm.201200293

E-M Craciun, T. Sadowski, L. Marsavina, A. Răbâea;


Mathematical Aspects Regarding Cracks Behaviour in Wood Composites; Key Engineering Mate-
rials 601:108-111, March 2014; DOI: 10.4028/www.scientific.net/KEM.601.108

ƒ Probleme de informatică date la bacalaureat


ƒ Probleme de informatică date la olimpiade - I.O.I.
ƒ Probleme de informatică date la olimpiade - liceu
ƒ Probleme de informatică date la olimpiade - gimnaziu
ƒ Algoritmi elementari + olimpiade EJOI gimnaziu
ƒ Algoritmi şi structuri de date + olimpiade EGOI liceu
ƒ Programa pentru bacalaureat - informatică liceu
ƒ Programa pentru olimpiadă - informatică gimnaziu + liceu

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
Cuprins

Prefaţă iii

Cuprins viii

Lista figurilor xv

Lista tabelelor xvii

Lista programelor xviii

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


1 OJI 2023 2
1.1 palindrom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.3 Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2 primprim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.3 Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2 OJI 2022 16
2.1 patratele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.1.3 Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 pseudocmp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.2.3 Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

3 OJI 2021 - OSEPI 52


3.1 campionat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
3.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
3.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
3.2 exclusiv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
3.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

4 OJI 2020 57
4.1 foto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
4.2 wind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
4.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

viii
4.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
4.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

5 OJI 2019 77
5.1 poarta - OJI 2019 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
5.2 valutar - OJI 2019 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
5.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
5.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
5.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90

6 OJI 2018 91
6.1 puzzle - OJI 2018 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
6.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
6.2 tbile - OJI 2018 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
6.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

7 OJI 2017 99
7.1 Cursuri - OJI 2017 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
7.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
7.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
7.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
7.2 joc - OJI 2017 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
7.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
7.2.2 Codul sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
7.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

8 OJI 2016 119


8.1 axyz - OJI 2016 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
8.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
8.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
8.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
8.2 galerie - OJI 2016 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
8.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
8.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
8.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

9 OJI 2015 129


9.1 ech - OJI 2015 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
9.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
9.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
9.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
9.2 lasere - OJI 2015 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
9.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
9.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
9.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146

10 OJI 2014 147


10.1 pătrat - OJI 2014 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
10.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
10.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
10.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
10.2 schi - OJI 2014 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
10.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
10.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
10.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162

11 OJI 2013 163


11.1 compar - OJI 2013 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
11.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
11.1.2 Codul sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
11.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
11.2 unific - OJI 2013 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
11.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
11.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
11.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171

12 OJI 2012 172


12.1 arme - OJI 2012 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
12.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
12.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
12.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
12.2 triunghi - OJI 2012 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
12.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
12.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
12.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183

13 OJI 2011 184


13.1 grupe - OJI 2011 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
13.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
13.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
13.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
13.2 litere - OJI 2011 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
13.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
13.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
13.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189

14 OJI 2010 190


14.1 Cuvinte - OJI 2010 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
14.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
14.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
14.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
14.2 Zar - OJI 2010 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
14.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
14.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
14.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195

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


15 ONI 2023 197
15.1 dominew . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
15.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
15.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
15.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
15.2 pix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
15.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
15.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
15.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
15.3 secvmin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
15.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
15.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
15.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
16 ONI 2022 205
16.1 microbuz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
16.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
16.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
16.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
16.2 raza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
16.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
16.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
16.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
16.3 text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
16.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
16.3.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
16.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215

17 ONI 2021 216


17.1 Cat2Pal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
17.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
17.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
17.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
17.2 virus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
17.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
17.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
17.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
17.3 zid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
17.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
17.3.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
17.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222

18 ONI 2020 - suspendat !!! 223

19 ONI 2019 224


19.1 domino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
19.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
19.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
19.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
19.2 tuburi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
19.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
19.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
19.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
19.3 venus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
19.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
19.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
19.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240

20 ONI 2018 241


20.1 evip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
20.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
20.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
20.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
20.2 nxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
20.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
20.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
20.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
20.3 viitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
20.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
20.3.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
20.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
21 ONI 2017 247
21.1 carte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
21.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
21.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
21.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
21.2 ghinde . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
21.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
21.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
21.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
21.3 submat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
21.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
21.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
21.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255

22 ONI 2016 256


22.1 birouri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
22.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
22.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
22.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
22.2 cristale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
22.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
22.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
22.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
22.3 parchet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
22.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
22.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
22.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281

23 ONI 2015 282


23.1 cript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
23.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
23.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
23.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
23.2 scadere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
23.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
23.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
23.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
23.3 tv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
23.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
23.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
23.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298

24 ONI 2014 299


24.1 codat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
24.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
24.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
24.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
24.2 nod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
24.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
24.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
24.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
24.3 placa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
24.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
24.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
24.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
25 ONI 2013 325
25.1 secvp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
25.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
25.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
25.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
25.2 patrate2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
25.2.1 *Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
25.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
25.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
25.3 cursa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
25.3.1 *Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
25.3.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
25.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330

26 ONI 2012 331


26.1 bile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
26.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
26.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
26.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
26.2 proiecte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
26.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
26.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
26.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
26.3 zigzag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
26.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
26.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
26.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339

27 ONI 2011 340


27.1 joc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
27.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
27.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
27.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
27.2 mesaj . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
27.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
27.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
27.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
27.3 zar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
27.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
27.3.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
27.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344

28 ONI 2010 345


28.1 char . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
28.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
28.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
28.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
28.2 maraton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
28.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
28.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
28.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
28.3 roboti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
28.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
28.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
28.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
Appendix A Programa olimpiadei - gimnaziu 357
A.1 Clasa a V-a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
A.2 Clasa a VI-a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
A.3 Clasa a VII-a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
A.4 Clasa a VIII-a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
A.5 Barajul de selecţie a lotului naţional lărgit . . . . . . . . . . . . . . . . . . . . . . . 359
A.6 Note . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359

Appendix B main(), cin, cout, fin, fout 360


B.1 Funcţia main() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360

Appendix C ”Instalare” C++ 362


C.1 Kit OJI 2017 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
C.1.1 Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
C.1.2 Folder de lucru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
C.1.3 Utilizare Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
C.1.4 Setări Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
C.1.5 Multe surse ı̂n Code Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
C.2 winlibs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
C.2.1 GCC şi MinGW-w64 pentru Windows . . . . . . . . . . . . . . . . . . . . . 372
C.2.2 PATH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
C.2.3 CodeBlocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375

Appendix D Exponenţiere rapidă 385


D.1 Analogie baza 2 cu baza 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
D.2 Notaţii, relaţii şi formule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
D.3 Pregătire pentru scrierea codului! . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
D.4 Codul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
D.5 Chiar este rapidă? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
D.6 Rezumat intuitiv! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391

Appendix E Căutare binară 393


E.1 Mijlocul = ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393
E.2 Poziţie oarecare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
E.2.1 Varianta iterativă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
E.2.2 Varianta recursivă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
E.3 Poziţia din stânga . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
E.3.1 Varianta iterativă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
E.3.2 Varianta recursivă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
E.4 Poziţia din dreapta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
E.4.1 Varianta iterativă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
E.4.2 Varianta recursivă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400

Appendix F ”Vecini” ... 401


F.1 Direcţiile N, E, S, V . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401

Index 403

Bibliografie 405

Lista autorilor 408


Lista figurilor

1 Descompunerea canonică a lui f . . . . . . . . . . . . . . . . . . . . . . . . . . . . i


2 Un desen ... i ` f¯ ` p f . . . . . . . . . . . . . . . . . . . . . . . . . . . i

2.1 oji2022-patratele12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 oji2022-patratele3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.3 oji2022-patratele un patratel fără linii . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.4 oji2022-patratele-NS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.5 oji2022-patratele-VE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.6 oji2022-patratele-pereti-codificati . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.7 oji2022-pseudocmp-frecvenţe1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.8 oji2022-pseudocmp-frecvenţe2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

6.1 Puzzle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.2 tbile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

7.1 joc1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108


7.2 joc2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
7.3 Puzzle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

8.1 galerie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122

10.1 patrat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

12.1 armeIR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173


12.2 triunghi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176

14.1 triunghi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193

19.1 domino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224


19.2 domino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
19.3 joc1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
19.4 tuburi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232

22.1 birouri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256

25.1 cursa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329

26.1 zigzag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337

27.1 joc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340


27.2 mesaj . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342

28.1 maraton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348

C.1 Fişierele din Kit OJI 2017 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362


C.2 CodeBlocks & C++ Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
C.3 Ce conţine C:¯ OJI ¯ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
C.4 Folder de lucru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
C.5 New -¿ Text document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
C.6 Schimbare nume fişier şi nume extensie fişier . . . . . . . . . . . . . . . . . . . . . . 365
C.7 Confirmare schimbare extensie ı̂n .cpp . . . . . . . . . . . . . . . . . . . . . . . . . 366

xv
C.8 Pregătit pentru Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
C.9 Pregătit pentru a scrie cod de program C++ ı̂n Code::Blocks . . . . . . . . . . . . 366
C.10 Primul cod de program C++ ı̂n Code::Blocks . . . . . . . . . . . . . . . . . . . . . 367
C.11 Build - compilare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
C.12 0 error(s), 0 warning(s) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
C.13 Run - execuţie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
C.14 Executat corect: a făcut “nimic” . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
C.15 Settings  % Compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
C.16 Toolchain executables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
C.17 Unde sunt acele programe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
C.18 Multe surse ı̂n Code Blocks - setări . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
C.19 Multe surse in Code Blocks - exemple . . . . . . . . . . . . . . . . . . . . . . . . . 371
C.20 mingw64 pe D: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
C.21 search path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
C.22 System properties –¿ Advanced . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
C.23 Environment Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
C.24 Edit Environment Variables –¿ New . . . . . . . . . . . . . . . . . . . . . . . . . . 374
C.25 Calea şi versiunea pentru gcc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
C.26 Settings –¿ Compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
C.27 Toolchain executables –¿ Auto-detect . . . . . . . . . . . . . . . . . . . . . . . . . . 376
C.28 New –¿ Text Document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
C.29 New text Document.txt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
C.30 Schimbare nume şi extensie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
C.31 Moore apps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
C.32 Look for another app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
C.33 Cale pentru codeblocks.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
C.34 Selectare codeblocks.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
C.35 Editare test01.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
C.36 Compilare test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
C.37 Mesaje după compilare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381
C.38 Execuţie test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381
C.39 Rezultat execuţie test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
C.40 Fişiere apărute după compilare! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
C.41 Creare test02.cpp gol! + ¡dublu click¿ sau ¡Enter¿ . . . . . . . . . . . . . . . . . . 382
C.42 Lista programelor de utilizat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
C.43 Selectare Code::Blocks IDE pentru fişierele .cpp . . . . . . . . . . . . . . . . . . 383
C.44 Editare+Compilare+Execuţie pentru test02 . . . . . . . . . . . . . . . . . . . . . 383
C.45 Selectare tab ce conţine test01.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 384

D.1 Analogie B2 cu B10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385

E.1 căutare binară: mijlocul zonei ı̂n vector . . . . . . . . . . . . . . . . . . . . . . . . 393

F.1 ”vecini” ı̂n matrice şi sistem Oxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401


F.2 Toţi ”pereţii” sunt la N sau la V ı̂n matricea ”bordată” . . . . . . . . . . . . . . . 401
F.3 Toţi ”pereţii” sunt codificaţi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402
Lista tabelelor

23.1 cript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283

xvii
Lista programelor

1.1.1 oji2023-palindrom.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.2 oji2023-cecker-palindrom.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.1 oji2023-primprim.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2.2 oji2023-cecker-primprim.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.1.1 oji2022-patratele-v1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.1.2 oji2022-patratele-v2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.1.3 patratele-checker.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.2.1 oji2022-pseudocmp-v1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.2.2 oji2022-pseudocmp-v2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2.2.3 oji2022-pseudocmp-v3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.2.4 oji2022-pseudocmp-frecvenţe3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 48
2.2.5 pseudocmp-checker.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.1.1 foto 1 c.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.1.2 foto 2 c.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.1.3 foto 1 cpp.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
4.1.4 foto 2 cpp.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.1.5 foto AB.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.1.6 foto AF.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.1.7 foto cm rec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.1.8 fotoCS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.1.9 fotoFB.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.1.10 fotoKB.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.1.11 fotoMM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
4.2.1 wind.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
4.2.2 wind.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
4.2.3 windCM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
4.2.4 windCS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.1.1 poarta1 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.1.2 poarta2 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.1.3 poarta3 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.1.4 poarta4 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
5.2.1 valutar cpp 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
5.2.2 valutar1 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
5.2.3 valutar2 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
6.1.1 p2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.1.2 p4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
6.2.1 t1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.2.2 t2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
7.1.1 cursuri Dan 100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
7.1.2 cursuri NlogN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
7.1.3 cursuri NxN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
7.1.4 cursuri raluca 100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
7.1.5 cursuri rm.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
7.2.1 joc.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
7.2.2 joc dl.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
7.2.3 joc pracsiu.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
7.2.4 joc rm.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
8.1.1 axyz.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
8.2.1 galerie0.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124

xviii
8.2.2 galerie1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
9.1.1 ech add11.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
9.1.2 ech lucia miron.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
9.1.3 ech Ovidiu Dumitrescu.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
9.1.4 ech raluca costinescu.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
9.1.5 ech vasile pit rada.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
9.2.1 lasere adrian pintea.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
9.2.2 lasere emanuela cerchez.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
9.2.3 lasere lucia miron.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
9.2.4 lasere nicu vlad.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
9.2.5 lasere raluca costinescu.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
9.2.6 lasere vasile pit rada.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
10.1.1 patratdl.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
10.1.2 patratdt.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
10.1.3 patratMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
10.1.4 patratsj.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
10.1.5 patratvg.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
10.2.1 schi.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
10.2.2 schidL.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
10.2.3 schidt.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
10.2.4 schiNQ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
10.2.5 schiNQcb.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
10.2.6 schiSortQ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
11.1.1 compar dm.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
11.1.2 compara en.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
11.2.1 unific em.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
11.2.2 unific eugen 1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
12.1.1 arme Adrian.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
12.1.2 arme ema.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
12.1.3 arme Ovidiu.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
12.2.1 tri adrian .cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
12.2.2 tri marinel .cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
12.2.3 triunghi Eugen00.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
12.2.4 triunghi Eugen11.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
12.2.5 triunghi Eugen22.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
12.2.6 triunghi O Marcu matrice.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
12.2.7 triunghi O Marcuo.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
13.1.1 RCGRUPE.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
13.2.1 litere.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
14.1.1 7 pr1 s.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
14.2.1 7 pr2 s.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
15.1.1 7 dominew.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
15.2.1 7 pix.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
15.3.1 7 secvmin.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
19.1.1 Domino 1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
19.1.2 domino 2 assert.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
19.1.3 domino 3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
19.2.1 tuburi 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
19.2.2 tuburiOficial.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
19.3.1 venus 1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
19.3.2 venus 2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
21.1.1 carte.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
21.2.1 ghinde.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
21.3.1 submat cpp.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
22.1.1 ABbirouri.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
22.1.2 CSbirouri.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
22.1.3 FUbirouri.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
22.2.1 ABcristale.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
22.2.2 CIcristale.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
22.2.3 CMcristale.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
22.2.4 CScristale.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
22.3.1 ABparchet.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
22.3.2 CSparchet.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
22.3.3 MSparchetBiti.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
22.3.4 MSparchetVector.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
23.1.1 cript emcerchez 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
23.1.2 cript LuciaMiron.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
23.1.3 cript odumitrascu.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
23.1.4 cript rcostineanu 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
23.2.1 scadere 100 emcerchez.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
23.2.2 scadere 100 odumitrascu.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
23.3.1 tv odumitrascu 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
24.1.1 codat mn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
24.1.2 codat patratic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
24.1.3 codat stack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
24.2.1 nod.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
24.2.2 nod dl pct1 2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
24.2.3 nod dt ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
24.3.1 placa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
24.3.2 placa1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
24.3.3 placa2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
24.3.4 placa3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
24.3.5 placa4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
24.3.6 placa5.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
24.3.7 placa6.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
24.3.8 placa8.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
24.3.9 placa10.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
25.1.1 secvp.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
26.1.1 bile.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
26.2.1 proiecte1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
26.2.2 proiecte2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
26.3.1 zigzag.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
28.1.1 CHAR.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
28.2.1 MARATON.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
28.2.2 MARAton2.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
28.3.1 Roboti.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
B.1.1 sss1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
B.1.2 sss2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
D.4.1 exponentiere rapida1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
D.4.2 exponentiere rapida2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388
D.4.3 exponentiere rapida3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
D.4.4 exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
D.5.1 exponentiere naiva MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
D.5.2 exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
D.6.1 secventa cod.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
E.2.1 cautare binara-v1-iterativ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
E.2.2 cautare binara-v1-recursiv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
E.3.1 cautare binara-v2-iterativ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
E.3.2 cautare binara-v2-recursiv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
E.4.1 cautare binara-v3-iterativ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
E.4.2 cautare binara-v3-recursiv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
Part I

OJI - Olimpiada judeţeană de


informatică

1
Capitolul 1

OJI 2023

1.1 palindrom
Problema 1 - Palindrom 100 de puncte
Un număr se numeşte palindrom dacă citit de la stânga la dreapta este identic cu numărul
citit de la dreapta la stânga. De exemplu, numerele 131 şi 15677651 sunt palindromuri. Un număr
care nu este palindrom poate fi transformat ı̂n palindrom adăugând la dreapta sa una sau mai
multe cifre.

Cerinţă

Dat fiind un şir de n numere naturale, scrieţi un program care să rezolve următoarele două
cerinţe:
1. să se determine numărul minim total de cifre care trebuie să fie adăugate, astfel ı̂ncât fiecare
valoare din şir să fie palindrom;
2. considerând că putem adăuga cel mult S cifre, să se determine numărul maxim de termeni
palindrom aflaţi pe poziţii consecutive ı̂n şirul obţinut.

Date de intrare

Fişierul de intrare palindrom.in conţine pe prima linie numărul C, reprezentând cerinţa care
trebuie să fie rezolvată (1 sau 2). Pe cea de a doua linie se află un număr natural n, reprezentând
numărul de valori din şir. Pe următoarele n linii se află cele n numere din şir, câte un număr pe o
linie. Dacă C 2, pe ultima linie a fişierului de intrare se va afla numărul natural S reprezentând
numărul maxim de cifre ce pot fi adăugate.

Date de ieşire

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


cerinţa C din fişierul de intrare.

Restricţii şi precizări

ˆ 1 & n & 50 000; 0 & S & 500 000


ˆ Numerele din şir au cel mult 50 de cifre.

# Punctaj Restricţii
1 15 C 1 şi n 1.
2 10 C 2, S 0, 1 $ n100 şi numerele din şir au cel mult 18 cifre.
3 14 C 1, 1 $ n 1 000 şi numerele din şir au cel mult 18 cifre.
4 15 C 2, S % 0, 1 $ n 1 000 şi numerele din şir au cel mult 18 cifre.
5 16 C 2, 1 000 $ n 50 000 şi numerele din şir au cel mult 18 cifre.
6 13 C 1, 1 000 $ n 50 000 şi numerele din şir au ı̂ntre 19 şi 50 de cifre.
7 17 C 2, 1 000 $ n 50 000 şi numerele din şir au ı̂ntre 19 şi 50 de cifre.

Exemple:

2
CAPITOLUL 1. OJI 2023 1.1. PALINDROM 3

palindrom.in palindrom.out Explicaţii


1 7 C 1, n 5. Pentru a transforma 12232 ı̂n palindrom trebuie
5 să adăugăm minimum două cifre (1223221),
12232 pentru 12345 trebuie să adăugăm minimum 4 cifre (123454321),
131 pentru 7717 trebuie să adăugăm minimum o cifră (77177),
12345 iar numerele 131 şi 0 sunt deja palindromuri.
0 În total 2+4+1=7.
7717
2 3 C 2, n 7, S 4, deci se pot adăuga maximum 4 cifre.
7 Putem adăuga cele 4 cifre numărului 12345 şi obţinem o
12232 secvenţă de lungime 3 formată numai din palindromuri (131,
131 123454321, 0). O altă variantă este de a adăuga o cifră la 7717
12345 şi două cifre la 1244 şi obţinem tot o secvenţă de lungime 3
0 formată numai din palindromuri (0, 77177, 124421).
7717 Pentru orice altă variantă, secvenţa de palindromuri obţinută
1244 are mai puţini termeni.
215809
4

1.1.1 Indicaţii de rezolvare

Propusă de: Prof. Emanuela Cerchez, Colegiul Naţional ”Emil Racoviţă”, Iaşi
Cerinţa 1. Vom citi succesiv numerele şi vom determina pentru fiecare număr citit numărul
minim de cifre care trebuie să fie adăugate pentru a transforma numărul respectiv ı̂n palindrom.
Pentru numere mari (de maximum 50 de cifre), vom citi fiecare număr caracter cu caracter,
până la ı̂ntâlnirea marcajului de sfârşit de linie şi vom reţine cifrele numărului ı̂ntr-un vector.
Pentru punctaj parţial (numere de maximum 18 cifre) se va citi numărul ı̂ntr-o variabilă de
tip long long int, apoi se vor extrage cifrele numărului şi se vor plasa ı̂ntr-un vector.
Pentru a determina numărul minim de cifre care trebuie adăugate la finalul numărului pentru a
transforma acest număr ı̂n palindrom putem determina lungimea celui mai lung sufix al numărului
care are proprietatea de a fi palindrom. Să notăm această lungime cu lgs iar lungimea numărului
cu lg. Numărul minim de cifre care trebuie să fie adăugate este nr lg  lgs (se adaugă la final
primele nr cifre ale numărului ı̂n ordine inversată). Desigur, pentru punctaj parţial este posibilă
şi o abordare ”prin ı̂ncercări”:

ˆ dacă numărul este deja palindrom, nr 0;


ˆ dacă numărul nu este palindrom, adăugăm la sfârşitul lui o cifră (prima cifră a numărului)
şi verificăm dacă se obţine un palindrom (ı̂n acest caz nr va fi 1);
ˆ apoi ı̂ncercăm cu două cifre, trei cifre, ş.a.m.d.
ˆ În cel mai defavorabil caz vom adăuga nr lg  1 cifre (primele lg  1 cifre ı̂n ordine
inversată).

Nu este necesar să reţinem toate numerele citite, vom reţine ı̂ntr-un vector nr cu n elemente
numărul minim de cifre care trebuie să fie adăugate pentru a transforma fiecare număr din şir ı̂n
palindrom. Rezultatul la cerinţa 1 este suma valorilor memorate ı̂n vectorul nr.
Cerinţa 2. Trebuie să determinăm cea mai lungă subsecvenţă a vectorului nr, construit
la cerinţa anterioară, care are suma elementelor mai mică sau egală cu S. Pentru aceasta vom
parcurge vectorul nr cât timp suma elementelor din secvenţa curentă (să o notăm sum) este & S.
Când am ajuns la o poziţie i pentru care sum  nri % S elementul curent nu mai poate fi
”ı̂nghiţit” ı̂n soluţie. Prin urmare:

ˆ comparăm lungimea secvenţei curente cu lungimea maximă şi o reţinem dacă este mai mare;
ˆ eliminăm elementele de la ı̂nceputul secvenţei curente, actualizând corespunzător sum, până
când este posibil să ”ı̂nghiţim” valoarea nri ı̂n soluţie (sum  nri & S).
CAPITOLUL 1. OJI 2023 1.1. PALINDROM 4

Când parcurgerea vectorului nr s-a ı̂ncheiat, trebuie să comparăm şi lungimea ultimei secvenţe
cu lungimea maximă.
2 3
Pentru punctaje parţiale sunt posibile şi abordări de complexitate O n  sau O n .

1.1.2 *Cod sursă

1.1.3 Rezolvare detaliată

Pentru S=0 este necesara determinarea lungimii maxime a zonelor de zerouri consecutive.

Functia calcul2S0() face acest lucru ... pentru orice eventualitate!

Listing 1.1.1: oji2023-palindrom.cpp


#include<iostream> // atentie la = si == (un egal si doua egaluri) ... !!!
#include<fstream> // separat pentru S=0 ...

#include<cstring> // strcpy, strlen

using namespace std;

//ifstream fin("palindrom.in");

//ifstream fin("palindrom1.in");
//ifstream fin("palindrom2.in");

//ifstream fin("..//teste_palindrom//01-palindrom.in"); // C1
//ifstream fin("..//teste_palindrom//02-palindrom.in"); // C1
//ifstream fin("..//teste_palindrom//03-palindrom.in"); // C1
//ifstream fin("..//teste_palindrom//06-palindrom.in"); // C1
//ifstream fin("..//teste_palindrom//08-palindrom.in"); // C1
//ifstream fin("..//teste_palindrom//10-palindrom.in"); // C1
//ifstream fin("..//teste_palindrom//12-palindrom.in"); // C1
//ifstream fin("..//teste_palindrom//18-palindrom.in"); // C1
//ifstream fin("..//teste_palindrom//20-palindrom.in"); // C1
//ifstream fin("..//teste_palindrom//22-palindrom.in"); // C1

//ifstream fin("..//teste_palindrom//04-palindrom.in"); // C2 S=0


//ifstream fin("..//teste_palindrom//05-palindrom.in"); // C2 S=0
//ifstream fin("..//teste_palindrom//07-palindrom.in"); // C2
//ifstream fin("..//teste_palindrom//09-palindrom.in"); // C2
//ifstream fin("..//teste_palindrom//11-palindrom.in"); // C2
//ifstream fin("..//teste_palindrom//13-palindrom.in"); // C2
//ifstream fin("..//teste_palindrom//14-palindrom.in"); // C2
//ifstream fin("..//teste_palindrom//15-palindrom.in"); // C2
//ifstream fin("..//teste_palindrom//16-palindrom.in"); // C2
//ifstream fin("..//teste_palindrom//17-palindrom.in"); // C2
//ifstream fin("..//teste_palindrom//19-palindrom.in"); // C2
//ifstream fin("..//teste_palindrom//21-palindrom.in"); // C2
//ifstream fin("..//teste_palindrom//23-palindrom.in"); // C2
ifstream fin("..//teste_palindrom//24-palindrom.in"); // C2

ofstream fout("palindrom.out");

int C; // cerinta
int n; // nr de numere
int S; // nr max cifre adaugate

char a[50001][51];
char nrp[105]; // construiesc palindrom
int nrca[50001]; // nr cifre adaugate

int lg; // nr initial de cifre


int lgs; // nr de cifre adaugate ca sa fie palindrom
CAPITOLUL 1. OJI 2023 1.1. PALINDROM 5

int nmtca=0; // nr minim total de cifre adaugate

int lgsc; // lungime secventa curenta


int lgsmax; // lungime secventa maxima

int i1; // indice inceput in secventa curenta


int i2; // indice sfarsit in secventa curenta
int insecventa; // insecventa=1: sunt in secventa de 0; insecventa=0: nu sunt !

int sumc; // suma din secventa i1..i2

int i, j; // alti indici

int calcul1() // C=1


{
//cout<<"\n--> calcul1()"<<endl<<endl;

for(i=1; i<=n;i++) // ...................


{
//strcpy(nrp,a[i]);
lg=strlen(a[i]);
//cout<<i<<" : "<<a[i]<<" lg = "<<lg<<endl;
for(lgs=0; lgs<=lg-1; lgs++) // lg cifre adaugate
{
for(j=0; j<=lgs-1; j++)
{
a[i][lg+j]=a[i][lgs-1-j];
}

// verific cand devine palindrom


int epalindrom=1;
for(j=0; j<=(lg+lgs)/2; j++)
{
//cout<<nrp[j]<<" ... "<<nrp[lg+lgs-j-1]<<endl;
if(a[i][j] != a[i][lg+lgs-j-1])
{
//cout<<"cifre diferite ..."<<endl;
epalindrom=0;
}
if(epalindrom == 0)
{
break;
}
}

if(epalindrom == 1)
{
//cout<<"pentru lgs = "<<lgs
// <<" a devenit palindrom "<<nrp
// <<" adaugant lgs = "<<lgs<<" cifre"<<endl;
nmtca=nmtca+lgs;
nrca[i]=lgs;

//cout<<"nrp = "<<nrp<<" lgs = "<<lgs<<endl;


break; // iese din for lgs
}
//cout<<endl;
}// for lgs
//cout<<endl;
}// sf for i

//cout<<"\n<-- calcul1()"<<endl<<endl;

return 0;
} // sf calcul1()

int calcul2() // C=2


{
//cout<<"--> calcul2()"<<endl;

calcul1();

lgsc=0; // lg secventa curenta


lgsmax=0; // lg secventa maxima <= S
i1=1; // inceput secventa
CAPITOLUL 1. OJI 2023 1.1. PALINDROM 6

// ma pozitionez pe o valoare de inceput <= S


// este posibil ca primele valori sa fie > S ... !!!

while((i1 <= n) && (nrca[i1] > S)) i1++;


//cout<<"i1 = "<<i1<<" nrca[i1] = "<<nrca[i1]<<endl;

sumc=nrca[i1];
//cout<<"sumc = "<<sumc<<endl;
//getchar();

i2=i1;

while((i1 <= i2) && (i2<=n))


{
while((i2+1 <= n) &&
(sumc+nrca[i2+1] <= S)) // avansez spre dreapta
{
sumc=sumc+nrca[i2+1];
i2=i2+1;
}

//cout<<"i1 = "<<i1<<" ... i2 = "<<i2<<" sumc = "<<sumc<<endl;


//cout<<"nrca["<<i2+1<<"]="<<nrca[i2+1]<<" nu mai incape in sumc ... !!!\n";

lgsc=i2-i1+1;
if(lgsc > lgsmax) lgsmax=lgsc;
//cout<<"lgsc = "<<lgsc<<" lgsmax = "<<lgsmax<<endl;

// elimin din inceputul secventei (maresc i1)


// pana cand incape nrca[i2+1]

sumc=sumc+nrca[i2+1]; // adaug ... (aici se depaseste S)


i2=i2+1; // pas spre dreapta
//cout<<"i1 = "<<i1<<" i2 = "<<i2<<" sumc = "<<sumc<<endl;

if(i2>n)
{
//cout<<"ATENTIE i2 = "<<i2
// <<" a depasit n = "<<n<<" **************"<<endl;
//getchar();
break; // a depasit n
}

// avansez cu i1 pana cand sumc <= S


// i1 sigur va fi eliminat; poate si alti cativa ... !
//cout<<"\navansez cu i1 = "<<i1<<" pana cand incape nrca["<<i2
// <<"] = "<<nrca[i2]<<endl;

//cout<<"sumc-nrca["<<i1<<"] = "<< sumc-nrca[i1]<<endl;


while((i1 <= i2) && (sumc-nrca[i1]) > S)
{
//cout<<"scot nrca["<<i1<<"] = "<<nrca[i1]<<endl;
sumc=sumc-nrca[i1];
i1=i1+1; // avansez spre dreapta
}

sumc=sumc-nrca[i1];
i1=i1+1;

//cout<<"s-a oprit la ";


//cout<<"i1 = "<<i1<<" ... i2 = "<<i2<<" sumc = "<<sumc<<endl;

//if(i1 > i2)


//{
// cout<<"\nATENTIE i1 = "<<i1<<" > i2 = "<<i2
// <<" ... !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"<<endl;
// getchar();
//}

// caut primul numar mai mic decat S si pun i1 acolo ...


//cout<<"caut primul numar mai mic decat S si pun i1 acolo ...\n";
while((i1<=n) && (nrca[i1] > S))
{
i1++;
}
CAPITOLUL 1. OJI 2023 1.1. PALINDROM 7

//cout<<"s-a oprit la i1 = "<<i1<<" ...nrca["<<i1<<"] = "<<nrca[i1]<<endl;


//getchar();

if(i1<=n)
{
sumc=nrca[i1];
i2=i1;
}
}// sf while(i2<=n)

if(lgsc > lgsmax) lgsmax=lgsc;


//cout<<"lgsc = "<<lgsc<<" lgsmax = "<<lgsmax<<endl;
//getchar(); // opreste executia pana apas <Enter>

//cout<<"\n<-- calcul2()"<<endl<<endl;

return 0;
} // sf calcul2()

int calcul2S0() // C=2


{
//cout<<"--> calcul2S0()"<<endl;

calcul1();

//for(i=1; i<=n; i++) cout<<i<<" : "<<nrca[i]<<endl;


//cout<<endl<<endl;
//cout<<"--------------------------\n";
//getchar();

// initializari
lgsc=0; // lg secventa curenta
lgsmax=0; // lg secventa maxima = 0
i1=0; // inceput secventa
i2=0; // sfarsit secventa
insecventa=0; // nu sunt in secventa

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


{
//cout<<"nrca["<<i<<"] = "<<nrca[i]<<" ";
if(nrca[i]==0)
{
if(insecventa==0)
{
//cout<<"incepe o secventa noua"<<endl;
i1=i;
i2=i;
insecventa=1;
}
else
if(insecventa==1) // ca sa fie lizibil !!!
{
i2=i;
}
}
else // nrca[i] != 0
{
insecventa=0;
lgsc=i2-i1+1;
if(lgsc > lgsmax)
{
lgsmax = lgsc;
}
}

}// sf for i

i2=i;
lgsc=i2-i1;
if(lgsc > lgsmax) lgsmax=lgsc;

//cout<<"\n<-- calcul2S0()"<<endl<<endl;

return 0;
} // sf calcul2S0()
CAPITOLUL 1. OJI 2023 1.1. PALINDROM 8

int main()
{
fin>>C; //cout<<"C = "<<C<<endl;
fin>>n; //cout<<"n = "<<n<<endl<<endl;

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


{
fin>>a[i];
lg=strlen(a[i]);
//cout<<i<<" : "<<a[i]<<" lg = "<<lg<<endl;
}

if(C==2) fin>>S;
//cout<<"S = "<<S<<endl;
//cout<<"------------------"<<endl;

if(C==1)
{
calcul1();
//cout<<nmtca;
fout<<nmtca;
//cout<<endl<<endl;
} // sf if(C==2)
else
if(C==2)
{
if(S==0)
{
calcul2S0();
}
else
{
calcul2();
}

//cout<<lgsmax;
fout<<lgsmax;
}// sf if(C==2)

fin.close();
fout.close();

return 0;
}

Listing 1.1.2: oji2023-cecker-palindrom.cpp


#include "testlib.h" // se gaseste usor pe internet !

using namespace std;

int main()
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../teste_palindrom/24-palindrom.in", // input
(char*)"../teste_palindrom/24-palindrom.ok", // rezultat corect
(char*)"palindrom.out", // rezultat de verificat si acordat punctaj
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("palindrom", argc, argv);

compareRemainingLines();
}
CAPITOLUL 1. OJI 2023 1.2. PRIMPRIM 9

1.2 primprim
Problema 2 - Primprim 100 de puncte
Pentru un număr natural a definim costul ca fiind valoarea absolută (modulul) diferenţei dintre
a şi numărul prim cel mai apropiat de a.
Asupra unui şir de n numere naturale, situate pe poziţii numerotate de la 1 la n, se aplică, ı̂n
ordine, o succesiune de q operaţii.
O operaţie constă dintr-o ı̂nlocuire şi o afişare şi este descrisă sub forma i x p, cu semnificaţia:

ˆ mai ı̂ntâi ı̂nlocuim cu x elementul din şir de pe poziţia i;


ˆ apoi afişăm suma minimă totală a costurilor unor elemente convenabil selectate de pe p
poziţii distincte din şir.

Cerinţe

Cunoscând n şi cele n elemente ale şirului, scrieţi un program care să determine:

1. suma costurilor tuturor elementelor din şirul dat;


2. rezultatele afişate ı̂n urma aplicării fiecăreia dintre cele q operaţii, date ı̂n forma precizată.

Date de intrare

Fişierul de intrare primprim.in va conţine pe prima linie un număr natural C, reprezentând


cerinţa care trebuie să fierezolvată (1 sau 2), pe a doua linie numărul natural n, cu semnificaţia
din enunţ, iar pe a treia linie cele n elemente din şir, ı̂n ordinea din şir.
Dacă C 2, pe a patra linie se află numărul natural q, reprezentând numărul de operaţii, iar
pe următoarele q linii se află cele q operaţii, câte o operaţie pe linie, ı̂n forma descrisă ı̂n enunţ.
Numerele scrise pe aceeaşi linie sunt separate prin câte un spaţiu.

Date de ieşire

Dacă C 1, fişierul de ieşire primprim.out va conţine o singură linie pe care va fi afişată


suma costurilor tuturor elementelor din şir.
Dacă C 2, fişierul de ieşire primprim.out va conţine q linii, pe linia i fiind scris rezultatul
afişat după executarea celei de a i-a operaţii din fişierul de intrare.

Restricţii şi precizări

ˆ 1 & q & 2 10
5

ˆ 1 & i, p & n & 10 ; 1 & x & 10


6 6

ˆ Elementele şirului sunt numere naturale nenule & 106 .

# Punctaj Restricţii
1 20 C = 1, n 1
2 22 C = 1, 1 $ n & 1000
3 28 C = 2, n & 1000, q & 10
4 30 C = 2, nu există restricţii suplimentare

Exemple:

primprim.in primprim.out Explicaţii


1 4 C 1, n 5, iar şirul este 8, 1, 3, 5, 9. Costurile elementelor
5 sunt, ı̂n ordine, 1, 1, 0, 0, 2, deci suma este 4.
81359
CAPITOLUL 1. OJI 2023 1.2. PRIMPRIM 10

2 2 C 2, n 5, iar şirul iniţial este 8, 1, 3, 5, 9. Se aplică


5 0 şirului 3 operaţii. După prima operaţie, pentru care i 2,
8 1359 3 x 6 şi p 4, şirul devine 8, 6, 3, 5, 9. Suma minimă totală
3 se obţine dacă selectăm valorile de pe poziţiile 1, 2, 3 şi 4,
2 64 costurile fiind 1+1+0+0=2. După a II-a operaţie, pentru
3 52 care i 3, x 5 şi p 2, şirul devine 8, 6, 5, 5, 9. Selectăm
5 12 5 valorile de pe poziţiile 3 şi 4 (acestea având costul 0). După
a III-a operaţie, pentru care i 5, x 12 şi p 5, şirul
devine 8, 6, 5, 5, 12. Selectăm toate valorile, deci suma este
1+1+0+0+1=3.

1.2.1 Indicaţii de rezolvare


Propusă de: Stud. Ştefan-Cosmin Dăscălescu, Universitatea Bucureşti
Pentru ambele cerinţe va fi necesar să determinăm, cât mai rapid, pentru un număr dat distanţa
faţă de cel mai apropiat număr prim.
O primă abordare ar fi ca pentru fiecare număr să verificăm mai ı̂ntâi dacă este prim (ı̂n acest
caz, costul ar fi 0), iar ı̂n caz contrar ne deplasăm la stânga şi la dreapta sa până când identificăm
un număr prim, calculând apoi costul folosind formula dată din enunţ.
Ô
Totuşi, o asemenea abordare ar avea complexitatea O x valmax, unde x reprezintă
distanţa maximă faţă de un număr prim. Deoarece x este cel mult 57, o asemenea abordare nu
obţine punctaj maxim.
Pentru a optimiza această abordare, vom precalcula costurile pentru toate numerele de la 1
6 6
la 10 . Pentru aceasta, vom utiliza ciurul lui Eratostene, pentru a genera numerele prime 10 ,
urmând ca mai apoi costul să fie calculat ı̂n O 1 pentru fiecare număr.
Complexitatea precalculării este O n log log n.

Cerinţa 1. Vom citi succesiv numerele şi vom afla succesiv costul pentru fiecare număr de la
intrare, reţinând ı̂ntr-o variabilă suma costurilor. În funcţie de abordarea folosită pentru calcularea
costurilor, se pot obţine diverse punctaje parţiale, dar punctajul maxim pe cerinţă se poate obţine
doar folosind metoda bazată pe ciurul lui Eratostene, abordare explicată mai sus.

Cerinţa 2. Pentru această cerinţă vom citi succesiv operaţiile şi le vom executa.
Pentru a obţine un cost total minim trebuie să adunăm cele mai mici p costuri. O abordare
eficientă se bazează pe observaţia că pentru orice număr costul este cel mult 57, fapt ce ne permite
să utilizăm un vector de frecvenţă f r, unde f ri = numărul de elemente din vectorul v care au
costul i.
Pentru fiecare operaţie, la modificarea unei valori din vector, vom decrementa frecvenţa costului
pentru vechea valoare şi vom incrementa frecvenţa costului pentru noua valoare.
Pentru a determina costul total minim pentru a obţine cel puţin p numere prime ı̂n vector
(valoarea afişată după executarea operaţiei), vom parcurge vectorul de frecvenţă de la stânga la
dreapta (i 0, ..., 57).
La fiecare pas i, pentru a calcula costul total minim adunăm la o variabilă cmin produsul
dintre f ri şi i (există f ri numere care pot fi transformate ı̂n numere prime cu costul i), iar
ı̂ntr-o variabilă nr reţinem câte numere prime au fost deja obţinute.
În momentul ı̂n care nr  f ri % p, parcurgerea se opreşte şi adunăm la cmin doar costul
obţinerii celor p  nr numere prime care mai sunt necesare (adică adunăm f ri p  nr.
În funcţie de cum selectăm cele mai mici p costuri şi ı̂n funcţie de cum calculăm costurile, se
pot obţine diverse punctaje parţiale.

1.2.2 *Cod sursă


CAPITOLUL 1. OJI 2023 1.2. PRIMPRIM 11

1.2.3 Rezolvare detaliată

Listing 1.2.1: oji2023-primprim.cpp


#include<iostream>
#include<fstream>

using namespace std;

ifstream fin("primprim.in");

//ifstream fin("primprim1.in");
//ifstream fin("primprim2.in");

//ifstream fin("..//teste_primprim//01-primprim.in"); // C1
//ifstream fin("..//teste_primprim//07-primprim.in"); // C1
//ifstream fin("..//teste_primprim//12-primprim.in"); // C1

//ifstream fin("..//teste_primprim//13-primprim.in"); // C2
//ifstream fin("..//teste_primprim//21-primprim.in"); // C2
//ifstream fin("..//teste_primprim//29-primprim.in"); // C2

ofstream fout("primprim.out");

int C; // cerinta
int n; //

int a[1000001];
int q;
int i, x, p;

bool ciur[1000005];
int fr[100]; // este initializat automat cu zero ("zona globala")

int distmaxprime; // distanta max intre doua nr prime consecutive


int distprime; // distanta intre doua nr prime consecutive

int cost;
int costmax;

int sumacost; // rezultat la iesire

int primstanga;
int primdreapta;

int diststanga;
int distdreapta;

void ciurEratostene()
{
//cout<<"--> ciurEratostene()"<<endl;
ciur[0]=true; // rezulta din exemplul dat in enunt ... !
ciur[1]=false;

// initializez toate cu true


for(int j=2; j<=1000004; j++) ciur[j]=true;

// marchez numerele pare > 2 cu false


for(int j=4; j<=1000004; j=j+2) ciur[j]=false;

// merg numai pe numere impare


for(int jj = 3; jj < 1000004; jj=jj+2)
{
if(ciur[jj] == true)
{
// marchez multiplii lui jj = prim (nu iau si nr pare!)
for(int j = 3 * jj; j<= 1000005; j = j + 2*jj)
{
ciur[j] = false;
}// sf for j
//cout<<"jj = "<<jj<<" j = "<<j<<endl;
//getchar();
}// sf if
}// sf for jj
CAPITOLUL 1. OJI 2023 1.2. PRIMPRIM 12

//for(k=0; k<=100; k++) // o mica verificare !!!


//{
// if(ciur[k]==true) cout<<k<<" ";
//}
//cout<<endl<<endl;
//cout<<"<-- ciurEratostene()"<<endl;
}// ciurEratostene()

void calcDistMax()// pentru "cultura generala numai"!


{
//cout<<"--> calcDistMax()"<<endl;
distmaxprime=4; // intre 7 si 11

int i1, i2; // doua numere prime consecutive


i1=13;
for(int i=15; i < 1000001; i=i+2)
{
if(ciur[i]==true)
{
distprime=i-i1; // i2=i ...
//cout<<i1<<" "<<i<<" "<<distprime<<" "<<distmaxprime<<endl;
if(distprime > distmaxprime)
distmaxprime=distprime;
i1=i;
//getchar();
}
}

//cout<<"\ndistmaxprime = "<<distmaxprime<<endl<<endl;
//cout<<"<-- calcDistMax()"<<endl;
} // calcDistMax()

int calculCost(int val)


{
//cout<<"--> calculCost("<<val<<")\n";
// este codul din calcul1 ... deci ... "copy-paste" !
// sau sa o folosim si in calcul 1 ...

int costval;

primstanga=val;
primdreapta=val;

while(ciur[primstanga]==false) primstanga=primstanga-1;
while(ciur[primdreapta]==false) primdreapta=primdreapta+1;

//cout<<primstanga<<" <= "<<val<<" <= "<<primdreapta<<endl;

diststanga=val-primstanga;
distdreapta=primdreapta-val;

costval=diststanga;

if(distdreapta < costval) costval = distdreapta;

//cout<<primstanga
// <<" <= "<<val
// <<" <= "<<primdreapta
// <<" costval = "<<costval<<endl;

//cout<<"<-- calculCost("<<val<<")\n\n";

return costval;
}

void afisa() // afisez a[ ]


{
for(int j=1; j<=n; j++)
{
cout<<j<<" : "<<a[j]<<endl;
}
cout<<endl;
getchar();
}
CAPITOLUL 1. OJI 2023 1.2. PRIMPRIM 13

void afisfr() // afisez frecventele


{
for(int j=0; j<=costmax; j++)
{
if(fr[j] > 0)
cout<<"fr["<<j<<"] = "<<fr[j]<<endl;
}
getchar();
}

void calcul1() // C=1


{
//cout<<"--> calcul1()"<<endl;

int sumfr=0;

costmax=0;
sumacost=0;

for(int i=1; i<=n; i++)


{
primstanga=a[i];
primdreapta=a[i];

while(ciur[primstanga]==false) primstanga=primstanga-1;
while(ciur[primdreapta]==false) primdreapta=primdreapta+1;

diststanga=a[i]-primstanga;
distdreapta=primdreapta-a[i];
//cout<<diststanga<<" "<<a[i]<<" "<<distdreapta<<endl;

cost=diststanga;
if(distdreapta < cost) cost = distdreapta;

//cout<<i<<" : "<<primstanga
// <<" <= "<<a[i]
// <<" <= "<<primdreapta
// <<" cost = "<<cost<<endl;

//cout<<a[i]<<" are cost = "<<cost<<endl;

fr[cost]++; // ***********************
if(cost > costmax) costmax = cost;

//cout<<"fr["<<cost<<"] = "<<fr[cost]<<endl<<endl;

sumacost=sumacost + cost;
//cout<<cost<<" "<<a[i]<<" "<<sumacost<<endl<<endl;
//getchar();
}// sf for i

//cout<<"\ncostmax = "<<costmax<<" !!!!!!!!!!\n";

for(int i=0; i<=costmax; i++) sumfr=sumfr+fr[i];

//cout<<"sumfr = "<<sumfr<<" !!!!!!!!!!\n\n";

//if(sumfr != n) cout<<"ete o eroare pe undeva ... !!!\n";

//cout<<"<-- calcul1()"<<endl;
} // sf calcul1()

void calcul2() // C=2


{
//cout<<"--> calcul2()"<<endl;

int costvalai;
int costvalx;
int nrpoz; // numarul pozitiilor alese

int sumamin;

calcul1();

//afisfr();
CAPITOLUL 1. OJI 2023 1.2. PRIMPRIM 14

for(int k=1; k<=q; k++)


{
sumamin=0;

fin>>i>>x>>p;

//cout<<"----------------------------------------------------\n";
//cout<<"k = "<<k<<" : "<<"i = "<<i<<" x = "<<x<<" p = "<<p<<endl;
//cout<<"a["<<i<<"] = "<<a[i]<<endl;

costvalai=calculCost(a[i]);

//cout<<"a["<<i<<"] = "<<a[i]<<" costvalai = "<<costvalai<<endl;

costvalx=calculCost(x);

//cout<<"x = "<<x<<" costvalx = "<<costvalx<<endl<<endl;

//cout<<"inainte de modificare frecvente:\n";


//afisfr();

fr[costvalai]--;
fr[costvalx]++;

//cout<<"dupa modificare frecvente:\n";


//afisfr();

//cout<<"inainte de actualizare a["<<i<<"]="<<x<<endl;


//afisa();

a[i]=x; // modific valoarea ...

//cout<<"dupa actualizare a["<<i<<"]="<<x<<endl;


//afisa();

//afisfr();

int j;
j=0;

nrpoz=0; // nr pozitii alese


while((nrpoz < p) && (nrpoz + fr[j] <= p))
{
//cout<<"la intrare in while j = "<<j<<" nrpoz = "<<nrpoz
// <<" p = "<<p<<" fr[j] = "<<fr[j]
// <<" sumamin = "<<sumamin<<endl;

sumamin=sumamin+fr[j]*j;
nrpoz=nrpoz+fr[j];

//cout<<"la iesire din while j = "<<j<<" nrpoz = "<<nrpoz


// <<" p = "<<p<<" fr[j] = "<<fr[j]
// <<" sumamin = "<<sumamin<<endl<<endl;
j++;
}

//cout<<"1. dupa while j = "<<j


// <<" sumamin = "<<sumamin
// <<" nrpoz = "<<nrpoz<<endl<<endl;

sumamin=sumamin+(p-nrpoz)*j; // iau numai o parte din fr[j]

//cout<<"2. dupa while j = "<<j<<" nrpoz = "<<nrpoz


// <<" p = "<<p<<" fr[j] = "<<fr[j]
// <<" sumamin = "<<sumamin<<endl;

//cout<<sumamin<<" *********************************\n"<<endl;

fout<<sumamin<<endl;

//getchar();
}// sf for k

//cout<<"<-- calcul2()"<<endl;
} // sf calcul2()
CAPITOLUL 1. OJI 2023 1.2. PRIMPRIM 15

int main()
{
fin>>C; //cout<<"C = "<<C<<endl;
fin>>n; //cout<<"n = "<<n<<endl<<endl;

ciurEratostene();
calcDistMax();

for(int k=1; k<=n; k++)


{
fin>>a[k];
//cout<<k<<" : "<<a[k]<<"\n";
}
//cout<<endl;

if(C==1)
{
calcul1();
//cout<<sumacost;
fout<<sumacost;
}

if(C==2)
{
fin>>q;
//cout<<"q = "<<q<<endl;
calcul2();
}

fin.close();
fout.close();

return 0;
}

Listing 1.2.2: oji2023-cecker-primprim.cpp


#include "testlib.h" // se gaseste usor pe internet !

using namespace std;

int main()
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../teste_primprim/29-primprim.in", // input
(char*)"../teste_primprim/29-primprim.ok", // rezultat corect
(char*)"primprim.out", // rezultat de verificat si acordat punctaj
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("primprim", argc, argv);

compareRemainingLines();
}
Capitolul 2

OJI 2022

2.1 patratele
Problema 1 - Pătrăţele 100 de puncte
Gigel are ı̂n faţa sa pe o foaie de matematică un desen obţinut prin trasarea mai multor linii
orizontale şi verticale de lungime 1 de-a lungul modelului foii de matematică.
Privind desenul de pe foaie el se ı̂ntreabă: ”Oare câte pătrate s-au format
din liniile trasate?”
În desenul alăturat se vede foaia formată din 3 linii şi 5 coloane, precum
şi liniile trasate până la un moment dat. Se pot distinge trei pătrate de
latură 1, două pătrate de latură 2 şi un pătrat de latură 3.
În problema noastră vom codifica fiecare pătrat de latură 1 de pe foaie cu un număr natural
cuprins ı̂ntre 0 şi 15 obţinut prin ı̂nsumarea codificării fiecărei laturi astfel:
1 - dacă latura de sus este trasată
2 - dacă latura din dreapta este trasată
4 - dacă latura de jos este trasată
8 - dacă latura din stânga este trasată
În acest fel desenul alăturat poate fi codificat printr-un tablou bidimensional de dimensiuni
3  5 cu valorile:

9 7 15 13 7
14 15 11 15 11
1 3 12 7 14

Cerinţe

Fiind date dimensiunile n şi m ale foii de matematică, precum şi tabloul bidimensional de
dimensiune n  m care conţine codificarea foii, să se determine:
1. numărul total de pătrate existente pe foaia de matematică ı̂n desenul realizat conform
codificării
2. distribuţia numărului de pătrate ı̂n ordinea strict crescătoare a lungimii laturilor
3. unde poate fi trasată ı̂ncă o linie astfel ı̂ncât numărul total de pătrate să crească şi să devină
maxim posibil

Date de intrare

Fişierul de intrare patratele.in conţine pe prima linie trei numere naturale n m t, separate
prin câte un spaţiu, indicând dimensiunile foii de matematică n  m, respectiv cerinţa care trebuie
rezolvată (1, 2 sau 3).
Fiecare dintre următoarele n linii conţine câte m numere naturale, fiecare dintre acestea
reprezentând codificarea foii de matematică.

Date de ieşire

Fişierul de ieşire patratele.out va conţine următoarele ı̂n funcţie de cerinţa cerută:


1. Dacă t 1, pe prima linie numărul total de pătrate determinat;

16
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 17

2. Dacă t 2, pe fiecare linie vor fi afişate câte două numere naturale nenule a şi b, separate
printr-un spaţiu, indicând lungimea laturii pătratelor - a, respectiv numărul de pătrate cu
latura de lungimea respectivă - b, ı̂n ordinea strict crescătoare a valorilor lui a;
3. Dacă t 3, prima linie va conţine numărul maxim de pătrate, iar linia a doua va conţine
2 valori naturale lin, col şi un cuvânt pozitie separate printr-un spaţiu, unde: lin, col
reprezintă coordonatele pătratului de latură 1 unde se trasează linia suplimentară; pozitie "
{SUS, DREAPTA, JOS, STANGA, NU} (se va afişa NU ı̂n cazul ı̂n care nu se poate trasa
nicio linie - ı̂n acest caz cele 3 valori numerice afişate vor fi de asemenea 0).

Restricţii şi precizări

ˆ Numerotarea liniilor şi coloanelor foii de matematică ı̂ncepe de la 1


ˆ Dacă la cerinţa t 3 se obţin mai multe poziţii de trasare a liniei, se va afişa soluţia cu
indicele liniei minim, iar ı̂n caz de egalitate după linii, se va afişa soluţia cu indicele coloanei
minim. În cazul ı̂n care există mai multe posibilităţi de trasare a unei linii ı̂n acelaşi pătrat,
poziţiile vor fi luate ı̂n ordinea SUS, DREAPTA, JOS, STANGA
ˆ 1 & n, m & 60

# Punctaj Restricţii
1 30 t=1
2 30 t=2
3 10 t = 3 şi 1 n,m 20
4 30 t=3

Exemple:

patratele.in patratele.out Explicaţii


351 6 Se rezolvă cerinţa 1.
9 7 15 13 7 În total au fost găsite 6 pătrate
14 15 11 15 11
1 3 12 7 14
352 13 Se rezolvă cerinţa 2.
9 7 15 13 7 22 3 pătrate de latură 1
14 15 11 15 11 31 2 pătrate de latură 2
1 3 12 7 14 1 pătrat de latură 3
353 9 Se rezolvă cerinţa 3.
9 7 15 13 7 2 5 JOS Dacă se trasează ˆncă o linie la pătratul din linia 2
14 15 11 15 11 coloana 5 jos, se mai obţin ˆncă 3 pătrate
1 3 12 7 14
333 0 Se rezolvă cerinţa 3.
913 0 0 NU Nu se poate adăuga niciun pătrat nou prin trasarea
802 unei singure linii
12 0 0

2.1.1 Indicaţii de rezolvare

Propusă de: Prof. Marinel Şerban


Observaţii. Problema testează cunoştinţe despre:
ˆ lucrul cu tablouri unidimensionale şi bidimensionale
ˆ reprezentarea numerelor ı̂n baza 2
ˆ operaţii pe biţi sau operaţii echivalente

Soluţia 1 - prof. Marinel Şerban. CN E. Racoviţă Iaşi


Această soluţie obţine ı̂ntre 70 şi 84 de puncte ı̂n funcţie de implementare.
Verificarea faptului că un pătrăţel are una dintre laturi desenată se face simplu utilizând
reprezentarea ı̂n baza 2 a codului pătrăţelului astfel:
Fie x valoarea codului pătrăţelului:
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 18

ˆ pentru latura de sus, dacă această latură există, atunci x&1 1 sau x%2 1
ˆ pentru latura din dreapta, dacă această latură există, atunci x&2 = 1 sau x©2%2 1
ˆ pentru latura de jos, dacă această latură există, atunci x&2 4 sau x©4%2 1
ˆ pentru latura din stânga, dacă această latură există, atunci x&2 8 sau x©8%2 1
2
Cerinţele 1 şi 2. Complexitate timp O n m min n, m 
(1) determinăm latura maximă lmax min n, m pe care o poate avea un pătrat
(2) pentru toate laturile posibile 1 & l & lmax verificăm toate posibilităţile de a exista un pătrat
de latura l, cu colţul stânga-sus ı̂n poziţia xs , ys  (evident xs parcurge valorile de la 1 la
n  l  1 iar ys toate valorile de la 1 la m  l  1)
(3) pentru fiecare pătrat de latură l şi colţul stânga-sus ı̂n xs , ys  verificăm dacă cele 4 laturi
sunt complet desenate, iar dacă da incrementam un contor.

Pentru cerinţa 1 afişăm numărul total de pătrate valide găsite.


Pentru cerinţă 2 trebuie să contorizăm fiecare latură de pătrat separat şi să le afişăm ı̂n ordine
crescătoare.
2 2 2
Cerinţa 3. Complexitate timp O n m min n, m 
(1) rezolvăm cerinţa 1 pentru tabloul nemodificat, reţinând numărul de pătrate Pinit
(2) Parcurgem toate elementele din matrice şi plasăm orice latură lipsă, apoi numărăm din nou
pătratele obţinuteşi reţinem maximul şi locul care a produs acest maxim Pmax .
(3) Dacă Pmax Pinit , ı̂nseamnă că nu există niciun mod de a desena o singură linie pentru a
mării numărul de pătrate, altfel afişăm rezultatul.

Soluţia 2 - stud. Bogdan-Ioan Popa. Facultatea de Matematică şi Informatică, Universi-


tatea din Bucureşti
Această soluţie obţine 100 de puncte.
În cele ce urmează vom numerota cei 4 pereţi ai unei celule conform figurii:
Cerinţele 1 şi 2. Complexitate timp O n m min n, m
Se precalculează următoarele tablouri bidimensionale:
ˆ upi,j = lungimea secvenţei maximale de pereţi de tip 1 care are ca punct de oprire ı̂n celula
i, j  şi se extinde ı̂n sus pe coloană
ˆ lef ti,j = lungimea secvenţei maximale de pereţi de tip 2 care are ca punct de oprire ı̂n celula
i, j  şi se extinde ı̂n stânga pe linie
ˆ righti,j = lungimea secvenţei maximale de pereţi de tip 0 care are ca punct de oprire ı̂n
celula i, j  şi se extinde ı̂n dreapta pe linie
ˆ downi,j = lungimea secvenţei maximale de pereţi de tip 3 care are ca punct de oprire ı̂n
celula i, j  şi se extinde ı̂n jos pe coloană
Pentru fiecare celulă i, j  şi fiecare dimensiune de latură k se verifică dacă pătratul care are
lungimea laturii k şi colţul stânga sus ı̂n celula i, j  are laturile desenate.
Condiţiile sunt următoarele:
ˆ downi,j ' k
ˆ righti,j ' k
ˆ upik1,j k1 ' k
ˆ lef tik1,j k1 ' k

Pentru prima cerinţă doar numărăm pătratele care respectă condiţia de mai sus.
Pentru cea de-a doua cerinţă, vom cunstrui un vector de frecvenţă cntk = numărul de pătrate
de latură k ce respectă condiţia. Se afisează toate perechile k, cntk , pentru care cntk % 0.
2 2
Cerinţa 3. Complexitate timp O n m min n, m
Pentru fiecare celulă i, j  şi pentru fiecare perete 0 & d & 3 se verifică dacă peretele d al celulei
i, j  este desenat sau nu. Dacă peretele nu este desenat:
ˆ se desenează (a nu se uita să se marcheze această desenare şi celulei cu care se ı̂nvecinează
celula i, j  prin peretele d)
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 19

ˆ se rezolvă prima cerinţ ă considerând modificarea făcută.


ˆ se actualizează maximul
ˆ se şterge peretele tocmai desenat

Soluţia 3 - Asist. Drd. Alexandru Ioniţă. Universitatea ”Alexandru Ioan Cuza” din Iaşi
Pentru rezolvarea cerinţei 3 există o soluţie mai eficientă. Această soluţie obţine de asemenea
100 de puncte.
2 2
Complexitate timp O n m 
Soluţia presupune următoarea precalculare:
Fiecare linie orizontală din partea de sus a unei celule i, j  din foaie va fi marcată ı̂ntr-o
matrice: sp0i,j 1. Peste matricea sp0 vom face apoi sume parţiale pentru a putea afla ı̂n O 1
câte linii din partea de sus sunt setate pe un anumit interval dintr-un rând. Vom repeta aceste
operaţii pentru toate tipurile de linii rămase, generând cate o astfel de matrice pentru fiecare
direcţie rămasă: sp1  dreapta, sp2  jos, sp3  stanga.
Vom itera astfel prin fiecare patrat (determinat unic de o celula x, y  si o lungime l) ce poate
fi ı̂ncadrat in foaia de dimensiune n  m, având 3 cazuri:
(1) pătratul are toate laturile complet acoperite de linii deja trasate
(2) pătratul mai are nevoie ca exact o singura linie sa fie trasată pentru a fi complet acoperit
(3) Pătratul are nevoie de mai mult de o linie sa fie trasată pentru a fi complet acoperit.
Cazurile (1) si (3) nu ne interesează ı̂n mod special (trebuiesc doar numărate câte pătrate sunt
in cazul (1), pentru a fi adăugate la răspuns).
În schimb, cazul (2) este de interes: pentru aceste pătrate trebuie să vedem care este linia
lipsă.
Dacă notăm cu x, y  celula liniei respective şi cu z tipul liniei (z 0 dacă este SUS, z 1, dacă
este DREAPTA, etc..) incrementând cu o unitate la coordonatele liniei ı̂ntr-o matrice usedx,y,z .
Această marcare trebuie făcută şi pentru celula vecina acestei linii (daca tocmai am analizat
o linie de tipul SUS din celula x, y , va trebui să incrementăm valoarea ı̂n used şi pentru celula
x  1, y  linia de JOS).
Observaţi că, făcând aceste marcări pentru fiecare pătrat, la final, ı̂n matricea used vom avea
la fiecare poziţie x, y, z  numărul de pătrate care vor fi completate ı̂n cazul in care trasăm linia
corespunzătoare acelei poziţii.
Astfel, nu ne rămâne decât să căutăm care este celula cu cea mai mare valoare din vectorul
used si să ı̂i afişăm coordonatele.

2.1.2 *Cod sursă

2.1.3 Rezolvare detaliată

Figura 2.1: oji2022-patratele12


CAPITOLUL 2. OJI 2022 2.1. PATRATELE 20

Figura 2.2: oji2022-patratele3

Figura 2.3: oji2022-patratele un patratel fără linii

Figura 2.4: oji2022-patratele-NS


CAPITOLUL 2. OJI 2022 2.1. PATRATELE 21

Figura 2.5: oji2022-patratele-VE

Listing 2.1.1: oji2022-patratele-v1.cpp


#include<iostream>// Atentie la && si &
#include<fstream> //
#include<iomanip> // pentru setw(2)
#include <chrono>

// Atentie la prioritati: ... mai bine pun mereu paranteze ... !!!
// 10: == si !=
// 11: a&b
//if( a[n][j]&sud != 0) // asa da gresit ... !!!
//if((a[n][j]&sud) != 0) // asa da corect ... !!!

//#include <bits/stdc++.h>

using namespace std;

//ifstream fin("patratele.in");
// ------------------------------------------------------
//ifstream fin("../teste_patratele/01-patratele.in"); //
//ifstream fin("../teste_patratele/05-patratele.in"); //
//ifstream fin("../teste_patratele/10-patratele.in"); //
// ------------------------------------------------------
//ifstream fin("../teste_patratele/11-patratele.in"); //
//ifstream fin("../teste_patratele/15-patratele.in"); //
//ifstream fin("../teste_patratele/20-patratele.in"); //
// ------------------------------------------------------
//ifstream fin("../teste_patratele/21-patratele.in"); // time : 0.035 s
//ifstream fin("../teste_patratele/31-patratele.in"); // time : 1.085 s
//ifstream fin("../teste_patratele/32-patratele.in"); // time : 1.142 s
//ifstream fin("../teste_patratele/37-patratele.in"); // time : 0.935 s
//ifstream fin("../teste_patratele/38-patratele.in"); // time : 2.142 s
//ifstream fin("../teste_patratele/42-patratele.in"); // time : 3.371 s
ifstream fin("../teste_patratele/44-patratele.in"); // time : 4.083 s s
//ifstream fin("../teste_patratele/45-patratele.in"); // time : 3.979 s
// ------------------------------------------------------

ofstream fout("patratele.out");

int n; // linii
int m; // coloane
int t; // cerinta

int sus=1;
int dreapta=2;
int jos=4;
int stanga=8;

const int nord=1;


CAPITOLUL 2. OJI 2022 2.1. PATRATELE 22

const int est=2;


const int sud=4;
const int vest=8;

int nrp1=0; // nr patratele pentru cerinta1


int nrp1max=0;
int nrp1initial=0; // nr patratele pentru cerinta1, fara adaugari de linii

int lgmaxnm; // lgmaxnm = min(n,m); // lg max patratel


int lgmax; // lgmax = cel mult lgmaxnm // lg max patratel analizat

int pozitia3d; // directia pentru cerinta 3: nord, est, sud, vest


int pozitia3i; // i pozitia pentru cerinta 3
int pozitia3j; // j pozitia pentru cerinta 3

int a[62][62]; // pentru o eventuala "bordare" in 0 si 61


int distributia[61]; // 0, 1, 2, ..., 60

clock_t t1, t2, t3, t4;


double t21, t32, t43;

// ----------------------------------------------------------------

void afisa() // pentru faza de testare ... datele din enunt ... !!!
{
for(int i=1; i<=n+1; i++)
{
for(int j=1; j<=m+1; j++)
{
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
cout<<endl;
//getchar();
}// void afisa()

// ====================================================================

void verific(int iss, int jss) // pentru lg=1, ..., ***


{
int i, j; // patratel (i,j)
int lg;
int lgmax=min(n-iss+1,m-jss+1);

bool ok; // util la cautari

// verific pentru lg=1;


lg=1;
if(((a[iss][jss] & est) != 0) &&
((a[iss][jss] & sud) != 0))
{
nrp1++;
distributia[lg]++;
}
else
{

// verific pentru lg=2, 3, ..., ;


lgmax=min(n-iss+1, m-jss+1);
for(lg=2; lg <= lgmax; lg++)
{
int ilg=iss+lg-1; // bordura de S
int jlg=jss+lg-1; // bordura de la E

// verific doloana din dreapta, la N


if((a[iss][jlg] & nord) == 0)
{
return; // nu mai exista mai departe ...
}
else
{

}
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 23

// verific linia de jos, la V


if((a[ilg][jss] & vest) == 0)
{
return; // nu mai exista mai departe ...
}
else
{

//verific coltul dreapta jos a[ilg][jlg]


ok=true;
if(((a[ilg][jlg] & est) == 0) || // lipseste linie la dreapta
((a[ilg][jlg] & sud) == 0) ) // lipseste linie in jos
{
ok=false;
}
else
{

if(ok==false) continue; // iau alt lg

// aici patratelul are N si V ok, si coltul dreapta-jos ok

// verific latura din dreapta (sus si jos sunt deja verificate!)


int ok=true;
for(int i=iss; i<=ilg-1; i++) // coltul a fost verificat deja
{
if((a[i][jlg]&est) == 0) // nu are linie la V
{
ok=false;
}
else
{

}
}
if(ok==false) continue; // iau alt lg

//aici latura E este ok

// verific latura de jos


ok=true;
for(int j=jss; j<=jlg-1; j++) // coltul a fost verificat deja
{
if((a[ilg][j]&sud) == 0) // nu are linie la S
{
ok=false;
break; // ies din for j
//continue;
}
else
{

}
}
if(ok==false) continue; // alt lg

// aici totul este ok ... !!!


nrp1++;
distributia[lg]++;
}// for lg

}// void verific(int iss, int jss) // pentru lg=1, ..., ***

// ------------------------------------------------------

void cerinta1() // for i ... for j ... for lg


{
int iss, jss; // (iss,jss) = coltul stanga sus al patratului verificat
int lg; // lungime latura patratel
nrp1=0; // rezultat pentru cerinta1
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 24

for(iss=1; iss<=n; iss++)


{
for(jss=1; jss<=m; jss++) // coltul stanga sus
{
// verific N si V la celula {xss, yss)
if(((a[iss][jss]&nord) == 0) ||
((a[iss][jss]&vest) == 0))
{
continue; // iau alt yss/xss
}
else
{

// aici a[xss][yss] are linie la stanga si sus


verific(iss,jss); // patrat cu coltul stanga sus si lg
}// for jss
}// for iss
} // void cerinta1()

// =============================================================

void cerinta2()
{
cerinta1();
for(int i=1; i<=lgmaxnm; i++)
{
if(distributia[i] > 0) // nu se afiseaza cele cu 0 ... !!!
{
fout<<i<<" "<<distributia[i]<<endl;
}
}
} // void cerinta2()

// =============================================================

void lipsaLaNord(int i, int j) // a[i][j] are linie lipsa N


{
a[i][j]=a[i][j]+nord; // PUN linie la N;
a[i-1][j]=a[i-1][j]+sud; // PUN linie la S pe randul anterior

cerinta1();

a[i][j]=a[i][j]-nord; // SCOT linie la N;


a[i-1][j]=a[i-1][j]-sud; // SCOT LINIe la S pe randul anterior

if(nrp1 > nrp1max)


{
nrp1max=nrp1; pozitia3i=i;
pozitia3j=j; pozitia3d=nord;
}
}// void lipsaLaNord(int i, int j)

// ------------------------------------------------------------------

void lipsaLaVest(int i, int j) // a[i][j] are lipsa V


{
a[i][j]=a[i][j]+vest; // PUN linie la V in a[i][j]
a[i][j-1]=a[i][j-1]+est; // PUN linie la E in a[i][j-1]

//cout<<"fara lipsa la Vest: "<<a[i][j-1]<<" "<<a[i][j]<<endl;

cerinta1();

a[i][j]=a[i][j]-vest; // SCOT linie la V in a[i][j]


a[i][j-1]=a[i][j-1]-est; // SCOT linie la E in a[i][j-1]

if(nrp1 > nrp1max)


{
nrp1max=nrp1; pozitia3i=i;
pozitia3j=j; pozitia3d=vest;
}
}// void lipsaLaVest(int i, int j)

// ============================================
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 25

void cerinta3()
{
t1 = clock();

// primul apel ...


cerinta1();
nrp1initial=nrp1; // nr initial de patratele
//cout<<"nrp1initial = "<<nrp1initial<<endl;

pozitia3d=0; // directie pentru cerinta 3: nord, est, sud, vest


pozitia3i=0; // i pozitia pentru cerinta 3: 1 <= i <= n
pozitia3j=0; // j pozitia pentru cerinta 3: 1 <= j <= m

t2 = clock();
t21=(double)(t2 - t1) / CLOCKS_PER_SEC;
//cout<<" t21 = "<<t21<<endl;
//---------------------------

// pentru cerinta 3 caut linii orizontale lipsa la N


for(int i=1; i<=n+1; i++) // si linia n+1 = "bordura"
{
for(int j=1; j<=m+1; j++) // si coloana n+1 = "bordura"
{
if((a[i][j] & nord) == 0) // nu are linie la nord
{
lipsaLaNord(i,j); // a[i][j] are lipsa N
}
if((a[i][j] & vest) == 0) // nu are linie la vest
{
lipsaLaVest(i,j); // a[i][j] are lipsa N
}
}// for j
}// for i
//---------------------------
t3 = clock();
t32=(double)(t3 - t2) / CLOCKS_PER_SEC;
//cout<<" t32 = "<<t32<<endl;
//---------------------------

if(nrp1max==nrp1initial)
{
cout<<"0"<<endl; cout<<"0 0 NU";
fout<<"0"<<endl; fout<<"0 0 NU";
return;
}

// pentru afisare ...


if((pozitia3d==nord) && (pozitia3i != 1))
{
pozitia3d=sud;
pozitia3i=pozitia3i-1;
}
else
if((pozitia3d==vest) && (pozitia3j != 1))
{
pozitia3d=est; // aici ESTE OK
pozitia3j=pozitia3j-1;
}

//cout<<nrp1max<<endl;
//cout<<pozitia3i<<" "<<pozitia3j<<" ";
//if(pozitia3d==1) cout<<"SUS"; else
//if(pozitia3d==2) cout<<"DREAPTA"; else
//if(pozitia3d==4) cout<<"JOS"; else
//if(pozitia3d==8) cout<<"STANGA";
fout<<nrp1max<<endl;
fout<<pozitia3i<<" "<<pozitia3j<<" ";
if(pozitia3d==1) fout<<"SUS"; else
if(pozitia3d==2) fout<<"DREAPTA"; else
if(pozitia3d==4) fout<<"JOS"; else
if(pozitia3d==8) fout<<"STANGA";
return;
} // cerinta3()
// ==========================================================
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 26

int main()
{
fin>>n>>m>>t;

//cout<<"n = "<<n<<" m = "<<m<<" t = "<<t<<endl; // verific !

lgmaxnm=min(n,m); // lg max patratel

for(int i=1; i<=n; i++)


{
for(int j=1; j<=m; j++)
{
fin>>a[i][j];
}
}

if(t==1)
{
cerinta1();
cout<<"nrp1 = "<<nrp1<<endl;
fout<<nrp1<<endl;
}
else
if(t==2)
{
cerinta2();
}
else
if(t==3)
{
// linia n+1
for(int j=1; j<=m; j++)
if((a[n][j]&sud) != 0) a[n+1][j]=nord;

// coloana m+1
for(int i=1; i<=n; i++)
if((a[i][m]&est) != 0) a[i][m+1]=vest;

cerinta3();

//cout<<"\n\n";
//cout<<" t21 = "<<t21<<endl;
//cout<<" t32 = "<<t32<<endl;
}

fin.close();
fout.close();

return 0;
}

Figura 2.6: oji2022-patratele-pereti-codificati


CAPITOLUL 2. OJI 2022 2.1. PATRATELE 27

Listing 2.1.2: oji2022-patratele-v2.cpp


1 #include<iostream>// Atentie la && si &
2 #include<fstream> //
3 #include<iomanip> // pentru setw(2)
4 #include <chrono>
5
6 // Atentie la prioritati: ... mai bine pun mereu paranteze ... !!!
7 // 10: == si !=
8 // 11: a&b
9 //if( a[n][j]&sud != 0) // asa da gresit ... !!!
10 //if((a[n][j]&sud) != 0) // asa da corect ... !!!
11
12 using namespace std;
13
14 //ifstream fin("patratele.in");
15 //ifstream fin("../teste_patratele/10-patratele.in"); //
16 //ifstream fin("../teste_patratele/20-patratele.in"); //
17 ifstream fin("../teste_patratele/45-patratele.in"); // time : 0.021 s
18 ofstream fout("patratele.out");
19
20 int n; // linii
21 int m; // coloane
22 int t; // cerinta
23
24 int sus=1; int dreapta=2; int jos=4; int stanga=8;
25
26 const int nord=1; const int est=2;
27 const int sud=4; const int vest=8;
28
29 int nrp1=0; // nr patratele pentru cerinta1
30 int lgmaxnm; // lgmaxnm = min(n,m); // lg max patratel
31 int lgmax; // lgmax = cel mult lgmaxnm // lg max patratel analizat
32
33 int nrpmax=0; // nr max patrate cand se traseaza o linie
34 int nrpmax1=0; // nr max patrate pe "liniile originale"
35
36 int patrateleinplus;
37 int patrateleinplusmax=0;;
38
39 int pozitia3d; // directia pentru cerinta 3: nord, est, sud, vest
40 int pozitia3i; // i pozitia pentru cerinta 3
41 int pozitia3j; // j pozitia pentru cerinta 3
42
43 bool lipsaNordi1; // lipsa linie la N
44 bool lipsaVestj1; // lipsa coloana la V
45
46 bool depasireNord; bool depasireSud;
47 bool depasireVest; bool depasireEst;
48
49 int verificNumaiUltimul=0;
50
51 int a[62][62]; // pentru o eventuala "bordare" in 0 si 61
52 int distributia[61]; // 0, 1, 2, ..., 60
53
54 clock_t t1, t2, t3, t4;
55 double t21, t32, t43;
56
57 // ----------------------------------------------------------------
58
59 void afisa() // pentru faza de testare ... datele din enunt ... !!!
60 {
61 for(int i=1; i<=n+1; i++)
62 {
63 for(int j=1; j<=m+1; j++)
64 {
65 cout<<setw(3)<<a[i][j];
66 }
67 cout<<endl;
68 }
69 cout<<endl;
70 //getchar();
71 }// void afisa()
72
73 // ====================================================================
74
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 28

75 void verific(int iss, int jss) // pentru lg=1, ..., ***


76 {
77 int i, j; // patratel (i,j)
78 int lg;
79 int lgmax=min(n-iss+1,m-jss+1);
80
81 bool ok; // util la cautari
82
83 // verific pentru lg=1;
84 lg=1;
85 if(((a[iss][jss] & est) != 0) &&
86 ((a[iss][jss] & sud) != 0))
87 {
88 nrp1++;
89 distributia[lg]++;
90 }
91 else
92 {
93
94 }
95
96 // verific pentru lg=2, 3, ..., ;
97 lgmax=min(n-iss+1, m-jss+1);
98 for(lg=2; lg <= lgmax; lg++)
99 {
100 int ilg=iss+lg-1; // bordura de S
101 int jlg=jss+lg-1; // bordura de la E
102
103 // verific doloana din dreapta, la N
104 if((a[iss][jlg] & nord) == 0)
105 {
106 return; // nu mai exista mai departe ...
107 }
108 else
109 {
110
111 }
112
113 // verific linia de jos, la V
114 if((a[ilg][jss] & vest) == 0)
115 {
116 return; // nu mai exista mai departe ...
117 }
118 else
119 {
120
121 }
122
123 //verific coltul dreapta jos a[ilg][jlg]
124 ok=true;
125 if(((a[ilg][jlg] & est) == 0) || // lipseste linie la dreapta
126 ((a[ilg][jlg] & sud) == 0) ) // lipseste linie in jos
127 {
128 ok=false;
129 }
130 else
131 {
132
133 }
134 if(ok==false) continue; // iau alt lg
135
136 // aici patratelul are N si V ok, si coltul dreapta-jos ok
137
138 // verific latura din dreapta (sus si jos sunt deja verificate!)
139 int ok=true;
140 for(int i=iss; i<=ilg-1; i++) // coltul a fost verificat deja
141 {
142 if((a[i][jlg]&est) == 0) // nu are linie la V
143 {
144 ok=false;
145 }
146 else
147 {
148
149 }
150 }
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 29

151 if(ok==false) continue; // iau alt lg


152
153 //aici latura E este ok
154
155 // verific latura de jos
156 ok=true;
157 for(int j=jss; j<=jlg-1; j++) // coltul a fost verificat deja
158 {
159 if((a[ilg][j]&sud) == 0) // nu are linie la S
160 {
161 ok=false;
162 break; // ies din for j
163 //continue;
164 }
165 else
166 {
167
168 }
169 }
170 if(ok==false) continue; // alt lg
171
172 // aici totul este ok ... !!!
173 nrp1++;
174 distributia[lg]++;
175 }// for lg
176 }// void verific(int iss, int jss) // pentru lg=1, ..., ***
177
178 // ------------------------------------------------------
179
180 void cerinta1() // for i ... for j ... for lg
181 {
182 lgmaxnm=min(n,m); // lg max patratel
183
184 int iss, jss; // (iss,jss) = coltul stanga sus al patratului verificat
185 int lg; // lungime latura patratel
186 nrp1=0; // rezultat pentru cerinta1
187
188 for(iss=1; iss<=n; iss++)
189 {
190 for(jss=1; jss<=m; jss++) // coltul stanga sus
191 {
192 // verific N si V la celula {xss, yss)
193 if(((a[iss][jss]&nord) == 0) ||
194 ((a[iss][jss]&vest) == 0))
195 {
196 continue; // iau alt yss/xss
197 }
198 else
199 {
200
201 }
202
203 // aici a[xss][yss] are linie la stanga si sus
204 verific(iss,jss); // patrat cu coltul stanga sus si lg
205 }// for jss
206 }// for iss
207 } // void cerinta1()
208
209 // =================================================================================
210
211 void cerinta2()
212 {
213 cerinta1();
214
215 for(int i=1; i<=lgmaxnm; i++)
216 {
217 if(distributia[i] > 0) // nu se afiseaza cele cu 0 ... !!!
218 {
219 fout<<i<<" "<<distributia[i]<<endl;
220 }
221 }
222 } // void cerinta2()
223
224 // ================================================================================
225
226 // patratele pe linia i2=i-1 si col=j1...j2 (pe linia i2 la SUD este ok)
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 30

227 bool calculSpreNord(int i,int j,int j1,int j2)


228 {
229 int lg=j2-j1+1;
230 int i1, i2;
231
232 i2=i-1;
233 i1=i2-lg+1; // unde ajunge in sus
234
235 if(i1<1) // iese afara in sus
236 {
237 depasireNord=true;
238 return false;
239 }
240
241 if(lg==1) // j1=j2=j
242 {
243 // verific numai un patratel la N, E, V (la S are linie!)
244
245 if(((a[i-1][j] & vest) == 0) || // nu are la vest
246
247 ((a[i-1][j] & nord) == 0) || // nu are la nord
248
249 ((a[i-1][j] & est) == 0)) // nu are la est
250 {
251 if((a[i-1][j] & vest)==0)
252 {
253 lipsaVestj1=true;
254 }
255
256 return false;
257 }
258 else
259 {
260 return true;
261 }
262 }
263
264 // verific coloana j1 la vest
265 // cand j2=j --> j2 este primul in secventa lui j1
266 if(j2==j) //j2=primul --> la j1 se verifica toata coloana j1
267 {
268 for(int ii=i1; ii<=i2; ii++)
269 if((a[ii][j1]&vest) == 0)
270 {
271 lipsaVestj1=true; return false;
272 }
273 }
274 else // j2 nu este primul --> se verifica numai ultimul din coloana j1
275 {
276 verificNumaiUltimul++;
277 if((a[i1][j1]&vest) == 0) // nu mai are rost verificare in j2 ... !!!
278 {
279 lipsaVestj1=true; return false;
280 }
281 }
282
283 // verific coloana j2 la E care este si coloana j2+1 la Vest ... !!!
284 for(int ii=i1; ii<=i2; ii++)
285 {
286 if((a[ii][j2+1]&vest) == 0) { return false; }
287 }
288
289 // verific linia i1 la Nord
290 for(int jj=j1; jj<=j2; jj++)
291 {
292 if((a[i1][jj]&nord) == 0)
293 {
294 return false;
295 }
296 }
297
298 // patratelul a scapat de toate verificarile ... !!!
299 return true;
300 } // calculSpreNord(int i,int j,int j1,int j2)
301
302 // ---------------------------------------------------------------------------
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 31

303
304 // patratel pe linia i si col=j1...j2, (pe linia i la N este ok)
305 bool calculSpreSud(int i, int j, int j1,int j2)
306 {
307 int lg=j2-j1+1;
308 int i1, i2;
309
310 i1=i;
311 i2=i1+lg-1; // unde ajunge in jos
312
313 if(i2>n) // iese afara in jos
314 {
315 depasireSud=true;
316 return false;
317 }
318
319 // linia la N este ok
320
321 if(lg==1) // j1=j2=j
322 {
323 if(((a[i][j] & vest) == 0) || // nu are la vest
324 ((a[i][j] & sud) == 0) || // nu are la sud
325 ((a[i][j] & est) == 0)) // nu are la est
326 {
327 if((a[i][j] & vest)==0)
328 {
329 lipsaVestj1=true;
330 }
331
332 return false;
333 }
334 else
335 {
336 return true;
337 }
338 }
339
340 // verific coloana j1
341 // cand j2=j --> j2 este primul in secventa lui j1
342 if(j2==j) //j2=primul --> j1 se verifica tot
343 {
344 for(int ii=i1; ii<=i2; ii++)
345 {
346 if((a[ii][j1]&vest) == 0)
347 {
348 lipsaVestj1=true; return false;
349 }
350 }
351 }
352 else // j2 nu este primul --> se verifica numai ultimul din coloana j1
353 {
354 verificNumaiUltimul++;
355 if((a[i2][j1]&vest) == 0)
356 {
357 lipsaVestj1=true; return false;
358 }
359 }
360
361 // aici coloana j1 este ok
362 // verific coloana j2 la E care este si coloana j2+1 la Vest ... !!!
363 for(int ii=i1; ii<=i2; ii++)
364 if((a[ii][j2+1]&vest) == 0) { return false; }
365
366 // verific linia i2 la S care este si linia i2-1 la N
367 for(int jj=j1; jj<=j2; jj++)
368 {
369 if((a[i2+1][jj]&nord) == 0)
370 {
371 return false;
372 }
373 }
374
375 return true;
376 } // calculSpreSud(int i, int j, int j1,int j2)
377
378 // =================================================================================
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 32

379
380 // patratel spre Vest, pe coloana j, intre liniile i1 ... i2
381 bool calculSpreVest(int i, int j, int i1,int i2)
382 {
383 int lg=i2-i1+1;
384 int j1, j2;
385
386 j2=j-1;
387 j1=j2-lg+1; // unde ajunge in stanga
388
389 if(j1<1) // iese afara in stanga
390 {
391 depasireVest=true; return false;
392 }
393
394 if(lg==1) // i1=i2=i
395 {
396 if(((a[i][j-1] & nord) == 0) || // nu are la nord
397
398 ((a[i][j-1] & vest) == 0) || // nu are la vest
399
400 ((a[i][j-1] & sud) ==0)) // nu are la sud
401 {
402 if((a[i][j-1] & nord)==0)
403 {
404 lipsaNordi1=true;
405 }
406
407 return false;
408 }
409 else
410 {
411 return true;
412 }
413 }
414
415 // verific linia i1 la Nord
416 // cand i2=i --> i2 este primul in secventa lui i1
417 if(i2==i) //i2=primul --> se verifica toata linia i1
418 {
419 for(int jj=j1; jj<=j2; jj++)
420 if((a[i1][jj]&nord) == 0)
421 {
422 lipsaNordi1=true;
423 return false;
424 }
425 }
426 else // i2 nu este primul --> se verifica numai ultimul din linia i1
427 {
428 if((a[i1][j1]&nord) == 0) // nu mai are rost verificare in i2 ... !!!
429 {
430 lipsaNordi1=true;
431 return false;
432 }
433 }
434
435 // verific linia i2 la S care este si linia i2+1 la Nord ... !!!
436 for(int jj=j1; jj<=j2; jj++)
437 if((a[i2+1][jj]&nord) == 0)
438 {
439 return false;
440 }
441
442 // verific linia V ... pe j=j1
443 for(int ii=i1; ii<=i2; ii++)
444 {
445 if((a[ii][j1]&vest) == 0)
446 {
447 return false;
448 }
449 }
450
451 return true;
452 }// bool calculSpreVest(int i, int j, int i1,int i2)
453
454 // ---------------------------------------------------------------------------
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 33

455
456 bool calculSpreEst(int i, int j, int i1,int i2)
457 {
458 int lg=i2-i1+1;
459 int j1, j2;
460
461 j1=j;
462 j2=j1+lg-1; // unde ajunge in stanga
463
464 if(j2>m) // iese afara in dreapta
465 {
466 depasireEst=true;
467 return false;
468 }
469
470 if(lg==1) // i1=i2=i
471 {
472 if(((a[i][j] & nord) == 0) || // nu are la nord
473
474 ((a[i][j] & est) == 0) || // nu are la est
475
476 ((a[i][j] & sud) == 0)) // nu are la sud
477 {
478 if((a[i][j] & nord)==0)
479 {
480 lipsaNordi1=true;
481 }
482
483 return false;
484 }
485 else
486 {
487 return true;
488 }
489 }
490
491 // verific linia i1
492 // cand i2=i --> i2 este primul in secventa lui i1
493 if(i2==i) //i2=primul --> se verifica toata linia i1
494 {
495 for(int jj=j1; jj<=j2; jj++)
496 if((a[i1][jj]&nord) == 0)
497 {
498 lipsaNordi1=true;
499 return false;
500 }
501 }
502 else // i2 nu este primul --> se verifica numai ultimul din linia i1
503 {
504 if((a[i1][j2]&nord) == 0) // nu mai are rost verificare in i2 ... !!!
505 {
506 lipsaNordi1=true;
507 return false;
508 }
509 }
510
511 // verific linia i2 la S care este si linia i2+1 la Nord ... dar nu conteza aici !!!
512 //cout<<"verific linia "<<i2<<" la S\n";
513 for(int jj=j1; jj<=j2; jj++)
514 if((a[i2+1][jj]&nord) == 0)
515 {
516 return false;
517 }
518
519 // verific coloana j2 la Este care este sicoloana j2+1 la Vest
520 for(int ii=i1; ii<=i2; ii++)
521 if((a[ii][j2+1]&vest) == 0)
522 {
523 return false;
524 }
525
526 return true;
527 }// calculSpreEst(int i, int j, int i1,int i2)
528
529 // =================================================================================
530
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 34

531 void verificLaNord(int i, int j, int jj1, int jj2) // am pus linie la N in a[i][j]
532 {
533 // caut patrate spre N care sa contina latura de N din a[i][j]
534 // cu coloanele [j1,j2] in zona [jj1,jj2]
535
536 for(int j1=j; j1>=jj1; j1--) // numai Nord ... !!!
537 {
538 for(int j2=j; j2<=jj2; j2++)// cand j2=j --> j2 este primul in secventa lui j1
539 {
540 // j2 modifica lg ...
541 depasireNord=false;
542 lipsaVestj1=false;
543
544 if(calculSpreNord(i,j,j1,j2)==true)
545 {
546 patrateleinplus++;
547 }
548 else // daca a depasit spre nord --> break la j2
549 {
550 if(depasireNord==true) break; // depasire la N --> alt i1
551 if(lipsaVestj1==true) break; // lipsa la V in j1 --> alt i1
552 }
553
554 }// for j2
555 }// for j1
556
557 // caut patrate spre S care sa contina latura de N din a[i][j]
558 // cu coloanele [j1,j2] in zona [jj1,jj2]
559
560 for(int j1=j; j1>=jj1; j1--) // numai Sud ... !!!
561 {
562 for(int j2=j; j2<=jj2; j2++)// cand j2=j --> j2 este primul in secventa lui j1
563 {
564 // j2 modifica lg ...
565 depasireSud=false;
566 lipsaVestj1=false;
567
568 if(calculSpreSud(i,j,j1,j2)==true)
569 { patrateleinplus++; }
570 else { }
571
572 if(depasireSud==true) break; // depasire la S --> alt j1
573 if(lipsaVestj1==true) break; // lipsa la V in j1 --> alt j1
574 }// for j2
575 }// for j1
576
577 }// void verificLaNord(int i, int j)
578
579 // ----------------------------------------------------------------------------
580
581 void verificLaVest(int i, int j, int ii1, int ii2) // am pus linie la N in a[i][j]
582 {
583 for(int i1=i; i1>=ii1; i1--)
584 {
585 for(int i2=i; i2<=ii2; i2++)// cand i2=i --> i2 este primul in secventa lui i1
586 {
587 // i2 modifica lg ...
588 depasireVest=false;
589 lipsaNordi1=false;
590
591 if(calculSpreVest(i,j,i1,i2)==true)
592 {
593 patrateleinplus++;
594 //cout<<patrateleinplus<<" V : "<<" i1 = "<<i1<<" i2 = "<<i2<<endl;
595 //getchar();
596 }
597 else // daca a depasit spre nord --> break la j2
598 {
599
600 }
601
602 if(depasireVest==true) break; // depasire la V --> alt i1
603 if(lipsaNordi1==true) break; // lipsa la N in i1 --> alt i1
604 }// for i2
605 }// for i1
606
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 35

607 // caut patrate spre E care sa contina latura de V din a[i][j]


608 // cu liniile [i11,ji] in zona [ii1,ii2]
609
610 for(int i1=i; i1>=ii1; i1--)
611 {
612 // cand i2=i --> i2 este primul in secventa lui i1
613 for(int i2=i; i2<=ii2; i2++)
614 {
615 // i2 modifica lg ...
616
617 depasireEst=false;
618 lipsaNordi1=false;
619
620 if(calculSpreEst(i,j,i1,i2)==true)
621 {
622 patrateleinplus++;
623 }
624 else
625 {
626 if(depasireEst==true) break; // --> alt i1
627 if(lipsaNordi1==true) break; // --> alt i1
628 }
629 }// for i2
630 }// for i1
631
632 }// void verificLaVest(int i, int j, int jj1, int jj2)
633
634
635 // ===========================================================
636
637 void lipsaLaNord(int i, int j) // a[i][j] are linie lipsa N
638 {
639 int jj1, jj2;
640 patrateleinplus=0;// initializez nr patrate in plus daca se pune linie aici
641
642 a[i][j]=a[i][j]+nord; // PUN linie la N;
643 a[i-1][j]=a[i-1][j]+sud; // PUN linie la S pe randul anterior
644
645 // determin coloanale jj1 si jj2
646 jj1=j;
647 while((a[i][jj1-1]&nord) != 0) jj1--; // este utila "bordarea" matricei
648
649 jj2=j;
650 while((a[i][jj2+1]&nord) != 0) jj2++; // este utila "bordarea" matricei
651
652 verificLaNord(i,j,jj1,jj2); // patrateleinplus=? linie lipsa la Nord
653
654 a[i][j]=a[i][j]-nord; // SCOT linie la N;
655 a[i-1][j]=a[i-1][j]-sud; // SCOT LINIe la S pe randul anterior
656
657 if(patrateleinplus > patrateleinplusmax)
658 {
659 patrateleinplusmax=patrateleinplus;
660 pozitia3i=i;
661 pozitia3j=j;
662 pozitia3d=nord;
663 }
664 }// void lipsaLaNord(int i, int j)
665
666 // ---------------------------------------------------------------------------
667
668 void lipsaLaVest(int i, int j) // a[i][j] are lipsa V
669 {
670 int ii1, ii2;
671
672 patrateleinplus=0; //initializez nr patrate in plus daca se pune linie aici
673
674 a[i][j]=a[i][j]+vest; // PUN linie la V;
675 a[i][j-1]=a[i][j-1]+est; // PUN linie la E pe coloana anterioara
676
677 // determin liniile ii1 si ii2
678 ii1=i; while((a[ii1-1][j]&vest) != 0) ii1--;
679
680 ii2=i; while((a[ii2+1][j]&vest) != 0) ii2++;
681
682 verificLaVest(i,j,ii1,ii2); // patrateleinplus=? linie lipsa la Vest
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 36

683
684 a[i][j]=a[i][j]-vest; // SCOT linie la V;
685 a[i][j-1]=a[i][j-1]-est; // SCOT linie la E pe coloana anterioara
686
687 if(patrateleinplus > patrateleinplusmax)
688 {
689 patrateleinplusmax=patrateleinplus;
690 pozitia3i=i;
691 pozitia3j=j;
692 pozitia3d=vest;
693 }
694 }// void lipsaLaVest(int i, int j)
695 // ========================================================================
696
697 void cerinta3()
698 {
699 t1 = clock();
700
701 // primul apel ...
702 cerinta1();
703 nrpmax1=nrp1; // nr initial de patratele
704
705 pozitia3d=0; // directie pentru cerinta 3: nord, est, sud, vest
706 pozitia3i=0; // i pozitia pentru cerinta 3: 1 <= i <= n
707 pozitia3j=0; // j pozitia pentru cerinta 3: 1 <= j <= m
708
709 t2 = clock();
710 t21=(double)(t2 - t1) / CLOCKS_PER_SEC;
711 //cout<<" t21 = "<<t21<<endl;
712 //---------------------------
713
714 // pentru cerinta 3 caut linii orizontale lipsa la N
715 for(int i=1; i<=n+1; i++) // si linia n+1="bordura"
716 {
717 for(int j=1; j<=m; j++) // a[i][j]
718 {
719 if((a[i][j] & nord) == 0) // nu are linie la nord
720 {
721 lipsaLaNord(i,j); // a[i][j] are lipsa N
722 }
723 }// for j
724 }// for i
725
726 //---------------------------
727
728 t3 = clock();
729 t32=(double)(t3 - t2) / CLOCKS_PER_SEC;
730 //cout<<" t32 = "<<t32<<endl;
731
732 // pentru cerinta 3 caut linii verticale lipsa la V
733 for(int i=1; i<=n; i++)
734 {
735 for(int j=1; j<=m+1; j++) // si coloana m+1="bordura"
736 {
737 if((a[i][j] & vest) == 0) // nu are linie la vest
738 {
739 lipsaLaVest(i,j); // a[i][j] are lipsa V
740 }
741 }// for j
742 }// for i
743
744 //---------------------------
745
746 t4 = clock();
747 t43 = (double)(t4 - t3) / CLOCKS_PER_SEC;
748 //cout<<" t43 = "<<t43<<endl;
749
750 if(patrateleinplusmax==0)
751 {
752 cout<<"0"<<endl; cout<<"0 0 NU";
753 fout<<"0"<<endl; fout<<"0 0 NU";
754 return;
755 }
756
757 nrpmax=nrpmax1+patrateleinplusmax;
758
CAPITOLUL 2. OJI 2022 2.1. PATRATELE 37

759 // pentru afisare ...


760
761 if((pozitia3d==nord) && (pozitia3i != 1))
762 {
763 pozitia3d=sud;
764 pozitia3i=pozitia3i-1;
765 }
766
767 else
768
769 if((pozitia3d==vest) && (pozitia3j != 1))
770 {
771 pozitia3d=est; // aici ESTE OK
772 pozitia3j=pozitia3j-1;
773 }
774
775 fout<<nrpmax<<endl;
776
777 fout<<pozitia3i<<" "<<pozitia3j<<" ";
778
779 if(pozitia3d==1) fout<<"SUS";
780 else
781 if(pozitia3d==2) fout<<"DREAPTA";
782 else
783 if(pozitia3d==4) fout<<"JOS";
784 else
785 if(pozitia3d==8) fout<<"STANGA";
786
787 return;
788 } // cerinta3()
789
790 // ===========================================================================
791
792 int main()
793 {
794 fin>>n>>m>>t;
795
796 for(int i=1; i<=n; i++)
797 {
798 for(int j=1; j<=m; j++)
799 {
800 fin>>a[i][j];
801 }
802 }
803
804
805 if(t==1)
806 {
807 cerinta1();
808 fout<<nrp1<<endl;
809 }
810 else
811 if(t==2)
812 {
813 cerinta2();
814 }
815 else
816 if(t==3)
817 {
818 // linia n+1
819 for(int j=1; j<=m; j++)
820 if((a[n][j]&sud) != 0) a[n+1][j]=nord;
821
822 // coloana m+1
823 for(int i=1; i<=n; i++)
824 if((a[i][m]&est) != 0) a[i][m+1]=vest;
825
826 cerinta3();
827 }
828
829 fin.close();
830 fout.close();
831
832 return 0;
833 }
CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 38

Listing 2.1.3: patratele-checker.cpp


#include "testlib.h"

using namespace std;

int main()
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../teste_patratele/45-patratele.in", // input
(char*)"../teste_patratele/45-patratele.ok", // rezultat corect
(char*)"patratele.out", // rezultat de verificat si acordat punctaj
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("patratele", argc, argv);

compareRemainingLines();
}

2.2 pseudocmp
Problema 2 - Pseudocmp 100 de puncte
Àles a primit ca temă următoarea problemă: ”Fiind dat un şir A cu N numere naturale
distincte, să se calculeze suma cifrelor fiecărui element al şirului”.
După ce şi-a terminat tema, acesta observă că sunt mai multe perechi de indici i, j  pentru
care dacă Ai $ Aj atunci Si % Sj , unde Si reprezintă suma cifrelor lui Ai . El le va numi pe acestea
perechi speciale de indici.
Cerinţe
Terminând prea repede tema, Àles primeşte o temă suplimentară cu două cerinţe:
1. Determină două numere aflate ı̂n şirul A, pentru care indicii corespunzători formează o
pereche specială.
2. Câte perechi speciale de indici i, j  se găsesc ı̂n şirul A?
Date de intrare
Pe prima linie a fişierului pseudocmp.in se găsesc două numere naturale: T şi N . Pe
următoarea linie se găsesc N numere naturale, separate printr-un spaţiu, reprezentând valorile
din şirul A. Numărul T reprezintă numărul cerinţei.
Date de ieşire
Pe prima linie a fişierului pseudocmp.out:
1. Dacă T 1, se găsesc două numere naturale x, y, cu x $ y, separate printr-un spaţiu,
reprezentând răspunsul pentru cerinţa 1 dacă există soluţie sau -1, dacă nu există soluţie.
Dacă există mai multe soluţii, se acceptă oricare dintre acestea.
2. Dacă T 2, se găseşte un singur număr natural, reprezentând răspunsul la cerinţa 2.
Restricţii şi precizări
ˆ 1 & N & 100 000
ˆ 1 & Ai & 1 000 000, pentru 1 & i & N

# Punctaj Restricţii
1 15 T 1, N & 1000
2 25 T 1, 1000 $ N
3 25 T 2, N & 1000
4 35 T 2, 1000 $ N
CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 39

Exemple:

pseudocmp.in pseudocmp.out Explicaţii


16 99 123 99 este mai mic decât 123 iar suma cifrelor lui
213 123 523 51 99 92 99 este 18, suma cifrelor lui 123 este 6, 18 % 6
26 6 Cele 6 perechi de indici sunt următoarele:
213 123 523 51 99 92 (5, 1)
(5, 2)
(5, 3)
(6, 1)
(6, 2)
(6, 3)
15 -1
65213

2.2.1 Indicaţii de rezolvare

Propusă de: Stud. Bogdan-Ioan Popa


Soluţie - stud. Bogdan-Ioan Popa.
Facultatea de Matematică şi Informatică, Universitatea din Bucureşti
Cerinţa 1. Această soluţie la prima cerinţ ă obţine 40 de puncte.
Complexitatea timp este O N Cmax  V almax , unde V almax reprezintă valoarea maximă a
unui număr din şir, iar Cmax reprezintă numărul maxim de cifre ale unui număr din şir.
Pentru a nu exista nicio pereche specială, ı̂nseamnă că pentru oricare doi indici i, j , dacă
Ai $ Aj atunci Si & Sj .
Această observaţie ne duce cu gândul la a sorta acest şir A.
Pentru că sunt multe numere (N & 100 000) şi valorile din şir sunt mici (Ai & 1 000 000) vom
folosi o sortare prin numărare.
Vom introduce numerele ı̂ntr-un vector caracteristic, apoi ı̂l vom parcurge de la 1 la 1 000 000
pentru a obţine şirul sortat.
După sortare facem următoarea observaţie: Nu va exista nicio pereche specială dacă şi numai
dacă Si & Si1 pentru orice 1 & i $ N .
Astfel, dacă şirul sortat respectă condiţia enunţată mai sus, răspunsul va fi 1, altfel răspunsul
va fi o pereche de numere Ak , Ak1 (ı̂n ordinea aceasta) pentru care Sk % Sk1 şi 1 & k $ N .

Cerinţa 2. Această soluţie la a doua cerinţă obţine 60 de puncte.


Complexitate timp O N Smax  V almax .
Cum şirul A este sortat crescător, perechile speciale vor respect condiţia i $ j.
Rămâne să calculăm pentru fiecare 1 & j & N câţi indici i există astfel ı̂ncât 1 &i$ j şi
Si % Sj .
La fiecare pas vom ţine vectorul de frecvenţă f reqs = caţi indici i aflaţi la stânga lui j există
astfel ı̂ncât Si s.
Apoi ı̂nsumăm valorile f reqs , pentru fiecare s de la Sj  1 la Smax , unde Smax reprezintă suma
maximă a cifrelor unui număr. Odată cu calcularea acestei sume, putem actualiza vectorul f req,
crescând f reqSj cu 1.

2.2.2 *Cod sursă


CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 40

2.2.3 Rezolvare detaliată

Listing 2.2.1: oji2022-pseudocmp-v1.cpp


#include<iostream>
#include<fstream> // ifstream, ofstream
#include<algorithm> // sort
#include<iomanip> //setw(3)

#include<iomanip> // pentru setw(2)


#include <chrono> //

using namespace std;

//ifstream fin("pseudocmp1.in");
//ifstream fin("pseudocmp2.in");
//ifstream fin("pseudocmp3.in");

// cazul1
//ifstream fin("../teste_pseudocmp/10-pseudocmp.in"); // -1
//ifstream fin("../teste_pseudocmp/11-pseudocmp.in"); // 77 80 / 4 111
//ifstream fin("../teste_pseudocmp/12-pseudocmp.in"); // 89 101 / 6 101
//ifstream fin("../teste_pseudocmp/13-pseudocmp.in"); // 982656 982682
//ifstream fin("../teste_pseudocmp/14-pseudocmp.in"); // 993547 993555 time : 0.913 s

//ifstream fin("../teste_pseudocmp/15-pseudocmp.in"); // 995465 995473 time : 2.336 s

//ifstream fin("../teste_pseudocmp/16-pseudocmp.in"); // 996392 996409 time : 3.335 s


//ifstream fin("../teste_pseudocmp/17-pseudocmp.in"); // 994096 994149 time : 3.674 s

// cazul2
//ifstream fin("../teste_pseudocmp/18-pseudocmp.in"); // 1564 time: 0.033 s
//ifstream fin("../teste_pseudocmp/19-pseudocmp.in"); // 32573 time: 0.034 s
//ifstream fin("../teste_pseudocmp/20-pseudocmp.in"); // 30493 time: 0.030 s
//ifstream fin("../teste_pseudocmp/21-pseudocmp.in"); // 161975 time: 0.042 s
//ifstream fin("../teste_pseudocmp/22-pseudocmp.in"); // 163499 time: 0.045 s

ifstream fin("../teste_pseudocmp/23-pseudocmp.in"); // 407455698 time: 4.073 s

//ifstream fin("../teste_pseudocmp/24-pseudocmp.in"); // 929807449 time: 8.731 s


//ifstream fin("../teste_pseudocmp/25-pseudocmp.in"); // 1187288553 time: 11.090 s
//ifstream fin("../teste_pseudocmp/26-pseudocmp.in"); // 2 323 419 429 time: 8.178 s
//ifstream fin("../teste_pseudocmp/27-pseudocmp.in"); // 2631709609 time : 9.046 s
//ifstream fin("../teste_pseudocmp/28-pseudocmp.in"); // 2631683600 time : 9.077 s
//ifstream fin("../teste_pseudocmp/29-pseudocmp.in"); // 2631701158 time : 9.051 s

ofstream fout("pseudocmp.out");

clock_t t1, t2, t3, t4;


double t21, t32, t43;

int T;
int N;
int ips, jps; // pereche speciala

long long nrps=0; // nr perechi speciale

int a[1000005]; // nr distincte --> i apare sau nu apare ... in sir


int s[1000005];

void afisa()
{
cout<<"a[]: ";
for(int i=1; i<=N; i++) { cout<<setw(3)<<a[i]<<" "; }
cout<<endl;
}

void afiss()
{
cout<<"s[]: ";
for(int i=1; i<=N; i++) { cout<<setw(3)<<s[i]<<" "; }
cout<<endl;
}
CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 41

int sumacifre(int nr) // sc <= 100 000 * 6*9 = 5 400 000


{
int sc=0;

while(nr>0)
{
sc=sc+nr%10;
nr=nr/10;
}

return sc;
}

void cerinta1()
{
// a[i]<a[j] --> s[i] <= s[j] pentru oricare i, j ...

bool existapereche=false;

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


{
for(int j=i+1; j<=N; j++) // i<j --> a[i]<a[j]
{
if(s[i]>s[j])
{
existapereche=true;
ips=i;
jps=j;
break;
}
}

if(existapereche==true) break;
}

if(existapereche==false)
{
cout<<"NU exista --> -1";
fout<<"-1";

return;
}
else
{
fout<<a[ips]<<" "<<a[jps];
return;
}

return;
}

void cerinta2()
{
for(int i=1; i <= N-1; i++)
{
for(int j=i+1; j<=N; j++) // i<j --> a[i]<a[j]
{
if(s[i]>s[j])
{
nrps++;
//cout<<a[i]<<" "<<a[j]<<endl;
}
}
}

//cout<<endl;

cout<<"nrps = "<<nrps<<endl;

fout<<nrps<<endl;

return;
}
CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 42

int main()
{
t1 = clock();

fin>>T;
fin>>N;

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


{
fin>>a[i];
}
t2 = clock();

cout<<"Cerinta = "<<T<<endl;

//afisa();
sort(a,a+N+1);
//afisa();
t3 = clock();

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


{
s[i]=sumacifre(a[i]);
}
//afiss();

if(T==1)
{
cerinta1();
}

if(T==2)
{
cerinta2();
}
t4 = clock();

fin.close();
fout.close();

t21=(double)(t2 - t1) / CLOCKS_PER_SEC; // t21 = 0.026 citire


t32=(double)(t3 - t2) / CLOCKS_PER_SEC; // t32 = 0.019 sort
t43=(double)(t4 - t3) / CLOCKS_PER_SEC; // t43 = 8.96 calcul
cout<<" t21 = "<<t21<<endl;
cout<<" t32 = "<<t32<<endl;
cout<<" t43 = "<<t43<<endl;

return 0;
}

Listing 2.2.2: oji2022-pseudocmp-v2.cpp


#include<iostream>
#include<fstream> // ifstream, ofstream
#include<algorithm> // sort
#include<iomanip> //setw(3)

#include<iomanip> // pentru setw(2)


#include <chrono> //

using namespace std;

//ifstream fin("pseudocmp1.in");
//ifstream fin("pseudocmp2.in");
//ifstream fin("pseudocmp3.in");

// cazul1
//ifstream fin("../teste_pseudocmp/10-pseudocmp.in"); // -1
//ifstream fin("../teste_pseudocmp/11-pseudocmp.in"); // 77 80 / 4 111
//ifstream fin("../teste_pseudocmp/12-pseudocmp.in"); // 89 101 / 6 101
//ifstream fin("../teste_pseudocmp/13-pseudocmp.in"); // 982656 982682
//ifstream fin("../teste_pseudocmp/14-pseudocmp.in"); // 993547 993555 time : 0.046 s
//ifstream fin("../teste_pseudocmp/15-pseudocmp.in"); // 995465 995473 time : 0.056 s
//ifstream fin("../teste_pseudocmp/16-pseudocmp.in"); // 996392 996409 time : 0.046 s
//ifstream fin("../teste_pseudocmp/17-pseudocmp.in"); // 994096 994149 time : 0.059 s
CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 43

// cazul2
//ifstream fin("../teste_pseudocmp/18-pseudocmp.in"); // 1564 time: 0.033 s
//ifstream fin("../teste_pseudocmp/19-pseudocmp.in"); // 32573 time: 0.034 s
//ifstream fin("../teste_pseudocmp/20-pseudocmp.in"); // 30493 time: 0.030 s
//ifstream fin("../teste_pseudocmp/21-pseudocmp.in"); // 161975 time: 0.042 s
//ifstream fin("../teste_pseudocmp/22-pseudocmp.in"); // 163499 time: 0.045 s
ifstream fin("../teste_pseudocmp/23-pseudocmp.in"); // 407455698 time: 4.073 s
//ifstream fin("../teste_pseudocmp/24-pseudocmp.in"); // 929807449 time: 8.731 s
//ifstream fin("../teste_pseudocmp/25-pseudocmp.in"); // 1187288553 time: 11.090 s
//ifstream fin("../teste_pseudocmp/26-pseudocmp.in"); // 2 323 419 429 time: 8.178 s
//ifstream fin("../teste_pseudocmp/27-pseudocmp.in"); // 2631709609 time : 9.046 s
//ifstream fin("../teste_pseudocmp/28-pseudocmp.in"); // 2631683600 time : 9.077 s
//ifstream fin("../teste_pseudocmp/29-pseudocmp.in"); // 2631701158 time : 9.051 s

ofstream fout("pseudocmp.out");

clock_t t1, t2, t3, t4;


double t21, t32, t43;

int T;
int N;
int ips, jps; // pereche speciala
long long nrps=0; // nr perechi speciale

int a[1000005]; // nr distincte --> i apare sau nu apare ... in sir


int s[1000005];

void afisa()
{
cout<<"a[]: ";
for(int i=1; i<=N; i++) { cout<<setw(3)<<a[i]<<" "; }
cout<<endl;
}

void afiss()
{
cout<<"s[]: ";
for(int i=1; i<=N; i++) { cout<<setw(3)<<s[i]<<" "; }
cout<<endl;
}

int sumacifre(int nr) // sc <= 100 000 * 6*9 = 5 400 000


{
int sc=0;
while(nr>0) { sc=sc+nr%10; nr=nr/10; }
return sc;
}

void cerinta1() // a[i]<a[j] --> s[i] <= s[j] pentru oricare i, j ...
{
bool existapereche=false;
for(int i=1; i <= N-1; i++) // a[i] < a[i+1]
{
if(s[i]>s[i+1])
{
existapereche=true;
ips=i;
break;
}
}

if(existapereche==false)
{
cout<<"NU exista --> -1";
fout<<"-1";
return;
}
else
{
fout<<a[ips]<<" "<<a[ips+1];
return;
}

return;
}
CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 44

void cerinta2()
{
for(int i=1; i <= N-1; i++)
{
for(int j=i+1; j<=N; j++) // i<j --> a[i]<a[j]
{
if(s[i]>s[j])
{
nrps++;
//cout<<a[i]<<" "<<a[j]<<endl;
}
}
}
//cout<<endl;

cout<<"nrps = "<<nrps<<endl;
fout<<nrps<<endl;
return;
}

int main()
{
t1 = clock();

fin>>T;
fin>>N;
cout<<"T = "<<T<<" N = "<<N<<endl;

for(int i=1; i<=N; i++) { fin>>a[i]; }

t2 = clock();

cout<<"Cerinta = "<<T<<endl;

//afisa();
sort(a,a+N+1);
//afisa();

t3 = clock();

for(int i=1; i<=N; i++) { s[i]=sumacifre(a[i]); }


//afiss();

if(T==1) { cerinta1(); }

if(T==2) { cerinta2(); }
t4 = clock();

fin.close();
fout.close();

t21=(double)(t2 - t1) / CLOCKS_PER_SEC; // t21 = 0.026 citire


t32=(double)(t3 - t2) / CLOCKS_PER_SEC; // t32 = 0.019 sort
t43=(double)(t4 - t3) / CLOCKS_PER_SEC; // t43 = 8.96 calcul
cout<<" t21 = "<<t21<<endl;
cout<<" t32 = "<<t32<<endl;
cout<<" t43 = "<<t43<<endl;

return 0;
}

Figura 2.7: oji2022-pseudocmp-frecvenţe1


CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 45

Listing 2.2.3: oji2022-pseudocmp-v3.cpp


#include<iostream>
#include<fstream> // ifstream, ofstream
#include<algorithm> // sort

#include<iomanip> // pentru setw(2)


#include <chrono> //

using namespace std;

//ifstream fin("pseudocmp1.in");
//ifstream fin("pseudocmp2.in");
//ifstream fin("pseudocmp3.in");

// cazul1
//ifstream fin("../teste_pseudocmp/10-pseudocmp.in"); // -1
//ifstream fin("../teste_pseudocmp/11-pseudocmp.in"); // 77 80 / 4 111
//ifstream fin("../teste_pseudocmp/12-pseudocmp.in"); // 89 101 / 6 101
//ifstream fin("../teste_pseudocmp/13-pseudocmp.in"); // 982656 982682
//ifstream fin("../teste_pseudocmp/14-pseudocmp.in"); //993547 993555 : 0.046 s
//ifstream fin("../teste_pseudocmp/15-pseudocmp.in"); //995465 995473 : 0.056 s
//ifstream fin("../teste_pseudocmp/16-pseudocmp.in"); //996392 996409 : 0.046 s
//ifstream fin("../teste_pseudocmp/17-pseudocmp.in"); //994096 994149 : 0.059 s

// cazul2
//ifstream fin("../teste_pseudocmp/18-pseudocmp.in"); // 1564 time: 0.033 s
//ifstream fin("../teste_pseudocmp/19-pseudocmp.in"); // 32573 time: 0.034 s
//ifstream fin("../teste_pseudocmp/20-pseudocmp.in"); // 30493 time: 0.030 s
//ifstream fin("../teste_pseudocmp/21-pseudocmp.in"); // 161975 time: 0.042 s
//ifstream fin("../teste_pseudocmp/22-pseudocmp.in"); // 0.045 s--> 0.016 s
//ifstream fin("../teste_pseudocmp/23-pseudocmp.in"); // 4.073 s--> 0.033 s
//ifstream fin("../teste_pseudocmp/24-pseudocmp.in"); // 8.731 s --> 0.045 s
//ifstream fin("../teste_pseudocmp/25-pseudocmp.in"); // 11.090 s --> 0.073 s
//ifstream fin("../teste_pseudocmp/26-pseudocmp.in"); // 8.178 s --> 0.072 s
//ifstream fin("../teste_pseudocmp/27-pseudocmp.in"); // 9.046 s --> 0.077 s
ifstream fin("../teste_pseudocmp/28-pseudocmp.in"); // 9.077 s --> 0.074 s
//ifstream fin("../teste_pseudocmp/29-pseudocmp.in"); // 9.051 s --> 0.075 s

ofstream fout("pseudocmp.out");

clock_t t1, t2, t3, t4;


double t21, t32, t43;

int T;
int N;

int ips, jps; // pereche speciala


long long nrps=0; // nr perechi speciale

int smaxcif=6*9; // nr <= 1.000.000 ... 999 999 --> suma cifrelor = 9*6
int smaxcif2=0; // suma maxima a cifrelor pe exemplul concret ... !!!

int sp=0; //suma partiala;


long long st=0; // suma totala

int a[1000005]; // nr distincte --> i apare sau nu apare ... in sir

int sc[1000005]; // sc[k] = suma cifrelor numarului a[k]

//short int sc[1000005]; // 1...54 = sc[k] = suma cifrelor numarului a[k]


//char sc[1000005]; // sc[k] = suma cifrelor numarului a[k]

int fr[55];

void afisa()
{
cout<<"a[]: ";
for(int i=1; i<=N; i++)
{
cout<<setw(2)<<a[i]<<" ";
}
cout<<endl;
}
CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 46

void afiss()
{
cout<<"s[]: ";
for(int i=1; i<=N; i++)
cout<<setw(2)<<sc[i]<<" ";
cout<<endl;
}

void afisfr()
{
cout<<"fr[]: ";
for(int i=1; i<=smaxcif2+1; i++)
cout<<setw(2)<<fr[i]<<" ";
cout<<endl;
}

int sumacifre(int nr) // sc[i] <= 100 000 * 6*9 = 5 400 000
{
int sc=0;
while(nr>0) { sc=sc+nr%10; nr=nr/10; }
return sc;
}

void cerinta1()
{
bool existapereche=false;
for(int i=1; i <= N-1; i++) // a[i] < a[i+1]
{
if(sc[i]>sc[i+1])
{
existapereche=true;
ips=i;
break;
}
}

if(existapereche==false)
{
//cout<<"NU exista --> -1";
fout<<"-1";
return;
}
else
{
fout<<a[ips]<<" "<<a[ips+1];
return;
}

return;
}

void cerinta2()
{
for(int j=1; j <= N; j++)
{
int aj=a[j];
int scj=sc[j];
int frscj=fr[scj];

fr[scj]++;

sp=0;
for(int k=sc[j]+1; k<=smaxcif2; k++)//a[..] mici <--> sc[..] mari !!!
{
sp=sp+fr[k];
}

st=st+sp;
}

nrps=st;
//cout<<"nrps = "<<nrps<<endl;
fout<<nrps<<endl;
return;
}
CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 47

int main()
{
t1 = clock();

fin>>T;
fin>>N;
//cout<<"T = "<<T<<" N = "<<N<<endl; // verificare citire fisier ... !!!

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


{
fin>>a[i];
}

//afisa();
t2 = clock();
sort(a,a+N+1);
t3 = clock();
//afisa();

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


{
sc[i]=sumacifre(a[i]);
if(sc[i]>smaxcif2) smaxcif2=sc[i];
}
//afiss();

if(T==1)
{
cerinta1();
}

if(T==2)
{
cerinta2();
}
t4 = clock();

fin.close();
fout.close();

//t21=(double)(t2 - t1) / CLOCKS_PER_SEC; // t21 = 0.026 citire


//t32=(double)(t3 - t2) / CLOCKS_PER_SEC; // t32 = 0.019 sort
//t43=(double)(t4 - t3) / CLOCKS_PER_SEC; // t43 = 8.96 calcul
//cout<<" t21 = "<<t21<<endl;
//cout<<" t32 = "<<t32<<endl;
//cout<<" t43 = "<<t43<<endl;

return 0;
}

Figura 2.8: oji2022-pseudocmp-frecvenţe2

Numerele fiind distincte, trebuie să ştim numai dacă un numar i apare sau nu apare ı̂n şir.
Tipul bool permite ı̂nregistrarea numai a două valori: false şi true. Putem să declarăm vectorul
a[ ] de tip bool şi astfel vom ocupa ı̂n spaţiul de memorie numai câte 1 octet (= 8 biţi), pentru
fiecare element a[i] ı̂n loc să declarăm vectorul a[ ] de tip int şi să ocupăm ı̂n spaţiul de memorie
câte 4 octeti (= 4*8 = 32 de biţi) pentru fiecare element a[i].
CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 48

Dacă ”ne ambiţionăm” putem să ocupăm ı̂n spaţiul de memorie câte 1 singur bit pentru
fiecare element a[i] (dacă folosim tipul bitset).
Din păcate, atunci când micşorăm spatiul de memorie, ”plătim” cu mărirea timpului de
execuţie!

Nu este nicio supărare pentru că se ı̂ntâmplă şi invers: când micşorăm timpul de execuţie
plătim cu mărirea spatiului de memorie!

Micşorarea spaţiului de memorie cu tipul bool este, ı̂n cazul acestei probleme, o iluzie
pentru că:
ˆ 100 000 de numere * 4 octeţi = 400 000 de octeti
ˆ valoarea maximă a elementelor a[i] este 1 000 000 şi pentru fiecare valoare avem nevoie de
câte 1 octet, deci avem nevoie de 1 000 000 de octeţi.
Cu tipul bitset se face economie de memorie
ˆ valoarea maximă a elementelor a[i] este 1 000 000 şi pentru fiecare valoare avem nevoie de
câte 1 bit, deci avem nevoie de 1 000 000 de biţi adică de 125 000 de octeţi (1 octet = 8 biţi)
şi ... este mai puţin decât 400 000 de octeţi de la tipul int
dar ... timpul de execuţie este cam la fel!

Listing 2.2.4: oji2022-pseudocmp-frecvenţe3.cpp


#include<iostream>
#include<fstream>
#include<algorithm> // sort
#include<bitset>

#include<iomanip> // pentru setw(2)


#include <chrono> //

using namespace std;

//ifstream fin("pseudocmp1.in");
//ifstream fin("pseudocmp2.in");
//ifstream fin("pseudocmp3.in");

// cazul1
//ifstream fin("../teste_pseudocmp/10-pseudocmp.in");
//ifstream fin("../teste_pseudocmp/11-pseudocmp.in");
//ifstream fin("../teste_pseudocmp/12-pseudocmp.in");
//ifstream fin("../teste_pseudocmp/13-pseudocmp.in");
//ifstream fin("../teste_pseudocmp/14-pseudocmp.in");
//ifstream fin("../teste_pseudocmp/15-pseudocmp.in");
//ifstream fin("../teste_pseudocmp/16-pseudocmp.in");
//ifstream fin("../teste_pseudocmp/17-pseudocmp.in");

// cazul2
//ifstream fin("../teste_pseudocmp/18-pseudocmp.in");
//ifstream fin("../teste_pseudocmp/19-pseudocmp.in");
//ifstream fin("../teste_pseudocmp/20-pseudocmp.in");
//ifstream fin("../teste_pseudocmp/21-pseudocmp.in");
//ifstream fin("../teste_pseudocmp/22-pseudocmp.in");
//ifstream fin("../teste_pseudocmp/23-pseudocmp.in"); // 4.073 s--> 0.065 s
//ifstream fin("../teste_pseudocmp/24-pseudocmp.in"); // 8.731
//ifstream fin("../teste_pseudocmp/25-pseudocmp.in"); // 11.090
//ifstream fin("../teste_pseudocmp/26-pseudocmp.in"); // 8.178 s
//ifstream fin("../teste_pseudocmp/27-pseudocmp.in"); // 9.046 s
//ifstream fin("../teste_pseudocmp/28-pseudocmp.in"); // 9.077 s --> 0.071 s
ifstream fin("../teste_pseudocmp/29-pseudocmp.in"); // 9.051 s --> 0.074 s

ofstream fout("pseudocmp.out");

clock_t t1, t2, t3, t4;


double t21, t32, t43;

int T;
int N;

int aimax=0; // max a[i]


CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 49

int i1=0; // a[i] dupa "sortare" prin pozitionarea lui a[i] pe locul lui
int i2=0; // a[i+1] dupa "sortare" ...

long long nrps=0; // nr perechi speciale

int smaxcif2=0; // suma maxima a cifrelor pe exemplul concret ... !!!


int sp=0; //suma partiala;
long long st=0; // suma totala

//int a[1000005]; // nr distincte --> i apare sau nu apare ... in sir


//bool abool[1000005]; // nr distincte --> i apare sau nu apare ... in sir
bitset<1000005> abit; // nr distincte --> i apare sau nu apare ... in sir

int sc[1000005]; // sc[k] = suma cifrelor numarului a[k]


//short int sc[1000005]; // 1...54 = sc[k] = suma cifrelor numarului a[k]
//char sc[1000005]; // sc[k] = suma cifrelor numarului a[k]

int fr[55];

void afisabit() // pentru depanarea programului cu datele din enunt ...


{
cout<<" a[]: ";
for(int i=1; i<=aimax; i++)
if(abit[i]==1) cout<<setw(3)<<i<<" ";
cout<<endl;
}

void afissc()// pentru depanarea programului cu datele din enunt ...


{
cout<<"sc[]: ";
for(int i=1; i<=aimax; i++)
if(abit[i]==1) cout<<setw(3)<<sc[i]<<" ";
cout<<endl;
}

void afisfr()// pentru depanarea programului cu datele din enunt ...


{
cout<<"fr[]: ";
for(int i=1; i<=54; i++)
{
if(fr[i] > 0)
{
cout<<setw(2)<<i<<": "<<fr[i]<<endl;
}
cout<<endl;
}
cout<<endl;
}

int sumacifre(int nr) // sc[i] <= 100 000 * 6*9 = 5 400 000
{
int sc=0;
while(nr>0) { sc=sc+nr%10; nr=nr/10; }
return sc;
}

void cerinta1()
{
bool existapereche=false;

i1=1;
while(i1 < aimax)
{
while((i1<aimax) && (abit[i1]==0)) { i1++; }

i2=i1+1;
while((i2<aimax) && (abit[i2]==0)) { i2++; }

if(sc[i1]>sc[i2])
{
existapereche=true;
break;
}
i1=i2;
}
CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 50

if(existapereche==false)
{
//cout<<"NU exista --> -1";
fout<<"-1";
return;
}
else
{
//cout<<i1<<" "<<i2; cout<<" : "<<sc[i1]<<" >? "<<sc[i2]<<endl;
fout<<i1<<" "<<i2;

return;
}

return;
}

void cerinta2()
{
for(int j=1; j <= aimax; j++)
{
int aj=abit[j];

if(aj==0) continue;

int scj=sc[j];
int frscj=fr[scj];

fr[scj]++;

sp=0;
for(int k=sc[j]+1; k<=smaxcif2; k++)//a[..] mici <--> sc[..] mari !!!
{
sp=sp+fr[k];
}
st=st+sp;
}

nrps=st;
//cout<<"nrps = "<<nrps<<endl;
fout<<nrps<<endl;
return;
}

int main()
{
t1 = clock();

int ai; // ai = a[i]

fin>>T;
fin>>N;
//cout<<"T = "<<T<<" N = "<<N<<endl; // verificare citire fisier ... !!!

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


{
fin>>ai; abit[ai]=1;
if(ai > aimax) aimax=ai;
}
//afisabit();

t2 = clock();
//sort(a,a+N+1); // nu mai este nevoie de sortare
t3 = clock();
//afisabit();

for(int i=1; i<=aimax; i++)


{
if(abit[i]==0) continue;

sc[i]=sumacifre(i);
if(sc[i]>smaxcif2) smaxcif2=sc[i];
}
//afissc();
CAPITOLUL 2. OJI 2022 2.2. PSEUDOCMP 51

if(T==1) { cerinta1(); }

if(T==2) { cerinta2(); }
t4 = clock();

fin.close();
fout.close();

//t21=(double)(t2 - t1) / CLOCKS_PER_SEC; // t21 = 0.026 citire


//t32=(double)(t3 - t2) / CLOCKS_PER_SEC; // t32 = 0.019 sort
//t43=(double)(t4 - t3) / CLOCKS_PER_SEC; // t43 = 8.96 calcul
//cout<<" t21 = "<<t21<<endl;
//cout<<" t32 = "<<t32<<endl;
//cout<<" t43 = "<<t43<<endl;

return 0;
}

Pentru verificarea mai uşoară a rezultatelor se poate folosi programul următor:

Listing 2.2.5: pseudocmp-checker.cpp


#include "testlib.h"

using namespace std;

int main()
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../teste_pseudocmp/15-pseudocmp.in", // input
(char*)"../teste_pseudocmp/15-pseudocmp.ok", // rezultat corect
(char*)"pseudocmp.out", // rezultat de verificat si acordat punctaj
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("pseudocmp", argc, argv);

compareRemainingLines();
}
Capitolul 3

OJI 2021 - OSEPI

3.1 campionat
Problema 1 - campionat 100 de puncte
Ne aflăm la un anumit moment al desfăşurării campionatului naţional de fotbal. O parte dintre
meciuri s-au jucat, altele urmează să fie disputate. Se cunoaşte numărul de puncte acumulate deja
de fiecare echipă ı̂naintea desfăşurării meciurilor restante.
Se cunoaşte, de asemenea, că un meci se poate termina egal, caz ı̂n care fiecare dintre echipe
primeşte câte un punct, sau cu victoria uneia dintre echipe, iar ı̂n acest caz acea echipă primeşte
trei puncte, iar cealaltă zero puncte.

Cerinţe

Avem de răspuns la ı̂ntrebări de două tipuri:

1. Care echipe ar fi pe locul I dacă toate meciurile restante s-ar termina la egalitate? O echipa
este pe locul I dacă are număr maxim de puncte.
2. Care echipe depind strict de propriile rezultate pentru a deveni campioane? O echipă devine
campioană (câştigă campionatul) dacă termină cu număr de puncte strict mai mare decât
oricare dintre celelalte echipe. Spunem că o echipă depinde strict de propriile rezultate
pentru a deveni campioană dacă ea devine campioană câştigând toate meciurile pe care
trebuie să le mai joace, indiferent de rezultatele celorlalte meciuri.

Date de intrare

Fişierul de intrare campionat.in conţine pe prima linie un număr T , reprezentând tipul de


ı̂ntrebare (1 sau 2).
Pe linia a doua se află un număr N reprezentând numărul de echipe din campionat (considerăm
că echipele sunt etichetate cu numere distincte de la 1 la N ).
Pe linia a treia se află N numere naturale separate prin câte un spaţiu, al i-lea număr
reprezentând punctajul celei de-a i-a echipe.
Pe linia a patra se află un număr D, reprezentând numărul de meciuri restante.
Pe fiecare dintre următoarele D linii se află câte două numere distincte i, j, cuprinse ı̂ntre 1 şi
N , cu semnificaţia că echipele i şi j au de disputat un meci restant.

Date de ieşire

Fişierul de ieşire campionat.out va conţine o singură linie.

ˆ Dacă T 1, linia va conţine etichetele echipelor care termină pe locul I, ı̂n cazul ı̂n care
toate meciurile restante se termină la egalitate.
ˆ Dacă T 2, linia va conţine etichetele echipelor care depind strict de propriile rezultate
pentru a deveni campioane. Dacă nicio echipă nu poate deveni campioană depinzând doar
de rezultatele sale, ı̂n fişierul de ieşire se va scrie doar numărul 0.

Atat pentru T 1, cât şi pentru T 2 etichetele echipelor vor fi scrise ı̂n ordine crescătoare,
separate prin câte un spaţiu.

52
CAPITOLUL 3. OJI 2021 - OSEPI 3.1. CAMPIONAT 53

Restricţii şi precizări

ˆ 2&N & 1 000


ˆ 1&D & 500 000
ˆ Punctajele iniţiale ale echipelor sunt numere naturale cel mult egale cu 1 000.

ˆ Regulile de desfăşurare a campionatului sunt mai ciudate, nu trebuie să vă puneţi problema
dacă este posibil ca echipele să aibă şirul dat al punctajelor ı̂n urma meciurilor disputate deja
(considerăm că până la momentul de faţă federaţia a acordat diverse bonusuri şi depunctări).
ˆ Dacă ı̂ntre meciurile rămase de jucat este vreunul care apare de mai multe ori (fie sub forma
i, j  fie sub forma j, i), el se va disputa o singură dată.
ˆ Programarea meciurilor s-a făcut ı̂n mod indisciplinat, deci este posibil ca unele echipe să
mai aibă de jucat mai multe meciuri decât altele, iar unele chiar să nu mai aibă de jucat
niciun meci.
ˆ Pentru teste valorând 22 de puncte T 1.
ˆ Pentru alte teste valorând 9 puncte T 2 şi fiecare echipă are de disputat exact 2 meciuri
cu alte echipe.
ˆ Pentru alte teste valorând 8 puncte T 2 şi fiecare echipă are de disputat câte un meci cu
fiecare altă echipă.
ˆ Pentru alte teste valorând 10 puncte T 2 şi există o singură echipă care joacă câte un meci
cu fiecare altă echipă, celelalte echipe neavând alte meciuri restante de jucat.

Exemple:

campionat.in campionat.out Explicaţii


1 12 Echipa 2 are de disputat un singur meci cu echipa 1, iar al
4 doilea meci se va disputa ı̂ntre echipele 1 şi 3 (chiar dacă ı̂n
2321 listă apare de două ori perechea 1 3, este vorba despre un
3 singur meci).
13 Cele două jocuri se vor termina la egalitate şi astfel punc-
12 tajele echipelor la final vor fi: 4 4 3 1. Aşadar echipele 1 şi
31 2 ies pe primul loc.
2 12 Echipa 1 devine campioană dacă ı̂nvinge ı̂n ambele meciuri,
4 nicio altă echipă neputând să o egaleze la puncte sau să o
1321 depăşească.
3 De asemenea, echipa 2 devine campioană dacă ı̂nvinge
13 echipa 1 ı̂n meciul restant, indiferent de rezultatul celuilalt
12 meci. Echipa 3, chiar dacă ı̂nvinge ı̂n meciul cu echipa 1, nu
31 depinde doar de rezultatul său ı̂ntrucât echipa 2 ar depăşi-o
la puncte dacă şi ea ar câştiga meciul pe care ı̂l mai are de
disputat.

Timp maxim de executare/test: 1.0 secunde


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

3.1.1 Indicaţii de rezolvare


Propunător: prof. Nicoli Marius, Colegiul Naţional ”Fraţii Buzeşti”, Craiova
Pentru fiecare echipă i (1 & i & N ) vom face următoarele notaţii:

ˆ cu scori vom nota scorul iniţial al echipei i, pe care ı̂l vom citi din input.

ˆ cu meciurii vom nota numărul de meciuri restante pe care le mai are de jucat echipa i.
CAPITOLUL 3. OJI 2021 - OSEPI 3.2. EXCLUSIV 54

ˆ Deoarece meciurile restante pot apărea ı̂n mod repetat ı̂n fişierul de intrare, vom construi
un tablou A cu N linii şi N coloane cu elemente din mulţimea x0, 1x astfel ı̂ncat Aij 
Aj i 1 dacă echipele i şi j mai au de jucat un meci restant, respectiv 0 ı̂n caz contrar.

Pentru a determina numărul de meciuri restante pe care trebuie să le mai joace echipa i este
suficient să adunăm valorile de pe linia i a tabloului A:

meciurii Ai1  Ai2  ...  AiN  1  AiN 

Cerinţa 1.
Pentru cerinţa 1 este suficient să determinăm punctajul final pe care fiecare echipă ı̂l va obţine
atunci când toate jocurile se termină la egalitate.
Acest scor este egal cu suma dintre punctajul iniţial al echipei şi numărul de jocuri restante pe
care echipa respectivă le mai are de jucat. Aflăm maximul dintre aceste punctaje finale scori 
meciurii, ¾i " r1, 2, ..., N x, iar pe locul I se vor situa toate echipele care au punctajul final egal
cu punctajul final maxim.
Cerinţa 2.
O echipă i poate deveni campioană strict pe baza propriilor rezultate dacă după ce va câştiga
toate meciurile pe care le mai are de jucat, indiferent de rezultatele celorlalte meciuri, va avea un
punctaj final strict mai mare decât al tuturor celorlalte echipe.
Vom parcurge toate echipele succesiv şi vom verifica pentru fiecare dacă poate ı̂ndeplini acest
criteriu.
Să analizăm dacă echipa i poate deveni campioană strict pe baza propriilor rezultate. Punctajul
final al echipei, dacă va câştiga toate meciurile, va fi egal cu scori  3 meciurii. Să notăm
acest punctaj cu scorM axi.
Vom determina pentru fiecare echipă j, (j j i) care ar fi punctajul maxim pe care aceasta l-ar
putea obţine (considerăm că j va câştiga toate meciurile restante ı̂n care este implicată, exceptând
meciul pe care eventual l-ar juca cu echipa i). Astfel scorul maxim pe care il poate obţine echipa
j este scorM axj   3 Aij .
Dacă scorM axi este strict mai mare decât punctajul maxim al oricărei alte echipe j, atunci
ipoate deveni campioană.

3.1.2 Cod sursă

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

3.1.3 *Rezolvare detaliată

3.2 exclusiv
Problema 2 - exclusiv 100 de puncte
Se consideră doi vectori care conţin numere naturale: s cu M elemente şi v cu N elemente.
Numim secvenţă i-exclusivă o secvenţă a vectorului s care nu conţine niciuna dintre valorile v 1,
v 2, ..., v i.

Cerinţe

Scrieţi un program care să determine, pentru orice 1 & i & N , lungimea maximă a unei secvenţe
i-exclusive.

Date de intrare

Fişierul de intrare exclusiv.in conţine pe prima linie numerele naturale M şi N . Pe linia a
doua se află M numere naturale reprezentând elementele vectorului s, iar pe linia a treia N numere
naturale reprezentând elementele vectorului v. Valorile scrise pe aceeaşi linie sunt separate prin
câte un spaţiu.

Date de ieşire
CAPITOLUL 3. OJI 2021 - OSEPI 3.2. EXCLUSIV 55

Fişierul de ieşire exclusiv.out va conţine N linii. Pe linia i (1 & i & N ) va fi scris un număr
natural care reprezintă lungimea maximă a unei secvenţe i-exclusive.
Restricţii şi precizări
ˆ 1&N & 2 000
3 & M & 10
5
ˆ
ˆ Vectorii s şi v conţin numere naturale &2 9
10 , memorate ı̂ncepând cu poziţia 1
ˆ Valorile din fiecare vector nu sunt obligatoriu distincte două câte două.
ˆ O subsecvenţă nevidă ı̂n s este formată din elemente situate pe poziţii consecutive
(si, si  1, ..., sj ), i & j. O subsecvenţă i-exclusivă poate fi şi vidă, lungimea ei fiind 0.
ˆ Pentru teste valorând 10 puncte N 1.
ˆ Pentru alte teste valorând 30 de puncte 1 $ N & 50 şi M & 1 000.
ˆ Pentru alte teste valorând 40 de puncte 50 $ N & 2 000 şi 1 000 $ M & 2 000.
Pentru alte valorand 20 de puncte N 2 000 şi 10 $ M & 10 .
4 5
ˆ

Exemple:

exclusiv.in exclusiv.out
20 6 12
11 5 11 7 2 10 11 9 2 77 88 88 88 2 7 2 2 77 2 11 12
11 5 7 9 5 2 7
6
6
4

Explicaţii:
Cea mai lungă secvenţă 1-exclusivă (care nu conţine valoarea 11)
este 9 2 77 88 88 88 2 7 2 2 77 2 şi are lungimea 12.
Cea mai lungă secvenţă 2-exclusivă (care nu conţine valorile 11 şi 5)
este 9 2 77 88 88 88 2 7 2 2 77 2 şi are lungimea 12.
Cea mai lungă secvenţă 3-exclusivă (care nu conţine valorile 11; 5 şi 7)
este 9 2 77 88 88 88 2 şi are lungimea 7.
Cea mai lungă secvenţă 4-exclusivă (care nu conţine valorile 11; 5; 7 şi 9)
este 2 77 88 88 88 2 şi are lungimea 6.
Cea mai lungă secvenţă 5-exclusivă (care nu conţine valorile 11; 5; 7; 9 şi 5)
este 2 77 88 88 88 2 şi are lungimea 6.
Cea mai lungă secvenţă 6-exclusivă (care nu conţine valorile 11; 5; 7; 9; 5 şi 2)
este 77 88 88 88 şi are lungimea 4.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 16 MB din care pentru stivă 8 MB
Dimensiune maximă a sursei: 10 KB

3.2.1 Indicaţii de rezolvare


Propunător: prof. Nistor Moţ, Şcoala ”Dr. Luca” Brăila
2
Soluţie de 100 puncte - complexitate O N  M log N 
Vom răspunde la cele N ı̂ntrebări de tipul ””care este lungimea maximă a unei secvenţe i-
exclusive” pentru i de la N la 1. Motivaţia acestui lucru este faptul că dacă parcurgem ı̂ntrebările
ı̂n sens invers, vom adăuga elemente noi la vectorul s, care iniţial va fi gol, ı̂n loc să ştergem
elemente.
Pentru a rezolva problema ı̂ntr-o complexitate optimă, vor trebui efectuate urmatoarele trans-
formari:
CAPITOLUL 3. OJI 2021 - OSEPI 3.2. EXCLUSIV 56

ˆ După ce se citesc datele de intrare se ordonează perechile v i, i crescător ı̂n funcţie de
valoare, iar ı̂n caz de egalitate crescător după poziţie.
ˆ Pentru fiecare element sj  se caută, utilizând o căutare binară, cea mai mică poziţie i astfel
incat sj  v i şi se ı̂nlocuieşte sj  cu i. În cazul in care sj  nu apare ı̂n v vom ı̂nlocui
sj  cu valoarea N  1. Astfel am obţinut un vector echivalent cu cel citit din punct de
vedere al răspunsului, dar care are valori din mulţimea r1, 2, ..., N, N  1x.

În continuare, vom realiza următoarele construcţii pentru a actualiza cât mai rapid actualizarea
de la o ı̂ntrebare la alta.

ˆ Vom construi un vector urm definit prin urmj  = următoarea poziţie din s unde apare
sj , respectiv urmj  M  1, dacă sj  nu mai apare ı̂n dreapta poziţiei j. Cu ajutorul
acestui vector urm vom parcurge, pentru fiecare valoare v i, doar poziţiile din s unde apare
valoarea i. Astfel parcurgerea tuturor valorilor din v ı̂n s se poate trecând o singură dată
peste fiecare element din s.
ˆ Vom utiliza şi vectorii stanga şi dreapta, cu semnificaţia, stangaj  = ı̂nceputul intervalului
maximal din care face parte elementul de pe poziţia j, respectiv dreaptaj  = sfârşitul
intervalului maximal din care face parte elementul de pe poziţia j. Aceşti vectori ne vor
ajuta să identificăm secvenţele maximale din s care nu conţin valori interzise la acea etapă.
Vectorii vor fi iniţializaţi cu stangaj  dreaptaj  0, ¾j " r1, 2, ..., M x.

Vom parcurge cu i N  1, N, ..., 3, 2. La etapa i vom determina secvenţele maximale din s


care nu au voie să conţină valori din mulţimea r1, 2, ..., i  1x. Pe parcursul determinării acestor
secvenţe maximale vom actualiza valorile lM axi, care vor conţine la final rezultatele căutate.
O observaţie importantă este aceea că la etapa i valoarea i nu poate apărea ı̂n s ı̂n interiorul
niciunei secvenţe maximale obţinute la etapele anterioare, deoarece ea a fost interzisă la etapele
anterioare. Astfel valoarea i poate forma secvenţe maximale formate doar din valoarea i (de
lungime 1) sau poate extinde sau unifica secvenţele maximale formate anterior. Astfel vom realiza
următorii paşi pentru fiecare etapă:

1. De fiecare dată când găsim sj  i actualizăm cu stangaj  j şi dreaptaj  j, ca pe o


secvenţă independentă;
2. Analizăm la stânga şi apoi la dreapta poziţiei j. Avem următoarele cazuri:

– Dacă stangaj  1 % 0, vom extinde secvenţa spre stânga astfel:


stangaj  stangaj  1
– Dacă dreaptaj  1 % 0, vom extinde secvenţa spre dreapta astfel:
dreaptaj  dreaptaj  1

3. Finalizarea se face cu:


dreaptastangaj  dreaptaj 
stangadreaptaj  stangaj 

Lungimea secvenţei maximale curente este actualizată la fiecare pas:

lM axi max lM ax1, dreaptaj   stangaj   1

La final se afişează valorile lM ax1, lM ax2, ..., lM axN .


Trebuie avut grijă la implementare deoarece apar diverse cazuri particulare. De exemplu:
valorile din v nu apar ı̂n s, valorile din v nu sunt distincte, toate valorile din s apar şi ı̂n v.

3.2.2 Cod sursă

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

3.2.3 *Rezolvare detaliată


Capitolul 4

OJI 2020

4.1 foto
Problema 1 - foto 100 de puncte
O fotografie alb-negru a surprins imaginea fulgerelor pe cerul ı̂ntunecat ı̂n timpul unei furtuni
electrice. Mărită, fotografia arată ca un caroiaj format din mici pătrate identice, albe sau negre,
dispuse alăturat pe N rânduri şi M coloane, câte M pe fiecare rând.
Pătratele albe formează fulgerele din fotografie, iar pătratele negre reprezintă cerul.
În fotografie, nu există două pătrate albe dispuse alăturat pe acelaşi rând. Un fulger este
format din pătrate albe situate pe rânduri consecutive care respectă următoarele condiţii:

a) pătratele albe situate pe două rânduri consecutive au un vârf comun sau o latură comună;
b) un fulger poate avea un singur pătrat alb pe un rând.

În fotografie, fulgerele sunt distincte, ele neavând pătrate albe cu laturi sau vârfuri comune.
Înălţimea unui fulger este dată de numărul de pătrate albe ale acelui fulger.
Pentru a putea fi analizată de către programatori,
fotografia este codificată cu ajutorul unui tablou bidi-
mensional cu N linii si M coloane, ale cărui elemente
sunt 0 şi 1. Valoarea 0 este codificarea pătratului negru,
iar valoarea 1 este codificarea pătratului alb.
Având codificarea, programatorii trebuie să găsească
numărul maxim P de pătrate negre dispuse alăturat pe
acelaşi rând, numărul de fulgere F precum şi ı̂nălţimea
maximă H a unui fulger din fotografie. De exemplu, fotografia alăturată este codificată de tabloul
T alăturat fotografiei .

Cerinţe

Scrieţi un program care citeşte numerele N şi M , cele N ˜ M elemente ale tabloului T care
codifică fotografia, şi rezolvă următoarele cerinţe:
1) afişează numărul maxim P de pătrate negre dispuse alăturat pe un rând ı̂n fotografie;
2) afişează numărul F de fulgere şi ı̂nălţimea maximă H a unui fulger din fotografie.

Date de intrare

Fişierul de intrare foto.in conţine pe prima linie un număr natural C reprezentând cerinţa
care trebuie rezolvată (1 sau 2).
Pe a doua linie se află cele două numere naturale N şi M , separate printr-un spaţiu, cu
semnificaţia din enunţ.
Pe fiecare dintre următoarele N linii se află câte M valori 0 sau 1, separate prin câte un spaţiu,
reprezentând elementele tabloului care codifică fotografia, ı̂n ordinea parcurgerii lor pe rânduri,
de sus ı̂n jos, şi de la stânga la dreapta ı̂n cadrul unui rând.

Date de ieşire

57
CAPITOLUL 4. OJI 2020 4.1. FOTO 58

Fişierul de ieşire foto.out va conţine, pe o singură linie:


a dacă C 1, numărul P , reprezentând răspunsul la cerinţa 1;
a dacă C 2, cele două numere F şi H, ı̂n această ordine, separate printr-un singur spaţiu,
reprezentând răspunsul la cerinţa 2.

Restricţii şi precizări

a N număr natural, 4 & N & 100


a M număr natural, 4 & M & 100
a Pentru rezolvarea cerinţei 1 se acordă 20 de puncte, pentru rezolvarea cerinţei 2 se acordă
70 de puncte (30 de puncte pentru F şi 40 de puncte pentru H).

Exemple:

foto.in foto.out Explicaţii


1 3 Se rezolvă cerinţa 1.
67 N 6, M 7
01001 0 0 Sunt maximum P 3 pătrate negre alăturate
10001 0 0 pe rândurile 2, 3 şi 4 (vezi fotografia din enunţ)
00100 0 1
01000 1 0
01001 0 0
00101 0 1
54 Se rezolvă cerinţa 2.
N 6, M 7
Sunt F 5 fulgere iar ı̂nălţimea maximă a
unui fulger este H 4 (vezi fotografia din
enunţ)

Timp maxim de executare/test: 0.2 secunde


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

4.1.1 Indicaţii de rezolvare


prof. Carmen Mincă, Colegiul Naţional de Informatică ”Tudor Vianu” Bucureşti

O soluţie posibilă, la nivelul programei clasei a VII-a, se poate poate obţine astfel:
Cerinţa 1.
Iniţializam variabila P 0
Se parcurge fiecare linie a tabloului (matrice).
La ı̂ntâlnirea primei valori de 0, ı̂ncepem să contorizăm 0-rile succesive din linia curentă (adică
determinăm lungimea maximă a unei secvenţe de valori 0 succesive ı̂n linie). Contorizarea se
ı̂ncheie la ı̂ntâlnirea primei valori 1 sau la ı̂ncheierea parcurgerii liniei.
Actualizăm valoarea lui P cu maximul dintre P şi valoarea contorului.
Apoi reiniţializăm contorul cu 0 şi căutăm următorul 0 ı̂n linie. Dacă a fost parcursă toată
linia, se trece la linia următoare.
Cerinţa 2.
Folosim variabila F pentru a număra fulgerele din fotografie. Iniţial F 0.
Folosim variabila H pentru a memora ı̂nălţimea maximă a unui fulger. Iniţial H 0.
I. Solutie complexitate O N ˜ N ˜ M
Pentru a evita ieşirea din matrice, e recomandat să bordaţi cu 0 matricea, adică să adaugaţi
linia 0, linia N  1, coloana 0 şi coloana M  1, toate având doar valori egale cu 0.
CAPITOLUL 4. OJI 2020 4.1. FOTO 59

Parcurgem matricea, de la linia 1 către linia N , iar fiecare linie este parcursă de la prima
coloană către ultima.
Prima valoare 1 ı̂ntâlnită, reprezintă primul pătrat alb dintr-un fulger.
Dacă T lincol 1 atunci vom proceda astfel:
a) Numărăm noul fulger: F=F+1; Iniţializam contorul h care va numără patratele albe ce
formează un fulger: h=0. El va fi ı̂nălţimea fulgerului. Fie I=lin şi J=col
b) Cât timp T[I][J]=1 procedăm astfel:
b1) Modificăm ı̂n matrice T[I][J]=0. Contorizam pătratul alb (h=h+1).
b2) Căutăm ı̂n linia I+1 următorul pătrat alb al fulgerului, ı̂n coloanele J-1 sau J+1.
Dacă T[I+1][J-1]=1, atunci pătratul alb este ı̂n coloana J-1 şi vom actualiza I=I+1
şi J=J-1. Mergi la b1)
Altfel dacă T[I+1][J-1]=1 atunci pătratul alb este ı̂n coloana J+1 şi vom actualiza
I=I+1şi J=J+1. Mergi la b1)
Altfel dacă T[I+1][J]=1atunci pătratul alb este ı̂n coloana J+1 şi vom actualiza
I=I+1. Mergi la b1)
Altfel, (adică dacă T[I+1][J-1]=0, T[I+1][J]=0 şi T[I+1][J+1]=0) atunci au fost
găsite toate pătratele albe care formează fulgerul. Actualizăm H=max(H,h) apoi
se va căuta următoarea valoare de 1 ı̂n matrice, revenind la pasul a) pentru noile
valori lin si col cu proprietatea că T[lin][col]=1. Mergi la pas a)
OBS: cel mult una dintre valorile T[I+1][J-1], T[I+1][J] şi T[I+1][J+1] este egală
cu 1
La ı̂ncheierea parcurgerii matricei T , aceasta va avea toate valorile egale cu 0.
Se vor afişa valorile finale ale variabilelor F şi H.
II. Solutie complexitate O N ˜ M
Se parcurge matricea.
Pentru fiecare element T[I][J]=1 procedăm astfel:
a verificam dacă vreunul cei trei vecini T[I-1][J-1], T[I-1][J], T[I-1][J-1], situati in linia I-1 este
nenul. Doar unul poate fi nenul.
a dacă toţi trei sunt nuli, atunci T[I][J] este primul patrat alb dintr-un fulger nou si numărăm
acest fulger (F++);
a dacă unul dintre ei este nenul, fie T[LIN][COL] acest vecin nenul. Atunci T[I][J]
şiT[LIN][COL] fac parte din acelaşi fulger şi vom memora ı̂n T[i][j] lungimea actuală a
fulgerului. : T[I][J]=T[LIN][COL]+1;
a se actualizează variabila ce memorează lungimea maxima a fulgerelor H=max(H, T[I][J]);
La ı̂ncheierea parcurgerii matricei T , se vor afişa valorile finale ale variabilelor F şi H.

4.1.2 Cod sursă

Listing 4.1.1: foto 1 c.c


1 #include <stdio.h>
2
3 int a[102][102];
4 FILE *f,*g;
5
6 int main()
7 {
8 int C,N,M;
9
10 f=fopen("foto.in","r");
11 g=fopen("foto.out","w");
12
13 fscanf(f,"%d%d%d",&C,&N,&M);
14
CAPITOLUL 4. OJI 2020 4.1. FOTO 60

15 if(C==1)
16 {
17 int x,P=0,i,j,p=0;
18 for(i=1;i<=N;i++)
19 { p=0;
20 for(j=1;j<=M;j++)
21 { fscanf(f,"%d",&x);
22 if(x==0)p++;
23 else
24 {if(P<p)P=p;
25 p=0;}
26 }
27 if(P<p)P=p;
28 }
29 fprintf(g,"%d\n",P);
30 }
31 else
32 {
33 int H=0,F=0,i,j,h,r,c;
34
35 for(r=1;r<=N;r++)
36 for(c=1;c<=M;c++)
37 fscanf(f,"%d",&a[r][c]);
38
39 for(r=1; r<=N; r++)
40 for(c=1; c<=M; c++)
41 if(a[r][c]==1)
42 { F++;
43 i=r;
44 j=c;
45 h=0;
46
47 while(a[i][j]==1)
48 { h++;
49 a[i][j]=0;
50 if(a[i+1][j]==1)i++;
51 else
52 if(a[i+1][j-1]==1){i++;j--;}
53 else
54 if(a[i+1][j+1]==1){i++; j++;}
55 }
56
57 if(H<h) H=h;
58 }
59
60 fprintf(g,"%d %d\n",F,H);
61 }
62
63 return 0;
64 }

Listing 4.1.2: foto 2 c.c


1 #include <stdio.h>
2
3 int T[102][102];
4 FILE *f,*g;
5
6 int main()
7 {
8 int C,N,M;
9
10 f=fopen("foto.in","r");
11 g=fopen("foto.out","w");
12
13 fscanf(f,"%d%d%d",&C,&N,&M);
14
15 if(C==1)
16 {
17 int x,P=0,i,j,p=0;
18 for(i=1;i<=N;i++)
19 { p=0;
20 for(j=1;j<=M;j++)
21 { fscanf(f,"%d",&x);
22 if(x==0)p++;
CAPITOLUL 4. OJI 2020 4.1. FOTO 61

23 else
24 {if(P<p)P=p;
25 p=0;}
26 }
27
28 if(P<p)P=p;
29 }
30
31 fprintf(g,"%d\n",P);
32 }
33 else
34 {
35 int H=0,F=0,i,j,hant;
36
37 for(i=1;i<=N;i++)
38 for(j=1;j<=M;j++)
39 { fscanf(f,"%d",&T[i][j]);
40 if(T[i][j]==1)
41 { hant=T[i-1][j-1]+
42 T[i-1][j]+
43 T[i-1][j+1];///lg fulger pana in aceasta pozitie
44 /// doar una dintre valorile
45 /// T[i-1][j-1],T[i-1][j],T[i-1][j+1]este nenula
46 if (hant==0) F++; ///primul patrat alb din fulger
47 T[i][j]=1+hant;
48 if(H<T[i][j])H=T[i][j];
49 }
50 }
51
52 fprintf(g,"%d %d\n",F,H);
53 }
54
55 return 0;
56 }

Listing 4.1.3: foto 1 cpp.cpp


#include <fstream>
#include <iostream>

using namespace std;

ifstream f("foto.in");
ofstream g("foto.out");

int T[102][102];

int main()
{
int C,N,M;
f>>C>>N>>M;

if(C==1)
{
int x,P=0,i,j,p=0;
for(i=1;i<=N;i++)
{ p=0;
for(j=1;j<=M;j++)
{ f>>x;
if(x==0)p++;
else
{P=max(p,P); p=0;}
}

P=max(P,p);
}

g<<P<<endl;
}
else
{
int H=0,F=0,i,j,h,r,c;

for(r=1;r<=N;r++)
for(c=1;c<=M;c++)
CAPITOLUL 4. OJI 2020 4.1. FOTO 62

f>>T[r][c];

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


for(c=1; c<=M; c++)
if(T[r][c]==1)
{ F++;
i=r;
j=c;
h=0;
while(T[i][j]==1)
{ h++;
T[i][j]=0;
if(T[i+1][j]==1)i++;
else
if(T[i+1][j-1]==1){i++;j--;}
else
if(T[i+1][j+1]==1){i++; j++;}
}

H=max(h,H);
}

g<<F<<" "<<H<<endl;
}

return 0;
}

Listing 4.1.4: foto 2 cpp.cpp


#include <fstream>
#include <iostream>

using namespace std;

ifstream f("foto.in");
ofstream g("foto.out");

int T[102][102];

int main()
{
int C,N,M;
f>>C>>N>>M;

if(C==1)
{
int x,P=0,i,j,p=0;
for(i=1;i<=N;i++)
{ p=0;
for(j=1;j<=M;j++)
{ f>>x;
if(x==0)p++;
else
{P=max(p,P); p=0;}
}

P=max(P,p);
}

g<<P<<endl;
}
else
{
int H=0,F=0,i,j,h,r,c;

for(r=1;r<=N;r++)
for(c=1;c<=M;c++)
f>>T[r][c];

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


for(c=1; c<=M; c++)
if(T[r][c]==1)
{ F++;
i=r;
CAPITOLUL 4. OJI 2020 4.1. FOTO 63

j=c;
h=0;
while(T[i][j]==1)
{ h++;
T[i][j]=0;
if(T[i+1][j]==1)i++;
else
if(T[i+1][j-1]==1){i++;j--;}
else
if(T[i+1][j+1]==1){i++; j++;}
}

H=max(h,H);
}

g<<F<<" "<<H<<endl;
}

return 0;
}

Listing 4.1.5: foto AB.cpp


#include <fstream>

using namespace std;

int a[103][103];

int main()
{
int i,j,nr=0,nrmax=0,c, hmax=0,nrfulgere=0,n,m;

ifstream f("foto.in");
ofstream g("foto.out");

f>>c>>n>>m;
for(i=1; i<=n; i++)
{
nr=0;
for(j=1; j<=m; j++)
{
f>>a[i][j];
if(a[i][j]==0)
{
nr++;
if(nr>nrmax)
nrmax=nr;
}
else
{
nr=0;
if(a[i-1][j]!=0)
a[i][j]=a[i-1][j]+1;
else if(a[i-1][j-1]!=0)
a[i][j]=a[i-1][j-1]+1;
else if(a[i-1][j+1]!=0)
a[i][j]=a[i-1][j+1]+1;
if(hmax<a[i][j])
hmax=a[i][j];
if (a[i][j]==1)nrfulgere++;
}
}
}

if(c==1)
g<<nrmax;
else
g<<nrfulgere<<" "<<hmax;

return 0;
}

Listing 4.1.6: foto AF.cpp


CAPITOLUL 4. OJI 2020 4.1. FOTO 64

#include <iostream>
#include <fstream>

using namespace std;

ifstream fin("foto.in");
ofstream fout("foto.out");

int a[105][105],i,j,n,m,c,k,mx,kmax,fmax,nr,kf;
int dl[]={-1,-1,-1,0,1,1,1,0};
int dc[]={-1,0,1,1,1,0,-1,-1};

void lee(int a[][105],int n,int m,int l, int c, int &nr)


{
int i,ln,cn;
a[l][c]=0;nr++;
for(i=0;i<=7;i++)
{
ln=l+dl[i];
cn=c+dc[i];
if(a[ln][cn]==1)
lee(a,n,m,ln,cn,nr);
}
}

int main()
{
fin>>c;
fin>>n>>m;
mx=0;
kmax=0;
for(i=1; i<=n; i++)
{
k=0;
for(j=1; j<=m; j++)
{
fin>>a[i][j];
if(a[i][j]==0)k++;
else
{if(k>kmax)
kmax=k;
k=0;}
}
if(k>kmax)
kmax=k;
}

if(c==1)
fout<<kmax;
else
{
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
if(a[i][j]==1)
{
nr=0;kf++;
lee(a,n,m,i,j,nr);
if(nr>fmax)
fmax=nr;
}
}

fout<<kf<<’ ’<<fmax;
}

return 0;
}

Listing 4.1.7: foto cm rec.cpp


#include <fstream>
#include <iostream>
CAPITOLUL 4. OJI 2020 4.1. FOTO 65

using namespace std;

ifstream f("foto.in");
ofstream g("foto.out");

int a[102][102],h, N, M;

void caut(int i, int j)


{
a[i][j]=0;
h++;
if(a[i+1][j-1])caut(i+1,j-1);
else if(a[i+1][j])caut(i+1,j);
else if(a[i+1][j+1])caut(i+1,j+1);
}

int main()
{
int C;//,N,M;
f>>C>>N>>M;

if(C==1)
{
int x,P=0,i,j,p=0;
for(i=1; i<=N; i++)
{
p=0;
for(j=1; j<=M; j++)
{
f>>x;
if(x==0)p++;
else
{
P=max(p,P);
p=0;
}
}
P=max(P,p);
}
g<<P<<endl;
}
else
{
int H=0,F=0,i,j,r,c;
for(i=1; i<=N; i++)
for(j=1; j<=M; j++)
f>>a[i][j];

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


for(c=1; c<=M; c++)
if(a[r][c]==1)
{
F++;
h=0;
caut(r,c);
H=max(h,H);
}

g<<F<< " "<<H;


}
return 0;
}

Listing 4.1.8: fotoCS.cpp


#include <fstream>

using namespace std;

ifstream fin("foto.in");
ofstream fout("foto.out");

int a[105][105],n,m,c,i,j;

int fulger(int i, int j)


CAPITOLUL 4. OJI 2020 4.1. FOTO 66

{ int k=i;
for(;i<n;i++)
{ a[i][j]=2;
if(a[i+1][j]==1) continue;
if(a[i+1][j-1]==1) j--;
else if(a[i+1][j+1]==1) j++;
else break;
}

if(i==n)a[i][j]=2;
return i-k+1;
}

void c1()
{int zmax=0, z=0, i, j;
for(i=1;i<=n;i++)
{ z=0;
for(j=1;j<=m;j++) if(a[i][j]==0)z++;
else {zmax=max(zmax,z);z=0;}
zmax=max(zmax,z);
}
fout<<zmax<<’\n’;
}

void c2()
{ int i,j, h, hmax=0, f=0;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(a[i][j]==1){f++; h=fulger(i,j);
hmax=max(hmax,h);
}
fout<<f<<’ ’<<hmax<<’\n’;
}

int main()
{fin>>c>>n>>m;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++) fin>>a[i][j];

if(c==1) c1();
else c2();
fin.close();
fout.close();
return 0;
}

Listing 4.1.9: fotoFB.cpp


#include <iostream>
#include <fstream>

using namespace std;

ifstream f("foto.in");
ofstream g("foto.out");

int i,j,n,m,a[101][101],k,maxim,C,fulgere;

int main()
{
f>>C;
if(C==1)
{
f>>n>>m;
for(i=1; i<=n; i++)
{
k=0;
for(j=1; j<=m; j++)
{
f>>a[i][j];
if(a[i][j]==0)
k++;
else
{
if(k>maxim)
CAPITOLUL 4. OJI 2020 4.1. FOTO 67

maxim=k;
k=0;
}
}
if(k>maxim) maxim=k;
}
g<<maxim;
}
else
{
f>>n>>m;
for(i=1; i<=n; i++)
{
for(j=1; j<=m; j++)
{
f>>a[i][j];
if(a[i][j])
{
if(a[i-1][j-1]==0 and a[i-1][j]==0 and a[i-1][j+1]==0)
fulgere++;
if(a[i-1][j-1])
a[i][j]=a[i-1][j-1]+1;

if(a[i-1][j])
a[i][j]=a[i-1][j]+1;

if(a[i-1][j+1])
a[i][j]=a[i-1][j+1]+1;
if(a[i][j]>maxim)
maxim=a[i][j];
}

}
}
g<<fulgere<<" "<<maxim;
}

return 0;
}

Listing 4.1.10: fotoKB.cpp


#include <fstream>
#include <cmath>
#include <limits.h>

#define Ni 105
#define Mi 105

using namespace std;

int main()
{
ifstream f("foto.in");
ofstream g("foto.out");

short c;
f>>c;

short N,M,i,j, T[Ni][Mi];


f>>N;

f>>M;

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


for(j=1; j<=M; j++)
f>>T[i][j];

if(c==1)
{
short maxi=0,kez;
for(i=1; i<=N; i++)
{
kez=0;
for(j=1; j<=M; j++)
CAPITOLUL 4. OJI 2020 4.1. FOTO 68

{
if(T[i][j]==1 && j>1)
{
if (maxi<kez)
{
maxi=kez;
}
kez=0;
}
else if(T[i][j]==0) kez++;
}
if(T[i][M]==0)
{
if (maxi<kez)
{
maxi=kez;
}
kez=0;
}
}
g<<maxi;
}
else
{
int ta=0,maxk=-1,k=0,l=0;

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


{
T[i][0]=T[i][M+1]=0;
}
for(i=0; i<=M+1; i++)
{
T[0][i]=T[N+1][i]=0;
}
for(i=1; i<=N; i++)
for(j=1; j<=M; j++)
{
if(T[i][j]>=1)
{
k=0;
if(T[i-1][j]>=1) k++;
if(T[i-1][j-1]>=1) k++;
if(T[i-1][j+1]>=1) k++;
if(k>1) return 1;
l=T[i+1][j]+T[i+1][j-1]+T[i+1][j+1];
if(k>1)
return 1;
if(k==0 && T[i][j]==1)
ta++;
if(k==1 && T[i][j]>=1)
{
if(T[i-1][j]>=1) T[i][j]=T[i-1][j]+1;
if(T[i-1][j+1]>=1) T[i][j]=T[i-1][j+1]+1;;
if(T[i-1][j-1]>=1) T[i][j]=T[i-1][j-1]+1;
}

if(l==0 && k==0 && T[i][j]==1) if (maxk<0) maxk=1;


if(l==0 && k==1)

if(maxk<=T[i][j]) maxk=T[i][j];
}
}

g<<ta<<" "<<maxk;
}
return 0;
}

Listing 4.1.11: fotoMM.cpp


#include <fstream>

using namespace std;

ifstream fin("foto.in");
CAPITOLUL 4. OJI 2020 4.1. FOTO 69

ofstream fout("foto.out");

int imin, imax, a[105][105],n,m,ct;

int platou(int lin)


{
int l=0,lmax=0,i;
for(i=1; i<=m; i++)
if(a[lin][i]==0)
l++;
else
{
if(l>lmax)
lmax=l;
l=0;
}

if(l>lmax)
lmax=l;

return lmax;
}

void umple(int x,int y, int &xmin, int &xmax)


{
a[x][y]=0;
if(x<xmin)
xmin=x;
if(x>xmax)
xmax=x;
if(y-1>0 && a[x][y-1]==1)
umple(x,y-1,xmin,xmax);
if(y-1>0 && x+1<=n && a[x+1][y-1]==1)
umple(x+1,y-1,xmin,xmax);
if(x+1<=n && a[x+1][y]==1)
umple(x+1,y,xmin,xmax);
if(y+1<=m && x+1<=n && a[x+1][y+1]==1)
umple(x+1,y+1,xmin,xmax);
if(y+1<=m && a[x][y+1]==1)
umple(x,y+1,xmin,xmax);
}

void citire()
{
int i,j;
fin>>ct>>n>>m;
for(i=1; i<=n; i++)
for(j=1; j<=m; j++)
fin>>a[i][j];
}

int main()
{
citire();
int i, maxi=0,x,j,xmin,xmax,fulgere=0;
if(ct==1)
{
for(i=1; i<=n; i++)
{
x=platou(i);
if(x>maxi)
maxi=x;
}

fout<<maxi<<" ";
fout<<’\n’;
return 0;
}

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


for(j=1; j<=m; j++)
if(a[i][j]==1)
{
fulgere++;
xmin=100;
xmax=0;
CAPITOLUL 4. OJI 2020 4.2. WIND 70

umple(i,j,xmin,xmax);
if(xmax-xmin>maxi)
maxi=xmax-xmin;
}

fout<<fulgere<<" "<<maxi+1<<’\n’;
return 0;
}

4.1.3 *Rezolvare detaliată

4.2 wind
Problema 2 - wind 100 de puncte
Domnul Vânt a pus pe marginea unei şosele N centrale eoliene, dintre care unele produc energie
electrică, iar altele, deocamdată, doar consumă energie. El a etichetat centralele cu numerele
naturale distincte de la 1 la N , ı̂n ordinea poziţionării lor pe şosea. Fiecare centrală eoliană are
la bază un ecran pe care este afişat un număr ı̂ntreg, reprezentând cantitatea de energie pe care
o produce (dacă numărul este pozitiv) sau pe care o consumă (dacă numărul este negativ).
Pentru a construi corect k oraşe de-a lungul acestei şosele, un arhitect trebuie să aibă ı̂n vedere
că:
a fiecărui oraş ı̂i va fi atribuit câte un grup format din centrale eoliene vecine pe şosea, toate
grupurile având acelaşi număr de centrale;
a cantitatea de energie repartizată unui oraş este egală cu suma numerelor afişate pe ecranele
centralelor eoliene din grupul atribuit; uneori este posibil ca, deocamdată, suma obţinută să
fie negativă;
a fiecare dintre cele N centrale eoliene trebuie să fie atribuită unui oraş;
a factorul de dezechilibru, notat cu P k , este valoarea maximă a diferenţei dintre energiile
repartizate oricăror două oraşe diferite, dintre cele k.

Cerinţe

Scrieţi un program care citeşte numărul N , valorile afişate pe cele N ecrane ale centralelor
eoliene şi rezolvă următoarele două cerinţe:
1. afişează numărul M de moduri ı̂n care se pot grupa cele N centrale pentru construcţia
corectă de oraşe;
2. afişează numărul maxim X de oraşe ce pot fi construite corect, dintre cele care au factorul de
dezechilibru minim, precum şi eticheta E a primei centrale eoliene atribuită oraşului cu cea
mai mare cantitate de energie repartizată, dintre cele X oraşe; dacă sunt mai multe astfel
de oraşe, se ia ı̂n considerare cel care are atribuite centrale etichetate cu numere mai mari.

Date de intrare

Fişierul wind.in conţine pe prima linie un număr natural C reprezentând cerinţa care trebuie
rezolvată (1 sau 2).
A doua linie a fişierului conţine un număr natural N , cu semnificaţia din enunţ.
A treia linie din fişier conţine N numere ı̂ntregi, separate prin câte un spaţiu, reprezentând
valorile afişate pe cele N ecrane ale centralelor eoliene, ı̂n ordinea poziţionării acestora pe şosea.

Date de ieşire

Fişierul wind.out va conţine pe prima linie:


a dacă C 1, numărul natural M , reprezentând răspunsul la cerinţa 1;
a dacă C 2, cele două numere naturale X şi E, ı̂n această ordine, separate printr-un singur
spaţiu, reprezentând răspunsul la cerinţa 2.
CAPITOLUL 4. OJI 2020 4.2. WIND 71

Restricţii şi precizări

a 2&N & 100 000, N număr natural;

a Numerele afişate pe ecranele centralelor sunt numere ı̂ntregi formate din cel mult 9 cifre;
a Se vor construi minimum 2 oraşe;
a Pentru rezolvarea cerinţei 1 se acordă 20 de puncte, pentru rezolvarea cerinţei 2 se acordă
70 de puncte (35 de puncte pentru X şi 35 de puncte pentru E).

Exemple:

wind.in wind.out Explicaţii


1 5 Cerinţa este 1.
12 Centralele eoliene se pot grupa câte 1, câte 2, câte 3,
2 4 -5 12 3 5 -6 4 5 7 -8 2 câte 4 sau câte 6.
2 31 Cerinţa este 2.
12 Posibilităţile de grupare:
2 4 -5 12 3 5 -6 4 5 7 -8 2 câte 1 centrală/oraş (sumele sunt 2, 4,-5, ..,2;
P(12)=20=12-(-8));
câte 2 centrale/oraş (sumele sunt: 6, 7, 8, -2, 12, -6;
P(6)=18=12-(-6));
câte 3 centrale/oraş (sumele sunt:1, 20, 3, 1;
P(4)=19=20-1);
câte 4 centrale/oraş (sumele sunt: 13, 6, 6;
P(3)=7=13-6);
câte 6 centrale/oraş (sumele sunt: 21 si 4;
P(2)=17=21-4).
Astfel, factorul de dezechilibru minim este P(3)=7,
deci X 3.
Pentru această grupare a centralelor, oraşul cu can-
titatea maximă de energie (13) corespunde primului
grup, care ı̂ncepe cu centrala etichetată cu E 1.

Timp maxim de executare/test: 0.3 secunde


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

4.2.1 Indicaţii de rezolvare

prof. Flavius Boian, Colegiul Naţional ”Spiru Haret” - Târgu-Jiu

Pentru a putea ı̂mpărţi cele n eoliene ı̂n mod egal ı̂n k oraşe trebuie să determinăm divizorii
numărului n.
Cerinţa 1: Se determină numărul de divizori al numărului de eoliene (n). Rezultatul va fi
numărul de divizori-1 deoarece se specifică ı̂n enunţ că se vor costrui cel puţin două oraşe, deci n
nu este un divizor valid.
Cerinţa 2: Se foloseşte un vector suma, fiecare element al acestuia memorând suma energiilor
dintre poziţia 1 (prima centrală) şi poziţia elementului respectiv (pozitia curentă).
sumai va reprezenta suma valorilor energiilor generate (pierdute) de centralele de la 1 la i.
Pentru a ı̂mpărţi centralele ı̂n mod corect trebuie să determinăm divizorii numărului n. Pentru
fiecare divizor se parcurge vectorul de sume din divizor in divizor şi se calculează diferenţa dintre
sumele de pe poziţiile respective. Se alege ı̂mpărţirea optimă.
CAPITOLUL 4. OJI 2020 4.2. WIND 72

4.2.2 Cod sursă

Listing 4.2.1: wind.c


1 #include <stdio.h>
2 #include <stdlib.h>
3
4 long long d,smin,smax,divx,poz,pozmax,minimm=99999999999999999,
5 C,n,i,k,s[200001],v[10001],x,j,dif,minim=99999999999999999,
6 numar,gasit;
7
8 FILE *f,*g;
9
10 int main()
11 {
12 f=fopen("wind.in","r");
13 g=fopen("wind.out","w");
14
15 fscanf(f,"%lli",&C);
16
17 if(C==1)
18 {
19 fscanf(f,"%lli",&n);
20 for(i=1; i*i<=n; i++)
21 if(n%i==0)
22 {
23 k++;
24 if(i!=n/i)
25 k++;
26 }
27 k=k-1;
28 fprintf(g,"%lli",k);
29 }
30 else
31 {
32 fscanf(f,"%lli",&n);
33 for(i=1; i<=n; i++)
34 {
35 fscanf(f,"%lli",&x);
36 s[i]=s[i-1]+x;
37 }
38 for(i=1; i*i<=n; i++)
39 if(n%i==0)
40 {
41 k++;
42 v[k]=i;
43 if(i!=n/i)
44 if(n/i!=n)
45 {
46 k++;
47 v[k]=n/i;
48 }
49 }
50
51 for(i=1; i<=k; i++)
52 {
53 x=v[i];
54 smin=99999999999999999;
55 smax=-99999999999999999;
56
57 for(j=x; j<=n; j=j+x)
58 {
59 if ( s[j]-s[j-x]< smin)
60 smin=s[j]-s[j-x];
61 if (s[j]-s[j-x] >= smax)
62 {
63 smax=s[j]-s[j-x];
64 poz=j-x+1;
65 }
66 }
67 dif=smax-smin;
68
69 if(dif<minimm)
70 {
CAPITOLUL 4. OJI 2020 4.2. WIND 73

71 minimm=dif;
72 divx=x;
73 pozmax=poz;
74 }
75 else if(dif==minimm && x<divx)
76 {
77 divx=x;
78 pozmax=poz;
79 }
80 }
81
82 n=n/divx;
83 fprintf(g,"%lli %lli\n",n,pozmax);
84 }
85
86 return 0;
87 }

Listing 4.2.2: wind.cpp


#include <iostream>
#include <fstream>
#include <algorithm>

using namespace std;

ifstream f("wind.in");
ofstream g("wind.out");

long long d,smin,smax,divx,poz,pozmax,minimm=99999999,C,n,i,k,


s[200001],v[10001],x,j,dif,minim=99999999,numar,gasit;
int main()
{
f>>C;
if(C==1)
{
f>>n;
for(i=1; i*i<=n; i++)
if(n%i==0)
{
k++;
if(i!=n/i)
k++;
}
g<<k-1;
}
else
{
f>>n;
for(i=1; i<=n; i++)
{
f>>x;
s[i]=s[i-1]+x;
}
for(i=1; i*i<=n; i++)
if(n%i==0)
{
k++;
v[k]=i;
if(i!=n/i and n/i!=n)
{
k++;
v[k]=n/i;
}
}

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


{
x=v[i];
smin=99999999999;
smax=-99999999999;

for(j=x; j<=n; j=j+x)


{
if ( s[j]-s[j-x]< smin)
CAPITOLUL 4. OJI 2020 4.2. WIND 74

smin=s[j]-s[j-x];
if (s[j]-s[j-x] >= smax)
{
smax=s[j]-s[j-x];
poz=j-x+1;
}
}
dif=smax-smin;
if(dif<minimm)
{
minimm=dif;
divx=x;
pozmax=poz;
}
else if(dif==minimm && x<divx)
{
divx=x;
pozmax=poz;
}
}
g<<n/divx<<" "<<pozmax;
}

return 0;
}

Listing 4.2.3: windCM.cpp


///#include <iostream>
#include <fstream>

using namespace std;

ifstream f("wind.in");
ofstream g("wind.out");

long long s[100001];


int N,cer;

int nr_divizori()
{ if(N==1)return 1;
int k=2,d;
for(d=2;d*d<=N;d++)
{
if(N%d==0)
{ k++;
if(d*d!=N)k++;
}
}
return k;
}

int main()
{
f>>cer>>N;
if(cer==1)
{g<<nr_divizori()-1; return 0;}

///cerinta 2
int ind=1,d,k,indk,nro=N,i,j,tt=2;
long long x, dmin, sd,sdmin,sdmax,difkmin, xmin, xmax;
f>>s[1];
xmin=xmax=s[1];
for(i=2;i<=N;i++)
{
f>>x;
xmin=min(xmin,x);
if(x>=xmax){xmax=x; ind=i;}
s[i]=s[i-1]+x;
}

dmin=xmax-xmin;
s[0]=0;
for(d=2;d<=N/2;d++)
{
CAPITOLUL 4. OJI 2020 4.2. WIND 75

if(N%d==0)
{ k=N/d; sdmin=sdmax=s[d];indk=1;

for(j=d;j<=N;j=j+d)
{ sd=(s[j]-s[j-d]);
if(sd<sdmin)sdmin=sd;
if(sd>=sdmax)
{ sdmax=sd; indk=j-d+1; }
}
difkmin=sdmax-sdmin;

if(difkmin<dmin)
{ nro=k; dmin=difkmin;ind=indk; tt=d; }
else if(difkmin==dmin && nro<k)
{ nro=k; ind=indk; }
}
}

g<<nro<<" "<<ind;
return 0;
}

Listing 4.2.4: windCS.cpp


#include <fstream>

using namespace std;

ifstream fin("wind.in");
ofstream fout("wind.out");

int c, n, i, j, k, dm, um, d, u, emax, emin;


long long e[100005],dif, difm;

void c1()
{
int nrd=-1,d;
for(d=1;d*d<n;d++)if(n%d==0)nrd+=2;
fout<< nrd+(d*d==n);
}

long long diferenta(int d, int &u)


{ long long smin,smax,s;
smin=smax=e[d];u=1;
for(int i=2*d;i<=n;i+=d)
{ s=e[i]-e[i-d];
if(s>=smax)smax=s,u=i-d+1;
else if(s<smin)smin=s;
}
return smax-smin;
}

void refresh(int d)
{ dif=diferenta(d,u);
if(dif<difm || dif==difm && d<dm)difm=dif,dm=d,um=u;
}

void c2()
{
dm=1;um=1;
fin>>e[1];emax=emin=e[1];
for(i=2;i<=n;i++){fin>>e[i];
if(e[i]<emin)emin=e[i];
else if(e[i]>=emax) emax=e[i],um=i;
e[i]+=e[i-1];}

difm=emax-emin;
for(d=2;d*d<n;d++)
if(n%d==0){ refresh(d);refresh(n/d);}
if(d*d==n)refresh(d);
fout<<n/dm<<’ ’<<um;
}

int main()
{ fin>>c>>n;
CAPITOLUL 4. OJI 2020 4.2. WIND 76

if(c==1)c1();
else c2();
fin.close();fout.close();
return 0;
}

4.2.3 *Rezolvare detaliată


Capitolul 5

OJI 2019

5.1 poarta - OJI 2019


Problema 1 - poarta 90 de puncte
Sindbad a descoperit un recipient care conţine o poţiune magică şi o inscripţie care descrie
cum se poate deschide poarta unui templu. Urmând instrucţiunile din inscripţie, Sindbad a ajuns
la un tunel acoperit cu dale pătrate, aliniate astfel ı̂ncât formează linii şi coloane. Tunelul are mai
multe linii, iar pe fiecare linie sunt câte N dale. Dalele din tunel sunt numerotate ı̂ncepând cu 1,
astfel ı̂ncât, parcurgându-le linie cu linie şi fiecare linie de la stânga la dreapta, se obţine un şir
strict crescător de numere naturale consecutive.
Sindbad se află la intrare, ı̂naintea primei linii. Pentru a deschide poarta templului, el trebuie
să ajungă pe dala numerotată cu P , călcând pe un număr minim de dale. Dacă există mai multe
astfel de soluţii, o va alege pe cea pentru care consumul total de picături de poţiune magică este
minim.
Pe parcursul deplasării el trebuie să respecte următoarele reguli:
a de la intrare, poate sări pe orice dală aflată pe prima line, fără a consuma poţiune magică;
a de pe o dală numerotată cu X, Sindbad poate sări fie pe dala numerotată cu X  1, consumând
o picătură de poţiune magică, fie pe dala numerotată cu 2 ˜ X, consumând două picături de poţiune
magică.

Cerinţe

Scrieţi un program care citeşte valorile N şi P cu semnificaţia din enunţ şi rezolvă următoarele
cerinţe:
1. afişează numărul minim de dale pe care trebuie să calce pentru a deschide poarta;
2. afişează numărul natural T , reprezentând numărul minim de picături de poţiune magică
necesare pentru deschiderea porţii.

Date de intrare

Fişierul de intrare poarta.in conţine pe prima linie un număr natural C reprezentând cerinţa
din problemă care trebuie rezolvată (1 sau 2). Pe a doua linie se află numărul natural N , iar pe
a treia linie se află numărul natural P cu semnificaţia din enunţ.

Date de ieşire

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

Restricţii şi precizări

2 & N $ 104
a P este număr natural nenul cu cel mult 1000 de cifre; pentru o parte dintre teste, valorând
ı̂n total 60 de puncte, P are cel mult 18 cifre.
a Recipientul conţine o cantitate suficientă de poţiune magică.
a Pentru rezolvarea cerinţei 1 se acordă maximum 60 de puncte, iar pentru rezolvarea cerinţei
2 se acordă maximum 30 de puncte.

77
CAPITOLUL 5. OJI 2019 5.1. POARTA - OJI 2019 78

Exemple
poarta.in poarta.out Explicaţii
1 3 Tunelul are 5 dale pe fiecare linie. Sindbad trebuie să ajungă pe dala
5 numerotată cu 9. Numărul minim de dale pe care trebuie să calce
9 pentru a ajunge pe dala 9 pentru a deschide poarta este 3.
De pe margine poate sări:
- pe dala numerotată cu 4 (consumă 0 picături de poţiune magică);
- de pe dala numerotată cu 4 pe cea numerotată cu 8 (consumă 2
picături de poţiune magică);
- de pe dala numerotată cu 8 pe cea numerotată cu 9 (consumă 1
picătură de poţiune magică).
2 3 Pentru a ajunge pe dala numerotată cu 9 are nevoie de cel puţin 3
5 picături de poţiune magică.
9

Timp maxim de executare/test: 0.1 secunde


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

5.1.1 Indicaţii de rezolvare

prof. Filonela Balasa - Colegiul National ’’Grigore Moisil’’, Bucuresti

Pentru rezolvarea problemei se foloseste metoda drumului inapoi. Se pleaca de pe da


numerotata cu P. Pentru a obtine numarul minim de pasi solicitat de enunt, in cazul
care numarul P este par valoarea lui se imparte la 2, altfel valoarea lui P scade

Acest pas se repeta cat timp valoarea lui P este mai mare decat N. in cazul in care
la ultimul pas, este posibil sa ajunga pe prima linie atat prin scadere cu 1 cat si
impartire la 2, vom alege sa scadem 1, astfel incat sa obtinem numarul minim de pic
magice.

Pentru rezolvarea cerintei 1: numarul minim de dale pe care calca este numarul de p
efectuati pana cand valoarea lui P devine mai mica sau egala cu N

Pentru rezolvarea cerintei 2, numarul minim de picaturi se determina astfel: pent


fiecare impartire la 2 valoarea lui T creste cu 2 (doua picaturi de potiune magica)
iar pentru fiecare scadere cu 1, valoarea lui T creste cu 1 (o picatura de potiune

5.1.2 Cod sursă

Listing 5.1.1: poarta1 100.cpp


//Rodica Balasa - 100
#include <fstream>
using namespace std;
int main()
{
int i,k=1,ck,nv,j,v[10001],nk=0,gata=0,cer,pas=0,n;
long long T=0;
char c;
ifstream f("poarta.in");
ofstream g("poarta.out");
f>>cer>>n;
nv=0;
while(f>>c)
if(c>=’0’&&c<=’9’)
v[++nv]=c-’0’;
CAPITOLUL 5. OJI 2019 5.1. POARTA - OJI 2019 79

//for(i=1;i<=nv;i++) g<<v[i];g<<"\n";
ck=n;
while(ck)
{
nk++;ck=ck/10;
}

while(!gata)
{
j=0;k++;
if(v[nv]%2==0)
{
T+=2;pas=2;
for(i=1;i<=nv;i++)
{
j=j*10+v[i];
v[i]=j/2;j=j%2;

}
}
else
{
T++;pas=1;
if(v[nv]>0) v[nv]--;
}
if(v[1]==0)
{
i=1;j=1;
while(v[i]==0)i++;nv=nv-i+1;
while(j<=nv)v[j++]=v[i++];
}

if(nv<=nk)
{
ck=0;
for(i=1;i<=nv;i++) ck=ck*10+v[i];
if(ck<=n)gata=1;
}

//for(i=1;i<=nv;i++) g<<v[i];g<<"\n";
}
if(pas==2&&ck*2==n+1){ck=n;T=T-1;}
if(cer==1) g<<k<<"\n";//ne minim de dale
else g<<T<<"\n";//nr minim de picaturi
//g<<k<<"\n"<<ck<<"\n"<<T<<"\n";

return 0;
}

Listing 5.1.2: poarta2 100.cpp


//Ungureanu Florentina 100
#include <fstream>
#include <cstring>
#include <cstdlib>

using namespace std;

ifstream in("poarta.in");
ofstream out("poarta.out");

const int MAXN = 1005;


char p[MAXN],N[20];
int n,c,l;

void Divide(char p[], int &n)


{ int i;
unsigned R=0;
for (i=n;i>=0;--i)
{ p[i]=’0’+(R=(10*R+p[i]-’0’))/2;
R%=2;
}
while (n && p[n]==’0’) p[n--]=0;
}
CAPITOLUL 5. OJI 2019 5.1. POARTA - OJI 2019 80

int Cmp(char p[], int&n, char N[])


{
if (n < l)
return -1;
else if (n > l)
return 1;
for (int i = n; i >= 0; --i)
if (p[i] < N[i])
return -1;
else if (p[i] > N[i])
return +1;
return 0;
}

void citire()
{
in>>c>>N;
in.get();
in.get(p,10001);
n=strlen(p)-1;
l=strlen(N)-1;
}

void rev(char p[], int n)


{
int i=0, j=n;
while(i<j)
{
char aux=p[i];p[i]=p[j];p[j]=aux;
i++;j--;
}
}

int main()
{
citire();
rev(p,n);
rev(N,l);
char pp[12];
unsigned long long pic=0,dal=1,pu;

while(Cmp(p,n,N)>0)
{
if(p[0]%2)
{
p[0]--;
dal++;
pic++;
}
else
{
if(n<=11)
{
strcpy(pp,p);
rev(pp,n);
pu=atoi(pp);
}
Divide(p,n);
pic+=2;
dal++;
}
//out<<p<<’ ’<<dal<<’ ’<<pic<<endl;
}

rev(N,l);
if(pu-1==atoi(N)) pic--;

if(c==1)
out<<dal<<’\n’;
else
out<<pic<<’\n’;

return 0;
}

Listing 5.1.3: poarta3 100.cpp


CAPITOLUL 5. OJI 2019 5.1. POARTA - OJI 2019 81

//Em. Cerchez 100


#include <fstream>
#define LGMAX 10002

using namespace std;

ifstream fin("poarta.in");
ofstream fout("poarta.out");

int N, cerinta;
int P[LGMAX];
int lgp;
int T, nrmin=1, X;

void imparte2(int P[LGMAX], int& lgp);


int compara(int P[LGMAX], int lgp, int nr);
int convert(int P[LGMAX], int lgp);

int main()
{char c;
int st, dr, aux;
fin>>cerinta>>N;
while (fin>>c)
P[lgp++]=c-’0’;
for (st=0, dr=lgp-1; st<dr; st++, dr--)
{aux=P[st]; P[st]=P[dr]; P[dr]=aux;}
while (1)
{
if (compara(P, lgp, N)<=0) {X=convert(P, lgp); break;}
if (compara(P, lgp, N+1)==0) {X=N; T++; nrmin++; break;}

if (P[0]%2)
{
P[0]--; nrmin++; T++;
}
else
{
imparte2(P, lgp);
nrmin++; T+=2;
}
}
if (cerinta==1)
fout<<nrmin<<’\n’;
else
fout<<T<<’\n’;
return 0;
}

void imparte2(int P[LGMAX], int& lgp)


{int i, v=0;
for (i=lgp-1; i>=0; i--)
{
v=v*10+P[i];
P[i]=v/2;
v=v%2;
}
if (P[lgp-1]==0) lgp--;
}

int compara(int P[LGMAX], int lgp, int nr)


//returneaza 1 daca P>nr
//returneaza -1 daca p<nr
//returneaza 0 daca P==nr
{int Q[20], lgq=0, i;
do {Q[lgq++]=nr%10; nr/=10;} while (nr);
if (lgp>lgq) return 1;
if (lgp<lgq) return -1;
for (i=lgp-1; i>=0 && P[i]==Q[i]; i--);
if (i<0) return 0;
if (P[i]<Q[i]) return -1;
return 1;
}

int convert(int P[LGMAX], int lgp)


{int rez=0, i;
CAPITOLUL 5. OJI 2019 5.1. POARTA - OJI 2019 82

for (i=lgp-1; i>=0; i--) rez=rez*10+P[i];


return rez;
}

Listing 5.1.4: poarta4 100.cpp


//Maria Nita - 100
#include <fstream>
#define MaxC 10005

using namespace std;

ifstream cin("poarta.in");
ofstream cout("poarta.out");

int nr[MaxC], k, n, nc, start;


int pr;
int pasi = 1, potiune;

void read()
{
char cif;
cin >> pr >> n;
while(cin >> cif)
nr[++k] = cif -’0’;
int aux = n;
while(aux >0)
{
nc ++;
aux /= 10;
}
}

void drum()
{
int c[MaxC] = {0}, kc, rest = 0, i, val;

while(k > nc)


{
kc = 0;
pasi ++;

if(nr[k]%2==0)
{
potiune += 2;
if(nr[1] < 2)
{
kc = 1;
c[kc] = (nr[1]*10 + nr[2])/2;
rest = (nr[1]*10 + nr[2])%2;
i = 3;
}
else
{
i = 1;
rest = 0;
}

for(; i<=k; i++)


{
rest = rest *10 + nr[i];
c[++kc] = rest/2;
rest = rest %2;
}

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


nr[i] = c[i];
k = kc;
}
else
{
potiune ++;
nr[k] --;
}
}
CAPITOLUL 5. OJI 2019 5.2. VALUTAR - OJI 2019 83

val = 0;
for(i=1; i<=k; i++)
val = val *10 + nr[i];

while(val > n)
{
if(val == n+1)
{
pasi ++;
potiune ++;
start = val-1;
break;
}

if(val % 2 == 0)
{
pasi ++;
potiune +=2;
val /=2;
}
else
{
pasi ++;
potiune ++;
val --;
}
if(val <= n)
start = val;
}
}

void write()
{
if(pr == 1)
cout << pasi << ’\n’;
else
cout << potiune << ’\n’;
}

int main()
{
read();
drum();
write();
return 0;
}

5.1.3 *Rezolvare detaliată

5.2 valutar - OJI 2019


Problema 2 - valutar 90 de
puncte
Valutar este un joc care poate fi jucat de oricâţi jucători. La ı̂nceputul jocului, fiecare jucător
primeşte L lei şi E euro, precum şi un jeton numerotat cu numărul jucătorului. Mai exact, dacă
există M jucători, vor fi M jetoane, numerotate de la 1 la M .
Tabla de joc este harta unui oraş pe care este ilustrat un traseu circular ce conţine N case de
schimb valutar, numerotate ı̂n ordinea de pe traseu de la 1 la N . Fiind circular, după casa N
urmează casa 1. Pentru fiecare casă de schimb valutar se cunosc două valori C şi V (C reprezintă
câţi lei plăteşte un jucător dacă vrea să cumpere 1 euro de la casa respectivă, iar V reprezintă câţi
lei primeşte jucătorul dacă vrea să vândă 1 euro). Fiecare casă are o anumită culoare ı̂n funcţie
de care jucătorul ajuns ı̂n punctul respectiv trebuie să efectueze o anumită acţiune astfel:

Exemple
CAPITOLUL 5. OJI 2019 5.2. VALUTAR - OJI 2019 84

valutar.in valutar.out Explicaţii


Alb A Jucătorul nu face nimic la această mutare.
Roşu R Jucătorul primeşte un cartonaş denumit ”pas”. Un jucător care
are un cartonaş ”pas” va folosi ulterior cartonaşul (o singură
dată, după care cartonaşul va fi scos din joc) şi astfel evită să
execute o acţiune pe care nu poate să o execute, pentru a nu fi
eliminat din joc.
Galben G Jucătorul trebuie să cumpere i euro (unde i este numărul casei
de schimb valutar la care se află). Dacă nu are suficienţi lei pen-
tru a face acest lucru şi nu deţine un cartonaş ”pas”, jucătorul
este eliminat din joc. Dacă are un cartonaş ”pas”, jucătorul ı̂l
va folosi şi nu va executa acţiunea, fără a fi eliminat din joc.
Verde V Jucătorul trebuie să vândă i euro (unde i este numărul casei de
schimb valutar la care se află). Dacă nu are suficienţi euro pen-
tru a face acest lucru şi nu deţine un cartonaş ”pas”, jucătorul
este eliminat din joc. Dacă are un cartonaş ”pas”, jucătorul ı̂l
va folosi şi nu va executa acţiunea, fără a fi eliminat.

Iniţial toţi jucătorii pornesc de la casa de schimb valutar 1 care este albă. Există N case
de schimb valutar şi M jucători. Jucătorii mută pe rând ı̂n ordinea jetoanelor. Mai ı̂ntâi mută
jucătorul 1, apoi 2, 3, ..., M . După jucătorul M va muta din nou 1 etc. La o mutare, un jucător
care nu a fost eliminat din joc:
- ”dă” cu zarul electronic; zarul va afişa un număr ı̂ntreg nr;
- avansează cu nr poziţii (adică dacă jetonul său este la casa i va ajunge la casa i  nr);
- execută acţiunea asociată casei de schimb valutar ı̂n care a ajuns, ı̂n funcţie de culoarea
acesteia.
Zarul electronic funcţionează astfel: la mutarea cu numărul j este generat numărul nrj calculat
după formula nrj a ˜ nrj  1  b%N  1, unde nrj  1 este numărul generat la mutarea j  1;
a, b şi nr0 sunt trei valori cunoscute, iar % reprezintă restul ı̂mpărţirii ı̂ntregi (mod).
Cerinţe
Scrieţi un program care să rezolve următoarele cerinţe:
1. determină numărul de jucători existenţi ı̂n joc după X mutări;
2. determină jucătorul care a rămas ı̂n joc şi care are cea mai mare sumă de Euro după X
mutări.
Date de intrare
Fişierul de intrare valutar.in conţine pe prima linie cerinţa care trebuie să fie rezolvată (1 sau
2).
Pe a doua linie se află numerele naturale a b şi nr0, cu semnificaţia din enunţ.
Pe a treia linie se află numerele naturale N M L E X, reprezentând numărul de case de
schimb valutar, numărul de jucători, câţi lei şi câţi euro primeşte fiecare jucător la ı̂nceputul
jocului, respectiv numărul de mutări din joc. Pe următoarele N linii sunt descrise casele de schimb
valutar, câte o casă pe o linie, ı̂n ordinea de la 1 la N , sub forma Cod C V , cu semnificaţiile din
enunţ. Valorile scrise pe aceeaşi linie sunt separate prin câte un spaţiu.
Date de ieşire
Fişierul de ieşire valutar.out va conţine o singură linie. Dacă cerinţa este 1, linia va conţine
un număr natural reprezentând numărul de jucători existenţi ı̂n joc după X mutări. Dacă cerinţa
este 2, linia va conţine numărul jetonului jucătorului rămas ı̂n joc şi care are cea mai mare sumă
de euro după X mutări.
Restricţii şi precizări
1 & M, C, V & 100
a 1 & a, b, nr0, N, X & 10000
a 1lL, E & 106
a Toate casele de schimb valutar au suficienţi lei şi euro pentru efectuarea oricărei acţiuni.
a Se garantează că pentru datele de test la cerinţa 2 va rămâne ı̂n joc după X mutări un singur
jucător cu suma maximă de euro.
a Pentru fiecare cerinţă se acordă 50% din punctajul obţinut pe teste.
CAPITOLUL 5. OJI 2019 5.2. VALUTAR - OJI 2019 85

Exemple

valutar.in valutar.out valutar.in valutar.out


1 1 2 2
327 327
53238 53238
A11 A11
G54 G54
G64 G64
V65 V65
R23 R23

Explicaţie:
Numerele care se obţin când se dă cu zarul se generează astfel: nrj 3 ˜ nrj  1  2%5  1,
unde nr0 7.
Există ı̂n joc 5 case de schimb valutar şi 3 jucători. Toţi jucătorii au iniţial 2 lei şi 3 euro şi se
află la casa de schimb valutar 1 care este albă. Se efectuează 8 mutări astfel:

Mu- Ju- Nu- De Unde Ac- Lei Euro Obs.


ta- ca- mar unde ajunge ti-
re tor pleaca une
1 1 4 1 5 R 2 3 A primit un cartonaş ”pas”.
2 2 5 1 1 A 2 3 Nu face nimic.
3 3 3 1 4 V 2 3 Trebuie să vândă 4 euro, dar nu are
decât 3, nu are cartonaş ”pas”, deci
este scos din joc.
4 1 2 5 2 G 2 3 Trebuie să cumpere 2 euro, care
costă 2 ˜ 5 10 lei, el nu are des-
tui bani, dar are un cartonaş ”pas”
pe care ı̂l foloseşte, deci rămâne ı̂n
joc.
5 2 4 1 5 R 2 3 A primit un cartonaş ”pas”.
6 1 5 2 2 G 2 3 Trebuie să cumpere 2 euro, care
costă 2 ˜ 5 10 lei, el nu are destui
bani, nu mai are niciuun cartonaş,
deci este eliminat din joc.
7 2 3 5 3 G 2 3 Trebuie să cumpere 3 euro care ar
costa 6 ˜ 3 18 lei, nu are bani
dar are un cartonaş ”pas” pe care ı̂l
foloseşte şi nu este eliminat din joc.
8 2 2 3 5 R 2 3 Primeşte un cartonaş ”pas”.

Timp maxim de executare/test: 0.1 secunde


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

5.2.1 Indicaţii de rezolvare

prof. Emanuela Cerchez - Colegiul National ’’Emil Racovita’’ Iasi

Pentru a memora datele despre casele de schimb valutar vom defini o structura
denumita casa cu 3 campuri: cod, C si V (conform enuntului).
Pentru a memora datele despre un jucator vom defini o structura denumita jucator
cu urmatoarele campuri:
-sl - suma in lei pe care o are jucatorul respectiv la un moment dat
-se - suma in euro pe care o are jucatorul la un moment dat
-pas - numarul de cartonase pas detinute de jucatorul respectiv.
CAPITOLUL 5. OJI 2019 5.2. VALUTAR - OJI 2019 86

-unde - casa de schimb valutar la care se afla jucatorul


-out - va avea valoarea 1 daca jucatorul a fost eliminat din joc si 0 altfel.
Jucatorii vor fi retinuti intr-un vector J cu elemente de tip jucator.
Initial toti jucatorii au campul sl=L, se=E, pas=0, unde=1 si out=0.
Rezolvarea problemei presupune simularea jocului.
Se efectueaza cele X mutari, una cate una, si la fiecare mutare:
-se genereaza urmatorul numar afisat de zar (utilizand formula din enunt);
-se determina jucatorul aflat la mutare (avand grija sa sarim peste jucatorii
care au fost eliminati din joc);
-se determina noua pozitie a jucatorului aflat la mutare;
-in functie de culoarea casei din noua pozitie se efectueaza actiunea asociata.
La final se contorizeaza numarul jucatorilor pentru care out este 0 (pentru cerinta
sau se determina suma maxima in euro si jucatorul care are aceasta suma (pentru cer

5.2.2 Cod sursă

Listing 5.2.1: valutar cpp 100.cpp


//Em. Cerchez 100
#include <fstream>
#define MMAX 102
#define NMAX 10002

using namespace std;

ifstream fin("valutar.in");
ofstream fout("valutar.out");

struct casa {char cod; int c, v;};

struct jucator {int sl, se, pas, unde; bool out;};

jucator J[MMAX];
casa C[NMAX];

int n, m, a, b, nr, X, cerinta, nrout;

int main()
{int i, L, E, cine=0, sum=-1, jmax;
char culoare;
fin>>cerinta>>a>>b>>nr>>n>>m>>L>>E>>X;

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


fin>>C[i].cod>>C[i].c>>C[i].v;

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


{J[i].unde=1; J[i].sl=L; J[i].se=E;}

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


{
if (nrout==m) break;

do {cine++; if (cine>m) cine=1;} while (J[cine].out);

nr=(a*nr+b)%n+1;
J[cine].unde+=nr; if (J[cine].unde>n) J[cine].unde-=n;
culoare=C[J[cine].unde].cod;

if (culoare==’A’) { continue;}
else
if (culoare==’R’) J[cine].pas++;
else
if (culoare==’G’) //cumpar euro
{
if (J[cine].sl>=J[cine].unde*C[J[cine].unde].c)
{//cumpar euro
J[cine].se+=J[cine].unde;
J[cine].sl-=J[cine].unde*C[J[cine].unde].c;
}
CAPITOLUL 5. OJI 2019 5.2. VALUTAR - OJI 2019 87

else
if (J[cine].pas>0) J[cine].pas--;
else {J[cine].out=1; nrout++;}
}
else //vand euro
if (J[cine].se>=J[cine].unde)
{
J[cine].se-=J[cine].unde;
J[cine].sl+=J[cine].unde*C[J[cine].unde].v;
}
else
if (J[cine].pas>0) J[cine].pas--;
else {J[cine].out=1; nrout++;}
}

if (cerinta==1)
fout<<m-nrout;
else
{
for (i=1; i<=m; i++)
if (!J[i].out)
if (J[i].se>sum) {sum=J[i].se; jmax=i;}
fout<<jmax;
}

fout<<’\n’;
fout.close();
return 0;
}

Listing 5.2.2: valutar1 100.cpp


//Ungureanu Florentina 100
#include <fstream>

using namespace std;

ifstream in("valutar.in");
ofstream out("valutar.out");

const int MAXN = 10005;


const int MAXM = 105;

int a,b,p,nr0,n,m,x;
long long l,e;

struct casa
{
char cod;
unsigned long long c,v;
} c[MAXN];

struct jucator
{
bool stare;
int cc,pas;
unsigned long long l,e;
} J[MAXN];

void citire()
{
in>>p;
in>>a>>b>>nr0;
in>>n>>m>>l>>e>>x;
for (int i=1; i<=n; ++i)
in>>c[i].cod>>c[i].c>>c[i].v;
for (int i=1; i<=m; ++i)
{
J[i].cc=1;
J[i].l=l;
J[i].e=e;
J[i].stare=true;
J[i].pas=0;
}
}
CAPITOLUL 5. OJI 2019 5.2. VALUTAR - OJI 2019 88

inline int aruncare(int zar)


{
return (a*zar+b)%n+1;
}

int main()
{
citire();
int i=1;
int p1=m;
for(int j=1; j<=x; ++j)
{
while(!J[i].stare)
{
++i;
if(i>m)i-=m;
}
nr0=aruncare(nr0);
J[i].cc+=nr0;
if(J[i].cc>n) J[i].cc-=n;
int cc=J[i].cc;
switch(c[cc].cod)
{
case ’R’:
J[i].pas++;
break;
case ’G’:
if(c[cc].c*cc<=J[i].l)
{
J[i].l-=c[cc].c*cc;
J[i].e+=cc;
}
else if(J[i].pas) J[i].pas--;
else
{
J[i].stare=false;
p1--;
}
break;
case ’V’:
if(cc<=J[i].e)
{
J[i].l+=c[cc].v*cc;
J[i].e-=cc;
}
else if(J[i].pas) J[i].pas--;
else
{
J[i].stare=false;
p1--;
}
}
++i;if(i>m)i-=m;
}
if(p==1) out<<p1<<’\n’;
else
{
unsigned long long k=0,maxi=0;
for (int i=1; i<n; ++i)
if(J[i].stare&&J[i].e>maxi){k=i;maxi=J[i].e;}
out<<k<<’\n’;
}
return 0;
}

Listing 5.2.3: valutar2 100.cpp


//Nita Maria - 100
#include <fstream>

#define Max 105


#define MaxC 10005

using namespace std;


CAPITOLUL 5. OJI 2019 5.2. VALUTAR - OJI 2019 89

ifstream cin("valutar.in");
ofstream cout("valutar.out");

struct case_schimb
{
char cul;
int c, v;
};

struct jucator
{
int sumL, sumE, pas, e_joc, poz;
};

jucator v[Max];
case_schimb c[MaxC];

int n, m, a, b, nra, X;
int pr;

void read()
{
int lei, euro;
cin >> pr;
cin >> a >> b >> nra;
cin >> n >> m >> lei >> euro >> X;
for(int i=1; i<=m; i++)
{
v[i].sumL = lei;
v[i].sumE = euro;
v[i].pas = 0;
v[i].e_joc = 1;
v[i].poz = 1;
}
for(int i=1; i<=n; i++)
{
cin >> c[i].cul >> c[i].c >> c[i].v;
}
}

void joc()
{
int nru, j, nrjucator, casa, val;
nrjucator = 1;
for(j=1; j<=X; j++)
{
nru = (a*nra+b)%n+1;
nra = nru;
while(v[nrjucator].e_joc == 0)
{
nrjucator ++;
if(nrjucator > m)
nrjucator = 1;
}
v[nrjucator].poz += nru;
v[nrjucator].poz = v[nrjucator].poz%n;
if(v[nrjucator].poz == 0)
v[nrjucator].poz = n;
casa = v[nrjucator].poz;
if(c[casa].cul ==’R’)
v[nrjucator].pas ++;
else
if(c[casa].cul == ’G’)
{
val = casa * c[casa].c;
if(v[nrjucator].sumL < val)
if(v[nrjucator].pas > 0)
v[nrjucator].pas --;
else
v[nrjucator].e_joc = 0;
else
{
v[nrjucator].sumL -= val;
v[nrjucator].sumE += casa;
}
CAPITOLUL 5. OJI 2019 5.2. VALUTAR - OJI 2019 90

}
else
if(c[casa].cul == ’V’)
{
val = casa * c[casa].v;
if(v[nrjucator].sumE < casa)
if(v[nrjucator].pas > 0)
v[nrjucator].pas --;
else
v[nrjucator].e_joc = 0;
else
{
v[nrjucator].sumE -= casa;
v[nrjucator].sumL += val;
}
}
nrjucator ++;
}
}

void write()
{
int i, nrj = 0, sum_max = 0, viz[100]={0}, k = 0;
for(i=1; i<=m; i++)
if(v[i].e_joc == 1)
{
nrj ++;
if(v[i].sumE > sum_max)
{
sum_max = v[i].sumE;
k=1;
viz[k] = i;
}
else
if(v[i].sumE == sum_max)
viz[++k] = i;
}
if(pr == 1)
cout << nrj << ’\n’;
else
{
for(i=1; i<=k; i++)
cout << viz[i] << ’ ’;
cout << ’\n’;
}
}

int main()
{
read();
joc();
write();
return 0;
}

5.2.3 *Rezolvare detaliată


Capitolul 6

OJI 2018

6.1 puzzle - OJI 2018


Problema 1 - puzzle 100 de puncte
Mihai a primit de ziua lui un joc de puzzle. Jocul are N piese confecţionate prin lipirea unor
bucăţi de dimensiune 1x1 (ilustrate ı̂n figurile de mai jos prin X); aceste bucăţi le vom numi ı̂n
continuare, pe scurt, X-uri. Pentru confecţionarea unei piese se respectă următoarele reguli:
1. X-urile sunt aşezate unul peste altul, formând coloane ce pot avea ı̂nălţimi diferite, apoi
coloanele se aliniază ı̂n partea de jos şi se lipesc ı̂ntre ele, una după cealaltă, de la stânga spre
dreapta;
2. pe o coloană sunt cel mult 9 X-uri;
3. toate piesele au acelaşi număr de coloane.
Exemple:

Figura 6.1: Puzzle

În figurile 1, 2, 3, 4 sunt piese de puzzle care respectă regulile descrise, iar ı̂n figura 5 şi ı̂n figura
6 NU sunt piese de puzzle, pentru că nu pot fi obţinute prin lipirea unor coloane de X-uri, una
după cealaltă, de la stânga spre dreapta.
Fiind mic, Mihai nu poate rezolva puzzle-ul, dar poate face o singură operaţie: alege două piese
şi le ı̂mbină ı̂n dreptul laturilor de sus, răsturnând una dintre piese sus-jos (fără să o rotească sau
să o răstoarne stânga-dreapta). Dacă ı̂n urma acestei operaţii el obţine un dreptunghi format din
coloane complete de X-uri, toate coloanele având aceeaşi ı̂nălţime, este mulţumit. De exemplu,
piesa din figura 1 şi cea din figura 2 pot fi ı̂mbinate ı̂n modul descris.
În figura 7 este piesa din figura 2 răsturnată sus-jos. În figura 8 este ilustrat dreptunghiul care
se obţine din piesa din figura 1 şi piesa din figura 2 răsturnată sus-jos.
Observaţi că, dacă am roti piesa din figura 4, am putea să o ı̂mbinăm cu piesa din figura 1,
dar rotaţia nu este permisă.
Vom codifica o piesă printr-un număr natural, fiecare cifră din număr reprezentând (ı̂n ordine
de la stânga la dreapta) câte X-uri se află pe coloana corespunzătoare din piesă.
De exemplu:
- piesa din figura 1 este codificată 4232;
- piesa din figura 2 este codificată 1323;
- piesa din figura 3 este codificată 4444;
- piesa din figura 4 este codificată 3231.

Cerinţe

Determinaţi care este numărul de moduri ı̂n care Mihai poate alege câte două piese dintre cele
N pentru a face o operaţie ı̂n modul descris mai sus.

91
CAPITOLUL 6. OJI 2018 6.1. PUZZLE - OJI 2018 92

Date de intrare

Fişierul de intrare puzzle.in conţine pe prima linie un număr natural N ce reprezintă numărul
de piese din joc. Pe linia a doua se găsesc N numere naturale, separate prin câte un singur spaţiu,
reprezentând codificările celor N piese.

Date de ieşire

Fişierul de ieşire puzzle.out va conţine o singură linie pe care va fi scris numărul cerut.

Restricţii şi precizări

2 & N & 105


a Numerele care reprezintă codificările pieselor au acelaşi număr de cifre (cel mult 5) şi nu
conţin cifra 0.
a Într-o operaţie nu contează care dintre piese este răsturnată, ca urmare perechea formată
din piesa a şi piesa b este considerată ca fiind aceeaşi cu perechea formată din piesa b şi piesa a.
a Dreptunghiul obţinut ı̂n urma unei operaţii poate avea ı̂nălţimea mai mare decât 9.
a Pentru teste valorând 30 de puncte N & 1000. Se acordă 10 puncte din oficiu.

Exemple
puzzle.in puzzle.out Explicaţii
5 3 Se pot ı̂mbina 3 perechi de piese: piesa 1 cu piesa 5, piesa
222 432 234 123 111 2 cu piesa 3, piesa 2 cu piesa 4. Piesele 3 şi 4 s-ar putea
ı̂mbina corect dacă una dintre ele ar fi răsturnată stânga-
dreapta sau rotită, dar acest lucru nu e permis.

Timp maxim de executare/test: 0.5 secunde


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

6.1.1 Indicaţii de rezolvare

Autor, prof. Marius Nicoli - Colegiul National ’’Fratii Buzesti’’ - Craiova

Observam ca doua numere reprezinta codificarile a doua piese ce pot fi cuplate daca
insumand doua cate doua cifrele de pe aceleasi pozitii in cele doua numere se obtin
aceeasi valoare.

O prima solutie, care obtine punctaj partial, neincadrandu-se in timp, fixeaza toat
perechile posibile de numere si aplica testul de mai sus.

O solutie mai buna, liniara este urmatoarea: observam ca doua configuratii sunt
identice daca scazand cu aceeasi valoare fiecare cifra a uneia, se obtine cealalta.
Astfel, fiecarui numar dat ii asociem drept cod valoarea obtinuta scazand din fieca
cifra a sa cifra cu valoare minima.

Astfel, intr-un vector c, pe pozitia fiecarui cod vom numara cate numere cu acel co
Pentru codul 0 adunam la solutie c[0] * (c[0] - 1) / 2, adica toate modurile de a a
piese cu toate laturile netede. Pentru fiecare dintre celelalte coduri i exista un
cod j pentru care piesele cu cele 2 coduri se pot imbina. Astfel, adunam la solutie
c[i] * c[j]. Codul j asociat unui cod i se obtine usor folosind algoritmul de parcu
cifrelor unui numar, aplicat lui i (practic, inlocuind fiecare cifra de valoare x a
valoarea maxim-x, unde maxim reprezinta valoarea celei mai mari cifre din i).

Structurile de date utilizate: vectori

Tipul problemei: prelucrarea cifrelor unui numar, vector de frecvente

Gradul de dificultate: 3
CAPITOLUL 6. OJI 2018 6.1. PUZZLE - OJI 2018 93

6.1.2 Cod sursă

Listing 6.1.1: p2.cpp


#include <fstream>
#include <iostream>

#define DIMN 100010


#define DIMK 100010

using namespace std;

int f[DIMK];
int v[DIMN];
int c[12];
long long sol;
int n, k;

int getCode(int x)
{
int t = 0, minim = 9;
while (x)
{
c[++t] = x%10;
x /= 10;
if (c[t] < minim)
minim = c[t];
}

int r = 0;
for (int i=t;i>=1;i--)
r = r * 10 + (c[i] - minim);
return r;
}

int getReverse(int code, int k)


{
int t = 0, aux = code, maxim = 0;
for (int i=1;i<=k;i++)
{
c[++t] = aux%10;
aux /= 10;
maxim = max(maxim, c[t]);
}

int r = 0;
for (int i = t; i>=1; i--)
{
r = r * 10 + (maxim - c[i]);
}
return r;
}

int numar(int n)
{
int s = 0;
while (n)
{
s++;
n /= 10;
}
return s;
}

int main ()
{

ifstream fin ("puzzle.in");


ofstream fout("puzzle.out");

int aux;
fin>>n;

for (int i=1;i<=n;i++)


CAPITOLUL 6. OJI 2018 6.1. PUZZLE - OJI 2018 94

{
fin>>v[i];
if (i == 1)
k = numar(v[i]);
f[aux = getCode(v[i])]++;
//cout<<v[i]<<" "<<aux<<" "<<getReverse(aux, k)<<"\n";
}

sol = 0;
for (int i=1;i<=100000;i++)
sol += f[i] * 1LL * f[ getReverse(i, k) ];

sol /= 2;

sol += f[0] * 1LL * (f[0] - 1) / 2;

fout<<sol<<"\n";
return 0;
}

Listing 6.1.2: p4.cpp


#include <fstream>

using namespace std;

ifstream f("puzzle.in");
ofstream g("puzzle.out");

int n,v[100000],nrc,x,y,k,i,Max;
long long s,z;

void red(int &x)


{ int y=x,c=x%10; x=x/10;
while(x)
{ c=min(c,x%10);
x/=10;
}
x=y-c*k;
}

int main()
{ f>>n>>x;
y=x;
while(y){nrc++;y=y/10; k=k*10+1;}
red(x);
if(x==0) z++;
else v[x]++;

for(i=2;i<=n;++i)
{ f>>x;
red(x);
if(x==0) z++;
else {Max=max(Max,x);v[x]++;}
}

s= z*(z-1)/2;
for(i=1;i<Max;i++)
if(v[i]) {x=9*k-i;
red(x);
if(x>i && v[x]) s=s+v[i]*v[x];
}

g<<s<<’\n’;
f.close();
g.close();
return 0;
}

6.1.3 *Rezolvare detaliată


CAPITOLUL 6. OJI 2018 6.2. TBILE - OJI 2018 95

6.2 tbile - OJI 2018


Problema 2 - tbile 100 de puncte
Roboţelul Nino a primit cadou un dispozitiv care
inscripţionează bile. Dispozitivul poate fi ı̂ncărcat cu n bile,
ce vor fi inscripţionate ı̂n ordine, cu numerele 1, 2, ..., n.
Nino trebuie să ı̂mpartă bilele inscripţionate ı̂n două şiruri, X
şi Y , astfel:
- La primul pas Nino va pune ı̂n primul şir bila cu numărul 1
(X1 1), iar ı̂n al doilea şir bila cu numărul 2 (Y1 2).
- La al doilea pas Nino va pune ı̂n primul şir bila cu numărul Figura 6.2: tbile
3 X2 3), iar ı̂n al doilea şir bila cu numărul 4 (Y2 4).
- La fiecare pas i ' 3 Nino va pune ı̂n şirul X bila Xi Xi1  Yi1 , iar ı̂n şirul Y , ı̂n ordine
crescătoare, bilele numerotate cu Xi1  1, Xi1  2, ..., Xi  1, cu excepţia bilei 4 care a fost pusă
deja.
Dacă la un pas k, Xk % n, bilele rămase vor fi inscripţionate cu valorile Xk1  1, Xk1  2, ..., n
şi vor fi puse ı̂n şirul Y .
Pentru că bilele se rostogolesc, Nino ı̂mpachetează ı̂n tuburi verticale de culoare galbenă, bilele
din primul şir, iar ı̂n tuburi verticale de culoare roşie, bilele din al doilea şir. În fiecare tub ı̂ncap
cel mult m bile, dispuse pe o singură coloană. Tuburile sunt aşezate vertical, ı̂ntâi cele galbene,
ı̂n ordinea umplerii, apoi cele roşii ı̂n ordinea umplerii lor. Bilele de la baza fiecărui tub formează
nivelul 1, cele situate imediat deasupra lor formează nivelul 2 etc., nivelul maxim putând fi m.

Cerinţe

Se dau numerele naturale n şi m şi se cere să se determine:


1. Numărul de tuburi de culoare roşie necesare pentru a ı̂mpacheta bilele din şirul Y şi numărul
total de bile conţinute de acestea.
2. Pentru un nivel v dat, suma numerelor inscripţionate pe bilele de pe nivelul v.

Date de intrare

Fişierul de intrare tbile.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 un număr natural n, reprezentând numărul
de bile ce se inscripţionează, iar pe cea de a treia linie un număr natural m, reprezentând numărul
de bile care ı̂ncap ı̂ntr-un tub. Dacă cerinţa este c 2, fişierul de intrare conţine, ı̂n plus, pe a
patra linie, un număr natural v reprezentând numărul unui nivel.

Date de ieşire

Dacă cerinţa este c 1, atunci, pe prima linie a fişierului tbile.out, vor fi scrise două numere
naturale, separate printr-un spaţiu, reprezentând, ı̂n această ordine, numărul de tuburi de culoare
roşie necesare pentru a ı̂mpacheta bilele din şirul Y , respectiv, numărul total de bile conţinute de
acestea.
Dacă cerinţa este c 2, atunci, pe prima linie a fişierului tbile.out va fi scris un număr natural
reprezentând suma numerelor inscripţionate pe bilele de pe nivelul v.

Restricţii şi precizări

5 & n & 2000000000


a 1 & c & m & 311445015
a Se acordă 10 puncte din oficiu, 30 de puncte pentru rezolvarea corectă a cerinţei 1 şi 60 de
puncte pentru rezolvarea corectă a cerinţei 2.

Exemple

tbile.in tbile.out Explicaţii


1 6 29 Primul şir va conţine 7 bile (1, 3, 7, 12, 18, 26, 35), iar cel de al
36 doilea 36  7 29 de bile (ca ı̂n figura de mai sus). Sunt necesare
5 6 tuburi de capacitate 5.
CAPITOLUL 6. OJI 2018 6.2. TBILE - OJI 2018 96

2 126 Pe nivelul 3 se găsesc bilele inscripţionate cu numerele


36 7, 5, 11, 17, 23, 29 şi 34.
5 Suma acestor valori este 126. .
3

Timp maxim de executare/test: 0.2 secunde


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

6.2.1 Indicaţii de rezolvare

Structurile de date utilizate: vectori.

Problema de generare, calcul matematic.

Gradul de dificultate: 4

Se genereaza intr-un vector termenii sirului X <= n. Fie k numarul acestora.


Se determina numarul br de bile din tuburile de culoare rosie ca fiind n - k.

Pentru cerinta 1 se afiseaza [br/m] (partea intreaga superioara!) si br.

Pentru rezolvarea cerintei doi o abordare posibila ar fi:


Se aduna la suma termenii:

X[v], X[v+m], X[v+2*m], ... avand valorile <= n (bilele situate pe nivelul v
in tuburi galbene);

v+r1, v+m+r2, v+2*m+r3, ... avand valorile <= n (bilele situate pe nivelul v
in tuburi rosii) unde ri este egal cu numarul de termeni din sirul X <= v+(i-1)*m.

Pentru a obtine punctajul maxim, suma valorilor inscriptionate pe bile din tuburi
rosii avand valorile situate intre doi termeni consecutivi din sirul X poate fi
determinata utilizand sume de tip Gauss.

6.2.2 Cod sursă

Listing 6.2.1: t1.cpp


#include <fstream>

using namespace std;

ifstream in("tbile.in");
ofstream out("tbile.out");

long long n,x[64000],y,k,m,v,p,br,c,nivg,nivr;


long long s=0;

void AdunaRosu(long long f)


{
long long r=k-1,t;
if(br+r<=f)
{
t=(f-br-r)/m;
s+=(br+r)*(t+1)+m*t*(t+1)/2;
br+=m*(t+1);
}
}

int main()
CAPITOLUL 6. OJI 2018 6.2. TBILE - OJI 2018 97

{
in>>v>>n>>m;
if(v==2)
in>>c;
x[1]=1;x[2]=3;
y=4;p=3;
k=2;nivg=2;
br=c;

while(x[k]+y<=n)
{
k++;
nivg++;
if(nivg>m)nivg=1;
x[k]=x[k-1]+y;
if(nivg==c) s+=x[k];
AdunaRosu(x[k]-1);
y++;
if(y==x[p]){y++;p++;}
}

if(v==1)
{
br=n-k;
if(br%m==0)
out<<br/m;
else
out<<br/m+1;

out<<’ ’<<br<<’\n’;
}
else
{
if(x[k]<n)
{
k++;
AdunaRosu(n);
}

out<<s<<’\n’;
}

return 0;
}

Listing 6.2.2: t2.cpp


#include <fstream>

using namespace std;

ifstream f("tbile.in");
ofstream g("tbile.out");

typedef unsigned long long ull;

ull galben[100000],s,t,p,rest;
int i,iRosu,v,r,n,m,c,j,nr;

int main()
{ f>>v>>n>>m;
galben[1]=1;galben[2]=3;
iRosu=2; r=4; i=2;

while(galben[i]+r<=n)
{ galben[++i]=galben[i-1]+r++;
if(r==galben[iRosu+1]) {iRosu++; r=galben[iRosu]+1;}
}

rest=n-galben[i];
r=n-i;

if(v==1)g<<r/m+(r%m>0)<<’ ’<<r<<’\n’;
else
{ f>>c;
CAPITOLUL 6. OJI 2018 6.2. TBILE - OJI 2018 98

if(c==1) s=3;
else if(c==2) s=2;

nr=1;
for(j=3;j<=i;j++)
{ if(j%m==c || j%m==0 && m==c) s=s+galben[j];
if(nr<c)
if(nr+galben[j]-galben[j-1]-1<c) {nr=nr+galben[j]-galben[j-1]-1; continue;}
else
p=galben[j-1]+(c-nr);
else
{
while(nr+(galben[j]-galben[j-1])-1<=m) {nr+=(galben[j]-galben[j-1]-1);j++;}
if(galben[j-1]+(m-nr)+c>=galben[j]) {nr=galben[j]-galben[j-1]-1-(m-nr);
continue;}
p=galben[j-1]+(m-nr+c);
}

nr=c;
t=(galben[j]-1-p)/m+1;
s+=p*t+(t-1)*t/2*m;
nr=c+galben[j]-(p+(t-1)*m)-1;
if(nr>m) nr-=m;
}

if(rest>0)
{
if(nr<c)
p=galben[i]+(c-nr);
else
p=galben[i]+(m-nr)+c;

while(p<=n) {s=s+p; p=p+m; }


}

g<<s<<’\n’;
}

f.close();
g.close();
return 0;
}

6.2.3 *Rezolvare detaliată


Capitolul 7

OJI 2017

7.1 Cursuri - OJI 2017


Problema 1 - Cursuri 90 de
puncte
Într-o tabără de vară se programează susţinerea unor cursuri ı̂n K săli de clasă. Sunt N
profesori care şi-au exprimat dorinţa de a participa, fiecare dintre ei specificând intervalul de timp
ai , bi  ı̂n care ı̂şi poate susţine cursul. Programarea pe săli a profesorilor trebuie să ţină cont de
faptul că ı̂ntr-o clasă, la un moment dat, nu poate preda decât un singur profesor.

Cerinţe

Cunoscându-se faptul că organizatorii doresc susţinerea a cât mai multor cursuri, să se deter-
mine:
1) Numărul maxim de cursuri care pot fi programate ı̂n cele K săli de clasă, ţinând cont de
restricţia indicată.
2) În dorinţa de a programa toate cursurile, ı̂n cele K săli, organizatorii decid să modifice
durata cursurilor, păstrând ı̂nsă neschimbată ora de ı̂nceput a lor. Astfel, ei hotărăsc ca toate
cursurile să dureze un interval egal de timp, care ı̂nsă nu va depăşi durata celui mai lung curs
propus iniţial de unul dintre cei N profesori. Determinaţi care poate fi durata maximă pe care o
pot avea cursurile ı̂n aceste condiţii.

Date de intrare

În fişierul de intrare cursuri.in se găseşte pe prima linie un număr natural C. Pentru toate
testele, C poate lua numai valorile 1 sau 2. Pe linia a doua se găseşte o pereche de numere naturale
N K, separate printr-un spaţiu, reprezentând numărul profesorilor şi numărul de săli de clasă. Pe
următoarele N linii se găsesc perechi de numere naturale ai bi , care reprezintă intervalele de timp
ı̂n care cei N profesori ı̂şi susţin cursurile. Numerele ı̂n cadrul unei linii sunt separate printr-un
spaţiu.

Date de ieşire

Dacă valoarea lui C este 1, se va rezolva numai punctul 1) din cerinţe. În acest caz, fişierul de
ieşire cursuri.out va conţine pe prima linie un număr natural reprezentând numărul maxim de
cursuri care pot fi programate ı̂n cele K săli de clasă, ţinând cont de restricţia indicată.
Dacă valoarea lui C este 2, se va rezolva numai punctul 2) din cerinţe. În acest caz, fişierul de
ieşire cursuri.out va conţine pe prima linie un număr natural reprezentând durata maximă pe
care o pot avea cele N cursuri, astfel ı̂ncât toate să poată fi susţinute ı̂n cele K săli disponibile.

Restricţii şi precizări

a 1 & N & 1000


a 1 & K & 1000
a 1 & ai $ bi & 100000, unde 1 & i & N
a ı̂n cazul cerinţei 2) se garantează că pentru toate testele există soluţie
a Pentru rezolvarea corectă a primei cerinţe se acordă 45 de puncte, iar pentru rezolvarea
corectă a celei de a doua cerinţe se acordă 45 de puncte. Se acordă 10 puncte din oficiu.

99
CAPITOLUL 7. OJI 2017 7.1. CURSURI - OJI 2017 100

Exemple
cursuri.in cursuri.out Explicaţii
1 3 O variantă de programare optimă este următoarea:
42 - ı̂n prima sală se vor susţine cursurile programate ı̂ntre 1, 3
2 16 şi 3, 18
13 - ı̂n a doua clasă se susţine cursul programat ı̂ntre 1, 20.
3 18
1 20
2 4 Durata maximă pe care o pot avea toate cursurile este 4.
42 Cursul al treilea se va mări şi se va desfăşura ı̂ntre 1, 5, celelalte
5 12 se vor micşora. Cursurile vor fi distribuite ı̂n cele două săli astfel:
9 18 Sala 1: al treilea şi primul profesor programaţi ı̂ntre 1, 5 re-
13 spectiv 5, 9;
17 Sala 2: al patrulea şi al doilea profesor programaţi ı̂ntre 1, 5
respectiv 9, 13;

Timp maxim de executare/test: 0.2 secunde


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

7.1.1 Indicaţii de rezolvare

prof. Daniela Lica, Centrul Judetean de Excelenta Prahova

Prima cerinta poate fi rezolvata printr-un algoritm, care presuspune sortarea inter
dupa momentul de sfarsit al cursurilor. Dupa aceasta operatie, se parcurg toate cur
si pentru fiecare se verifica daca poate fi sustinut, adica daca exista vreo sala c
libera la momentul de inceput al cursului. Daca exista mai multe sali disponibile,
va fi programat in sala care minimizeaza durata cand aceasta ramane neocupata, adic
in care cursul anterior programat se termina cel mai tarziu. Complexitate O(N*N +N*

A doua cerinta se poate rezolva astfel:


Metoda 1) Sortam crescator dupa momentul de intrare, ocupam cele K sali cu primele
(cele care debuteaza cel mai devreme) apoi traversam cursurile ramase, din K in K,
maxima comuna fiind obtinuta ca diferenta minima intre momentele de inceput a cursu
A[i] si A[i-K], oricare K < i <= N.
Metoda 2) Cautam binar durata maxima a cursurilor, astfel incat toate cele N cursur
se poata desfasura. Complexitatea algoritmului fiind O(N*N *log Durata_max).
------------------------------------ ??? ’’log’’ la clasa a 7-a ??? ---------------

7.1.2 Cod sursă

Listing 7.1.1: cursuri Dan 100p.cpp


#include <bits/stdc++.h>

#define nmax 1005


#define inFile "cursuri.in"
#define outFile "cursuri.out"

using namespace std;

struct Interval
{
int a, b;
};

Interval t[nmax];

// dmax[i] = timpul final al ultimului interval


// pus in sala i, i=1..K
CAPITOLUL 7. OJI 2017 7.1. CURSURI - OJI 2017 101

int d[nmax];

int n, K, op, M;

void Citire()
{
int i;
ifstream fin(inFile);
fin >> op;
fin >> n >> K;
for (i = 1; i <= n; i++)
{
fin >> t[i].a >> t[i].b;
M = max(M, t[i].b - t[i].a);
}
fin.close();
}

inline bool Cmp(const Interval X, const Interval Y)


{
if (X.b == Y.b) return X.a < Y.a;
return X.b < Y.b;
}

void Optiune1()
{
int i, j, p, ans, D;
sort(t + 1, t + n + 1, Cmp);
ans = 0;
for (i = 1; i <= n; i++)
{
// caut d[p] maxim, p=1..K
// cat mai aproape de capatul stang al intervalului i
p = 0; D = -1;
for (j = 1; j <= K; j++)
if (d[j] <= t[i].a && d[j] > D)
{
p = j;
D = d[j];
}
if (p != 0)
{
d[p] = t[i].b;
ans++;
}
}

ofstream fout(outFile);
fout << ans << "\n";
fout.close();
}

// returneaza 1 daca toate intervalele (de lungime L)


// se incadreaza in K clase
int Verifica(int L)
{
int i, j, p;
for (i = 1; i <= n; i++)
t[i].b = t[i].a + L;

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


d[i] = 0;

sort(t + 1, t + n + 1, Cmp);

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


{
// caut d[p] minim, p=1..K
p = 1;
for (j = 2; j <= K; j++)
if (d[p] > d[j]) p = j;

if (d[p] <= t[i].a)


d[p] = t[i].b;
else
return 0;
CAPITOLUL 7. OJI 2017 7.1. CURSURI - OJI 2017 102

return 1;
}

void Optiune2()
{
int st, dr, L, sol;
st = 1;
dr = M;
sol = 0;
while (st <= dr)
{
L = (st + dr) / 2;
if (Verifica(L))
{
sol = L;
st = L + 1;
}
else
dr = L - 1;
}

ofstream fout(outFile);
fout << sol << "\n";
fout.close();
}

int main()
{
Citire();
if (op == 1)
Optiune1();
else
Optiune2();

return 0;
}

Listing 7.1.2: cursuri NlogN.cpp


//Dana Lica
#include <cstdio>
#include <cstring>
#include <algorithm>

#define inf 100001


#define Nmax 1010

using namespace std;

struct curs {int first; int second;};

curs A[Nmax], B[Nmax];


int N, K, T, Max, Lg;

bool cmps(curs x, curs y){ return x.second < y.second;}

bool cmpf(curs x, curs y){ return x.first< y.first;}

int solve_1(curs A[Nmax]){


int nr = 1, Sala[Nmax], Min, poz;
memset(Sala,0,sizeof(Sala));

sort(A + 1, A + N +1, cmps);

Sala[1]=A[1].second;
for(int i=2; i<=N; i++)
{
Min = inf;
for(int j=1; j<=K; j++)
if (A[i].first>=Sala[j] && Min> A[i].first - Sala[j])
{
Min = A[i].first - Sala[j];
poz = j;
CAPITOLUL 7. OJI 2017 7.1. CURSURI - OJI 2017 103

if(Min!= inf){ nr++; Sala[poz]=A[i].second;}


}

return nr;
}

int solve_2(curs A[Nmax])


{
curs B[Nmax]; int i, tmax=0, ans;

sort (A + 1, A + N + 1, cmpf);

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


tmax = max(tmax, A[i].second - A[i].first);

ans = tmax;
for(i=K+1; i<=N; ++i)
ans = min(ans, A[i].first - A[i-K].first);

return ans;
}

int main()
{
freopen("cursuri.in", "r", stdin);
freopen("cursuri.out", "w", stdout);

scanf("%d\n%d %d\n", &T, &N, &K);

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


scanf("%d %d\n", &A[i].first, &A[i].second);

if (T==1)
printf("%d\n", solve_1(A));
else
printf("%d\n", solve_2(A));

return 0;
}

Listing 7.1.3: cursuri NxN.cpp


//Dana Lica
#include <cstdio>
#include <cstring>
#include <algorithm>

#define inf 100001


#define Nmax 1010

using namespace std;

struct curs {int first; int second;};

curs A[Nmax];

int N, K, T, Max, Lg;

void sortN2_second(curs A[Nmax])


{
curs aux;

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


for(int j=i+1; j<=N; j++)
if(A[i].second>A[j].second ||
A[i].second== A[j].second && A[i].first>A[j].first)
aux=A[i], A[i]=A[j], A[j]=aux;
}

void sortN2_first(curs A[Nmax])


{
curs aux;
CAPITOLUL 7. OJI 2017 7.1. CURSURI - OJI 2017 104

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


for(int j=i+1; j<=N; j++)
if(A[i].first>A[j].first)
aux=A[i], A[i]=A[j], A[j]=aux;
}

int solve_1(curs A[Nmax])


{
int nr = 1, Sala[Nmax], Min, poz;

memset(Sala,0,sizeof(Sala));

sortN2_second(A);

Sala[1]=A[1].second;
for(int i=2; i<=N; i++)
{
Min = inf;
for(int j=1; j<=K; j++)
if (A[i].first>=Sala[j] && Min> A[i].first - Sala[j])
{
Min = A[i].first - Sala[j];
poz = j;
}

if(Min!= inf){ nr++; Sala[poz]=A[i].second;}


}

return nr;
}

int solve_2(curs A[Nmax])


{
int i, tmax=0, ans;

sortN2_first(A);

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


tmax = max(tmax, A[i].second - A[i].first);

ans = tmax;
for(i=K+1; i<=N; ++i)
ans = min(ans, A[i].first - A[i-K].first);

return ans;
}

int main()
{
freopen("cursuri.in", "r", stdin);
freopen("cursuri.out", "w", stdout);

scanf("%d\n%d %d\n", &T, &N, &K);

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


scanf("%d %d\n", &A[i].first, &A[i].second);

if (T==1)
printf("%d\n", solve_1(A));
else
printf("%d\n", solve_2(A));

return 0;
}

Listing 7.1.4: cursuri raluca 100p.cpp


//Raluca Costineanu
#include <fstream>

using namespace std;

ifstream f("cursuri.in");
ofstream g("cursuri.out");
CAPITOLUL 7. OJI 2017 7.1. CURSURI - OJI 2017 105

int cerinta, n, k, dmax;

struct curs
{
int oi,of;
bool p;
}a[1010];

void sort()
{
int i, ok, poz, nn=n;
curs x;
do
{
ok=1;
for(i=1;i<nn; i++)
if(a[i].of>a[i+1].of)
x=a[i], a[i]=a[i+1], a[i+1]=x, poz=i, ok=0;
nn=poz;
} while(!ok);
}

int cerinta1()
{
int cate=0, i, j;
curs sal[1010];

for(i=0;i<=k;i++)
sal[i].oi=sal[i].of=0;

for(i=1;i<=n;i++)
{
int p=0;
for(j=1;j<=k;j++)
if(a[i].oi>=sal[j].of && a[i].oi-sal[j].of<=a[i].oi-sal[p].of)
p=j;

if(p)
sal[p]=a[i], cate++;
}

return cate;
}
bool verif(int x)
{
int i;

for(i=1;i<=n;i++)
a[i].of=a[i].oi+x, a[i].p=false;

sort();

if(cerinta1()==n)
return true;

return false;
}

int cerinta2()
{
int i, gasit=1;

for(i=1;i<=n;i++)
if(a[i].of-a[i].oi>dmax)
dmax=a[i].of-a[i].oi;

int s=1, d=dmax, m;

while(s<=d)
{
m=(s+d)/2;
if(verif(m))
gasit=m, s=m+1;
else
d=m-1;
}
CAPITOLUL 7. OJI 2017 7.1. CURSURI - OJI 2017 106

return gasit;
}

int main()
{
int i;
f>>cerinta>>n>>k;

for(i=1;i<=n;i++)
f>>a[i].oi>>a[i].of, a[i].p=false;

sort();

if(cerinta==1)
g<<cerinta1();
else
g<<cerinta2();

return 0;
}

Listing 7.1.5: cursuri rm.cpp


// Rotar Mircea
#include <bits/stdc++.h>

using namespace std;

struct curs
{
int a,b;
};

bool cmp(const curs X, const curs Y)


{
if (X.a == Y.a) return X.b > Y.b;

return X.a > Y.a;


}

int caut_bin(int v[],int n, int x)


{
int i = 0;
for(int step = (1<<20)/*2ˆ20*/; step > 0; step /= 2)
if(i+step < n && v[i+step] <= x)
i += step;
return i;
}

int nr_suprapuneri(curs st[1010], const int n, const int maxn)


{
static int arr[2500000 + 100];

for(int i = 0; i < n; ++i)


++arr[st[i].a], --arr[st[i].b];

for(int i = 1; i < maxn + 100; ++i)


arr[i] += arr[i-1];

int best = 0;
for(int i = 0; i < maxn +100; ++i)
best = max(best, arr[i]), arr[i] = 0;

return best;
}

int nr_cursuri(curs v[1010], const int n, const int k)


{
static curs arr[2020];
int poz = 0;

for(int i = 0; i < n; ++i)


if(arr[poz++] = v[i], nr_suprapuneri(arr, poz, 2010) > k)
--poz;
CAPITOLUL 7. OJI 2017 7.1. CURSURI - OJI 2017 107

return poz;
}

void elimin_duplicat(int a[],int &len)


{
int dup=0;

for(int i=1;i<len;i++)
{
if(a[i-1]==a[i])
{
dup++;
}
else
{
a[i-dup]=a[i];
}
}

len=len-dup;
}

int main()
{
ifstream f("cursuri.in");
ofstream g("cursuri.out");

int t, n, k;

f >> t >> n >> k;

curs v[1010];

for(int i = 0; i < n; ++i)


f >> v[i].a >> v[i].b;

if(t == 1)
{
static int norm[2020], len = 2*n;

for(int i = 0; i < n; ++i)


norm[2*i] = v[i].a, norm[2*i+1] = v[i].b;

sort(norm, norm+len);

//elimina duplicatele cu exceptia primului


// len = unique(norm, norm+len)-norm;

elimin_duplicat(norm,len); //intr-o secv de elem consecutive

for(int i = 0; i < n; ++i)


{
v[i].a = caut_bin(norm,len,v[i].a);
v[i].b = caut_bin(norm,len,v[i].b);
}

sort(v, v+n, cmp);

g << nr_cursuri(v, n, k);


}
else
{
int rez,st=1, dr=0, mij;
for(int i = 0; i < n; ++i)
dr = max(dr, v[i].b - v[i].a);

while(st<=dr)
{
mij=st+(dr-st)/2;
for(int i = 0; i < n; ++i)
v[i].b = v[i].a + mij;
if(nr_suprapuneri(v, n, 100000 + 10) <= k)
rez=mij, st=mij+1;
else
dr=mij-1;
CAPITOLUL 7. OJI 2017 7.2. JOC - OJI 2017 108

g<<rez<<endl;
}

return 0;
}

7.1.3 *Rezolvare detaliată

7.2 joc - OJI 2017


Problema 2 - joc 90 de puncte
Inspiraţi de clasicul joc Tic-Tac-Toe (X şi 0), Teodora şi ştefan ı̂şi propun
să joace ceva asemănător, adăugând jocului clasic câteva reguli noi:
- tabla de joc este un pătrat de latură N , care este ı̂mpărţit ı̂n N ˜ N
celule, aşezate pe N linii şi N coloane; celulele pătratului sunt numerotate
2
de la 1 la N parcurgând liniile de sus ı̂n jos, şi coloanele de la stânga la
dreapta;
- Teodora va marca celulele cu X (litera X), iar Ştefan cu 0 (cifra 0);
Figura 7.1: joc1
- ı̂n cadrul unei runde, copiii marchează alternativ câte o celulă din pătrat,
nemarcată anterior;
2
- o rundă a jocului este descrisă printr-un şir format din exact N numere
naturale reprezentând celulele pătratului, ı̂n ordinea ı̂n care au fost marcate
succesiv de cei doi copii;
- jocul are K runde; prima este ı̂ncepută de Teodora, a doua de Ştefan,
a treia Teodora, a patra Ştefan şi aşa mai departe;
- o rundă este câştigată de jucătorul care reuşeşte primul să marcheze
complet o linie, o coloană, diagonala principală sau una din cele două semidi-
Figura 7.2: joc2
agonale paralele şi alăturate cu aceasta (figura 1), diagonala secundară sau
una din cele două semidiagonale paralele şi alăturate acesteia (figura 2);
2
- o rundă se ı̂ncheie fără un câştigător dacă după marcarea celor N celule nu există pe tabla
de joc nicio linie, coloană, diagonală sau semidiagonală marcate cu acelaşi simbol.

Cerinţe

Cunoscând numerele N , K şi cele K şiruri de numere care reprezintă rundele jucate, scrieţi
un program care să rezolve una dintre următoarele două cerinţe:
1. Să se determine câte runde a câştigat fiecare copil.
2. Să se determine care este cel mai mare număr de marcări efectuate până la câştigarea unei
runde.

Date de intrare

Fişierul de intrare joc.in conţine pe prima linie un număr natural C. Pentru toate testele, C
poate lua numai valorile 1 sau 2. Pe a doua linie se află două numere naturale N şi K, separate
prin câte un spaţiu, reprezentând dimensiunea tablei de joc şi respectiv numărul de runde jucate.
Pe următoarele K linii sunt descrise rundele de joc, câte o rundă pe câte o linie a fişierului. În
cadrul liniilor, numerele sunt separate prin câte un spaţiu.

Date de ieşire

Dacă valoarea lui C este 1, se va rezolva numai punctul 1) din cerinţe. În acest caz, fişierul
de ieşire joc.out va conţine pe prima linie două numere naturale t şi s, separate printr-un spaţiu,
unde t reprezintă numărul de runde câştigate de Teodora, iar s numărul rundelor câştigate de
Ştefan.
Dacă valoarea lui C este 2, se va rezolva numai punctul 2) din cerinţe. În acest caz, fişierul
de ieşire joc.out va conţine pe prima linie numărul cel mai mare de marcări efectuate până la
câştigarea unei runde.
CAPITOLUL 7. OJI 2017 7.2. JOC - OJI 2017 109

Restricţii şi precizări

a 3 & N & 100


a 1 & K & 25
a La fiecare joc se câştigă cel puţin o rundă.
a Pentru rezolvarea corectă a primei cerinţe se acordă 45 de puncte, iar pentru rezolvarea
corectă a celei de a doua cerinţe se acordă 45 de puncte. Se acordă 10 puncte din oficiu.

Exemple

joc.in joc.out Explicaţii


1 21 Ilustrarea rundelor de joc până la mo-
44 mentul identificării unui câştigător este
16 13 15 9 10 1 5 2 6 14 3 7 11 4 8 12 următoarea:
1 2 3 4 5 6 7 8 12 11 10 9 13 14 15 16
1 5 9 6 2 7 3 8 4 10 11 12 13 14 15 16
1 2 3 4 8 7 6 5 12 11 10 9 16 15 14 13

Figura 7.3: Puzzle

joc.in joc.out Explicaţii


2 14 Doar 3 dintre cele 4 runde jucate au fost
44 câştigate. Până ı̂n momentul câştigării ı̂n
16 13 15 9 10 1 5 2 6 14 3 7 11 4 8 12 prima rundă s-au făcut 7 marcări, ı̂n a doua
1 2 3 4 5 6 7 8 12 11 10 9 13 14 15 16 14, iar ı̂n a treia 8. Deci numărul maxim
1 5 9 6 2 7 3 8 4 10 11 12 13 14 15 16 de marcări făcute până la câştigarea unei
1 2 3 4 8 7 6 5 12 11 10 9 16 15 14 13 runde este 14.

Timp maxim de executare/test: 0.3 secunde


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

7.2.1 Indicaţii de rezolvare

prof. Costineanu Raluca, Colegiul National ’’Stefan cel Mare’’ Suceava

O solutie posibila ar fi contorizarea pe parcursul desfasurarii rundelor de joc a n


de celule marcate de fiecare jucator pe fiecare linie/coloana/diagonala/semidiagona

Pentru a simplifica retinerea datelor definim structura ’’marcare’’ ale carei campu
memora numarul de celule marcate de fiecare jucator, corespunzator liniei, coloanei
diagonalei sau semidiagonalei din care face parte.

Jucatorului Teodora ii va corespunde jucator[0], iar lui stefan jucator[1]


Pentru fiecare din cele K runde de joc:
- stabilim jucatorul care incepe runda
CAPITOLUL 7. OJI 2017 7.2. JOC - OJI 2017 110

- initializam cu 0 campurile corespunzatoare celor 2 jucatori


- citim cele n*n numere corespunzatoare rundei si pentru fiecare numar citit,
pana se ajunge la castigarea rundei:
-- determinam linia si coloana pe tabla de joc corespunzatoare numarului cit
-- actualizam corespunzator linia/coloana/digonala/semidiagonala marcata
-- verificam daca marcarea curenta a condus la finalizarea rundei, caz in ca
--- actualizam numarul corespunzator castigatorului
--- verificam daca numarul de marcari facute pana la castigarea runde
curente este mai mare decat maximul si in caz afirmativ actualiza
valoarea maximului
--- continuam doar cu citirea numerelor pana la sfarsitul runde

O solutie in care se simuleaza jocul prin costruirea matricei corespunzatoare table


si in care se verifica la fiecare marcare daca runda a fost castigata poate obtine
partial de maximum 40 de puncte.

7.2.2 Codul sursă

Listing 7.2.1: joc.cpp


#include <fstream>

using namespace std;

struct marcare
{
int l[151], c[151];
int dp, dp1, dp2;
int ds, ds1, ds2;
} jucator[2];

int cT, cS, maxim, jucatorul;

int main()
{
ifstream f("joc.in");
ofstream g("joc.out");

int n, k, i, nr, j, m, lin, col, p, mutari, joc, gata, c;

f>>c>>n>>k;
m=n*n;
maxim=-1;

for(i=1;i<=k;i++)
{
j=i%2;//jucatorul care incepe runda
gata=0;
for(joc=0;joc<2;joc++)
{
for(p=1;p<=n;p++)
jucator[joc].l[p]=jucator[joc].c[p]=0;

jucator[joc].dp=jucator[joc].dp1=jucator[joc].dp2=0;
jucator[joc].ds=jucator[joc].ds1=jucator[joc].ds2=0;
}

mutari=m+1;
for(p=1; p<=m; p++)
{
joc=(j+p%2)%2;//jucatorul care marcheaza 0=T, 1=S
f>>nr;

if(gata==0) //runda nu s-a incheiat


{ //determin linia si coloana celulei marcate
lin=nr/n;
col=nr%n;
CAPITOLUL 7. OJI 2017 7.2. JOC - OJI 2017 111

if(col==0)
col=n;
else
lin++;

//actualizez corespunzator nr celulelor marcate


jucator[joc].l[lin]++;
jucator[joc].c[col]++;
if(lin==col)
jucator[joc].dp++;
else
if (lin==col+1)
jucator[joc].dp1++;
else
if(lin+1==col)
jucator[joc].dp2++;

if(lin+col==n+1)
jucator[joc].ds++;
else
if (lin+col==n)
jucator[joc].ds1++;
else
if(lin+col==n+2)
jucator[joc].ds2++;

//verific daca runda s-a incheiat


if(jucator[joc].l[lin]==n ||
jucator[joc].c[col]==n ||
jucator[joc].dp==n ||
jucator[joc].dp1==n-1||
jucator[joc].dp2==n-1||
jucator[joc].ds==n ||
jucator[joc].ds1==n-1 ||
jucator[joc].ds2==n-1)
{
gata=1;
if(joc==0)
cT++;
else
cS++;

mutari=p;
}
}
}

if(mutari>maxim && mutari<m+1)


maxim=mutari;
}

if(c==1)
g<<cT<<’ ’<<cS<<’\n’;
else
g<<maxim<<’\n’;

f.close();
g.close();
return 0;
}

Listing 7.2.2: joc dl.cpp


//Dana Lica
#include <cstdio>
#include <algorithm>
#include <cstring>

#define Nmax 155

using namespace std;

int diag1T[5], diag1S[5], diag2T[5], diag2S[5],


LT[Nmax], LS[Nmax], CT[Nmax], CS[Nmax];
int N, K, test, T, winS, winT, i, x, moves;
CAPITOLUL 7. OJI 2017 7.2. JOC - OJI 2017 112

bool win;

bool addT(int x)
{
if(win) return false;

int linie, coloana, dif;

if(x%N==0)
linie = x/N, coloana = N;
else
linie = x/N+1, coloana = x%N;

++LT[linie];
if(LT[linie]==N)
return true;

++CT[coloana];
if(CT[coloana]==N)
return true;

dif = linie-coloana+2;

if(dif>=1 && dif<=3)


{
++diag1T[dif];
if((dif==2 && diag1T[dif]==N) ||
(dif!=2 && diag1T[dif]==N-1))
return true;
}

dif = linie+coloana-(N-1);
if(dif>=1 && dif<=3)
{
++diag2T[dif];
if((dif==2 && diag2T[dif]==N) ||
(dif!=2 && diag2T[dif]==N-1))
return true;
}

return false;
}

bool addS(int x)
{
if(win) return false;

int linie, coloana, dif;

if(x%N==0)
linie = x/N, coloana = N;
else
linie = x/N+1, coloana = x%N;

++LS[linie];
if(LS[linie]==N) return true;

++CS[coloana];
if(CS[coloana]==N) return true;

dif = linie-coloana+2;
if(dif>=1 && dif<=3)
{
++diag1S[dif];
if((dif==2 && diag1S[dif]==N) ||
(dif!=2 && diag1S[dif]==N-1))
return true;
}

dif = linie+coloana-(N-1);
if(dif>=1 && dif<=3)
{
++diag2S[dif];
if((dif==2 && diag2S[dif]==N) ||
(dif!=2 && diag2S[dif]==N-1))
return true;
CAPITOLUL 7. OJI 2017 7.2. JOC - OJI 2017 113

return false;
}

void initialize()
{
memset(LT, 0, sizeof(LT));
memset(LS, 0, sizeof(LS));
memset(CT, 0, sizeof(CT));
memset(CS, 0, sizeof(CS));
memset(diag1T, 0, sizeof(diag1T));
memset(diag1S, 0, sizeof(diag1S));
memset(diag2T, 0, sizeof(diag2T));
memset(diag2S, 0, sizeof(diag2S));
}

int main()
{
freopen("joc.in", "r", stdin);
freopen("joc.out", "w", stdout);

scanf("%d\n", &T);
scanf("%d %d\n", &N, &K);

for(test=1; test<=K; ++test)


{
initialize();
win = 0;
for(i=1; i<=N*N; ++i)
{
scanf("%d", &x);

if((test%2==1 && i%2==1) || (test%2==0 && i%2==0))


{
if(addT(x))
{
++winT;
win = 1;
moves = max(moves, i);
}
}
else
if(addS(x))
{
++winS;
win = 1;
moves = max(moves, i);
}
}
}

if(T==1)
printf("%d %d\n", winT, winS);
else
printf("%d\n", moves);

return 0;
}

Listing 7.2.3: joc pracsiu.cpp


#include <bits/stdc++.h>

#define nmax 155

using namespace std;

ifstream fin("joc.in");
ofstream fout("joc.out");

struct scor
{
int A, B;
};
CAPITOLUL 7. OJI 2017 7.2. JOC - OJI 2017 114

int n, N; /// N = n * n
scor a[nmax * nmax];
scor L[nmax], C[nmax], DP[3], DS[3];
/**
a[i] =(x,y) : coordonatele din matrice corespunzatoare
valorii i, unde i=1..n*n
L[i]=(x, y) : pe linia i sunt puse x X-uri si y zeroruri
D[i]=(x, y) : pe coloana i sunt puse x X-uri si y zeroruri
DP[i]=(x,y) : pe diagonalele paralele cu diagonala principala
sunt puse x X-uri si y zeroruri
DS[i]=(x,y) : pe diagonalele paralele cu diagonala secundara
sunt puse x X-uri si y zeroruri
*/

void Matrice()
{
int i, j, p;
p = 0;
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
{
p++;
a[p].A = i;
a[p].B = j;
}
}

void Runda(int r, int &whoWins, int &nrMoves)


{
int i, laMutare, x, lin, col;
for (i = 1; i <= n; i++)
L[i].A = L[i].B = C[i].A = C[i].B = 0;
for (i = 0; i <= 2; i++)
DP[i].A = DP[i].B = DS[i].A = DS[i].B = 0;
whoWins = -1;
nrMoves = 0;
laMutare = 1 - r % 2;
for (i = 1; i <= N; i++)
{
fin >> x;
if (whoWins >= 0) continue;
nrMoves++;
lin = a[x].A;
col = a[x].B;

/// actualizare scor pe linia lin


if (laMutare == 0) L[lin].A++;
else L[lin].B++;
if (L[lin].A == n) {whoWins = 0; continue;}
else if (L[lin].B == n) {whoWins = 1; continue;}

/// actualizare scor pe coloana col


if (laMutare == 0) C[col].A++;
else C[col].B++;
if (C[col].A == n) {whoWins = 0; continue;}
else if (C[col].B == n) {whoWins = 1; continue;}

/// actualizare scor pe diag. principala


if (lin == col)
{
if (laMutare == 0) DP[1].A++;
else DP[1].B++;
if (DP[1].A == n) {whoWins = 0; continue;}
else if (DP[1].B == n) {whoWins = 1; continue;}
}

/// actualizare scor sub diag. principala; (i+1,i)


if (lin == col + 1)
{
if (laMutare == 0) DP[0].A++;
else DP[0].B++;
if (DP[0].A == n - 1) {whoWins = 0; continue;}
else if (DP[0].B == n - 1) {whoWins = 1; continue;}
}
CAPITOLUL 7. OJI 2017 7.2. JOC - OJI 2017 115

/// actualizare scor deasupra diag. principala; (i-1,i)


if (lin == col - 1)
{
if (laMutare == 0) DP[2].A++;
else DP[2].B++;
if (DP[2].A == n - 1) {whoWins = 0; continue;}
else if (DP[2].B == n - 1) {whoWins = 1; continue;}
}

/// actualizare scor pe diag. secundara


if (lin + col == n + 1)
{
if (laMutare == 0) DS[1].A++;
else DS[1].B++;
if (DS[1].A == n) {whoWins = 0; continue;}
else if (DS[1].B == n) {whoWins = 1; continue;}
}

/// actualizare scor sub diag. secundara; (lin+col=n+2)


if (lin + col == n + 2)
{
if (laMutare == 0) DS[0].A++;
else DS[0].B++;
if (DS[0].A == n - 1) {whoWins = 0; continue;}
else if (DS[0].B == n - 1) {whoWins = 1; continue;}
}

/// actualizare scor deasupra diag. secundara; (lin+col=n)


if (lin + col == n)
{
if (laMutare == 0) DS[2].A++;
else DS[2].B++;
if (DS[2].A == n - 1) {whoWins = 0; continue;}
else if (DS[2].B == n - 1) {whoWins = 1; continue;}
}
laMutare = 1 - laMutare;
}
}

int main()
{
int nrMoves, whoWins, r, op, K;
int scorA, scorB, maxMoves;

fin >> op;


fin >> n >> K;
N = n * n;

Matrice();

scorA = scorB = maxMoves = 0;


for (r = 1; r <= K; r++)
{
Runda(r, whoWins, nrMoves);
if (whoWins == 0) scorA++;
else if (whoWins == 1) scorB++;
if (whoWins != -1) maxMoves = max(maxMoves, nrMoves);
}

if (op == 1)
fout << scorA << " " << scorB << "\n";
else
fout << maxMoves << "\n";

fout.close();
return 0;
}

Listing 7.2.4: joc rm.cpp


///prof. Rotar Mircea
#include <fstream>

using namespace std;


CAPITOLUL 7. OJI 2017 7.2. JOC - OJI 2017 116

ifstream f("joc.in");
ofstream g("joc.out");

int v,n,k;

void zero(auto &a,int n)


{
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
a[i][j]=0;
}
}
}

int test(auto a, int x, int y, int p)


{
int lin=1,col=1,d1=1,d2=1,dif,i1,j1;

for(int j=1;j<=n;j++) ///linia


{
if(a[x][j]!=p){
lin=0;
break;
}
}

if(lin==1) return 1;
for(int i=1;i<=n;i++) ///coloana
{
if(a[i][y]!=p){
col=0;
break;
}
}

if(col==1) return 1;

dif = y-x; ///diag principala


if(dif>=-1 && dif<=1)
{
if(dif>=0)
{
i1=1; j1=1+dif;
}
else
{
dif=(-1)*dif;
i1 = 1 + dif; j1=1;
}

for(int i=0;i<=n-dif-1;i++)
{
if(a[i1+i][j1+i]!=p)
{
d1=0; break;
}
}
}
else
{
d1=0;
}

if(d1==1) return 1;

dif=x-(n-y+1); ///diag secundara


if(dif>=-1 && dif<=1)
{
if(dif<=0)
{
dif=dif*(-1);
i1=1; j1=n-dif;
}
else
CAPITOLUL 7. OJI 2017 7.2. JOC - OJI 2017 117

{
i1=1+dif; j1=n;
}

for(int i=0;i<=n-dif-1;i++)
{
if(a[i1+i][j1-i]!=p)
{
d2=0; break;
}
}
}
else
{
d2 = 0;
}

if(d2==1) return 1;

return 0;
}

void incepe_jocul()
{
int x,lin,col,contor=0,contormax=0,primul=1,poz1=1,poz2=2;
int crt,Teo=0,Stef=0;

f>>v>>n>>k;

///vector<vector<int>> a(n+1,vector<int>(n+1,0));
int a[60][60];

while(f>>x)
{
contor++;
(contor%2==0) ? crt=poz2 : crt=poz1;

lin=(x-1)/n+1;

(x%n==0) ? col=n: col=x%n;

a[lin][col]=crt;

if(test(a,lin,col,crt)==1)
{
(crt==1) ? Teo++ : Stef++;

contormax=max(contor,contormax);

for(int i=1;i<=n*n-contor;++i)
f>>x;
if(primul==1)
{
primul=2;
poz1=2; poz2=1;
}
else
{
primul=1;
poz1=1; poz2=2;
}

zero(a,n);
contor=0;
}
}

if(v==1)
{
g<<Teo<<" "<<Stef<<"\n";
}
else
{
g<<contormax<<"\n";
}
}
CAPITOLUL 7. OJI 2017 7.2. JOC - OJI 2017 118

int main()
{
incepe_jocul();
return 0;
}

7.2.3 *Rezolvare detaliată


Capitolul 8

OJI 2016

8.1 axyz - OJI 2016


Problema 1 - axyz 1000 de puncte
Se consideră numerele naturale A (format din două sau trei cifre, toate distincte şi nenule) şi
X (format din N cifre, toate nenule).
Din numărul X, folosind toate cele N cifre ale sale, se poate construi un cel mai mare număr
natural Y strict mai mic decât X. De exemplu, pentru X 121621 se construieşte Y 121612.
Tot din numărul X, se poate obţine numărul A prin ştergerea unor cifre din scrierea lui X şi
alipirea celor rămase, fără a le schimba ordinea. De exemplu, dacă X 121621 şi A 12, există
Z 3 posibilităţi distincte prin care să obţinem numărul A din X şi anume: 1) 121621; 2)
1216 21; 3) 12 16 21.
Cerinţe

Cunoscându-se numerele A, N şi cele N cifre ale lui X, să se determine:


1. cel mai mare număr natural Y , strict mai mic decât X, care se poate obţine rearanjând
cifrele lui X;
2. numărul maxim Z de posibilităţi distincte prin care se poate obţine numărul A din numărul
X prin ştergerea unor cifre şi alipirea celor rămase, fără a le schimba ordinea.

Date de intrare

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


- pe prima linie un număr natural p; pentru toate testele de intrare, numărul p poate avea doar
valoarea 1 sau valoarea 2;
- pe a doua linie, numărul A, cu semnificaţia din enunţ;
- pe a treia linie numărul de cifre ale numărului X;
- pe a patra linie, un şir de N cifre, separate prin câte un spaţiu, reprezentând cifrele numărului
X, ı̂n această ordine.

Date de ieşire

a Dacă valoarea lui p este 1, atunci se va rezolva numai cerinţa 1. În acest caz, fişierul de ieşire
axyz.out va conţine pe prima linie un şir de cifre reprezentând numărul natural Y determinat
(răspunsul la cerinţa 1).
a Dacă valoarea lui p este 2, atunci se va rezolva numai cerinţa 2. În acest caz, fişierul de
ieşire axyz.out va conţine pe prima linie un număr natural reprezentând numărul Z determinat
(răspunsul la cerinţa 2).

Restricţii şi precizări

a 12 & A & 987


a 10 & N & 30000
a Pentru toate datele de test, numerele Y şi A pot fi obţinute din numărul X
a Pentru rezolvarea corectă a cerinţei 1 se acordă 30% din punctaj, iar pentru rezolvarea
corectă a cerinţei 2 se acordă 70% din punctaj.

119
CAPITOLUL 8. OJI 2016 8.1. AXYZ - OJI 2016 120

Exemple
axyz.in axyz.out Explicaţii
1 121612 Se rezolvă cerinţa 1.
12 A=12, N=6, X=121621
6 Cel mai mare număr Y strict mai mic ca X este: Y=121612.
121621
2 3 Se rezolvă cerinţa 2. A=12, N=6, X=121621
12 Sunt Z=3 posibilităţi distincte prin care se obţine numărul A
6 din X: 1) 121621; 2) 1216 21; 3) 12 16 21
121621

Timp maxim de executare/test: 0.1 secunde


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

8.1.1 Indicaţii de rezolvare

Autor prof. Carmen Minca


Colegiul National de Informatica ’’Tudor Vianu’’, Bucuresti

Descrierea solutiei

Cerinta 1 (30 puncte)

O solutie se poate obtine astfel:

- Parcurgem sirul cifrelor de la pozitia N catre prima, intrerupand parcurgerea la


cifra X[k] cu proprietatea ca:
X[k]>X[k+1] <= X[k+2] <=...<= X[N]

- Determinam pozitia poz a celei mai mari cifre dintre cele situate pe pozitiile
k+1,k+2,...,N si cu proprietatea X[k]>X[poz]

- Interschimbam cifrele X[k] si X[poz].

- Sortam descrescator in vectorul X cifrele de pe pozitiile k+1,k+2,...,N restul nu


nemodificandu-se. Complexitatea este data de algoritmul de sortare folosit,
maximum O(N2) - solutia obtine un punctaj partial

- Pentru a obtine o complexitate liniara (O(N)), ne vom folosi de faptul ca in vect


cifrele de pe pozitiile k+1,k+2,...,N sunt sortate crescator. Nu are sens sa le sor
descrescator, deoarece le putem scrie in fisierul de iesire in ordine inversa, solu
cautata fiind X[1], X[2],..., X[K], X[N], X[N-1],..., X[K+1]

Cerinta 2 (70 puncte)

O solutie cu punctaj maxim se poate obtine astfel:

2.1.Pentru A format din 2 cifre

-Separam cifrele lui A: ab, a=[A/10], b=A%10

-Parcurgem sirul cifrelor lui X de la pozitia N catre prima numarand aparitiile


cifrelor b in variabila NB (initial cu valoarea 0). Variabila Z va memora
numarul perechilor cerute (initial este 0).

-in timpul acestei parcurgeri, se studiaza fiecare cifra astfel:


- Daca cifra curenta este egala cu b atunci o numaram ( NB=NB+1).
CAPITOLUL 8. OJI 2016 8.1. AXYZ - OJI 2016 121

- Altfel, daca cifra curenta este a atunci se pot forma cu NB numere A din
cifra a curenta si cele NB cifre b situate la dreapta lui a in X. Adaugam
numarul acestora la celelalte gasite pana in acest moment (Z=Z+NB)

2.2.Pentru A format din 3 cifre

-Separam cifrele lui A: abc, a=[A/100], b=[A/10%10], c=A%10;

-Parcurgem sirul cifrelor lui X de la pozitia N catre prima numarand aparitiile


cifrelor c in variabila NC (initial cu valoarea 0). La intilnirea unei cifre b
se pot genera NC numere de forma bc, astfel vom incrementa variabila NBC
(initial cu valoarea 0) cu valoarea NC. La intilnirea unei cifre a se pot genera
NBC numere de forma abc, astfel vom incrementa variabila Z (initial cu valoarea 0)
cu valoarea NBC. Variabila Z va memora numarul perechilor cerute.

-in timpul acestei parcurgeri, se studiaza fiecare cifra astfel:


- Daca cifra curenta este egala cu c atunci o numaram ( NC=NC+1).
- Daca cifra curenta este egala cu b atunci se pot forma inca NC numere de
si adaugam acest numar la variabila NBC ( NBC=NBC+NC).
- Altfel, daca cifra curenta este a atunci se pot forma cu NBC numere A di
curenta si cele NBC numere bc situate la dreapta lui a in X. Adaugam numarul ace
la celelalte gasite pana in acest moment (Z=Z+NBC)

-Complexitate O(N)

8.1.2 Cod sursă

Listing 8.1.1: axyz.cpp


#include <fstream>

using namespace std;

int x[30001];
ifstream f("axyz.in");
ofstream g("axyz.out");

int main()
{
int N,A,B,C,NB,Z,i,k,j, poz, t,cer;
f>>cer>>A>>N;
for(i=1; i<=N; i++)
f>>x[i];
///cerinta 1) - problema are solutie pentru toate testele propuse
if(cer==1)
{
k=N-1;
while(k>0 && x[k]<=x[k+1])
k--;
poz=N;
while(poz>k && x[poz]>=x[k])
poz--;
swap(x[k],x[poz]);
for(i=1; i<=k; i++)
g<<x[i];
for(i=N; i>k; i--)
g<<x[i];
g<<’\n’;
}
else ///cerinta 2
if(A<100)
{
B=A%10;
A=A/10;
Z=0;
NB=0;
for(i=N; i>=1; i--)
CAPITOLUL 8. OJI 2016 8.2. GALERIE - OJI 2016 122

if(x[i]==B)NB++;
else if(x[i]==A)Z+=NB;
g<<Z<<’\n’;
}
else
{
int i,C,NC=0,NBC=0,Z=0;

C=A%10;
A/=10;
B=A%10;
A/=10;

for (i=N; i>=1;i--)


if(x[i]==C)
NC++;
else
if(x[i]==B)
NBC+=NC;
else
if(x[i]==A)
Z+=NBC;

g<<Z<<’\n’;
}

return 0;
}

8.1.3 *Rezolvare detaliată

8.2 galerie - OJI 2016


Problema 2 - galerie 100 de puncte
La ı̂ntâlnirea anuală a cârtiţelor, la concursul pentru selecţia noilor
membri ai consiliului director, a fost propusă următoarea problemă:
De jur-ı̂mprejurul unui teren dreptunghiular ı̂mpărţit ı̂n n  m celule
de formă pătrată, cu aceeaşi arie, cârtiţele au săpat galerii exterioare.
Celulele aflate pe marginea terenului sunt numerorate consecutiv, de
la 1 la 2 ˜ n  m, ı̂ncepând din colţul din stânga-sus, ca ı̂n imaginea
alăturată. În galeriile exterioare, pe marginea terenului, se află t cârtiţe
care sunt pregătite să sape galerii interioare. Cârtiţele aflate pe latura
de Nord a terenului se vor deplasa către Sud, cele care se află pe latura
de la Est se vor deplasa către Vest, cele care se află pe latura de la Sud
se vor deplasa către Nord, iar cele care se află pe latura de la Vest se
Figura 8.1: galerie
vor deplasa către Est.
Cârtiţele ı̂ncep să sape ı̂n acelaşi timp. ı̂n fiecare oră, o cârtiţă sapă ı̂ntr-o singură celulă a
terenului.
O cârtiţă se opreşte dacă:
a ajunge ı̂ntr-o altă galerie interioară; ea nu sapă ı̂n aceasta, iar galeria ei se uneşte cu cea ı̂n
care ajunge;
a ı̂n celula ı̂n care sapă, mai sapă şi alte cârtiţe, ı̂n aceeaşi oră; galeriile lor se unesc ı̂ntr-o
singură galerie şi toate aceste cârtiţe se opresc;
a ajunge pe marginea terenului, ı̂n partea opusă celei din care a plecat, galeria săpată de ea
până ı̂n acest moment comunicând acum cu galeria exterioară, ı̂n care ea nu sapă.
De exemplu, dacă pe marginea unui teren format din 7  5 celule, se află 5 cârtiţe, ı̂n celulele
3, 8, 10, 19 şi 23, atunci, după o oră, terenul are configuraţia din fig.1, după două ore, configuraţia
din fig.2, după trei ore, configuraţia din fig.3 (ultima cârtiţă ajunge ı̂n galeria primei cârtiţe şi
primele două cârtiţe sapă ı̂n aceeaşi celulă şi apoi se opresc), după 4 ore, configuraţia din fig.4,
după 5 ore, configuraţia din fig.5, când cele două cârtiţe rămase sapă pe marginea terenului şi apoi
se opresc pentru că au ajuns ı̂n galeria exterioară (fig.6). Galeriile acestora nu se unesc pentru că
niciuna dintre ele nu a intrat ı̂n galeria celeilalte şi nici nu s-au ı̂ntâlnit ı̂ntr-o celulă.
1‘
CAPITOLUL 8. OJI 2016 8.2. GALERIE - OJI 2016 123

Cerinţe

Cunoscându-se numerele n, m, t şi cele t celule exterioare ı̂n care se află cârtiţele, să se
determine:
1. numărul maxim de celule ı̂n care sapă o cârtiţă până la oprirea tuturor cârtiţelor;
2. numărul maxim de celule din care este formată o galerie interioară.

Date de intrare

Fişierul de intrare galerie.in conţine pe prima linie, una dintre valorile 1 sau 2 reprezentând
cerinţa 1, dacă se cere determinarea numărului maxim de celule ı̂n care sapă o cârtiţă până la
oprirea tuturor cârtiţelor, respectiv cerinţa 2, dacă se cere determinarea numărul maxim de celule
din care este formată o galerie interioară.
Linia a doua conţine, separate prin câte un spaţiu, trei numere naturale: n şi m (reprezentând
dimensiunile terenului) şi t (reprezentând numărul de cârtiţe aflate ı̂n galeriile exterioare).
Linia a treia conţine cele t numere naturale separate prin câte un spaţiu, reprezentând poziţiile
celor t cârtiţe.

Date de ieşire

Fişierul de ieşire galerie.out conţine pe prima linie o valoarea naturală reprezentând numărul
maxim de celule ı̂n care sapă o cârtiţă până la oprirea tuturor cârtiţelor, dacă cerinţa a fost 1,
respectiv un număr natural reprezentând numărul maxim de celule din care este formată o galerie
interioară, dacă cerinţa a fost 2.

Restricţii şi precizări

a 3 & n, m & 200; 1 & t & 2 ˜ n  m;


a ı̂ntr-o celulă numerotată, exterioară terenului, se poate afla o singură cârtiţă.
a Pentru rezolvarea corectă a cerinţei 1 se acordă 30% din punctaj, iar pentru rezolvarea
corectă a cerinţei 2 se acordă 70% din punctaj.

Exemple

galerie.in galerie.out Explicaţii


1 5 Cârtiţa care pleacă din poziţia 23 sapă ı̂n două celule; cârtiţele
755 care pleacă din poziţiile 3 şi 8, sapă ı̂n 3 celule, iar celelalte două
19 3 8 10 23 cârtiţe ı̂n 5 celule.
2 7 S-au format trei galerii interioare, două formate din câte 5 celule
755 şi o galerie interioară formată din 7 celule (fig.6).
19 3 8 10 23

Timp maxim de executare/test: 0.5 secunde


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

8.2.1 Indicaţii de rezolvare

Autor prof. Cristina Sichim Colegiul National ’’Ferdinand I’’, Bacau

Cerinta 1 ( 30 puncte)

Problema se restrange la fiecare pas la multimea cartitelor active (cele care sapa)

Pentru fiecare moment de timp

* se determina, pentru fiecare dintre cartitele active, celula in care ajunge;

* se elimina apoi din multimea cartitelor active, cartitele care se opresc, fie pen
CAPITOLUL 8. OJI 2016 8.2. GALERIE - OJI 2016 124

in celula in care au ajuns se mai afla si alte cartite, fie pentru ca au ajuns in
alta galerie (pe marginea terenului sau intr-o galerie interioara);

* se contorizeaza momentele de timp in care cel putin o cartita este activa.

Cerinta 2 ( 70 de puncte)

Pentru fiecare cartita ci, se memoreaza lungimea li a galeriei interioare in care s

Daca o cartita sapa singura intr-o celula, atunci, se aduna o unitate la lungimea
traseului sapat.

Daca o cartita c1 sapa intr-o galerie interioara, de lungime l1, si ajunge in galer
unei alte cartite c2, care se afla intr-o galerie interioara de lungime l2, atunci
cartita c1 se opreste, galeriile se unesc, si lungimea galeriei l2, in care se afl
cartita c2, devine l1+l2.

Daca intr-o celula ajung, in acelasi moment de timp, mai multe cartite, c1, c2, ...
atunci, toate cartitele se opresc si se formeaza o galerie de lungime l1+l2+...+lk.

Cand toate cartitele se opresc, se determina max{li | 1 <= i <= t}.

8.2.2 Cod sursă

Listing 8.2.1: galerie0.cpp


#include <fstream>
#include <iostream>
#include <set>

using namespace std;

ifstream f("galerie.in");
ofstream g("galerie.out");

int n,m,t,k,x,i,pas,inou,jnou,a[205][205],
di[]={-1,0,1,0},dj[]={0,1,0,-1},sapa_cineva;

struct cartita{int i,j,l,di,dj;}C[1000];

set<int> s;
set<int> sterse;
set<int>::iterator it;

set<int>::iterator it1;

int interior(int i, int j)


{ if(i*j==0)return 0;
if(i>n)return 0;
if(j>m)return 0;
return 1;
}

void uneste_galerii(int i,int j,int c1,int c2)


{
int k,i1,j1;

if(interior(i,j))
for(k=0;k<4;k++)
{
i1=i+di[k];
j1=j+dj[k];
if(a[i1][j1]==c1)
{
a[i1][j1]=c2;
uneste_galerii(i1,j1,c1,c2);
}
CAPITOLUL 8. OJI 2016 8.2. GALERIE - OJI 2016 125

}
}

int main()
{ f>>k>>n>>m>>t;

for(i=1;i<=t;i++)
{f>>x;
if(x<=m) {C[i].i=0;C[i].j=x;C[i].di=1;C[i].dj=0;}
else
if(x<=n+m) {C[i].i=x-m;C[i].j=m+1; C[i].di=0;C[i].dj=-1;}
else
if(x<=2*m+n){C[i].i=n+1;C[i].j=2*m+n+1-x;C[i].di=-1;C[i].dj=0;}
else {C[i].i=2*n+2*m+1-x;C[i].j=0;C[i].di=0;C[i].dj=1;}

s.insert(i);
}

for(i=1;i<=n;i++)
a[i][0]=a[i][m+1]=-1;
for(i=1;i<=m;i++)
a[0][i]=a[n+1][i]=-1;

while(!s.empty())
{ sapa_cineva=0;
sterse.clear();

for(it=s.begin();it != s.end(); it++)


{ i=*it;
inou=C[i].i+C[i].di;
jnou=C[i].j+C[i].dj;
x=a[inou][jnou];

if(x==0)
{
a[inou][jnou]=i;
C[i].l++;
C[i].i=inou;
C[i].j=jnou;
sapa_cineva=1;
}
else
{ if(x!=-1)
{
uneste_galerii(inou,jnou,i,x);
C[x].l+=C[i].l;
if (x<i && a[inou+C[x].di][jnou+C[x].dj]!=x)
sterse.insert(x);
}

sterse.insert(i);
}
}

for(it=sterse.begin();it != sterse.end(); it++)


{ i=*it;
if(s.count(i)!=0) s.erase(i);
}

if(sapa_cineva) pas++;
}

if(k==1)
g<<pas<<’\n’;
else
{
x=C[1].l;
for(i=2;i<=t;i++)
if(C[i].l>x)
x=C[i].l;
g<<x<<’\n’;
}

f.close();
g.close();
return 0;
CAPITOLUL 8. OJI 2016 8.2. GALERIE - OJI 2016 126

Listing 8.2.2: galerie1.cpp


#include <fstream>
using namespace std;

ifstream f("galerie.in");
ofstream g("galerie.out");

short n,m,t,k,x,i,j,pas,inou,jnou,a[205][205],s[805],
di[]={-1,0,1,0},dj[]={0,1,0,-1},sapa_cineva,nr_active,nr_sterse;

struct cartita{short i,j,l,di,dj;}C[805];

bool sterg[805];

int interior(int i, int j)


{ if(i*j==0)return 0;
if(i>n)return 0;
if(j>m)return 0;
return 1;
}

void uneste_galerii(int i,int j,int c1,int c2)


{
int k,i1,j1;
if(interior(i,j))
for(k=0;k<4;k++)
{
i1=i+di[k];
j1=j+dj[k];
if(a[i1][j1]==c1)
{
a[i1][j1]=c2;
uneste_galerii(i1,j1,c1,c2);
}
}
}

int main()
{
f>>k>>n>>m>>t;

for(i=1;i<=t;i++)
{
f>>x;

if(x<=m)
{
C[i].i=0;
C[i].j=x;
C[i].di=1;
C[i].dj=0;
}
else
if(x<=n+m)
{
C[i].i=x-m;
C[i].j=m+1;
C[i].di=0;
C[i].dj=-1;
}
else
if(x<=2*m+n)
{
C[i].i=n+1;
C[i].j=2*m+n+1-x;
C[i].di=-1;
C[i].dj=0;
}
else
{
C[i].i=2*n+2*m+1-x;
C[i].j=0;
CAPITOLUL 8. OJI 2016 8.2. GALERIE - OJI 2016 127

C[i].di=0;
C[i].dj=1;
}

s[i]=i;
}

for(i=1;i<=n;i++)
a[i][0]=a[i][m+1]=-1;
for(i=1;i<=m;i++)
a[0][i]=a[n+1][i]=-1;

nr_active=t;

while(nr_active)
{
sapa_cineva=0;
nr_sterse=0;

for(j=1;j<=nr_active;j++)
{
i=s[j];
inou=C[i].i+C[i].di;
jnou=C[i].j+C[i].dj;
x=a[inou][jnou];
if(x==0)
{
a[inou][jnou]=i;
C[i].l++;
C[i].i=inou;
C[i].j=jnou;
sapa_cineva=1;
}
else
{
if(x!=-1)
{
uneste_galerii(inou,jnou,i,x);
C[x].l+=C[i].l;
if (x<i && a[inou+C[x].di][jnou+C[x].dj]!=x)
sterg[x]=true;
}

sterg[i]=true;
}
}

j=0;
for(i=1;i<=nr_active;++i)
if(!sterg[s[i]])
s[++j]=s[i];

nr_active=j;
if(sapa_cineva)
pas++;
}

if(k==1)
g<<pas<<’\n’;
else
{
x=C[1].l;
for(i=2;i<=t;i++)
if(C[i].l>x)
x=C[i].l;
g<<x<<’\n’;
}

f.close();
g.close();
return 0;
}
CAPITOLUL 8. OJI 2016 8.2. GALERIE - OJI 2016 128

8.2.3 *Rezolvare detaliată


Capitolul 9

OJI 2015

9.1 ech - OJI 2015


Problema 1 - ech 100 de puncte
Numim număr echilibrat un număr natural pentru care suma cifrelor de pe poziţii pare este
egală cu suma cifrelor de pe poziţii impare.
De exemplu numărul 13552 este echilibrat, pentru că 1  5  2 8 3  5.

Cerinţe

Dat fiind un număr natural N să se determine cel mai mic număr echilibrat, strict mai mare
decât N .

Date de intrare

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

Date de ieşire

Fişierul de ieşire ech.out va conţine o singură linie pe care va fi scris cel mai mic număr
echilibrat, strict mai mare decât N .

Restricţii şi precizări

a Numărul N are cel mult 23 de cifre.


a Pentru teste valorând 40% din punctaj, N are cel mult 18 cifre.

Exemple
ech.in ech.out Explicaţii
99 110 1+0=1.
123133 123134 1+3+3=2+1+4.

Timp maxim de executare/test: 1.0 secunde


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

9.1.1 Indicaţii de rezolvare

Autor prof. Emanuela Cerchez Colegiul National ’’Emil Racovita’’ Iasi

Solutie 80 de puncte

Vom incrementa numarul N pana cand obtinem un numar echilibrat.


Pentru 40% dintre teste se poate lucra cu tipurile de date standard (problema
se reduce doar la a verifica proprietatea de numar echilibrat)

129
CAPITOLUL 9. OJI 2015 9.1. ECH - OJI 2015 130

Pentru a lucra cu numere de cel mult 23 de cifre va trebui sa extragem


cifrele lui N, sa le plasam intr-un vector si sa simulam incrementarea.

Solutie 100 puncte

Se observa ca numerele echilibrate sunt divizibile cu 11.


Bazandu-ne pe aceasta observatie, putem aduna initial la N valoarea 11-r
(unde r=N%11) pentru a obtine primul numar divizibil cu 11 strict mai mare
decat N. La fiecare pas verificam daca numarul obtinut este echilibrat.
In caz contrar, adunam 11.

Solutie optima 100 puncte, prof. Pit-Rada Ionel-Vasile

Precalculam sumele partiale

si[i]=suma cifrelor din pozitiile impare incepand cu cea mai semnificativa


cifra si terminand cu pozitia i;

sp[i]= suma cifrelor din pozitiile pare incepand cu cea mai semnificativa
cifra si terminand cu pozitia i.

Precalculam si cea mai mare suma cu care putem inlocui pozitiile impare
(numarul de cifre de 9) respectiv pare.

Parcurgem numarul de la pozitia 0 (cifra cea mai putin semnificativa,


cifra unitatilor) si analizam doar pozitiile i unde cifra este < 9 si
poate fi imbunatatita. La fiecare astfel de pozitie verificam, in functie
de paritate, daca avem posibilitatea sa echilibram sumele.

In cazul in care numarul are N cifre si sunt toate egale cu 9 si N este par
algoritmul se va opri la pozitia primei cifre zero din fata numarului.

Daca la o pozitie i am gasit cifra mai mica decat 9 determinam cea mai buna
inlocuire (cea mai mica cifra pentru care avem sanse la echilibru).

9.1.2 Cod sursă

Listing 9.1.1: ech add11.cpp


//Em. Cerchez 100 puncte
#include <fstream>
#include <cassert>

using namespace std;

ifstream fin("ech.in");
ofstream fout("ech.out");

int a[50];
int lg=0;
int nx[50];

void add(int x);


int rest();
int ech();
void citire();
void afisare();
CAPITOLUL 9. OJI 2015 9.1. ECH - OJI 2015 131

int main()
{int r;
citire();
r=rest();
if (r) add(11-r);
else add(11);
while (!ech())
add(11);
afisare();
return 0;
}

void add(int x)
{int i, t=0, s;
nx[0]=x%10; nx[1]=x/10;
for (i=0; i<lg; i++)
{s=a[i]+nx[i]+t;
a[i]=s%10;
t=s/10;}
if (t+nx[i]) a[lg++]=t+nx[i];
}

int rest()
{int i, r=0;
for (i=lg-1; i>=0; i--)
{
r=r*10+a[i];
r=r%11;
}
return r;
}

int ech()
{ int s1=0, s2=0;
for (int i=0; i<lg; i+=2)
s1+=a[i], s2+=a[i+1];
return s1==s2;
}

void citire()
{char c;
int st, dr, aux;
while (fin>>c)
a[lg++]=c-’0’;
//inversez a
for (st=0, dr=lg-1; st<dr; st++, dr--)
{ aux=a[st]; a[st]=a[dr]; a[dr]=aux; }
}

void afisare()
{int i;
for (i=lg-1; i>=0; i--) fout<<a[i];
fout<<’\n’;
fout.close();
}

Listing 9.1.2: ech lucia miron.cpp


// Lucia Miron
// numerele echilibrate sunt divizibile cu 11,
// verific doar aceste numere

#include <fstream>

using namespace std;

ifstream fin("ech.in");
ofstream fout("ech.out");

int n,v[50],r;

void citire()
{
char ch;
CAPITOLUL 9. OJI 2015 9.1. ECH - OJI 2015 132

int i,aux;
while(fin>>ch)
n++,v[n]=ch-’0’;
for(i=1;i<=n/2;i++)
aux=v[i],v[i]=v[n+1-i],v[n-i+1]=aux;
v[0]=n;
}

void aduna11(int r)
{
int i,t,aux;
t=r;
for(i=1;i<=v[0];i++)
{
aux=v[i]+t;
v[i]=aux%10;
t=aux/10;
}
if(t)v[++v[0]]=t;
}

int ech()
{
int i,sp=0,si=0;
for (i=1;i<=v[0];i++)
if(i%2)si+=v[i];
else sp+=v[i];
if(si==sp)return 1;
else return 0;
}

int rest11()
{
int i,r=0;
for(i=v[0];i>=1;i--)
r=(r*10+v[i])%11;
return r;
}

void afis()
{
int i;
for(i=v[0];i>=1;i--)
fout<<v[i];
fout<<’\n’;
}

int main()
{
citire();
r=rest11();

if(r)
aduna11(11-r);
else
aduna11(11);

while(!ech())
aduna11(11);

afis();
fout.close();
return 0;
}

Listing 9.1.3: ech Ovidiu Dumitrescu.cpp


//100 de puncte, optim
#include<cstdio>
#include<algorithm>
#include<cstring>

#define Nmax 3000

using namespace std;


CAPITOLUL 9. OJI 2015 9.1. ECH - OJI 2015 133

int n,i,j,s1,s2,nr,p;
char s[Nmax],S[Nmax];
int x[Nmax];

int cmp(const int a,const int b)


{
return s[a]<s[b];
}

void detp()
{
int i;
for (i=1;i<=n;i++)
if (s[i]>S[i])
{p=i; break;}
}

void solve()
{
int aux1,aux2;
if (s1>s2)
{
i=n; if (i%2==0) i++; s[n+1]=’0’;
while (s1-s2>s[i]-’0’+(9-s[i-1]+’0’))
{
s1-=(s[i]-’0’);
s[i]=’0’;
s2+=(9-s[i-1]+’0’);
s[i-1]=’9’;
i=i-2;
}

if (s[i]-’0’>s1-s2 && s1!=s2)


{
s[i]-=(s1-s2-1);
while (s[i-1]==’9’ &&i-1>0) i=i-2;
s[i-1]+=1;
}
else if (s1!=s2)
{
aux1=s[i]; aux2=i;
s1-=s[i]-’0’; s[i]=’0’;
if (s1-s2==0) s[i]=’1’,s1++;
i--;
while (s[i]+(s1-s2)>’9’ && i>0) i-=2;
s[i]+=(s1-s2);
if (i==0)
{
s[1]=’1’;
for (i=1;i<n;i++) s[i+1]=’0’;
s[++n]=’1’;
}
}
}

if (s2>s1)
{
i=n; if (i%2==1) i++;
swap(s1,s2); s[n+1]=’0’;
while (s1-s2>s[i]-’0’+(9-s[i-1]+’0’))
{
s1-=(s[i]-’0’);
s[i]=’0’;
s2+=(9-s[i-1]+’0’);
s[i-1]=’9’;
i=i-2;
}

if (s[i]-’0’>s1-s2 && s1!=s2)


{
s[i]-=(s1-s2-1);
while (s[i-1]==’9’ &&i-1>0) i=i-2;
s[i-1]+=1;
}
else if (s1!=s2)
CAPITOLUL 9. OJI 2015 9.1. ECH - OJI 2015 134

{
aux1=s[i]; aux2=i;
s1-=s[i]-’0’; s[i]=’0’;
if (s1-s2==0) s[i]=’1’,s1++;
i--;
while (s[i]+(s1-s2)>’9’ && i>0) i-=2;
s[i]+=(s1-s2);
if (i==0)
{
s[1]=’1’;
for (i=1;i<n;i++) s[i+1]=’0’;
s[++n]=’1’;
}
}
}
}

int main()
{
freopen("ech.in","r",stdin);
freopen("ech.out","w",stdout);

gets(s+1);

n=strlen(s+1);
memcpy(S,s,sizeof(S));

for (i=1;i<=n;i++)
{
if (i%2==1) s1=s1+(s[i]-’0’);
else s2=s2+(s[i]-’0’);
}

solve();
detp();

if (!p)
{
i=n;
while (s[i]==’9’) i--;
if (i==0){
s[1]=’1’;
for (i=1;i<=n;i++) s[i+1]=’0’;
n++; s1=1; s2=0;}
else
{
s[i]++;
if (i%2==1) s1++; else s2++;
}
solve();
}

detp();

for (i=n;i>p;i--)
if (i%2==1) x[++x[0]]=i;

sort(x+1,x+x[0]+1,cmp);

for (i=p+1;i<=n;i++)
if (i%2==0)
{
nr=min(s[i],s[x[x[0]]]);
s[i]=s[i]-nr+’0’;
s[x[x[0]]]=s[x[x[0]]]-nr+’0’;
--x[0];
if (s[x[x[0]]]==’0’) break;
}

for (i=1;i<=n;i++)
printf("%c",s[i]);
return 0;
}

Listing 9.1.4: ech raluca costinescu.cpp


CAPITOLUL 9. OJI 2015 9.1. ECH - OJI 2015 135

#include <fstream>

using namespace std;

ifstream f("ech.in");
ofstream g("ech.out");

int main()
{
char s[50],c;
int si=0, sp=0, i, x=0;

while(f>>c)if(c>=’0’ and c<=’9’) s[x++]=c;

x--;
for(i=0;i<=x;i++)
if(i%2)
sp+=s[i]-’0’;
else
si+=s[i]-’0’;

do
{
int ok=0;
for(i=x;i>=0 && !ok; i=i-1)
if(s[i]<’9’)
{
s[i]++;
ok++;
if(i%2)
sp++;
else
si++;
}
else
{
s[i]=’0’;
if(i%2)
sp-=9;
else
si-=9;
}

if(!ok)
{
si=1;sp=0;
s[0]=’1’;
s[x+1]=’0’;
x++;
}
} while(si!=sp);

for(i=0;i<=x;i++)
g<<s[i];
g<<’\n’;
g.close();
return 0;
}

Listing 9.1.5: ech vasile pit rada.cpp


//100 de puncte, optim
#include<fstream>

using namespace std;

struct numar
{
int v[50],n;
};

numar next(numar a)
{
int i, s1, s2, s[2], t[2], i2, j, x, k;
a.v[a.n+1]=0;
CAPITOLUL 9. OJI 2015 9.1. ECH - OJI 2015 136

s[0]=0;
s[1]=0;
for(i=1;i<=a.n+1;i++)
{
i2=i%2;//paritatea curenta; 1-i2 paritatea inversa celei curente
s[i2]=s[i2]+a.v[i];
}
t[0]=0;
t[1]=0;
for(i=1;i<=a.n+1;i++)
{
i2=i%2;
a.v[i-1]=0;
if(i>1)t[1-i2]=t[1-i2]+9;

s1=s[1-i2]; // suma cifrelor de pe pozitii > i


// cu alta paritate decat pozitia i (curenta)
s2=s[i2]-a.v[i]; // suma cifrelor de pe pozitii > i
// cu aceeasi paritate decat pozitia i
s[i2]=s2;//actualizez s[i2]

if(a.v[i]<9)//am gasit o cifra care poate fi crescuta;


{
if(s2+a.v[i]<s1)
{
if(s1>s2+9+t[i2])continue;//nu se poate face echilibrare
if(s2 + a.v[i]+1 + t[i2] >= s1)
{
j=1;
}
else
{
j=s1-(s2 + a.v[i] + t[i2]);
}
x=s1-(s2 + a.v[i]+j);
k=2-i2;
}
else
{
if(s2 + a.v[i]+1 > s1 + t[1-i2])
continue;//nu se poate face echilibrare
j=1;
x=(s2 + a.v[i]+1)-s1;
k=1+i2;
}

//construiesc numarul cautat


a.v[i]=a.v[i]+j;
while(k<=i-1)
{
if(x>=9)
{
a.v[k]=9;
}
else
{
a.v[k]=x;
}
x=x-a.v[k];
k=k+2;
}
break;
}
}
if(a.v[a.n+1]>0)a.n++;//a fost nevoie de o cifra in plus, cifra 1
return a;
}

int main()
{
char ch;
int aux,i;
numar a, b;

ifstream fin("ech.in");
CAPITOLUL 9. OJI 2015 9.2. LASERE - OJI 2015 137

ofstream fout("ech.out");

a.n=0;
while(fin>>ch)
{
if(ch>=’0’ && ch<=’9’)
{
a.n++;
a.v[a.n]=ch-48;
}
}

for(i=1;i<=a.n/2;i++)
{
aux=a.v[i];
a.v[i]=a.v[a.n+1-i];
a.v[a.n+1-i]=aux;
}
b=next(a);
for(i=b.n;i>=1;i--)
{
fout<<b.v[i];
}
fout<<’\n’;

fout.close();
fin.close();

return 0;
}

9.1.3 *Rezolvare detaliată

9.2 lasere - OJI 2015


Problema 2 - lasere 100 de
puncte
Se consideră un teren reprezentat printr-o matrice cu n linii şi n coloane având elemente numere
naturale. În fiecare element al matricei este memorată ı̂nălţimea zonei de teren corespunzătoare
ca poziţie elementului respectiv. Pe acest teren sunt amplasate m lasere, ı̂n poziţii cunoscute. Un
laser este ı̂ndreptat spre unul dintre cele 4 puncte cardinale, codificate prin numere astfel: Nord
prin valoarea 1, Est prin valoarea 2, Sud prin valoarea 3 şi respectiv Vest prin valoarea 4. Fiecare
laser va executa o singură tragere şi ca urmare va scădea cu 1 valorile tuturor elementelor din
matrice din direcţia sa de tragere, exceptând poziţia laserului respectiv.
După efectuarea tuturor tragerilor, se caută poziţiile tuturor gropilor şi ale tranşeelor.
Numim groapă un element din matrice pentru care toate cele 8 elemente ı̂nvecinate pe linie,
coloană sau diagonale au valori mai mari sau egale decât el.
Numim tranşee o secvenţă maximală formată din două sau mai multe gropi situate pe aceeaşi
linie, pe coloane consecutive. Secvenţa se numeşte maximală dacă nu mai poate fi prelungită la
niciunul dintre capete.

Cerinţe

Cunoscând configuraţia terenului şi amplasarea laserelor, să se rezolve una dintre următoarele
două cerinţe:
1. să se determine numărul de gropi din teren, după executarea tragerilor;
2. să se determine numărul de tranşee existente, după executarea tragerilor.

Date de intrare

Fişierul de intrare lasere.in conţine pe prima linie un număr natural c care reprezintă cerinţa ce
urmează să fie rezolvată (1 sau 2). Pe a doua linie se află două numere naturale n şi m, reprezentând
numărul de linii şi de coloane ale matricei, respectiv numărul de lasere. Pe următoarele n linii
se află câte n numere naturale, reprezentând elementele matricei. Pe următoarele m linii sunt
CAPITOLUL 9. OJI 2015 9.2. LASERE - OJI 2015 138

descrise cele m lasere, câte un laser pe o linie. Pe o linie care descrie un laser se află 3 numere
naturale i j d, cu semnificaţia că se află un laser pe linia i şi coloana j (1 & i, j & n), care trage
ı̂n direcţia d (1 & d & 4). Valorile situate pe aceeaşi linie sunt separate prin spaţiu.

Date de ieşire
Fişierul de ieşire lasere.out va conţine pe prima linie un singur număr natural. Acest număr
reprezintă numărul de gropi (dacă c 1) sau numărul de tranşee (dacă c 2).
Restricţii şi precizări
a 4 & n & 200
a 1 & m & 200
a Numerotarea liniilor şi a coloanelor este de la 1 la n.
a Elementele matricei din fişierul de intrare sunt numere naturale de maxim 4 cifre.
a Poziţiile laserelor sunt distincte.
a Pentru teste valorând 30% din punctaj cerinţa este 1.

Exemple

lasere.in lasere.out Explicaţii


1 6 După ce acţionează laserele terenul arată astfel:
53 1 1 3 4 4
1 1 3 4 5 8 7 6 5 4
8 7 6 5 4 9 3 4 6 7
9 3 5 6 7 0 0 -1 9 8
1 1 1 9 1 1 0 5 6
8 Există 6 gropi şi o tranşee.
1 1 1 5 6 Se număra gropile chiar dacă fac parte dintr-o tranşee.
233
444
142
2 1 După ce acţionează laserele terenul arată astfel:
53 1 1 3 4 4
1 1 3 4 5 8 7 6 5 4
8 7 6 5 4 9 3 4 6 7
9 3 5 6 7 0 0 -1 9 8
1 1 1 9 1 1 0 5 6
8 Există 6 gropi şi o tranşee.
1 1 1 5 6 Se număra gropile chiar dacă fac parte dintr-o tranşee.
233
444
142

Timp maxim de executare/test: 0.2 secunde


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

9.2.1 Indicaţii de rezolvare

Autor: prof. Nicu Vad Laurentiu, Liceul Teoretic Mihail Kogalniceanu Vaslui

Varianta 1 -100p

Se citesc datele si se modifica tabloul conform pozitilor laserelor in ordinea in c


Pentru a afla numarul de gropi se calculeaza poziile valorilor minime in cele opt d
si contorizarea acestora.
Pentru transee se foloseste un vector auxiliar cu valori de 0 si 1, 1 pentru poziti
se cauta secventa maxima de valori consecutive de 1 si se contorizeaza.
CAPITOLUL 9. OJI 2015 9.2. LASERE - OJI 2015 139

9.2.2 Cod sursă

Listing 9.2.1: lasere adrian pintea.cpp


//Pintea Adrian Doru -Inspectoratul Scolar Judetean Cluj
#include<fstream>
#include<iostream>

using namespace std;

ifstream fin("lasere.in");
ofstream fout("lasere.out");

int a[202][202];
int cerinta,i,j,m,n,x,y,z,d;

int N(int x,int y)


{
int k;
for(k=1;k<x;k++)
if(a[k][y]>0) a[k][y]--;
return 0;
}

int S(int x,int y)


{
int k;
for(k=x+1;k<=n;k++)
if(a[k][y]>0) a[k][y]--;
return 0;
}

int V(int x,int y)


{
int k;
for(k=1;k<y;k++)
if(a[x][k]>0) a[x][k]--;
return 0;
}

int E(int x,int y)


{
int k;
for(k=y+1;k<=n;k++)
if(a[x][k]>0) a[x][k]--;
return 0;
}

int groapa(int x,int y)


{
if(a[x][y]<=a[x][y+1]&&a[x][y]<=a[x][y-1]&&a[x][y]<=a[x+1][y]&&
a[x][y]<=a[x-1][y]&&a[x][y]<=a[x+1][y+1]&&a[x][y]<=a[x-1][y+1]&&
a[x][y]<=a[x-1][y-1]&&a[x][y]<=a[x+1][y-1])
return 1;else return 0;
}

int transee(int l)
{
int k=0,p=1,q;
while(p<n)
{
q=1;
while((a[l][p]==a[l][p+q])&&groapa(l,p)&&groapa(l,p+q)) q++;
if(q>1) k++;
p=p+q;
}
return k;
}

int main()
{
fin>>cerinta;
fin>>n>>m;
int max=0;
CAPITOLUL 9. OJI 2015 9.2. LASERE - OJI 2015 140

for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{fin>>a[i][j];if(a[i][j]>max) max=a[i][j];}
max++;
for(i=0;i<=n+1;i++){a[0][i]=max;a[n+1][i]=max;a[i][0]=max;a[i][n+1]=max;}

for(i=1;i<=m;i++)
{
fin>>x>>y>>d;
if(d==1) N(x,y);
else if(d==2) E(x,y);
else if(d==3) S(x,y);
else V(x,y);
}
fin.close();

int nrT=0,nrG=0;

if(cerinta==1)
{
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(groapa(i,j))nrG++;
fout<<nrG<<’\n’;
}
else
{
for(i=1;i<=n;i++) nrT+=transee(i);
fout<<nrT<<’\n’;
}
fout.close();
return 0;
}

Listing 9.2.2: lasere emanuela cerchez.cpp


//Emanuela Cerchez Colegiul National Emil Racovita 100 puncte
#include <fstream>

#define NMAX 208


#define INF 1000004

using namespace std;

ifstream fin("lasere.in");
ofstream fout("lasere.out");

int T[NMAX][NMAX];
bool G[NMAX][NMAX]; //G[i][j]=true daca in pozitia i j exista o groapa

int n, m;
int gropi, transee;
int c;
int dl[]={0, -1, 0, 1, 0, -1,-1, 1, 1};
int dc[]={0, 0, 1, 0, -1, -1, 1, -1, 1};

bool este_groapa(int lin, int col);


int cauta_gropi();
void bordare();
void trage(int lin, int col, int d);
int cauta_transee();

int main()
{int i, j, lin, col, d;
fin>>c>>n>>m;
for (i=1; i<=n; i++)
for (j=1; j<=n; j++) fin>>T[i][j];

bordare();
for (i=1; i<=m; i++)
{
fin>>lin>>col>>d;
trage(lin,col,d);
}
CAPITOLUL 9. OJI 2015 9.2. LASERE - OJI 2015 141

gropi=cauta_gropi();
/*for (i=1; i<=n; i++)
{for (j=1; j<=n; j++) fout<<G[i][j]<<’ ’;
fout<<’\n’;}
*/
if (c==1)
fout<<gropi<<’\n’;
else
fout<<cauta_transee()<<’\n’;
fout.close();
return 0;
}

bool este_groapa(int lin, int col)


{ int k;
for (k=1; k<=8; k++)
if (T[lin+dl[k]][col+dc[k]]<T[lin][col]) return 0;
return 1;
}

int cauta_gropi()
{int i, j, nr=0;
for (i=1; i<=n; i++)
for (j=1; j<=n; j++)
if (este_groapa(i, j))
{
nr++; G[i][j]=1;
}
return nr;
}

void bordare()
{int i;
for (i=0; i<=n+1; i++)
T[0][i]=T[i][0]=T[n+1][i]=T[i][n+1]=INF;
}

void trage(int lin, int col, int d)


{
do
{
lin+=dl[d]; col+=dc[d];
if (T[lin][col]==INF) return;
T[lin][col]--;
}
while (1);
}

int cauta_transee()
{int i, j, nr=0, lg;
for (i=1; i<=n; i++)
for (j=1; j<=n+1; j++)
if (G[i][j])
if (!G[i][j-1]) //incepe o transee
lg=1;
else
lg++;
else
if (G[i][j-1]) //se termina o transee
if (lg>1) nr++;
return nr;
}

Listing 9.2.3: lasere lucia miron.cpp


//Miron Lucia Colegiul "C. Negruzzi", Iasi
#include <fstream>

using namespace std;

ifstream fin("lasere.in");
ofstream fout("lasere.out");

struct el
{
CAPITOLUL 9. OJI 2015 9.2. LASERE - OJI 2015 142

int v,g;
} a[202][202];

int dx[]={-1,-1,0,1,1,1,0,-1};
int dy[]={0,1,1,1,0,-1,-1,-1};
int n,m,i,j,k,nrg,nrt,c,l;

void bordare()
{
int i;
for(i=0;i<=n+1;i++)
a[i][0].v=a[i][n+1].v=10000;
for(i=0;i<=n+1;i++)
a[0][i].v=a[n+1][i].v=10000;
}

int groapa(int i,int j)


{
int k;
for(k=0;k<8;k++)
if(a[i+dx[k]][j+dy[k]].v<a[i][j].v)
return 0;
return 1;
}

void citire()
{
int i,j,k,x,y,d;
fin>>c;
fin>>n>>m;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
fin>>a[i][j].v;
for(i=1;i<=m;i++)
{
fin>>x>>y>>d;
if(d==1)
for(j=x-1;j>=1;j--)
a[j][y].v--;
if(d==2)
for(j=y+1;j<=n;j++)
a[x][j].v--;
if(d==3)
for(j=x+1;j<=n;j++)
a[j][y].v--;
if(d==4)
for(j=y-1;j>=1;j--)
a[x][j].v--;
}
}

int main()
{
citire();
bordare();
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(groapa(i,j)==1)
{
nrg++;
a[i][j].g=1;
}
if(c==1)fout<<nrg<<’\n’;
else
{
for(i=1;i<=n;i++)
{
l=0;
for(j=1;j<=n;j++)
if(a[i][j].g==1)l++;
else
{
if(l>=2)nrt++;
l=0;
}
if(l>=2)nrt++;
CAPITOLUL 9. OJI 2015 9.2. LASERE - OJI 2015 143

}
fout<<nrt<<’\n’;
}
return 0;
}

Listing 9.2.4: lasere nicu vlad.cpp


//Nicu Vlad Laurentiu - Liceul Teoretic "Mihail Kogalniceanu" Vaslui 100 puncte
#include <fstream>

#define N 204
#define INF 10000
#define M 10000

using namespace std;

ifstream cin("lasere.in");
ofstream cout("lasere.out");

struct laser
{
int x,y,dir;
} X[N];

int A[N][N],n,m, i,j,k, B[M],nr=0,tr,p1,p2,s,cer;


int dx[8]={-1,-1,-1,0,1,1,1,0};
int dy[8]={-1,0,1,1,1,0,-1,-1};

int main()
{
cin>>cer;
cin>>n>>m;
for(i=1; i<=n; i++)
for(j=1; j<=n; j++) cin>>A[i][j];
for(i=0;i<=n+1;i++) A[0][i]=A[i][0]=A[i][n+1]=A[n+1][i]=INF;

for(k=0; k<m; k++)


{
cin>>X[k].x>>X[k].y>>X[k].dir;
if(X[k].dir==1)for(i=X[k].x-1; i>=1; i--) A[i][X[k].y]--;
else if(X[k].dir==2)for(i=X[k].y+1; i<=n; i++) A[X[k].x][i]--;
else if(X[k].dir==3)for(i=X[k].x+1; i<=n; i++) A[i][X[k].y]--;
else if(X[k].dir==4)for(i=X[k].y-1; i>=1; i--) A[X[k].x][i]--;
}
tr=0;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
int ok=1;
for(int l=0;l<8 && ok;l++)
if(A[i][j]>A[i+dx[l]][j+dy[l]])ok=0;
if(ok==1){ B[j]=1; nr++;}

}
p1=0;
for(s=1;s<n;)
if(B[s]==1)
{
p1=s;
while(B[s]) s++;
if(s-p1>=2)tr++;
}
else s++;

for(s=1;s<=n;s++){ B[s]=0;}
}
if(cer==1)cout<<nr<<"\n";
else cout<<tr<<"\n";
/* for(i=1; i<=n; i++)
{
for(j=1; j<=n; j++) cout<<A[i][j]<<" ";
cout<<"\n";
}*/
CAPITOLUL 9. OJI 2015 9.2. LASERE - OJI 2015 144

Listing 9.2.5: lasere raluca costinescu.cpp


//Costineanu Veronica Raluca Colegiul National Stefan cel Mare Suceava
#include <fstream>

#define vmax 999999999

using namespace std;

ifstream f("lasere.in");
ofstream g("lasere.out");

int a[210][210],n, m, c, gr[210][210];

void trage(int x, int y, int d)


{
int i, j;
if(d==1)
for(i=x-1;i>=1;i--)
a[i][y]--;
else
if(d==2)
for(j=y+1;j<=n;j++)
a[x][j]--;
else
if(d==3)
for(i=x+1;i<=n;i++)
a[i][y]--;
else
for(j=y-1;j>=1;j--)
a[x][j]--;
}

int groapa(int x, int y)


{
if(a[x][y]<=a[x-1][y-1] and a[x][y]<=a[x-1][y] and
a[x][y]<=a[x-1][y+1] and a[x][y]<=a[x][y-1] and
a[x][y]<=a[x][y+1] and a[x][y]<=a[x+1][y-1] and
a[x][y]<=a[x+1][y] and a[x][y]<=a[x+1][y+1])
return 1;
return 0;
}

int main()
{
f>>c;
f>>n>>m;
int i, j, x, y, d, nr=0,p;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
f>>a[i][j];
for(i=0;i<=n+1;i++)
a[0][i]=a[n+1][i]=a[i][0]=a[i][n+1]=vmax;
for(p=1;p<=m;p++)
{
f>>x>>y>>d;
trage(x,y,d);
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(groapa(i,j))
gr[i][j]=1, nr++;
int tr=0;
for(i=1;i<=n;i++)
{
j=1;
while(j<=n)
{
while(j<=n and gr[i][j]==0)j++;
x=0;
while(j<=n and gr[i][j]==1)j++, x++;
if(x>1)tr++;
}
CAPITOLUL 9. OJI 2015 9.2. LASERE - OJI 2015 145

if(c==1)
g<<nr<<’\n’;
else
g<<tr<<’\n’;
return 0;
}

Listing 9.2.6: lasere vasile pit rada.cpp


//PIT-RADA IONEL-VASILE COLEGIUL NATIONAL TRAIAN
#include<fstream>

using namespace std;

ifstream fin("lasere.in");
ofstream fout("lasere.out");

int n,m,nrg,nrt,i,j,k,c,caz,li,co,dir;

short int a[203][203], b[203][203];


short int dl[8]={-1,-1, 0,+1, 0,-1,+1,+1};
short int dc[8]={-1, 0,+1, 0,-1,+1,+1,-1};

int main()
{
fin>>caz>>n>>m;

for(i=0;i<=n+1;i++)
{
a[i][0]=10000;
a[0][i]=10000;
a[n+1][i]=10000;
a[i][n+1]=10000;
}

for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
fin>>a[i][j];
}
}

for(i=1;i<=m;i++)
{
fin>>li>>co>>dir;
switch(dir)
{
case 1 :
{
for(j=li-1;j>=1;j--)
{
a[j][co]--;
}
break;
}
case 3 :
{
for(j=li+1;j<=n;j++)
{
a[j][co]--;
}
break;
}
case 4 :
{
for(j=co-1;j>=1;j--)
{
a[li][j]--;
}
break;
}
case 2 :
CAPITOLUL 9. OJI 2015 9.2. LASERE - OJI 2015 146

{
for(j=co+1;j<=n;j++)
{
a[li][j]--;
}
break;
}
}
}

nrg=0;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
c=0;
for(k=0;k<=7;k++)
{
li=i+dl[k];
co=j+dc[k];
if(a[li][co]<a[i][j])
{
c++;
break;
}
}
if(c==0)
{
nrg++;
b[i][j]=1;
}
else
{
b[i][j]=0;
}
}
}

nrt=0;
for(i=1;i<=n;i++)
{
for(j=0;j<=n-2;j++)
{
if(b[i][j]==0 && b[i][j+1]==1 && b[i][j+2]==1)
{
nrt++;
}
}
}

if(caz==1)
fout<<nrg;
else
fout<<nrt;

fout.close();
fin.close();
return 0;
}

9.2.3 *Rezolvare detaliată


Capitolul 10

OJI 2014

10.1 pătrat - OJI 2014


Problema 1 - patrat 100 de
puncte
Cel mai mare observator astronomic din România şi din Europa de
Est, aflat la Galaţi, a captat o imagine a boltei cereşti, ce surprinde
toate stelele vizibile ı̂n acel moment. Imaginea este ı̂n format digital,
codificată sub forma unui tablou bidimensional, cu N linii şi M coloane.
Fiecare element al tabloului conţine un număr natural care reprezintă
intensitatea luminoasă a unei stele.
Numim stea strălucitoare o stea care are intensitatea luminoasă mai
Figura 10.1: patrat
mare decât a tuturor stelelor ı̂nvecinate direct cu ea, pe orizontală,
verticală sau diagonală. Numim constelaţie pătrată patru stele strălucitoare care se află plasate ı̂n
colţurile unui pătrat cu laturile paralele cu marginile tabloului. Lungimea laturii unei constelaţii
pătrate este egală cu numărul de stele din care este formată latura. O stea strălucitoare poate
face parte din mai multe constelaţii pătrate.

Cerinţe

Scrieţi un program care să determine:


a) Numărul stelelelor strălucitoare;
b) Numărul constelaţiilor pătrate;
c) Lungimea laturii pătratului care reprezintă cea mai mare constelaţie pătrată.

Date de intrare

Din fişierul patrat.in se citesc de pe prima linie, două numere naturale N şi M , separate
printr-un spaţiu, reprezentând dimensiunile tabloului bidimensional, iar de pe următoarele N
linii, câte M numere naturale separate prin câte un spaţiu, reprezentând intensitatea luminoasă
a stelelor.

Date de ieşire

În fişierul patrat.out se va scrie pe prima linie un număr natural reprezentând răspunsul la
cerinţa a). Pe cea de-a doua linie se va scrie un număr natural reprezentând răspunsul la cerinţa
b). Pe a treia linie se va scrie un număr natural reprezentând răspunsul la cerinţa c).

Restricţii şi precizări

a 1 $ N & 200
a 1 $ M & 200
a 1 & intensitatea unei stele & 1000
a pentru rezolvarea corectă a cerinţei a) se acordă 40% din punctajul fiecărui test, pentru
rezolvarea corectă a cerinţei b) se acordă 40% din punctajul fiecărui test iar pentru rezolvarea
corectă a cerinţei c) se acordă 20% din punctajul fiecărui test.
a Respectaţi formatul fişierului de ieşire! Pentru a obţine punctajul acordat unei cerinţe,
trebuie ca răspunsul din fişier să fie corect şi scris exact pe linia precizată ı̂n enunţ.

147
CAPITOLUL 10. OJI 2014 10.1. PĂTRAT - OJI 2014 148

Exemple
patrat.in patrat.out Explicaţii
68 11 În tabloul bidimensional cu 6 linii şi 8 coloane există 11 stele
1 85 71 6 3 strălucitoare. Tabloul conţine 3 constelaţii pătrate iar cea mai
3 4 5 mare are latura pătratului de lungime 5.
12311521
1 71 911
81
63516431
1 95 71 8
21
1565313
6
23 0 În tabloul bidimensional cu 2 linii şi 3 coloane nu există nicio
111 0 stea strălucitoare. Tabloul conţine 0 constelaţii pătrate iar cea
111 0 mai mare are latura pătratului este de dimensiune 0.

Timp maxim de executare/test: 0.6 secunde


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

10.1.1 Indicaţii de rezolvare


Autor prof. Sanda Junea Liceul Teoretic Gr Moisil Timisoara

Definesc un tip structurat stea cu doua campuri pentru a retine atat intesitatea
luminoasa cat si pentru a memora faptul ca o stea este stralucitoare sau nu.
Definesc o matrice de tipul structurat stea in care se citesc datele de intrare.

1. Determinarea numarului stelelor stralucitoare:

Parcurg matricea si pentru fiecare element verific daca valoarea lui este mai
mica decat valorile continute in cele 8 elemente vecine. In cazul in care
conditia este indeplinita, am gasit o stea stralucitoare si o contorizez.
i-1,j-1 i-1,j i-1,j+1
i,j-1 i,j i,j+1
i+1,j-1 i+1,j i+1,j

2. Determinarea numarului constelatiilor patrate si laturii celei mai mari


constelatii patrate:

Parcurg matricea pe linii iar in momentul in care intalnesc o stea


stralucitoare A, voi cauta de-a lungul liniei urmatoarea stea stralucitoare B.

Daca exista, notam cu k distanta dintre cele doua stele si verific daca la
distante egale cu k de cele doua stele (dar aceasta distanta nu trebuie sa
depaseasca marginea tabloului), se gasesc alte doua stele stralucitoare,
de-a lungul coloanelor pe care se afla stelele A si B, fie acestea C si D.

In cazul in care am gasit cele 4 stele stralucitoare in colturile unui patrat


voi contoriza o constelatie patrata.

Retin latura patratului determinat daca aceasta este cea mai mare pana in
acest moment.

Complexitatea algoritmului este O(L3) unde L=maximum(N si M).

10.1.2 Cod sursă


CAPITOLUL 10. OJI 2014 10.1. PĂTRAT - OJI 2014 149

Listing 10.1.1: patratdl.cpp


#include <cstdio>
#include <algorithm>
#include <cassert>

#define MAXN 210

using namespace std;

int N, M, K, nr, nrct=0, nrst=0,x,j, Lmax=0, L, Lm, k,lat, y, c, i, l, d;


int dx[8]= {0,0,1,1,1,-1,-1,-1};
int dy[8]= {1,-1,1,-1,0,1,-1,0};

int A[MAXN][MAXN];
bool B[MAXN][MAXN], ok;

int main()
{
freopen("patrat.in","r",stdin);
freopen("patrat.out","w",stdout);

scanf("%d %d\n",&N, &M);


assert(N>=2 && N<=200 && M>=2 && M<=200);

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


for(j=1; j<=M; j++)
{
scanf("%d",&A[i][j]);
assert(A[i][j]>=1 && A[i][j]<=1000);
}

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


for(j=1; j<=M; j++)
{
Lm=0;
for(k=0; k<8; k++)
{
l=i+dx[k];
c=j+dy[k];
if(A[l][c]>Lm) Lm=A[l][c];
}
if(A[i][j]>Lm)
{
nrst++;
B[i][j]=true;
}
}

printf("%d\n", nrst);
for(i=1; i<=N; i++)
for(j=1; j<=M; j++)
if(B[i][j])
{
L=min(N - i + 1, M - j + 1);
for(d=L; d>1 ; d--)
if(B[i][j + d - 1] &&
B[i + d - 1][j] &&
B[i + d - 1][j + d - 1])
{
nrct++;
Lmax=(Lmax<d?d:Lmax);
}
}

printf("%d\n%d\n", nrct, Lmax);


return 0;
}

Listing 10.1.2: patratdt.cpp


#include <fstream>

using namespace std;


CAPITOLUL 10. OJI 2014 10.1. PĂTRAT - OJI 2014 150

ifstream f("patrat.in");
ofstream g("patrat.out");

int a[203][203],n,m,sf;

struct stea
{
int x,y;
} v[4000];

int vecini(int x,int y)


{
if(x-1>0 &&y-1>0 && a[x][y]<=a[x-1][y-1]) return 0;
if(x-1>0&&a[x][y]<=a[x-1][y]) return 0;
if(x-1>0 && y+1<=m && a[x][y]<=a[x-1][y+1])return 0;
if(y-1>0 && a[x][y]<=a[x][y-1]) return 0;
if(y+1<=m &&a[x][y]<=a[x][y+1]) return 0;
if(x+1<=n&&y-1>0 &&a[x][y]<=a[x+1][y-1]) return 0;
if(x+1<=n && a[x][y]<=a[x+1][y]) return 0;
if(x+1<=n && y+1<=m &&a[x][y]<=a[x+1][y+1]) return 0;
return 1;
}

int parcurg()
{int i,j,nr=0;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(vecini(i,j)!=0)
{
nr++;++sf;
v[sf].x=i;v[sf].y=j;
}
return nr;
}

void constelatie(int &nr, int &max)


{
int poz1=1,k,lg,ok1,ok,poz2;

while(poz1+3<=sf)
{
poz2=sf;
while(poz2-3>=poz1)
{
lg=v[poz2].x-v[poz1].x;
if(v[poz2].y-v[poz1].y==lg)
{
k=poz1+1;
ok=0;
while (v[k].x==v[poz1].x)
{if(v[k].y==v[poz1].y+lg) ok=1; k++;}

if(ok)
{
k=poz2-1;ok1=0;
while(v[k].x==v[poz2].x)
{if(v[k].y+lg==v[poz2].y)
ok1=1;k--;
}

if(ok1)
{
nr++;
if(max<lg)
max=lg;
}
}
}

poz2--;
}
poz1++;
}
}

int main()
CAPITOLUL 10. OJI 2014 10.1. PĂTRAT - OJI 2014 151

{ int i,j,nr=0,p=0,max=0;
f>>n>>m;
//for(k=1;k<n/3;k++)
//{
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
f>>a[i][j];
nr=nr+parcurg();
g<<nr<<’\n’;
constelatie(p,max);
g<<p<<’\n’<<max+1<<’\n’;
f.close();
g.close();

return 0;
}

Listing 10.1.3: patratMN.cpp


//nˆ3
#include <fstream>
#include <iostream>

#define DIM 300


#define INF 1000000

using namespace std;

int di[] = {-1, 1,-1,1,0, 0,1,-1};


int dj[] = { 1,-1,-1,1,1,-1,0, 0};

int a[DIM][DIM];
int n, m, i, j, k, maxim, stele, patrate, ok, d, iv, jv, t;

int main()
{
ifstream fin("patrat.in");
ofstream fout("patrat.out");

fin>>n>>m;
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
fin>>a[i][j];

for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
{
ok = 1;
for (d=0;d<=7;d++)
{
iv = i + di[d];
jv = j + dj[d];
if (a[iv][jv] >= a[i][j])
ok = 0;
}
stele+=ok;
if (ok == 1)
{
a[i][j] = INF;
//cout<<i<<" "<<j<<"\n";
}
}

for (i=1;i<n;i++)
for (j=i+1;j<=n;j++)
{
t = j-i;
for (k=1; k+t<=m; k++)
if (a[i][k] == INF &&
a[i][k+t] == INF &&
a[j][k] == INF &&
a[j][k+t] == INF)
{
patrate++;
if (t > maxim)
CAPITOLUL 10. OJI 2014 10.1. PĂTRAT - OJI 2014 152

maxim = t;
}
}

fout<<stele<<"\n"<<patrate<<"\n"<<maxim+1;
return 0;
}

Listing 10.1.4: patratsj.cpp


#include <iostream>
#include<stdio.h>
#include <fstream>

using namespace std;

ifstream f("patrat.in");
ofstream g("patrat.out");

struct stea{
int x;
char s;
};

int m,n,nrs,nrc,v[]={-1,-1,0,1,1,1,0,-1},w[]={0,1,1,1,0,-1,-1,-1};

stea t[202][202];

/* nrs-numar stele stralucitoare


nrc-numar constelatii patrate
v-modifica indicele de linie
w-modifica indicele de coloana
*/

int main()
{
int k,sw,maxim=0; //maxim-retine latura celei mai mari constelatii
int i,j;

f>>m>>n;

for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
f>>t[i][j].x;

for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
{
sw=1;
for(k=0;k<8;k++) // verific cei 8 vecini
if(t[i][j].x<=t[i+v[k]][j+w[k]].x)
{
sw=0;
k=8;
}
if(sw)
{
nrs++; // numar stelele stralucitoare
t[i][j].s=1; // memorez steaua stralucitoare
}
}

g<<nrs<<’\n’;
for(i=1;i<=m;i++) // parcurg liniile
for(j=1;j<=n;j++) // parcurg coloanele
if(t[i][j].s==1)
{ // daca gasesc o stea stralucitoare
for(k=1;k<=n-j;k++)//caut urmatoarea stea stalucitoare
if(t[i][j+k].s==1) //daca o gasesc
if(i+k<=m && t[i+k][j].s==1 && t[i+k][j+k].s==1)
{
// verific daca exista stele stralucitoare
// in celelalte doua colturi
nrc++;
if(k+1>maxim)
CAPITOLUL 10. OJI 2014 10.1. PĂTRAT - OJI 2014 153

maxim=k+1;//retin maximum
}
}

g<<nrc<<’\n’<<maxim;
f.close();
g.close();
return 0;
}

Listing 10.1.5: patratvg.cpp


#include <iostream>
#include <fstream>

using namespace std;

ifstream f("patrat.in");
ofstream g("patrat.out");

const int valmax=1001;

int a[210][210],m,n,ss=0,cp=0,lmax=1;
int dx[]={0,-1,-1,-1,0,1,1,1};
int dy[]={1,1,0,-1,-1,-1,0,1};

void citire()
{
int i,j;
f>>m>>n;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
f>>a[i][j];

void afisare()
{
int i,j;

for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
g<<a[i][j]<<’ ’;
g<<endl;
}
}

void stralucire()
{
int i,j,p,k;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
{
p=0;
for(k=0;k<8;k++)
p=p+(a[i][j] > a[i+dx[k]][j+dy[k]]);

if(p==8)
a[i][j]=valmax,ss++;
}
}

void constelatie()
{
int i,j,k;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
if(a[i][j]==valmax)
{
for(k=1;k<=n-j&&k<=m-i;k++)
if((a[i][j+k]==valmax)&&
(a[i+k][j]==valmax)&&
(a[i+k][j+k]==valmax))
{
CAPITOLUL 10. OJI 2014 10.2. SCHI - OJI 2014 154

cp++;
if(k+1>lmax)
lmax=k+1;
}
}
}

int main()
{
citire();
stralucire();
// afisare();
constelatie();
g<<ss<<’\n’;
g<<cp<<’\n’;
g<<lmax<<’\n’;
return 0;
}

10.1.3 *Rezolvare detaliată

10.2 schi - OJI 2014


Problema 2 - schi 100 de puncte
La proba de sărituri cu schiurile din cadrul jocurilor olimpice de iarnă participă N concurenţi,
numerotaţi cu numere de la 1 la N .
Regulile de desfăşurare a probei sunt următoarele:
- concurenţii evoluează pe rând, ı̂n ordine de la 1 la N ;
- fiecare concurent va efectua o singură săritură;
- după efectuarea săriturii fiecare concurent primeşte un anumit punctaj;
- pe tot parcursul concursului, comisia de arbitri are obligaţia să alcătuiască o listă cu punc-
tajele obţinute de concurenţi, ı̂n ordinea evoluţiei lor;
- evoluţia unui concurent durează exact un minut;
- nu se face pauză ı̂ntre evoluţiile a doi concurenţi care au numere de ordine consecutive;
- afişarea punctajului nu necesită timp suplimentar după efectuarea săriturii;
- proba se ı̂ncheie la un minut după evoluţia ultimului concurent.
Pe tot parcursul concursului se ţine ı̂n mod neoficial şi un clasament parţial, pe baza rezultatelor
obţinute de concurenţii care au evoluat până ı̂n acel moment. Asta pentru că şeful comisiei de
arbitri are o curiozitate aparte şi pune K ı̂ntrebări sub forma următoare: Câte minute s-a ocupat
primul loc din clasament cu un punctaj egal cu X puncte ? Dacă nici un concurent nu s-a clasat
pe primul loc cu X puncte atunci primeşte ca răspuns valoarea 0.

Cerinţe

Scrieţi un program care determină răspunsul pentru fiecare dintre cele K ı̂ntrebări puse de
şeful comisiei de arbitri.

Date de intrare

În fişierul schi.in, pe prima linie este scris un număr natural, N reprezentând numărul de
concurenţi. Pe a doua linie a fişierului sunt scrise cele N numere naturale separate prin câte un
spaţiu, reprezentând punctajele obţinute de fiecare dintre cei N concurenţi, ı̂n ordinea ı̂n care
aceştia au evoluat. Pe a treia linie a fişierului este scris numărul natural K ce reprezintă numărul
de ı̂ntrebări puse de şef. Pe a patra linie a fişierului sunt scrise K numere naturale separate prin
câte un spaţiu, reprezentând valorile X ale punctajelor alese de şeful comisiei de arbitri.

Date de ieşire

În fişierul schi.out se vor scrie K numere, separate prin câte un spaţiu, reprezentând, ı̂n
ordine, răspunsurile la cele K ı̂ntrebări.

Restricţii şi precizări


CAPITOLUL 10. OJI 2014 10.2. SCHI - OJI 2014 155

a 1 & N & 100000


a 1 & K & 100000
a 0 & punctajele obţinute de concurenţi & 1000000000
a 0 & valorile X alese de şeful arbitrilor & 1000000000

Exemple

schi.in schi.out Explicaţii


10 014401 Cu punctajul 5 nu s-a ocupat niciodată locul 1, cu punctajul 1
1653688619 s-a ocupat primul loc un singur minut, cu punctajele 6 şi 8 s-a
6 ocupat locul 1 câte 4 minute. Cu punctajul 2 nu s-a ocupat locul
516829 1. El nici nu a fost obţinut de vreun concurent. Cu punctajul 9
s-a ocupat locul 1 un minut.

Timp maxim de executare/test: 0.5 secunde


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

10.2.1 Indicaţii de rezolvare


Autor: prof. Marius Nicoli, C. N. ’’Fratii Buzesti’’, Craiova

Observam ca sirul P al punctajelor cu care se ocupa primul loc odata cu trecerea


minutelor este unul crescator.

La o intrebare X ne intereseaza lungimea secventei in care valoarea X apare in


sirul crescator P.

-Pentru punctaj maxim, trebuie sa determinam atat prima cat si ultima pozitie
din P unde se gaseste valoarea X folosind algoritmul de cautarea binara.

-Alt mod de a obtine punctaj maxim este urmatorul: Se sorteaza intrebarile


(cu un algoritm rapid de ordonare) si se parcurg deodata sirul de intrebari
si cel al punctajelor, care este si el sortat.

Exista mai multe abordari prin care se pot obtine punctaje partiale:

-La fiecare intrebare X parcurgem sirul pana cind intalnim toate


aparitiile lui X;
-La fiecare intrebare X determinam prin cautare binara o pozitie
pe care se gaseste X in sirul P apoi ne deplasam la dreapta si
la stanga cat timp intalnim X (cazul defavorabil este cand X apare
de multe ori in P, situatie in care cautarea devine liniara).

-Pastram o structura de date cu rezultatele la toate intrebarile


puse deja. Astfel, nu va fi necesar sa procesam o intrebare de 2 ori.
In functie de structura aleasa vor aparea depasiri ale timpului
de executare impus sau a memoriei disponibile.

10.2.2 Cod sursă

Listing 10.2.1: schi.cpp


#include <fstream>

#define DIM 100010

using namespace std;

int v[DIM];
int n, i, T, st, dr, x, q, maxim;
CAPITOLUL 10. OJI 2014 10.2. SCHI - OJI 2014 156

int cautMinim(int x);


int cautMaxim(int x);

int main()
{
ifstream fin("schi.in");
ofstream fout("schi.out");

fin>>n;

maxim = -1;
for (i=1;i<=n;i++)
{
fin>>x;
if (x > maxim)
maxim = x;
v[i] = maxim;
}

fin>>q;
for (i=1;i<=q;i++)
{
fin>>x;
st = cautMinim(x);
if (st == -1)
fout<<"0 ";
else
{
dr = cautMaxim(x);
fout<<dr-st+1<<" ";
}
}

return 0;
}

int cautMinim(int x)
{
int p = 1, u = n, mid;

while (p<=u)
{
mid = (p+u)/2;
if (x<=v[mid])
{
u = mid-1;
}
else
{
p = mid+1;
}
}

if (v[p] == x)
return p;
else
return -1;
}

int cautMaxim(int x)
{
int p = 1, u = n, mid;

while (p<=u)
{
mid = (p+u)/2;
if (x>=v[mid])
{
p = mid+1;
}
else
{
u = mid-1;
}
CAPITOLUL 10. OJI 2014 10.2. SCHI - OJI 2014 157

if (v[u] == x)
return u;
else
return -1;
}

Listing 10.2.2: schidL.cpp


#include <cstdio>
#include <cassert>

#define MAXN 100010

#include <cstring>

using namespace std;

int N, M, K, c, i, j, v, Q, x, p;

struct punct{int p, t;};

int A[MAXN];
punct C[MAXN];

int cb(int x)
{
int st, dr, m, poz;
st=1; dr=v; poz=0;
while (st<=dr && poz==0)
{
m=(st+dr)/2;
if(C[m].p==x)
poz=m;
else
if (C[m].p>x) dr=m-1; else st=m+1;
}
return poz;
}

int main()
{
freopen("schi.in","r",stdin);
freopen("schi.out","w",stdout);

scanf("%d\n",&N);
assert(N>=1 && N<=100000);

C[0].p=-1; v=0;
for(i=1; i<=N; i++)
{
scanf("%d",&A[i]);
assert(A[i]>=0 && A[i]<=1000000000);
if(C[v].p>=A[i])
C[v].t++;
else
{
C[++v].p=A[i]; C[v].t=1;
}
}

scanf("%d\n",&Q);
assert(Q>=1 && Q<=100000 );

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


{
scanf("%d",&x);
assert(x>=0 && x<=1000000000);
p=cb(x);
if (p==0)
printf("0 " );
else
printf("%d ", C[p].t);
}
CAPITOLUL 10. OJI 2014 10.2. SCHI - OJI 2014 158

printf("\n");
return 0;
}

Listing 10.2.3: schidt.cpp


#include <fstream>

using namespace std;

ifstream f("schi.in");
ofstream g("schi.out");

struct
{
int x,y;
} v[100003];

int p;

int caut(int val)


{
int in=1,sf=p,mij=(in+sf)/2;
while(in<=sf)
{
if(v[mij].x==val) return mij;
else if(v[mij].x<val) in=mij+1;
else sf=mij-1;
mij=(in+sf)/2;
}
return 0;
}

int main()
{
int n,i,a,j=1,b,m;
f>>n>>a;
v[1].x=a;
v[1].y=1;

for(i=2;i<=n;i++)
{
f>>a;
if(a<=v[j].x)
v[j].y++;
else
v[++j].x=a;
}

p=j;
f>>m;
for(i=1;i<=m;i++)
{
f>>a;
b=caut(a);
if(b==0)
g<<0<<’ ’;
else
{
if(a==v[1].x)
g<<v[b].y<<’ ’;
else
g<<v[b].y+1<<’ ’;
}

g<<’\n’;

f.close();
g.close();
return 0;
}
CAPITOLUL 10. OJI 2014 10.2. SCHI - OJI 2014 159

Listing 10.2.4: schiNQ.cpp


#include <fstream>

#define DIM 100010

using namespace std;

int v[DIM];
int n, q, i, x, maxim, nr, j;

int main()
{
ifstream fin("schi.in");
ofstream fout("schi.out");

fin>>n;
maxim = -1;
for (i=1;i<=n;i++)
{
fin>>x;
if (x > maxim)
maxim = x;
v[i] = maxim;
}

fin>>q;
for (i=1;i<=q;i++)
{
fin>>x;

nr = 0;
for (j=1;j<=n;j++)
{
if (v[j] > x)
break;
if (x == v[j])
{
nr++;
}
}
fout<<nr<<" ";
}

return 0;
}

Listing 10.2.5: schiNQcb.cpp


#include <fstream>

#define DIM 100010

using namespace std;

int v[DIM];
int n, i, T, st, dr, x, q, maxim, mid;

int caut(int x);

int main()
{
ifstream fin("schi.in");
ofstream fout("schi.out");

fin>>n;
maxim = -1;
for (i=1;i<=n;i++)
{
fin>>x;
if (x > maxim)
maxim = x;
v[i] = maxim;
}
CAPITOLUL 10. OJI 2014 10.2. SCHI - OJI 2014 160

fin>>q;
for (i=1;i<=q;i++)
{
fin>>x;
mid = caut(x);
if (mid == -1)
fout<<"0 ";
else
{
st = mid;
while (v[st] == x)
st--;
st++;
dr = mid;
while (v[dr] == x)
dr++;
dr--;
fout<<dr-st+1<<" ";
}
}

return 0;
}

int caut(int x)
{
int p = 1, u = n, mid;

while (p<=u)
{
mid = (p+u)/2;
if (x==v[mid])
{
break;
}
else
{
if (x < v[mid])
u = mid-1;
else
p = mid+1;
}
}

if (p<=u)
return mid;
else
return -1;
}

Listing 10.2.6: schiSortQ.cpp


#include <fstream>
#include <algorithm>

#define DIM 100010

using namespace std;

struct qry
{
int v;
int p;
int r;
};

struct punctaj
{
int v;
int nr;
};

int cmpv(const qry &a, const qry &b)


{
CAPITOLUL 10. OJI 2014 10.2. SCHI - OJI 2014 161

return a.v < b.v;


}

int cmpp(const qry &a, const qry &b)


{
return a.p < b.p;
}

qry w[DIM];
int v[DIM];
punctaj s[DIM];

int n, i, x, k, L, maxim, q, j;

int main()
{
ifstream fin("schi.in");
ofstream fout("schi.out");

fin>>n;
v[0] = -1;

for (i=1;i<=n;i++)
{
fin>>x;
if (x > maxim)
maxim = x;
v[i] = maxim;

if (i>1 && v[i] > v[i-1])


{
s[++k].v = v[i-1];
s[k].nr = L;
}

if (v[i] == v[i-1])
L++;
else
L=1;
}

s[++k].v = v[n];
s[k].nr = L;

fin>>q;
for (i=1;i<=q;i++)
{
fin>>x;
w[i].v = x;
w[i].p = i;
}

sort(w+1,w+q+1,cmpv);

i = 1;
j = 1;

while (i<=q && j<=k)


{
if (w[i].v == s[j].v)
{
w[i].r = s[j].nr;
i++;
}
else
if (w[i].v < s[j].v)
i++;
else
j++;
}

sort(w+1, w+q+1, cmpp);

for (i=1;i<=q;i++)
fout<<w[i].r<<" ";
CAPITOLUL 10. OJI 2014 10.2. SCHI - OJI 2014 162

return 0;
}

10.2.3 *Rezolvare detaliată


Capitolul 11

OJI 2013

11.1 compar - OJI 2013


Problema 1 - compar 100 de puncte
Ana şi Bogdan au inventat jocul ”Compar”. Ana scrie pe tablă o secvenţă formată din N
numere naturale distincte cuprinse ı̂ntre 1 şi N , apoi compară fiecare două numere ı̂nvecinate din
secvenţă scriind ı̂ntre ele semnul ¡ sau semnul ¿, după caz.
De exemplu, dacă secvenţa de pe tablă este 6 4 2 1 3 5, după compararea elementelor ı̂nvecinate
şi inserarea semnelor ı̂n secvenţă, Ana obţine:
6%4%2%1$3$5
După aceea Ana şterge cele N elemente ale secvenţei şi păstrează numai semnele, astfel:
%%%$$
La final, Ana ı̂i arată lui Bogdan şirul semnelor şi ı̂i cere să reconstituie secvenţa de numere
naturale scrisă iniţial pe tablă.
Cerinţe
Cunoscând şirul semnelor construit de Ana, scrieţi un program care să ı̂l ajute pe Bogdan să
reconstituie secvenţa de numere naturale distincte scrisă iniţial pe tablă.
Date de intrare
Fişierul de intrare compar.in conţine pe prima linie o secvenţă de caractere din mulţimea {’¡’,
’¿’}, reprezentând şirul semnelor obţinut de Ana după compararea elementelor vecine din secvenţa
iniţială.
Date de ieşire
Fişierul de ieşire compar.out va conţine pe prima linie numărul natural N , reprezentând
lungimea secvenţei iniţiale. Pe a doua linie vor fi scrise N numere naturale distincte cuprinse ı̂ntre
1 şi N , separate prin câte un spaţiu, reprezentând elementele secvenţei iniţiale, reconstituită pe
baza semnelor din fişierul de intrare.
Restricţii şi precizări
a 1 $ N & 100000
a Dacă există mai multe soluţii, afişaţi oricare dintre acestea.
a Pentru determinarea corectă a lungimii secvenţei se acordă 10% din punctajul pe test.

Exemple
compar.in compar.out Explicaţii
¿¿¿¡¡ 6 6¿4¿2¿1¡3¡5
642135 Există şi alte soluţii posibile, aceasta este doar una dintre ele.

Timp maxim de executare/test: 0.2 secunde


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

163
CAPITOLUL 11. OJI 2013 11.1. COMPAR - OJI 2013 164

11.1.1 Indicaţii de rezolvare


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

Citim secventa de semne intr-un vector de caractere si calculam numarul de


caractere citite.

Incepem sa reconstituim secventa initiala incepand de la valoarea crt=1.

Parcurgem secventa de semne si daca semnul curent este < atunci scriu
valoarea curenta, apoi o incrementez.

Daca insa semnul curent este > identific intreaga subsecventa care urmeaza
formata numai din semnul >; sa consideram ca lg este lungimea secventei;
vom plasa valorile de la crt pana la crt+lg in ordine descrescatoare in
secventa reconstituita.

Obtinem intotdeauna cea mai mica solutie din punct de vedere lexicografic.

Nu sunt necesare operatii cu siruri de caractere.

Complexitatea solutiei este liniara.

Solutia 2 - prof. Dana Marcu

Se considera un minim, initial 1 si un maxim, initial egal cu n.

Se parcurge sirul.

Daca pe pozitia curenta, i, din sir se afla caracterul ’<’, se afiseaza


valoarea minimului si se reactualizeaza prin incrementare cu 1.

Daca caracterul curent din sir este ’>’, se afiseaza maximul si


se micsoreaza cu 1 valoarea acestuia.

11.1.2 Codul sursă

Listing 11.1.1: compar dm.cpp


//Dana Marcu 100 puncte
#include<cstring>
#include<fstream>

using namespace std;

char s[100000];

int main()
{
ifstream fin("compar.in");
ofstream fout("compar.out");

fin>>s;
int N;
N=strlen(s)+1;
fout<<N<<"\n";

int maxx=N, minn=1;

for(int i=0;i<N;i++)
if(s[i]==’<’)
fout<<minn<<" ",minn++;
else
CAPITOLUL 11. OJI 2013 11.2. UNIFIC - OJI 2013 165

fout<<maxx<<" ",maxx--;

fout.close();
return 0;
}

Listing 11.1.2: compara en.cpp


//Eugen Nodea 100 puncte
# include <fstream>
# include <cstring>

# define lmax 100001

using namespace std;

ifstream f("compar.in");
ofstream g("compar.out");

int i,N,Max,Min,j,k;
char s[lmax];

int main()
{
f.getline(s,lmax);
N=strlen(s);
g<<N+1<<"\n";

Max=N+1;
Min=1;
i=0;
while(i<N)
{
if (s[i]==’<’)
{
g<<Min<<" ";
++Min; ++i;
}
else
{
j=i;
while (s[j]==’>’ && j<N) ++j;
for (k=i;k<j;++k)
{
g<<Max<<" ";
--Max;
}
i=j;
}
}

if (s[N-1]==’<’)
g<<Min<<"\n";
else
g<<Max<<"\n";

return 0;
}

11.1.3 *Rezolvare detaliată

11.2 unific - OJI 2013


Problema 2 - unific 100 de
puncte
Se consideră un şir A A1 , A2 , ..., AN , format din N numere naturale nenule. Două numere
se consideră vecine dacă se află pe poziţii alăturate (Ai are ca vecini pe Ai1 şi Ai1 , pentru orice
1 $ i $ N , A1 are ca vecin doar pe A2 , iar AN are ca vecin doar pe AN 1 ).
CAPITOLUL 11. OJI 2013 11.2. UNIFIC - OJI 2013 166

Dacă două elemente vecine Ai , Ai1 (1 & i $ N ) au cel puţin o cifră comună, ele se pot unifica.
Procedeul de unificare constă ı̂n eliminarea din numerele Ai şi Ai1 a tuturor cifrelor comune
şi adăugarea prin alipire a numărului obţinut din Ai1 la numărul obţinut din Ai , formându-se
astfel un nou număr. Numărul Ai va fi ı̂nlocuit cu noul număr, iar numărul Ai1 va fi eliminat din
şir. (De exemplu, numerele Ai 23814 şi Ai1 40273 au cifrele 2, 3, 4 comune, după unificare
obţinem Ai 817, iar Ai1 este eliminat; observaţi că dacă după eliminarea cifrelor comune,
numerele ı̂ncep cu zerouri nesemnificative, acestea vor fi eliminate, apoi se realizează alipirea).
Dacă ı̂n urma eliminării cifrelor comune, unul dintre numere nu mai are cifre, atunci numărul
rezultat va avea cifrele rămase ı̂n celălalt. Dacă ı̂n urma eliminării cifrelor comune atât Ai cât şi
Ai1 nu mai au cifre, atunci ambele numere vor fi eliminate din şir, fără a fi ı̂nlocuite cu o altă
valoare.
Ordinea ı̂n care se fac unificările ı̂n şir este importantă: la fiecare pas se alege prima pereche de
elemente vecine Ai Ai1 care poate fi unificată, considerând şirul parcurs de la stânga la dreapta.
(De exemplu, considerând Ai 123, Ai1 234, Ai2 235, se unifică Ai cu Ai1 % Ai 14,
iar unificarea cu următorul număr nu mai este posibilă).

Cerinţe

Cunoscându-se şirul celor N numere naturale, să se determine:


a) cifra care apare cel mai frecvent ı̂n scrierea tuturor celor N numere; dacă există mai multe
cifre cu aceeaşi frecvenţă de apariţie maximă, se va reţine cea mai mică cifră.
b) şirul obţinut prin efectuarea unui număr maxim de unificări, după regulile descrise ı̂n enunţ.

Date de intrare

Fişierul de intrare unific.in conţine pe prima linie o valoare naturală N , iar pe următoarele
N linii, ı̂n ordine, cele N numere naturale din şirul A, câte un număr pe o linie.

Date de ieşire

Fişierul de ieşire unific.out va conţine pe prima linie un număr natural c reprezentând cifra
care apare cel mai frecvent ı̂n scrierea celor N numere naturale. Pe cea de a doua linie un număr
natural N r reprezentând numărul de numere naturale rămase ı̂n şir după efectuarea unui număr
maxim de unificări. Pe cea de a treia linie se vor scrie cele N r numere naturale rămase, ı̂n ordinea
din şir, separate prin câte un spaţiu.
Dacă ı̂n urma procedeului de unificare, toate numerele vor fi eliminate, fişierul de ieşire va
conţine o singură linie, pe care se va scrie cifra care apare cel mai frecvent ı̂n scrierea celor N
numere naturale.

Restricţii şi precizări

a 1 & N & 100000


a Numerele din şirul iniţial, precum şi numerele obţinute ı̂n urma unificărilor, nu vor depăşi
1018
a Pentru datele de test şirul obţinut ı̂n urma unificărilor este nevid.
a Pentru 30% dintre teste N & 1000
a Pentru 70% dintre teste numere naturale din şir au cifrele nenule.
a Pentru determinarea corectă a primei cerinţe se acordă 10% din punctajul pe test. Punctajul
integral se acordă pe ambele cerinţe rezolvate corect.

Exemple

unific.in unific.out Explicaţii


CAPITOLUL 11. OJI 2013 11.2. UNIFIC - OJI 2013 167

10 3 Cifra care apare cel mai frecvent este 3 (de 6 ori).


6 2 Se unifică: 47 cu 67 =¿ 46. şirul rămas: 6 46 40 123 231 1238 331 2035
47 0 837 50007
67 Se unifică: 6 cu 46 =¿ 4. şirul rămas: 4 40 123 231 1238 331 2035 50007
40 Se unifică: 4 cu 40 =¿ 0. şirul rămas: 0 123 231 1238 331 2035 50007
123 Se unifică: 123 cu 231, ambele numere rămân fără cifre, deci vor fi
231 ambele eliminate.
1238 şirul rămas: 0 1238 331 2035 50007
331 Se unifică: 1238 cu 331 =¿ 28. şirul rămas: 0 28 2035 50007
2035 Se unifică: 28 cu 2035 =¿ 835. şirul rămas: 0 835 50007
50007 Se unifică: 835 cu 50007 =¿ 837. şirul rămas: 0 837.

Timp maxim de executare/test: 1.0 secunde


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

11.2.1 Indicaţii de rezolvare


prof. Eugen Nodea Colegiul National ’’Tudor Vladimirescu’’, Tg-Jiu

Solutia propusa analizeaza secvential numerele inca din citirea din fisier.

a)
Pentru realizarea primei cerinta se construieste vectorul caracteristic al
cifrelor care apar in scrierea celor N numere naturale din sir.

b)
Trebuie avut in vedere ca prin unificarea lui A[i] cu A[i+1], numarul nou
obtinut in A[i] poate genera la randul lui posibile unificari cu numerele
anterior determinate A[i-1],A[i-2], ...

Citeste a[1]
i = 1,
Cat timp Not Eof() Executa
Citeste xa - valoarea curenta
Daca comun(a[i], xa)
Atunci
a[i] = unifica (a[i],xa);

//verificam posibile unificari


Cat timp (comun(a[i],a[i-1]) && i>1) Executa
a[i-1] = unific(a[i-1],a[i])
i--;
Sf. cat timp
Altfel
a[++i] = xa
Sf. daca
Sf. cat timp

Un caz aparte il reprezinta numerele care contin cifra 0.

O solutie brute-force care elimina prin deplasare elementele


(A[i])i=1,N obtine cel mult 50p.

11.2.2 Cod sursă


CAPITOLUL 11. OJI 2013 11.2. UNIFIC - OJI 2013 168

Listing 11.2.1: unific em.cpp


//Emanuela Cerchez 100 puncte
#include <fstream>
#include <cassert>

#define NMAX 100001

using namespace std;

ifstream fin("unific.in");
ofstream fout("unific.out");

int n;
long long int a[NMAX], rez, vmax;
int nrc[10];
int cmax;
int c[10];//cifrele comune perechii curente

int comun(long long int x, long long int y);


int concat(long long int x, long long int y, long long int &rez);
void numara_cifre(long long int x);

int main()
{int i, ok, nr, maxim, cmax,ii;
for (i=vmax=1; i<=18; i++) vmax=vmax*10;

fin>>n;
assert(1<=n && n<=100000);

fin>>a[0]; nr=1;
numara_cifre(a[0]);
assert(a[0]<=vmax);

for (i=1; i<n; i++,nr++)


{fin>>a[nr]; assert(a[nr]<=vmax); assert(fin.good());
numara_cifre(a[nr]);
//fout<<i<<’ ’<<a[nr]<<": ";
while (nr>0 && comun(a[nr],a[nr-1]))
{
ok=concat(a[nr-1],a[nr], rez);
if (ok) {a[nr-1]=rez; nr--;}
else {nr-=2;}
}
//{for (ii=0; ii<=nr; ii++) fout<<a[ii]<<’ ’;fout<<’\n’;}
}

for (maxim=-1, i=0; i<10; i++) if (nrc[i]>maxim) {maxim=nrc[i]; cmax=i;}


fout<<cmax<<’\n’;
fout<<nr<<’\n’;

if (nr) {for (i=0; i<nr; i++) fout<<a[i]<<’ ’;fout<<’\n’;}


fout.close();
return 0;
}

int comun(long long int x, long long int y)


//returnez 1 daca x si y au cifre in comun
{int cy[10], i;
//c[i]=vectorul caracteristic al multimii cifrelor comune
for (i=0; i<10; i++) cy[i]=c[i]=0;
do{c[x%10]=1; x/=10;} while (x);
do {cy[y%10]=1; y/=10;} while(y);
for (i=0; i<10; i++) c[i]=c[i]&&cy[i];
for (i=0; i<10; i++) if (c[i]) return 1;
return 0;
}

int v[20];

int concat(long long int x, long long int y, long long int &rez)
//returneaza 0 daca dupa unificare rezultattul nu mai are cifre;
{
int lgrez=0, lg, i;
rez=0;
do {if (!c[x%10]) v[lgrez++]=x%10; x/=10;} while (x);
CAPITOLUL 11. OJI 2013 11.2. UNIFIC - OJI 2013 169

while (lgrez>1 && !v[lgrez-1]) {lgrez--;}


for (i=lgrez-1; i>=0; i--) rez=rez*10+v[i];
lg=0;
do {if (!c[y%10]) v[lg++]=y%10; y/=10;} while (y);
while (lg>1 && !v[lg-1]) {lg--;}
lgrez+=lg;
assert(lgrez<=18);
//lipesc cifrele din v la rez
for (i=lg-1; i>=0; i--) rez=rez*10+v[i];
return lgrez;
}

void numara_cifre(long long int x)


{if (!x) {nrc[0]++; return ;}
while (x) {nrc[x%10]++;x/=10;} }

Listing 11.2.2: unific eugen 1.cpp


/*
Solutie 100 p
prof. Eugen Nodea
*/
# include <cstdio>

using namespace std;

int N, k, i, c;
long long a[100001], xa, x;
int ap[10];

// construire vect. frecventa de aparitii


void aparitii(long long x)
{
while(x > 0)
{
ap[x % 10]++;
x /= 10;
}
}

//verific daca doua valori vecine au cifre comune


inline int comun(long long x, long long y)
{
short c, uz[10];

for (c = 0; c <= 9; ++c)


uz[c]=0;

if (x == 0)
uz[0] = 1;
else
while (x > 0)
{
uz[x % 10] = 1;
x /= 10;
}

if (y == 0 && uz[0] == 1)
return 1;

while(y > 0)
{
c = y % 10; y /= 10;
if (uz[c]) return 1;
}

return 0;
}

//unific (y alipit la x)
inline long long unific (long long y, long long x)
{
short c, i = 0, uz[10], uc;
long long z, p;
CAPITOLUL 11. OJI 2013 11.2. UNIFIC - OJI 2013 170

for (c = 0; c <= 9; ++c)


uz[c]=0;

z = y;
if (z == 0)
uz[0] = 1;
else
while (z > 0)
{
uz[z % 10] = 1;
z /= 10;
}

z = x;
if (z == 0 && uz[0])
uz[0] = 2;
else
{
p = 1; x = 0;
while (z > 0)
{
c = z % 10;
if (uz[c] == 0)
{
x = x + c * p;
p *= 10;
}
else uz[c] = 2;
z /= 10;
}
}

if (y > 0)
{
z = y; p = 1; y = 0;
while (z > 0)
{
c = z % 10;
if (uz[c] == 1)
{
y = y + c * p;
p *= 10; ++i; uc = c;
}
z /= 10;
}
}

if (x * y > 0)
{
if (uc == 0) p /= 10;
return x * p + y;
}
else
{
if (x > 0)
{
if (i > 0) x *= 10;
return x;
}
else
{
if (i == 0) y = -1;
return y;
}
}
}

int main()
{
FILE * f = fopen("unific.in", "r");
FILE * g = fopen("unific.out", "w");

fscanf(f, "%d", &N);

//punem in vector primul numar


k = 1;
CAPITOLUL 11. OJI 2013 11.2. UNIFIC - OJI 2013 171

i = 1;
fscanf(f, "%lld", &a[k]); aparitii(a[k]);

while (i < N)
{
//citim valoarea curenta
fscanf(f, "%lld", &a[++k]); aparitii(a[k]);
++i;

//unific cat timp pot


while(comun(a[k-1], a[k]) && k > 1)
{
x = unific(a[k], a[k-1]);
if (x == -1) k -= 2;
else a[k-1] = x, --k;
}
}

c = 0;
for (i = 1; i<=9; ++i)
if (ap[i] > ap[c]) c = i;

fprintf(g, "%d\n%d\n", c, k);


for (i=1; i<k; ++i)
fprintf(g, "%lld ", a[i]);
fprintf(g, "%lld\n", a[k]);

return 0;
}

11.2.3 *Rezolvare detaliată


Capitolul 12

OJI 2012

12.1 arme - OJI 2012


Problema 1 - arme 100 de puncte
Vasile joacă (din nou!) jocul său preferat cu ı̂mpuşcături. Personajul său are la brâu N arme,
aşezate ı̂n N huse speciale, numerotate de la 1 la N . Arma din husa i are puterea pbi (1 & i & N ).
În camera armelor a găsit M arme, aşezate pe perete, ı̂n M locaţii, numerotate de la 1 la M .
Pentru fiecare armă j (1 & j & M ) este cunoscută puterea sa pcj .
Vasile poate ı̂nlocui arme pe care le are la brâu cu arme aflate pe perete ı̂n camera armelor.
La o ı̂nlocuire el ia arma de pe perete din locaţia j (1 & j & M ) şi o pune la brâu ı̂n husa i
(1 & i & N ), iar arma din husa i o pune pe perete ı̂n locaţia j.

Cerinţe

Scrieţi un program care să determine suma maximă a puterilor armelor pe care le va avea la
brâu Vasile după efectuarea ı̂nlocuirilor.

Date de intrare

Fişierul de intrare arme.in conţine pe prima linie numerele naturale N M , reprezentând


numărul de arme pe care le are la brâu, respectiv numărul de arme aflate ı̂n camera armelor. Pe
a doua linie se află N numere naturale pb1 pb2 ... pbN reprezentând ı̂n ordine puterile armelor pe
care Vasile le are la brâu. Pe a treia linie se află M numere naturale pc1 pc2 ... pcM reprezentând
ı̂n ordine puterile armelor aflate ı̂n camera armelor. Numerele scrise pe aceeaşi linie sunt separate
prin spaţiu.

Date de ieşire

Fişierul de ieşire arme.out va conţine o singură linie pe care va fi scrisă suma maximă a
puterilor armelor de la brâul lui Vasile, după efectuarea ı̂nlocuirilor.

Restricţii şi precizări

a 1 & N, M & 1000


a Puterile armelor sunt numere naturale & 10000.

Exemple
arme.in arme.out
32 16
317
45

Timp maxim de executare/test: 0.2 secunde


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

172
CAPITOLUL 12. OJI 2012 12.1. ARME - OJI 2012 173

12.1.1 Indicaţii de rezolvare


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

Solutie 1 - Emanuela Cerchez


----------------------------
Vom utiliza metoda Greedy.

Sortam armele de la brau crescator dupa punctaj.


Sortam si armele din camera armelor crescator dupa punctaj.
Parcurgem in ordine crescatoare armele de la brau si le inlocuim pe cele
mai mici arme de la brau cu cele mai mari din camera armelor
(doar daca punctajul se mareste).

Solutie 2 - Marinel serban


--------------------------
Utilizand interclasarea a doi vectori sortati (puterile armelor), insumam
primele n valori din cei doi vectori sortati descrescator. Nu retinem
vectorul realizat dupa interclasare.

Solutie 3 - Marcu Ovidiu/Mot Nistor


-------------------------------------
Se ordoneaza crescator sirul puterilor armelor de la brau si descrecator
sirul puterilor armelor de pe perete. Se inlocuieste arma cu cea mai mica
putere cu arma cu putere maxima dintre cele aflate pe perete
(daca este posibil) si asa mai departe pana cand nu mai putem alege nimic
de pe perete (pbi>=pci) sau nu mai avem arme la dispozitie (m<n).

Suma initiala a puterilor pbi va creste la fiecare inlocuire cu diferenta


pci-pbi.

O scurta justificare a faptului ca acest mod de alegere conduce la suma maxima


este urmatoarea demonstratie:
Notam :
-sirul puterilor armelor de la brau cu a[1..n] si
-sirul puterilor de pe perete cu b[1..m].

Figura 12.1: armeIR

12.1.2 Cod sursă

Listing 12.1.1: arme Adrian.cpp


CAPITOLUL 12. OJI 2012 12.1. ARME - OJI 2012 174

//Pintea Adrian
#include <iostream>
#include <fstream>

using namespace std;

ifstream fin("arme.in");
ofstream fout("arme.out");

int n,m,i,j,p[2001],aux,rezultat;

int main()
{
fin>>n>>m;
for(i=0;i<n;i++)
fin>>p[i];
for(i=n;i<n+m;i++)
fin>>p[i];

rezultat=0;
for(i=0;i<n;i++)
{
for(j=i+1;j<n+m;j++)
{
if (p[i]<p[j])
{
aux=p[i];
p[i]=p[j];
p[j]=aux;
}
}

rezultat+=p[i];
}

fout<<rezultat<<"\n";
fout.close();
return 0;
}

Listing 12.1.2: arme ema.cpp


//Emanuela Cerchez
#include <fstream>

using namespace std;

#define InFile "arme.in"


#define OutFile "arme.out"
#define NMAX 1001

struct arma {short int nr, p;} b[NMAX], c[NMAX], mut[NMAX];


short int N, M, cate;
long int smax;

char T[10][10];
char s[100];

void citire();
void afisare();
void sortare(arma[], short int);
long int suma();

int main()
{
citire();
sortare(b,N);
sortare(c,M);
smax=suma();
afisare();
return 0;
}

void citire()
CAPITOLUL 12. OJI 2012 12.1. ARME - OJI 2012 175

{
int i;
ifstream fin(InFile);
fin>>N>>M;
for (i=1; i<=N; i++)
{fin>>b[i].p; b[i].nr=i;}
for (i=1; i<=M; i++)
{fin>>c[i].p; c[i].nr=i;}
}

void sortare(arma b[NMAX], short int N)


{
short int i, sch;
arma aux;
do
{
sch=0;
for (i=1; i<N; i++)
if (b[i].p>b[i+1].p)
{aux=b[i]; b[i]=b[i+1]; b[i+1]=aux; sch=1;}
}while (sch);
}

long int suma()


{
short int i, j, aux;
long int s=0;
cate=0;
for (i=1, j=M; i<=N && j>0;i++, j--)
if (b[i].p<c[j].p)
{
aux=b[i].p; b[i].p=c[j].p; c[j].p=aux;
mut[cate].nr=i; mut[cate].p=j; cate++;
}
else
break;
for (i=1; i<=N; i++) s+=b[i].p;
return s;
}

void afisare()
{
int i;
ofstream fout(OutFile);
fout<<smax<<’\n’;
/*
fout<<cate<<’\n’;
for (i=0; i<cate; i++)
fout<<mut[i].nr<<’ ’<<mut[i].p<<’\n’;
*/
fout.close();
}

Listing 12.1.3: arme Ovidiu.cpp


//Marcu Ovidiu
#include<iostream>

using namespace std;

int a[1001],b[1001],m,n,i,j,s;

int cmp1(const void *p,const void *q)


{
int x=*(int *)p;
int y=*(int *)q;
//return x-y;
if(x>=y)
return +1;
else
return -1;
}

int cmp2(const void *p,const void *q)


{
CAPITOLUL 12. OJI 2012 12.2. TRIUNGHI - OJI 2012 176

int x=*(int *)p;


int y=*(int *)q;
//return x-y;
if(x>=y)
return -1;
else
return +1;
}

void afis(int x[1001],int k)


{
int j;
for(j=1;j<=k;j++)
cout<<x[j]<<" ";
cout<<endl;
}

int main()
{
freopen("arme.in","r",stdin);
freopen("arme.out","w",stdout);

cin>>n>>m;

for(i=1;i<=n;i++)
{
cin>>a[i];
s=s+a[i];
}

for(i=1;i<=m;i++)
cin>>b[i];

qsort(a+1,n,sizeof(a[0]),cmp1);//a crescator
qsort(b+1,m,sizeof(b[0]),cmp2);//b descrescator
//afis(a,n);afis(b,m);

int k=n;
if(m<n)
k=m;
i=1;
while(i<=k && a[i]<b[i])
{
s=s+(b[i]-a[i]);
i++;
}

cout<<s;
return 0;
}

12.1.3 *Rezolvare detaliată

12.2 triunghi - OJI 2012


Problema 2 - triunghi 90 de
puncte
Se consideră un triunghi alcătuit din numere naturale scrise pe n linii
ca ı̂n figura alăturată.
Liniile triunghiului sunt numerotate de la 1 la n, ı̂ncepând cu linia de
la baza triunghiului (linia de jos), iar poziţiile pe linie sunt numerotate
ı̂ncepând cu 1 de la stânga la dreapta.
Fiecare număr din triunghi, exceptând pe cele de pe linia 1, este egal
cu suma numerelor aflate imediat sub el, ı̂n stânga şi respectiv ı̂n dreapta
lui.

Cerinţe Figura 12.2: triunghi


CAPITOLUL 12. OJI 2012 12.2. TRIUNGHI - OJI 2012 177

Cunoscând câte un număr de pe fiecare linie a triunghiului, determinaţi toate numerele de pe


linia 1.

Date de intrare

Fişierul de intrare triunghi.in conţine pe prima linie numărul natural n reprezentând numărul
de linii din triunghi. Pe următoarele n linii sunt descrise informaţiile despre triunghi. Mai exact,
pe linia i (1 & i & n) dintre cele n se află două numere naturale separate prin spaţiu pi vi indicând
poziţia şi respectiv valoarea numărului cunoscut de pe linia i a triunghiului.

Date de ieşire

Fişierul de ieşire triunghi.out va conţine o singură linie, pe care se găsesc n numere naturale
separate prin câte un spaţiu, reprezentând ı̂n ordinea de la stânga la dreapta numerele scrise pe
linia 1 a triunghiului.

Restricţii şi precizări

a 1 & n & 1000


a 1 & pi & n  1  i, pentru 1 & i & n
a Toate numerele din triunghi sunt numere naturale cu cel mult 18 cifre.
a Pentru elevii care implementează ı̂n C: când utilizaşi MINGW, dacă citiţi/afişaţi valori
de tip long long int folosind scanf(), respectiv printf() utilizaţi specificatorul de format %I64d.

Exemple

triunghi.in triunghi.out Explicaţii


5 12342 Triunghiul este:
44
25 45
3 13 20 25
2 25 8 12 13
1 45 3 5 7 6
1 2 3 4 2

Timp maxim de executare/test: 0.2 secunde


Memorie: total 20M B din care pentru stivă 10M B
Dimensiune maximă a sursei: 5KB

12.2.1 Indicaţii de rezolvare


prof. Nistor Mot C. N. ’’N. Balcescu’’ Braila

Solutia1 - reconstruirea matricii (Pintea Adrian, Nodea Eugen, Marcu Ovidiu)


----------------------------------------------------------------------------
Evident, cunoscand singurul numar de pe linia n si unul dintre cele doua
numere de pe linia n-1 il putem afla imediat pe celalalt, printr-o scadere.

Apoi, cunoscand linia n-1 si unul dintre numerele de pe linia n-2 le putem afla
pe celelalte doua, tot prin scaderi etc.

Trebuie sa fim doar atenti cum facem scaderile: daca cunoastem numerele de pe
linia i: vi, 1, vi, 2,..., vi, n+1-i si de pe linia i+1 cunoastem valoarea
V=v[i+1,p], de pe pozitia p, calculam numerele de pe pozitiile p+1, p+2, ..
succesiv: vi+1, p+1=v[i,p]-v[i+1,p], vi+1, p+2=v[i,p+1]-vi+1, p+1 etc., iar
numerele de pe pozitiile anterioare p-1, p-2, ...1
la fel: vi+1,p-1=vi,p-1-vi,p, etc.

Solutia 2 - utilizarea doar a 2 vectori


CAPITOLUL 12. OJI 2012 12.2. TRIUNGHI - OJI 2012 178

(Mot Nistor, serban Marinel, Nodea Eugen, Marcu Ovidiu)


-------------------------------------------------------
Nu am folosit matrice ci, ca si in triunghiul lui Pascal, numai doua
linii: linia anterioara L1 care este completata si linia curenta, L2,
care se calculeaza in acest moment.

Dupa calculare linia curenta L2 devine linie precedenta L1 si procesul


continua. Initial linia L1 este formata doar din elementul din varful
triunghiului L1[1].

12.2.2 Cod sursă

Listing 12.2.1: tri adrian .cpp


//Adrian Pintea
#include <fstream>
//#include <iostream>

using namespace std;

ifstream fin("triunghi.in");
ofstream fout("triunghi.out");

long long a[1001][1001],p[1001],x,i,j,n;

int citire()
{
fin >> n;
for (i=n;i>0;i--) { fin>>p[i]>>x; a[i][p[i]]=x;}
fin.close();

//for (i = 1; i <= n; i++)


//{
//for (j = 1; j <= i; j++)
//cout << a[i][j] << ’ ’;
//cout << ’\n’;
//}
return 0;
}

//matricea de sus in jos


int construire()
{
for (i = 2; i <= n; i++)
{
if(p[i]>1)
{ for(j=p[i]-1;j>0;j--) a[i][j]=a[i-1][j]-a[i][j+1];
for(j=p[i];j<=i;j++) a[i][j]=a[i-1][j-1]-a[i][j-1];
} else for(j=p[i]+1;j<=i;j++) a[i][j]=a[i-1][j-1]-a[i][j-1];
}

//for (i = 1; i <= n; i++)


//{
//for (j = 1; j <= i; j++)
// cout << a[i][j] << ’ ’;
// cout << ’\n’;
// }

return 0;
}

int afisare()
{
for (j = 1; j <= n; j++)
fout << a[n][j] << ’ ’;
return 0;
}

int main()
{
citire();
CAPITOLUL 12. OJI 2012 12.2. TRIUNGHI - OJI 2012 179

construire();
afisare();
fout.close();
return 0;
}

Listing 12.2.2: tri marinel .cpp


//Serban Marinel //februarie 2012
#include <fstream>

#define DIM 1001

using namespace std;

ifstream fin("triunghi.in");
ofstream fout("triunghi.out");

int N, p[DIM];
unsigned long long L1[DIM], L2[DIM], v[DIM];

void citire()
{
int i;
fin >> N;
for (i = N; i >0; i--) //le citesc invers ca sa lucrez
fin >> p[i] >> v[i]; //"mai usor" la reconstruire
fin.close();
}

void refa()
{
int i, j;
for (i = 2; i <= N; i++) //refa linia i (de sus in jos)
{
L2[p[i]] = v[i]; //pun in L2 valoarea cunoscuta
if (p[i] > p[i - 1]) //detectez sensul recalcularii
{
for (j = p[i] - 1; j > 0 ; j--) //intai spre stanga
L2[j] = L1[j] - L2[j + 1];
for (j = p[i] + 1; j <= i; j++) //apoi spre freapta
L2[j] = L1[j - 1] - L2[j - 1];
}
else
{
for (j = p[i] + 1; j <= i; j++) //intai spre dreapta
L2[j] = L1[j - 1] - L2[j - 1];
for (j = p[i] - 1; j > 0; j--) //apoi spre stanga
L2[j] = L1[j] - L2[j + 1];
}
for (j = 1; j <= i; j++) //copiez L2 in L1
L1[j] = L2[j]; //adica L2 devine L1
for (j = 1; j <= i; j++) //"curat" L2
L2[j] = 0; //nu era neaparat necesar
}
}

void afisare()
{
int i;
for (i = 1; i < N; i++) //afisez primele N-1 valori
fout << L1[i] << ’ ’; //urmate de un spatiu
fout << L1[N] << ’\n’; //apoi ultima valoare urmata de ENTER
}

int main()
{
citire();
L1[1] = v[p[1]];
refa();
afisare();
fout.close();
return 0;
}
CAPITOLUL 12. OJI 2012 12.2. TRIUNGHI - OJI 2012 180

Listing 12.2.3: triunghi Eugen00.cpp


//Nodea Eugen
# include <fstream>

using namespace std;

ifstream f("triunghi.in");
ofstream g("triunghi.out");

long long a[1001][1001], i, n, j, k, v[1001],p[1001];

int main()
{
f>>n;
for (i=n;i>0;--i)
{
f>>p[i]>>v[i];
}
a[1][1]=v[1];
for (i=2;i<=n;++i)
{
//refac linia i
j=p[i];
a[i][j]=v[i];
//refac stanga
k=j-1;
while (k)
{
a[i][k]=a[i-1][k]-a[i][k+1];
--k;
}
//refac dreapta
k=j+1;
while (k<=i)
{
a[i][k]=a[i-1][k-1]-a[i][k-1];
++k;
}
}
// for (i=1;i<=n;++i)
// {
for (j=1;j<=n;++j)
g<<a[n][j]<<’ ’;
// g<<’\n’;
// }
return 0;
}

Listing 12.2.4: triunghi Eugen11.cpp


//Nodea Eugen
# include <fstream>

using namespace std;

ifstream f("triunghi.in");
ofstream g("triunghi.out");

long long a[1001][1001];


long long i, n, j, k, v[1001], p[1001];

int main()
{
f>>n;
for (i=n; i>0; --i)
{
f>>p[i]>>v[i];
}
a[1][1]=v[1];
for (i=2; i<=n; ++i)
{
//refac linia i
j=p[i];
a[i][j]=(long long)v[i];
CAPITOLUL 12. OJI 2012 12.2. TRIUNGHI - OJI 2012 181

//refac stanga
k=j-1;
while (k)
{
a[i][k]=a[i-1][k]-a[i][k+1];
--k;
}
//refac dreapta
k=j+1;
while (k<=i)
{
a[i][k]=a[i-1][k-1]-a[i][k-1];
++k;
}
}

for (j=1; j<n; ++j)


g<<a[n][j]<<’ ’;
g<<a[n][n]<<’\n’;

return 0;
}

Listing 12.2.5: triunghi Eugen22.cpp


//nodea eugen
# include <fstream>

using namespace std;

ifstream f("triunghi.in");
ofstream g("triunghi.out");

long long L[2][1001];


long long i, n, j, k, v[1001], p[1001];

int main()
{
f>>n;
for (i=n; i>0; --i)
{
f>>p[i]>>v[i];
}
L[0][1]=v[1];
for (i=2; i<=n; ++i)
{
//refac linia i
j=p[i];
L[1][j]=(long long)v[i];

//refac stanga
k=j-1;
while (k)
{
L[1][k]=L[0][k]-L[1][k+1];
--k;
}

//refac dreapta
k=j+1;
while (k<=i)
{
L[1][k]=L[0][k-1]-L[1][k-1];
++k;
}

//copiez linia
for (j=1; j<=i; ++j)
L[0][j]=L[1][j];
}

for (j=1; j<n; ++j)


g<<L[1][j]<<’ ’;
g<<L[1][n]<<’\n’;
CAPITOLUL 12. OJI 2012 12.2. TRIUNGHI - OJI 2012 182

return 0;
}

Listing 12.2.6: triunghi O Marcu matrice.cpp


//Marcu Ovidiu
#include<iostream>
#include<iomanip>

using namespace std;

long long a[1001][1001],p,v,x[1001],m,n,i,j;

void citire()
{cin>>n;
for(i=n;i>=1;i--)
{cin>>p>>v;
x[i]=p;
// y[i]=v;
a[i][p]=v;
}
}

void sol()
{for(i=1;i<=n;i++)
{ p=x[i];//a[i][p]are valoare
for(j=p+1;j<=i;j++) // completez la dreapta
a[i][j]=a[i-1][j-1]-a[i][j-1];
for(j=p-1;j>=1;j--)
a[i][j]=a[i-1][j]-a[i][j+1];
}
}

void afis()
{
unsigned int i;
for(i=1;i<=n;i++)
cout<<a[n][i]<<" ";
}

int main()
{freopen("triunghi.in","r",stdin);
freopen("triunghi.out","w",stdout);
citire();
sol();
afis();

return 0;
}

Listing 12.2.7: triunghi O Marcuo.cpp


//Marcu Ovidiu
#include<iostream>
#include<iomanip>

using namespace std;

long long p,v,x[1001],y[1001],s1[1001],s2[1001],m,n,i,j;

void citire()
{cin>>n;
for(i=n;i>=1;i--)
{cin>>p>>v;
x[i]=p;
y[i]=v;
}
}

void sol()
{ s1[x[1]]=y[1];
for(i=2;i<=n;i++)
{ p=x[i];
v=y[i];
CAPITOLUL 12. OJI 2012 12.2. TRIUNGHI - OJI 2012 183

s2[p]=v;
for(j=p+1;j<=i;j++)
s2[j]=s1[j-1]-s2[j-1];
for(j=p-1;j>=1;j--)
s2[j]=s1[j]-s2[j+1];
for(j=1;j<=i;j++)
s1[j]=s2[j];
}
}

void afis()
{
int i;
for(i=1;i<=n;i++)
cout<<s1[i]<<" ";
cout<<endl;
}

int main()
{
freopen("triunghi.in","r",stdin);
freopen("triunghi.out","w",stdout);

citire();
sol();
afis();

return 0;
}

12.2.3 *Rezolvare detaliată


Capitolul 13

OJI 2011

13.1 grupe - OJI 2011


Problema 1 - grupe 100 de puncte
Se consideră un tablou bidimensional cu m linii, n coloane şi elemente numere naturale. Pentru
fiecare element se determină numărul de divizori pozitivi. Se formează apoi grupe cu elementele
tabloului care au acelaşi număr de divizori, grupe notate G1 , G2 , ..., Gk . Se ordonează descrescător
grupele după numărul de elemente ce le conţin. Se ştie că o grupă G1 se află ı̂n faţa unei alte grupe
G2 dacă G1 are mai multe elemente decât G2 sau, ı̂n cazul ı̂n care cele două grupe conţin acelaşi
număr de elemente, numărul de divizori ai elementelor din grupa G1 este mai mare decât numărul
de divizori ai elementelor din grupa G2 . După ordonarea descrescătoare a grupelor, notăm prima
grupă cu A şi a doua grupă cu B. În cazul ı̂n care toate elementele vor avea acelaşi număr de
divizori, va exista o singură grupă, grupa A.

Cerinţe

Scrieţi un program care citeşte m, n, elementele tabloului şi afişează:


a) numărul de divizori pozitivi pentru grupa A, numărul de elemente din grupă şi cea mai
mare valoare din grupă;
b) numărul de divizori pozitivi pentru grupa B, numărul de elemente din grupă şi cea mai
mare valoare din grupă; ı̂n cazul ı̂n care nu există grupa a doua, se va afişa de trei ori valoarea 0.

Date de intrare

Fişierul grupe.in conţine pe prima linie valorile lui m şi n separate printr-un spaţiu, iar pe
celelalte m linii câte n elemente separate două câte două printr-un spaţiu, reprezentând elementele
tabloului.

Date de ieşire

Fişierul grupe.out va conţine:


- pe prima linie valoarea numărului de divizori pozitivi din grupa A, numărul de elemente din
grupa A şi cea mai mare valoare din grupa A, valori separate două câte două printr-un singur
spaţiu;
- pe a doua linie valoarea numărului de divizori pozitivi din grupa B, numărul de elemente
din grupa B şi cea mai mare valoare din grupa B, valori separate două câte două printr-un singur
spaţiu.

Restricţii şi precizări

- 0 $ m, n $ 101;
- elementele tabloului bidimensional iniţial sunt mai mici sau egale decât 100000 şi mai mari
decât 1;
- o grupă poate fi compusă dintr-un singur element.
Se acordă punctaje parţiale:
- 50% pentru afişarea valorilor corecte pe prima linie din fişierul grupe.out (cerinţa a));
- 50% pentru afişarea valorilor corecte pe a doua linie din fişierul grupe.out (cerinţa b)).

184
CAPITOLUL 13. OJI 2011 13.1. GRUPE - OJI 2011 185

Exemple
grupe.in grupe.out Explicaţii
23 4 2 10 Numărul divizorilor pentru fiecare element al tabloului: 5 divizori
16 2 4 225 (pentru valoarea 16), 2 divizori (pentru valoarea 2), 3 divizori (pentru
10 6 5 valoarea 4), 4 divizori (pentru valoarea 10), 4 divizori (pentru valoarea
6) şi 2 divizori (pentru valoarea 5).
Se pot forma grupele: cu 2 divizori (elementele 2, 5), cu 4 divizori
(elementele 10,6), cu 3 divizori (elementul 4) şi cu 5 divizori (elementul
16). După ordonarea descrescătoare a grupelor, grupele cu cele mai
multe elemente sunt cele care conţin 2 elemente: (10, 6), respectiv
(2, 5). Pentru că elementele 10 şi 6 au 4 divizori, ele vor face parte
din grupa A, iar 2 şi 5, având doar 2 divizori fiecare, vor face parte
din grupa B. Deci grupa A are 4 divizori, 2 elemente şi cel mai mare
element din grupă este 10, iar grupa B are 2 divizori, 2 elemente şi cel
mai mare element din grupă este 5.
23 4 3 15 Numărul divizorilor pentru fiecare element al tabloului: 2 divizori
2 15 4 225 (pentru valoarea 2), 4 divizori (pentru valoarea 15), 3 divizori (pentru
10 6 5 valoarea 4), 4 divizori (pentru valoarea 10), 4 divizori (pentru valoarea
6) şi 2 divizori (pentru valoarea 5). După ordonarea descrescătoare a
grupelor, grupa cu cele mai multe elemente este cea formată din ele-
mentele 10, 6, 15, fiecare element având exact 4 divizori. Aceasta va fi
grupa A. Grupa B va fi cea formată din două elemente, celelaltă grupă
având un singur element. Deci grupa A are 4 divizori, 3 elemente şi
cel mai mare element din grupă este 15, iar grupa B are 2 divizori, 2
elemente şi cel mai mare element din grupă este 5.

Timp maxim de executare/test: 1.0 secunde

13.1.1 Indicaţii de rezolvare


Timplaru Roxana - Liceul de Informatica ’’Stefan Odobleja’’, ISJ Dolj

Pentru fiecare element se determina numarul de divizori pozitivi. Se poate opta


pentru modificarea elementului cu valoarea numarului sau de divizori, dar nu se
mai poate determina astfel cel mai mare element dintr-o grupa. De aceea, se va
folosi un vector v in care v[i] reprezinta numarul de elemente care au exact i
divizori pozitivi si vectorul w in care w[i] reprezinta cel mai mare element
care are exact i divizori pozitivi.

Se poate opta pentru utilizarea a doua matrice de dimensiuni mai mici, in a doua
pastrandu-se numarul de divizori pozitivi pentru fiecare element, dar o astfel
de solutie nu va functiona pentru toate testele.

Sursele care nu verifica eficient numarul de divizori nu vor primi punctaj maxim.

13.1.2 Cod sursă

Listing 13.1.1: RCGRUPE.CPP


#include <fstream>
#include <math.h>

using namespace std;

ifstream f("grupe.in");
ofstream g("grupe.out");

long int maxx[200], x[200];


CAPITOLUL 13. OJI 2011 13.2. LITERE - OJI 2011 186

long int nr_div(long int x)


{
long int i,nr=2;

for(i=2;i<=sqrt(x);i++)
if (x%i==0)
nr+=2;

if ((int)sqrt(x)==sqrt(x))
nr--;
return nr;
}

int main()
{
long int nr,i,max1,max2,poz1,poz2,n,m;
long int a;
f>>n>>m;

for(i=1;i<=n*m;i++)
{
f>>a;
nr=nr_div(a);
x[nr]++;
if (a>maxx[nr]) maxx[nr]=a;
}
f.close();

max1 = x[2];
poz1 = 2;
max2=0;
for(i=3;i<=200;i++)
if (x[i]>=max1)
{
max2 = max1;
poz2 = poz1;
max1 = x[i];
poz1 = i;
}
else
if (x[i]>=max2)
{
max2 = x[i];
poz2 = i;
}

g<<poz1<<" "<<x[poz1]<<" "<<maxx[poz1]<<endl;

if (max2==0)
g<<"0 0 0";
else
g<<poz2<<" "<<x[poz2]<<" "<<maxx[poz2];
g.close();

return 0;
}

13.1.3 *Rezolvare detaliată

13.2 litere - OJI 2011


Problema 2 - litere 100 de puncte
Algorel a primit un joc care conţine n jetoane pe care sunt scrise litere mari ale alfabetului.
Fiecare literă are asociat un cod format dintr-o singură cifră nenulă. Jetoanele se aşează ı̂n ordinea
dată iniţial, iar prin citirea literelor de pe acestea, de la primul la ultimul jeton, se formează un
cuvânt. Dacă se citesc numerele de pe fiecare jeton, ı̂ncepând de la primul la ultimul, se obţine
un număr k1 . Jocul continuă la fel, dar se aşează jetoanele ı̂ncepând de la al doilea la ultimul,
obţinându-se un nou număr k2 . Apoi, se aşează jetoanele ı̂ncepând de la al treilea la ultimul,
CAPITOLUL 13. OJI 2011 13.2. LITERE - OJI 2011 187

obţinându-se un nou număr k3 , ş.a.m.d. până se ajunge la aşezarea doar a ultimului jeton, caz ı̂n
care se obţine numărul kn .

Cerinţe

Scrieţi un program care citeşte numărul n de jetoane, cele n litere asociate jetoanelor, precum
şi codurile asociate literelor, ı̂n ordinea apariţiei lor şi afişează:
a) numărul de perechi de litere consecutive din cuvântul iniţial care au proprietatea că o literă
este vocală şi cealaltă este consoană (ordinea lor nu contează);
b) numărul k1 , format din aşezarea iniţială a jetoanelor;
c) suma k1  k2  ...  kn .

Date de intrare

Fişierul de intrare litere.in va conţine pe prima linie valoarea lui n, reprezentând numărul de
jetoane, pe a doua linie un cuvânt format din n litere mari (de la ’A’ la ’Z’); literele sunt scrise una
după alta, fără să fie separate cu spaţii, astfel ı̂ncât prima literă este cea aflată pe primul jeton, a
doua literă pe al doilea jeton ş.a.m.d. Pe a treia linie din fişier se află un număr m ce reprezintă
numărul de litere distincte, iar pe a patra linie m valori reprezentând codurile literelor distincte ce
apar ı̂n cuvânt. Codurile sunt date ı̂n ordinea apariţiei literelor ı̂n cuvânt şi sunt numere naturale
nenule formate dintr-o singură cifră, separate printr-un spaţiu, pentru fiecare literă codul fiind dat
o singură dată, chiar dacă litera se repetă.

Date de ieşire

Fişierul de ieşire litere.out va conţine pe prima linie numărul de perechi de litere consecutive
din cuvânt care au proprietatea că o literă este vocală şi cealaltă consoană (ordinea lor nu con-
tează), pe a doua linie numărul k1 , (format din aşezarea iniţială a jetoanelor), iar pe a treia linie
suma k1  k2  ...  kn .

Restricţii şi precizări

- 0 $ n & 10000
- 0 $ m $ 27
Se acordă punctaje parţiale:
- 20% pentru afişarea valorii corecte pe prima linie din fişierul rezultat (cerinţa a));
- 40% pentru afişarea valorii corecte pe a doua linie din fişierul rezultat (cerinţa b));
- 40% pentru afişarea valorii corecte pe a treia linie din fişierul rezultat (cerinţa c)).

Exemple

litere.in litere.out Explicaţii


3 0 Nu există perechi de litere consecutive care să ı̂ndeplinească cerinţa.
CSC 121 Sunt 2 litere distincte {’C’, ’S’}, cod(’C’)=1, cod(’S’)=2. k1 121,
2 143 k2 21, k3 1, iar k1  k2  k3 143.
12
6 5 Există 5 perechi de litere consecutive care să ı̂ndeplinească cerinţa:
CABABE 256567 ’CA’, ’AB’, ’BA’, ’AB’, ’BE’. Sunt 4 litere distincte {’C’, ’A’, ’B’,
4 320342 ’E’}, cod(’C’)=2, cod(’A’)=5, cod(’B’)=6, cod(’E’)=7. k1 256567,
2567 k2 56567, k3 6567, k4 567, k5 67, k6 7, iar k1  k2  k3 
k4  k5  k6 320342

Timp maxim de executare/test: 1.0 secunde

13.2.1 Indicaţii de rezolvare


solutie posibila pentru 100 puncte

//citirea datelor folosind siruri de caractere(char)


// se numara succesiunile de forma vocala-consoana sau consoana-vocala
// text[i]=vocala si text[i+1]=consoana sau
// text[i+1]=vocala si text[i]=consoana
CAPITOLUL 13. OJI 2011 13.2. LITERE - OJI 2011 188

//calcularea sumelor
inc=100;
for(i=0;i<n;i++) {
sum[inc+i]=(i+1)*cost[text[i]];
}
//deplasarea cifrelor daca este cazul
for(i=n-1;(i>0)||(sum[inc+i]>9);i--) {
sum[inc+i-1]+=sum[inc+i]/10;
sum[inc+i]%=10;
}
//scrierea rezultatelor in formatul cerut

13.2.2 Cod sursă

Listing 13.2.1: litere.cpp


//O(N)
#include <stdio.h>
#include <string.h>

char S[30002];
char c;
char *voc = "AEIOU";
long int a, N, C, k, j, i, T, aux;
char P[130], cod[130];
long int L[30];

int main()
{
FILE *f = fopen("litere.in","r");
FILE *g = fopen("litere.out","w");
fscanf(f,"%d",&N);
fscanf(f,"%s",S+1);

a = 0;
k = 0;
for (i=1;i<N;i++)
{
if ((strchr(voc, S[i]) && !strchr(voc, S[i+1])) ||
(!strchr(voc, S[i]) && strchr(voc, S[i+1])))
{
a++;
}
if (!P[S[i]])
P[S[i]] = ++k;
}
if (!P[S[N]])
P[S[N]] = ++k;
fprintf(g,"%d\n",a);
fscanf(f,"%d",&C);

for (i=1;i<=C;i++)
{
fscanf (f,"%d",&L[i]);
}

for (i=1;i<=N;i++)
S[i] = L[P[S[i]]];

for (i=1;i<=N;i++)
{
fprintf(g,"%c",S[i] + ’0’);
}

for (i=1,j=N;i<=j;i++,j--)
{
c = S[i];
S[i] = S[j];
S[j] = c;
}
CAPITOLUL 13. OJI 2011 13.2. LITERE - OJI 2011 189

for (i=1, T = 0;i<=N;i++)


{
aux = S[i] * (N-i+1) + T;
S[i] = aux%10;
T = aux/10;
}

while (T)
{
S[++N] = T%10;
T/=10;
}

fprintf(g,"\n");

for (i=N;i>=1;i--)
{
fprintf(g,"%c",S[i] + ’0’);
}

fclose(f);
fclose(g);
return 0;
}

13.2.3 *Rezolvare detaliată


Capitolul 14

OJI 2010

14.1 Cuvinte - OJI 2010


Problema 1 - Cuvinte 100 de puncte
Se consideră un şir de cuvinte separate două câte două printr-un spaţiu. Fiecare cuvânt este
caracterizat prin numărul de ordine care reprezintă poziţia lui ı̂n şirul de cuvinte (primul cuvânt
are numărul de ordine 1). Unui cuvânt i se pot aplica ı̂n mod repetat următoarele transformări:
primul caracter al cuvântului (cel mai din stânga) se şterge de acolo şi se adaugă după ultimul
caracter din cuvânt. Astfel, dintr-un cuvânt s cu k caractere se pot obţine alte k  1 cuvinte
pe care le numim cuvinte obţinute din transformarea cuvântului s. De exemplu, dintr-un cuvânt
format din 4 caractere c1 c2 c3 c4 , cuvintele obţinute prin transformarea lui sunt: c2 c3 c4 c1 , c3 c4 c1 c2 ,
c4 c1 c2 c3 .
Se caută ı̂n şirul de cuvinte prima pereche de cuvinte vecine a, b, ı̂n care al doilea cuvânt
din pereche (cuvântul b) este identic cu un cuvânt obţinut din transformarea lui a. Dacă există o
astfel de pereche, se şterge cuvântul b din şir. Prin ştergerea cuvântului b din şir, acesta va avea
mai puţin cu un cuvânt! Se repetă operaţia de căutare de mai sus până când ı̂n şirul rămas nu
mai există o pereche a, b de cuvinte vecine, astfel ı̂ncât b să fie obţinut prin transformarea lui a.
Se ştie că pe parcursul modificărilor, cuvintele nu-şi schimbă numerele de ordine pe care le-au
avut iniţial.

Cerinţe

Scrieţi un program care să citească şirul de cuvinte şi să afişeze:
a) numărul de ordine al primului cuvânt şters sau valoarea 0 ı̂n cazul ı̂n care nu se şterge niciun
cuvânt
b) numerele de ordine ale cuvintelor rămase după finalizarea operaţiilor de modificare.

Date de intrare

Fişierul cuvant.in conţine o singură linie pe care se află şirul de cuvinte separate două câte
două printr-un spaţiu.

Date de ieşire

Fişierul cuvant.out va conţine:


- pe prima linie numărul de ordine al primului cuvânt şters sau valoarea 0 ı̂n cazul ı̂n care nu
se şterge niciun cuvânt
- pe a doua linie numerele de ordine ale cuvintelor rămase ı̂n final ı̂n şirul de cuvinte, despărţite
două câte două printr-un spaţiu.

Restricţii şi precizări

- După ultimul cuvânt din şirul iniţial există caracterul !


- Fiecare cuvânt are maxim 10 caractere, iar ı̂n şirul iniţial nu există mai mult de 25 cuvinte.
- şirul de cuvinte iniţial este format din cel puţin un cuvânt. O pereche de cuvinte vecine a, b,
din şirul de cuvinte este caracterizată prin faptul că, după cuvântul a se afla imediat cuvântul b.
- Se acordă punctaje parţiale: cerinţa a) 40% din punctaj, cerinţa b) 60% din punctaj

190
CAPITOLUL 14. OJI 2010 14.1. CUVINTE - OJI 2010 191

Exemple
cuvinte.in cuvinte.out
alfa faal alfa fala lafa afal calfa calfa! 2
13478

Explicaţii:
Cuvintele obţinute prin transformarea cuvântului alfa sunt: lfaa, faal, aalf. Prima pereche
de cuvinte vecine care ı̂ndeplinesc cerinţele sunt cuvintele cu numerele de ordine 1 şi 2. Va fi şters
cuvântul cu numărul de ordine 2. şirul rezultat este format din următoarele 7 cuvinte: alfa alfa
fala lafa afal calfa calfa. Prima pereche care ı̂ndeplineşte cerinţele din noul şir este perechea
formată din cuvintele fala şi lafa, având numerele de ordine 4 şi 5, pentru că lista de cuvinte
obţinute prin transformarea cuvântului fala sunt: alaf, lafa, afal. Se va şterge cuvântul cu
numărul de ordine 5. şirul rezultat după ştergere este: alfa alfa fala afal calfa calfa. Prima
pereche care ı̂ndeplineşte cerinţele problemei ı̂n noul şir este fala şi afal. Se şterge afal cu numărul
de ordine 6. şirul rezultat după ştergere este: alfa alfa fala calfa calfa. ı̂n acest şir nu se mai
găseşte nicio pereche care să ı̂ndeplinească cerinţele. Au rămas deci cuvintele: alfa, alfa, fala,
calfa, calfa care au numerele de ordine 1, 3, 4, 7, 8.

Timp maxim de executare/test: 1.0 secunde

14.1.1 Indicaţii de rezolvare


Timplaru Roxana Liceul de Informatica ’’Stefan Odobleja’’ Craiova

Pentru primul cuvant se determina toate cuvintele obtinute prin transformarea lui.

Se verifica apoi cuvantul al doilea in lista de cuvinte obtinuta anterior.

Daca acesta se regaseste in lista, se sterge si se verifica al treilea cuvant daca


este in lista, daca si acesta este se sterge si se continua in acelasi mod pana nu
mai exista cuvinte in lista.

In momentul in care se gaseste un cuvant care nu mai e xista in lista,


se construieste lista de cuvinte pentru acesta cu cuvintele obtinute prin
transformarea sa. Se continua in acelasi mod pana se epuizeaza toate cuvintele.

14.1.2 Cod sursă

Listing 14.1.1: 7 pr1 s.cpp


#include<fstream>
#include<string.h>

using namespace std;

char x[25][255],y[25][255];
int poz[25];

void transforma(char s[255])


{
unsigned int i;
for(i=0;i<strlen(s);i++)
{
strncat(s,s,1);
strcpy(s,s+1);
strcpy(y[i],s);
}
}

int verifica(unsigned int j, unsigned int n)


{
unsigned int i;
for(i=0;i<n;i++)
{
CAPITOLUL 14. OJI 2010 14.2. ZAR - OJI 2010 192

if (strcmp(x[j],y[i])==0)
return 1;
}

return 0;
}

int main()
{
char s[255],*p;
unsigned int n,i=0,t,j,prima=0;

ifstream f("cuvant.in");
ofstream g("cuvant.out");

f.get(s,255);
p=strtok(s," !");

while(p)
{
strcpy(x[i++],p);
poz[i-1]=i;
p=strtok(NULL," !");
}

n=i;
i=0;
j=1;
while(i<n-1)
{
transforma(x[i]);
if (verifica(j,strlen(x[i])-1))
{
for(t=j;t<n-1;t++)
{
strcpy(x[t],x[t+1]);
poz[t]=poz[t+1];
}

if (prima==0)
prima=j+1;

n--;
}
else
{
i++;
j=i+1;
}
}

if (poz)
g<<prima<<"\n";
else
g<<"0"<<"\n";

for(j=0;j<n;j++)
g<<poz[j]<<" ";

f.close();
g.close();

return 0;
}

14.1.3 *Rezolvare detaliată

14.2 Zar - OJI 2010


Problema 2 - Zar 100 de puncte
CAPITOLUL 14. OJI 2010 14.2. ZAR - OJI 2010 193

Zarul folosit la diverse jocuri este un cub care are desenat pe fiecare
faţă a sa 1, 2, 3, 4, 5 sau 6 puncte. Pe un zar nu există două feţe cu
acelaşi număr de puncte şi suma punctelor de pe oricare două feţe opuse
este egală cu 7.
Pe o masă de joc este desenat un traseu ı̂n formă de pătrat, cu latura
de dimensiune n. Fiecare latură a traseului este ı̂mpărţită ı̂n n pătrăţele Figura 14.1: triunghi
identice, care au latura egală cu cea a zarului. Zarul este aşezat iniţial ı̂n
colţul din stânga sus al traseului şi apoi rostogolit de pe o faţă pe alta,
din pătrăţel ı̂n pătrăţel, de-a lungul traseului parcurs ı̂n sensul acelor de ceasornic.
În orice moment ne-am uita la zar, putem vedea numărul punctelor desenate pe trei din feţele
sale (aşa cum se vede ı̂n desenul de mai sus).
Notăm cu f 1 faţa cubului orientată spre noi, f 2 faţa superioară a cubului, respectiv cu f 3
faţa laterală din dreapta. Pentru exemplul din figură: n 4, faţa dinspre noi (f 1) conţine trei
puncte, faţa superioară (f 2) conţine două puncte, faţa laterală din dreapta (f 3) conţine un punct,
iar sensul de deplasare este cel precizat prin săgeţi.

Cerinţe

Cunoscând dimensiunea n a traseului şi numărul punctelor de pe cele trei feţe ale zarului ı̂n
poziţia iniţială, determinaţi după k rostogoliri numărul punctelor ce se pot observa pe fiecare din
cele trei feţe ale zarului.

Date de intrare

Fişierul zar.in conţine:


- pe prima linie numerele naturale n şi k despărţite print-un spaţiu.
- pe linia a doua trei numere naturale separate printr-un spaţiu ce corespund numărului de
puncte de pe feţele f 1, f 2, respectiv f 3 ale zarului ı̂n poziţia iniţială.

Date de ieşire

Fişierul zar.out va conţine o singură linie cu trei numere naturale separate prin câte un spaţiu,
care reprezintă numărul punctelor ce se pot observa pe feţele f 1, f 2 şi f 3 (ı̂n această ordine) după
ce au fost efectuate krostogoliri pe traseul dat.

Restricţii şi precizări

2 & n & 20000, 1 & k & 1000000

Exemple
zar.in zar.out
4 11 153
321

Explicaţii:
Fiecare latură a traseului este formată din 4 căsuţe şi se vor efectua 11 rostogoliri. După prima
rostogolire spre dreapta, valorile celor trei feţe (f 1, f 2, respectiv f 3) ale zarului vor fi 3, 6 şi 2.
După a doua rostogolire obţinem numerele 3, 5, 6, iar după a treia rostogolire valorile feţelor vor
fi 3, 1 şi 5. ı̂n acest moment zarul a parcurs o latură a traseului. Următoarele trei rostogoliri se
vor efectua ı̂n jos, de-a lungul traseului iar feţele vor avea succesiv valorile 1, 4, 5 apoi 4, 6, 5 şi 6,
3, 5. Urmează rostogolirile spre stânga, pe feţele zarului vom observa valorile 6, 5, 4 apoi 6, 4, 2
şi respectiv 6, 2, 3. Ultimele două rostogoliri se vor efectua ı̂n sus de-a lungul laturii din stânga a
traseului. După penultima rostogolire obţinem 5, 6, 3, iar după ultima rostogolire valorile feţelor
vor fi 1, 5 şi 3.

Timp maxim de executare/test: 1.0 secunde


CAPITOLUL 14. OJI 2010 14.2. ZAR - OJI 2010 194

14.2.1 Indicaţii de rezolvare


Iordaiche Cristina Liceul ’’Grigore Moisil’’ Timisoara

Pozitia fetelor zarului este periodica. Din acest motiv, din patru in patru
rostogoliri pe aceeasi latura a traseului, pe fetele zarului se vor observa
aceleasi valori.

Pentru rostogoliri spre stanga sau spre dreapta de-a lungul traseului, vom
tine cont de modificarea valorilor celor patru fete implicate in rostogilirea
pe aceasta directie:
f2 f3 7-f2 7-f3
Analog vom proceda pentru fetele implicate in rostogoliri in sus sau in jos
de-a lungul celorlaltor doua laturi ale traseului. Cele patru valori ale
fetelor implicate in acest tip de rostogolire sunt:
f2 7-f1 7-f2 f1
Prin permutari ale acestor valori, vom obtine pozitia finala a zarului dupa
k rostogoliri pe traseul dat.

14.2.2 Cod sursă

Listing 14.2.1: 7 pr2 s.cpp


#include <fstream>
#include<iostream>

using namespace std;

ifstream f("zar.in");
ofstream g("zar.out");

int st_dr[4], sus_jos[4], f1, f2, f3;


long n,k;

void rostogolire_dreapta(int st_dr[],long p)


{
int i,j,aux;
p=p%4;
for(i=1;i<=p;i++)
{
aux=st_dr[1];
for(j=1;j<=3;j++)
st_dr[j]=st_dr[j+1];
st_dr[4]=aux;
}
}

void rostogolire_stanga(int st_dr[],long p)


{
int i,j,aux;
p=p%4;
for(i=1;i<=p;i++)
{
aux=st_dr[4];
for(j=4;j>=1;j--)
st_dr[j]=st_dr[j-1];
st_dr[1]=aux;
}
}

int main()
{
f>>n>>k;
f>>f1>>f2>>f3;

st_dr[1]=7-f2;
st_dr[2]=f3;
st_dr[3]=f2;
st_dr[4]=7-f3;
CAPITOLUL 14. OJI 2010 14.2. ZAR - OJI 2010 195

sus_jos[1]=7-f2;
sus_jos[2]=f1;
sus_jos[3]=f2;
sus_jos[4]=7-f1;

while(k>0)
{
if (k<n)//dreapta
{
rostogolire_dreapta(st_dr,k);
k=0;
}
else
{
rostogolire_dreapta(st_dr,n-1);
k=k-(n-1);
}

sus_jos[1]=st_dr[1];
sus_jos[3]=st_dr[3];

if (k<n)//jos
{
rostogolire_dreapta(sus_jos,k);
k=0;
}
else
{
rostogolire_dreapta(sus_jos,n-1);
k=k-(n-1);
}

st_dr[1]=sus_jos[1];
st_dr[3]=sus_jos[3];

if (k<n)//stanga
{
rostogolire_stanga(st_dr,k);
k=0;
}
else
{
rostogolire_stanga(st_dr,n-1);
k=k-(n-1);
}

sus_jos[1]=st_dr[1];
sus_jos[3]=st_dr[3];

if (k<n)//sus
{
rostogolire_stanga(sus_jos,k);
k=0;
}
else
{
rostogolire_stanga(sus_jos,n-1);
k=k-(n-1);
}

st_dr[1]=sus_jos[1];
st_dr[3]=sus_jos[3];
}

g<<sus_jos[2]<<" "<<st_dr[3]<<" "<<st_dr[2];

return 0;
}

14.2.3 *Rezolvare detaliată


Part II

ONI - Olimpiada naţională de


informatică

196
Capitolul 15

ONI 2023

15.1 dominew
Problema 1 - Dominew 100 de puncte
Pentru că se plictiseşte şi este foarte inteligent, Radu l-a rugat pe prietenul lui, savantul
Feder, să creeze o activitate care să-i pună mintea la ı̂ncercare. Savantul Feder a adus N piese
dreptunghiulare pe care sunt scrise numere naturale şi le-a aşezat pe masă ı̂n ordinea crescătoare
a valorilor scrise pe ele, pe poziţii consecutive, una lângă cealaltă. Apoi ı̂i dă lui Radu, una câte
una, alte M piese dreptunghiulare, pe care sunt scrise numere naturale, ı̂ntr-o ordine oarecare.
Când Radu primeşte o piesă el trebuie să o aşeze ı̂n şirul de pe masă pe cea mai mică poziţie
posibilă, astfel ı̂ncât piesele din şir să rămână ı̂n ordine crescătoare. Evident, şirul de pe masă
se modifică pe măsură ce Radu aşază piesele ı̂n şir.

Cerinţă

Cunoscând şirul pieselor de pe masă, ı̂n ordinea ı̂n care sunt aşezate, precum şi cele M piese
pe care le primeşte succesiv Radu, scrieţi un program care să afişeze pentru fiecare dintre cele M
piese poziţia pe care aceasta este aşezată ı̂n şir.

Date de intrare

Fişierul dominew.in conţine pe prima linie numărul natural N . Pe a doua linie se află N
numere naturale, ı̂n ordine crescătoare, reprezentând valorile pieselor din şirul aflat iniţial pe
masă. Pe a treia linie se află numărul natural M . Pe cea de-a patra linie sunt M numere naturale,
reprezentând valorile pieselor pe care le primeşte Radu, ı̂n ordinea ı̂n care acesta le primeşte.
Numerele scrise pe aceeaşi linie sunt separate prin câte un spaţiu.

Date de ieşire

Fişierul de ieşire dominew.out va conţine o singură linie pe care vor fi scrise M valori separate
prin câte un spaţiu, cea de a i-a valoare fiind poziţia pe care este aşezată ı̂n şirul de pe masă cea
de a i-a piesă primită de Radu (1 & i & M ).

Restricţii şi precizări

ˆ 2&N & 1 000 000


ˆ 1&M & 8 000
ˆ Valorile scrise pe piese sunt numere naturale & 109 .
ˆ Pozit, iile elementelor din şirul de pe masă sunt numerotate ı̂ncepând cu 1.

# Punctaj Restricţii
1 8 M 1
2 14 1 $ N, M & 100
3 8 100 $ N & 50 000, 100 $ M & 250
4 24 N & 100 000, 250 $ M & 1 000
5 46 N & 1 000 000, 1 000 $ M & 8 000

197
CAPITOLUL 15. ONI 2023 15.1. DOMINEW 198

Exemple:

dominew.in dominew.out Explicaţii


6 219 Exemplul 1: Iniţial pe masă se află
2 5 5 9 10 11 N = 6 piese, ı̂n ordine crescătoare.
3 2 5 5 9 10 11 Radu primeşte M 3
5 1 12 piese.

Prima piesă are valoarea 5 şi va fi


aşezată ı̂n şirul de pe masă pe poziţia
2. Şirul va deveni 2 5 5 5 9 10 11.

A doua piesă are valoarea 1, va fi


aşezată ı̂n şirul de pe masă pe poziţia
1. Şirul va deveni 1 2 5 5 5 9 10 11.

A treia piesă are valoarea 12 şi va fi


aşezată pe poziţia 9 ı̂n şirul de pe
masă.

Şirul va deveni 1 2 5 5 5 9 10 11 12.


14 11 5 13 1 12 20 18 Exemplul 2: După primele 3 inserări,
2 2 2 4 7 8 9 10 12 16 20 21 23 24 şirul va fi 2 2 2 4 7 7 8 9 10 12 16 18
7 20 20 21 23 24, valoarea 20 ajungând
18 7 20 1 16 25 23 pe poziţia 13.

15.1.1 Indicaţii de rezolvare

Propusă de: Prof. Flavius Boian, Colegiul Naţional ”Spiru Haret”, Târgu Jiu
La citire se reţin elementele ı̂n 2 vectori distincţi (primul va avea N elemente, iar cel de-al
doilea M elemente).
În primul rând, se poate observa că numărul maxim de actualizări este mult mai mic decât
numărul de valori iniţiale (M & 8 000), mult mai mic decât valoarea maximă a lui N , ceea ce ne
duce cu gândul la a procesa separat cei doi vectori pentru a obţine răspunsul ı̂ntr-un timp optim.
Pe măsură ce se citesc elementele din cel de-al doilea vector se identifică poziţia pe care ar
trebui inserate acestea ı̂n vectorul sortat până la acel moment. Vom nota valoarea pe care o
verificăm la un pas cu x.
Pentru fiecare element din şirul de M valori, va trebui mai ı̂ntâi să căutăm binar ı̂n vectorul cu
valorile iniţiale poziţia ı̂n care se află cea mai mare valoare mai mică decât x. Să notăm această
poziţie cu a.
Apoi, vom afla şi câte valori din vectorul de lungime M situate ı̂naintea valorii curente sunt
mai mici decât x. Datorită valorii mici a lui M , putem face acest lucru verificând fiecare poziţie
anterioară poziţiei curente. Să notăm acest răspuns cu b.
Astfel, răspunsul corespunzător pentru o valoare citită va fi a  b  1.
2
Soluţia are complexitatea totală O M  M log N .
Soluţie alternativă. Se vor citi mai ı̂ntâi valorile şi se vor sorta valorile din vectorul de
lungime M . Apoi, vom aplica un algoritm de interclasare pentru a răspunde ı̂n manieră offline la
toate ı̂ntrebările, memorând mai ı̂ntâi poziţia unde se găseau acele actualizări ı̂n vectorul al doilea,
având grijă să prioritizăm valoarea din vectorul al doilea ı̂n caz de egalitate, pentru a respecta
restricţia din enunţ.
2
Această soluţie are complexitatea totală O M  M  N .
CAPITOLUL 15. ONI 2023 15.2. PIX 199

15.1.2 Cod sursă

Listing 15.1.1: 7 dominew.cpp


/*Flavius Boian 100*/

#include <iostream>
#include <fstream>

using namespace std;

//ifstream f("dominew.in");
ofstream g("dominew.out");

int n,m,i,j,x,st,dr,mij,poz,t,a[1000001],b[8001];

int main()
{
f>>n;

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


f>>a[i];

f>>m;
for(i=1; i<=m; i++)
{
f>>b[i];
st=1;
dr=n;
poz=0;
while(st<=dr)
{
mij=(st+dr)/2;
if(a[mij]<b[i])
{
poz=mij;
st=mij+1;
}
else
dr=mij-1;
}

///caut liniar pozitia pe care trebuie sa inserez in noul vector


for(t=1; t<i; t++)
if(b[i]>b[t])
poz++;
g<<poz+1<<" ";
}

return 0;
}

15.1.3 *Rezolvare detaliată

15.2 pix
Problema 2 - Pix 100 de puncte
Robotul Vasile s-a angajat la un depozit de pixuri. Aici pixurile sunt ambalate ı̂n cutii.
Există N tipuri de cutii; ı̂ntr-o cutie de tipul i (1 & i & N ) sunt ambalate exact nri pixuri
(nr1 & nr2 & ... & nrN ). În depozit există un număr atât de mare de cutii de fiecare tip ı̂ncât Vasile
poate utiliza oricâte cutii doreşte, de orice tip. Sarcina robotului Vasile este să livreze pixurile
comandate de diferite firme de birotică. El nu ştie câte pixuri va avea de livrat la următoarea
comandă, dar ştie că vor fi cel mult Vmax pixuri. Ca urmare, pentru a fi eficient, robotul Vasile
vrea să ı̂şi pregătească ı̂n camera de livrare un număr minim de cutii de pixuri astfel ı̂ncât să
poată livra orice număr de pixuri cuprins ı̂ntre 1 şi Vmax folosind cutiile pregătite, evident, fără
a deschide cutiile.
CAPITOLUL 15. ONI 2023 15.2. PIX 200

Cerinţe

Scrieţi un program care citeşte valorile N , nr1 , nr2 , ..., nrN şi V max şi determină numărul
minim de cutii pe care robotul Vasile trebuie să le pregătească ı̂n camera de livrare astfel ı̂ncât să
poată livra orice număr de pixuri cuprins ı̂ntre 1 şi Vmax , fără a deschide nicio cutie.

Date de intrare

Fişierul de intrare pix.in conţine pe prima linie numărul natural N reprezentând numărul de
tipuri de cutii.
Pe a doua linie se află N numere naturale ı̂n ordine crescătoare, separate prin câte un spaţiu,
nr1 , nr2 , ..., nrN reprezentând numărul pixuri ambalate ı̂n fiecare tip de cutie.
Pe a treia linie se află numărul natural Vmax cu semnificaţia din enunt,.

Date de ieşire

Fişierul de ieşire pix.out va conţine o singură linie pe care va fi scris un număr natural
reprezentând numărul minim de cutii pe care robotul Vasile trebuie să le pregătească ı̂n camera
de livrare astfel ı̂ncât să poată livra orice număr de pixuri cuprins ı̂ntre 1 şi Vm ax.

Restricţii şi precizări

ˆ 1 & N & 100 000


ˆ 1 & Vmax , nri & 10 , pentru 1 & i & N
12

ˆ Se garantează că pentru toate datele de test există soluţie.

# Punctaj Restricţii
1 20 1 & N $ 15
2 10 15 & N $ 600
3 40 Vmax & 100 000

Exemple:

pix.in pix.out Explicaţii


4 5 Numărul minim de cutii pe care trebuie să le pregătească Vasile este 5 (o
1235 cutie de tipul 1, două de tipul 2 şi două de tipul 4, numărul de pixuri din
15 aceste cutii fiind 1, 2, 2, 5, 5). El poate astfel livra orice număr de pixuri
ı̂ntre 1 şi 15, o modalitate posibilă fiind:
1: cutia de tip 1
2: o cutie de tip 2
3: o cutie de tip 1 şi o cutie de tip 2
4: două cutii de tip 2
5: o cutie de tip 4
6: o cutie de tip 1 şi o cutie de tip 4
7: o cutie de tip 2 şi o cutie de tip 4
8: câte o cutie de tipurile 1, 2, 4
9: o cutie de tip 4 şi două cutii de tip 2
10: două cutii de tip 4
11: două cutii de tip 4 şi o cutie de tip 1
12: două cutii de tip 4 şi o cutie de tip 2
13: două cutii de tip 4, o cutie de tip 2 şi o cutie de tip 1
14: două cutii de tip 4 şi două cutii de tip 2
15: toate cutiile
Aceasta nu este singura posibilitate de a alege un număr minim de cutii
pentru a obţine toate valorile de la 1 la 15.
CAPITOLUL 15. ONI 2023 15.2. PIX 201

15.2.1 Indicaţii de rezolvare

Propusă de: Prof. Emanuela Cerchez, Colegiul Naţional ”Emil Racovit, ă”,Ias, i
Reţinem ı̂ntr-un vector nr capacităţile distincte & V max.
Deoarece se garantează că există soluţie, obligatoriu nr1 1.
Vom lua o cutie de tipul 1.
Analizăm apoi nr2. Dacă nr2 % nr1 atunci comenzile pentru capacitatea x din mulţimea
r1, 2, ..., nr 2  1x nu pot fi obţinute decât din mai multe cutii cu capacitatea nr 1 1.

Deci vom lua nr2  1 cutii cu capacitatea nr1 1.


Pentru a vedea câte cutii cu capacitatea nr2 luăm trebuie să analizăm nr3, pentru ca toate
valorile până la nr3  1 inclusiv să fi obţinute.
Observăm că dacă am lua j cutii de capacitate nr2 am obţine toate comenzile cu capacitatea
x din mulţimea 1, 2, j  1 nr2  1.
Să generalizăm acum.
Să notăm cu sum suma capacităţilor cutiilor luate până la pasul i (acestea reprezintă valoarea
maximă pentru care avem soluţie până la acest pas).
Câte cutii cu capacitatea nri vom lua?
Dacă nri  1  1 & sum nu are rost să luăm cutii cu capacitatea nri.
Determinăm câte de cutii cu capacitatea nri sunt necesare astfel ı̂ncât

nri  1  1 ' cate nri  sum.

Pentru ca formula să funcţioneze şi pentru i n vom iniţializa componenta n  1 a vectorului
nr cu Vmax  1, astfel că la pasul n vom asigura toate comenzile pentru capacităţile x & Vmax .

nr[n+1] = Vmax+1;
rez = nr[2]-1; sum=n[2]-1; // in rez contorizam cutiile
for(i=2; i<=n; i++)
{
if(nr[i+1]-1 <= sum) continue;
cate = (nr[i+1]-1-sum)/nr[i];
if(cate * nr[i] + sum < nr[i+1]-1) cate++;
rez += cate;
sum += cate * nr[i];
}

15.2.2 Cod sursă

Listing 15.2.1: 7 pix.cpp


//Em. Cerchez 100
#include <fstream>
#include <algorithm>

#define NMAX 100002

using namespace std;

ifstream fin("pix.in");
ofstream fout("pix.out");

int n, lg;
long long int nr[NMAX];
long long int a[NMAX];
long long int vmax, rez, sum, ultima, cate;
CAPITOLUL 15. ONI 2023 15.3. SECVMIN 202

int main()
{
int i;
fin>>n;

for (i=1; i<=n; i++) fin>>a[i];

fin>>vmax;

//sort(a+1,a+n+1);

///retin in nr doar valorile distincte <=vmax


nr[1]=a[1];
lg=1;
for (i=2; i<=n; i++)
{
if (a[i]==a[i-1]) continue;
if (a[i]>vmax) break;
nr[++lg]=a[i];
}

n=lg;
nr[n+1]=vmax+1;
rez=nr[2]-1;
sum=nr[2]-1;

for (i=2; i<=n; i++)


{
if (nr[i+1]-1<=sum) continue;
cate=(nr[i+1]-1-sum)/nr[i];
if (cate*nr[i]+sum<nr[i+1]-1) cate++;
rez+=cate;
sum+=cate*nr[i];
}

fout<<rez<<’\n’;
fout.close();

return 0;
}

15.2.3 *Rezolvare detaliată

15.3 secvmin
Problema 3 - Secvmin 100 de puncte
Fie un şir de n numere naturale v1 , v2 , ..., vn , unde vi reprezintă al i-lea număr din şir. O
subsecvenţă x, y  a şirului v (cu 1 & x & y & n) conţine toate elementele vx , vx1 , ..., vy1 , vy .

Cerinţă

Fiind date două numere naturale n şi k şi un şir v de n numere naturale, scrieţi un program
care să răspundă la următoarea ı̂ntrebare: câte subsecvent,e conţin simultan cele mai mici k valori
distincte din şir?

Date de intrare

Fişierul de intrare secvmin.in conţine pe prima linie numerele n şi k, iar pe cea de a doua linie
se vor afla numerele naturale v1 , v2 , ..., vn , cu semnificaţia din enunţ. Numerele scrise pe aceeaşi
linie sunt separate prin câte un spaţiu.

Date de ieşire

Fişierul de ieşire secvmin.out va conţine o singură linie pe care va fi scris răspunsul la cerinţa
problemei.
CAPITOLUL 15. ONI 2023 15.3. SECVMIN 203

Restricţii şi precizări

ˆ 1 & n & 1 000 000

ˆ 1&k &5
ˆ 1 & vi & 1 000 000 000, pentru 1 & i & n
ˆ Se garantează că şirul va conţine cel puţin k valori distincte.

# Punctaj Restricţii
1 23 k 1
2 32 k 2
3 45 3&k&5

Exemple:

secvmin.in secvmin.out Explicaţii


71 19 k = 1. Cea mai mică valoare din şir este egală cu 1.
1322132
Subsecvenţele care conţin valoarea 1 sunt:

[1 1], [1 2], [1 3], [1 4], [1 5], [1 6], [1 7],


[2 5], [2 6], [2 7],
[3 5], [3 6], [3 7],
[4 5], [4 6], [4 7],
[5 5], [5 6], [5 7].
721522152 15 k = 2. Cea mai mică valoare din şir este egală cu 1, iar a
doua cea mai mică valoare din şir este egală cu 2.

Subsecvenţele care conţin valorile 1 şi 2 sunt:

[1 3], [1 4], [1 5], [1 6], [1 7],


[2 5], [2 6], [2 7],
[3 5], [3 6], [3 7],
[4 5], [4 6], [4 7],
[5 7].

15.3.1 Indicaţii de rezolvare

Propusă de: Stud. Theodor-Gabriel Tulba-Lecu şi Ioan-Cristian Pop, Universitatea Politehnica
Bucureşti
Să considerăm cele mai mici valori distincte din şir y1 , y2 , ..., yk . Pentru fiecare poziţie i din şir
vom calcula şi vom actualiza valorile last1 , last2 , ..., lastk cu semnificaţia lastj este cea mai mare
poziţie mai mică sau egală decât i ı̂n care a apărut valoarea yj .
Se observă că o secvenţă care are capătul dreapta ı̂n poziţia i va conţine toate cele k valori
minime dacă şi numai dacă capătul stânga al secvenţei este mai mic sau egal decât toate valorile
lastj . Pentru a obţine soluţia, la fiecare i actualizăm şirul last şi adunăm la soluţie valoarea
minimă a acestui şir.
Pentru a forma şi actualiza şirul last este necesar ca ı̂n prealabil să avem calculate cele k valori
minime, dar aceasta se poate realiza ı̂ncă de la citire prin inserarea ı̂n şirul y a celor mai mici k
valori atunci când ele apar.
Şirul last se iniţializează cu 0 şi se actualizează de fiecare dată când la o poziţie i apare una
dintre cele k valori minime. Soluţia are complexitatea O N K 
CAPITOLUL 15. ONI 2023 15.3. SECVMIN 204

15.3.2 Cod sursă

Listing 15.3.1: 7 secvmin.cpp


//A. Panaete 100
#include <bits/stdc++.h>

using namespace std;

ifstream f("secvmin.in");
ofstream g("secvmin.out");

const int N = 1000010;


int n,k,x[N],y[10],last[10];
int64_t sol;

int main()
{
f>>n>>k;

for(int i=1;i<=k;i++) y[i]=1000000010;

for(int i=1; i<=n; i++)


{
int val;
f>>val;

if(val<y[k])
{
int p=1;
while(val>y[p]) p++;

if(val<y[p])
{
int q=k;
while(q>p)
{
y[q]=y[q-1];
q--;
}
y[p]=val;
}
}

x[i]=val;
}// for i

for(int i=1; i<=n; i++)


{
int cnt=n+1;
for(int j=1; j<=k; j++)
{
if(x[i]==y[j])last[j]=i;
cnt=min(cnt,last[j]);
}

sol+=cnt;
}// for i

g<<sol;
return 0;
}

15.3.3 *Rezolvare detaliată


Capitolul 16

ONI 2022

16.1 microbuz
Problema 1 - Microbuz 100 de puncte
O companie de transport cu microbuze din judeţul Iaşi a adoptat o strategie proprie pentru
rutele din judeţ :
ˆ niciun traseu nu poate avea mai mult de 165 kilometri
ˆ distanţa ı̂ntre două staţii consecutive este de un kilometru
ˆ un pasager poate pleca din orice staţie şi poate să ı̂şi cumpere bilete pentru parcurgerea a
1, 2, . . . , 10 kilometri
ˆ fiecare dintre cele zece distanţe posibile au bilete cu preţuri distincte

De exemplu:

1 km 2 km 3 km 4 km 5 km 6 km 7 km 8 km 9 km 10 km
11 lei 14 lei 18 lei 23 lei 29 lei 36 lei 44 lei 45 lei 53 lei 64 lei

Gigel, care călătoreşte cu microbuzul exact N kilometri, poate să aleagă una sau mai multe
distanţe dintre cele zece stabilite şi să cumpere biletele corespunzătoare. Compania impune ca un
pasager să poată cumpăra maxim 3 bilete de acelaşi tip.
Gigel observă că pentru aceeaşi sumă poate achiziţiona seturi diferite de bilete cu preţuri
distincte. Pentru exemplu de mai sus, cu 98 de lei el poate cumpăra câte un bilet cu preţurile 11,
14, 29, 44 lei sau câte un bilet cu preţurile 45, 53 lei (11 + 14 + 29 + 44 = 45 + 53).

Cerinţe

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


1. determină preţul minim al unui set de bilete ce poate fi achiziţionat pentru parcurgerea a
exact N kilometri;
2. determină distanţele alese de Gigel, astfel ı̂ncât preţul total al călătoriei să fie minim;
3. determină două seturi distincte de bilete pentru care preţul total al biletelor este acelaşi şi
este cel mai mare posibil. Pentru fiecare set nu este permisă alegerea mai multor bilete cu
acelaşi preţ şi nu există două bilete cu acelaşi preţ ı̂n ambele seturi.

Date de intrare

Fişierul de intrare microbuz.in are trei linii. Pe prima linie se află un număr natural C ce
reprezintă cerinţa (1, 2 sau 3). Linia a doua conţine zece valori naturale ordonate strict crescător,
separate prin câte un spaţiu, reprezentând preţurile pentru parcurgerea a 1, 2, . . . , 10 kilometri.
Linia a treia conţine valoarea N reprezentând distanţa pe care doreşte să o parcurgă Gigel.

Date de ieşire

Fişierul de ieşire microbuz.out are următoarea structură:


1. dacă C 1, pe prima linie se va afişa un ı̂ntreg reprezentând costul minim al călătoriei;
2. dacă C 2, pe fiecare linie se vor afişa câte două numere ı̂ntregi, separate printr-un spaţiu,
reprezentând distanţa parcursă şi preţul biletului corespunzător;

205
CAPITOLUL 16. ONI 2022 16.1. MICROBUZ 206

3. dacă C = 3, pe prima linie se va afişa preţul comun al celor două seturi de bilete, iar pe
următoarele două linii câte un set de bilete dat prin numărul de km pentru biletele din set
ı̂n ordine strict crescătoare, separate prin câte un spaţiu.
Restricţii şi precizări

ˆ C " r1, 2, 3x
ˆ 1 & N & 165
ˆ cele 10 preţuri sunt numere naturale din intervalul [10, 99 ]
ˆ răspunsul la cerinţa 3 nu depinde de valoarea lui N

# Punctaj Restricţii
1 28 C 1
2 38 C 2
3 34 C 3

Exemple:

microbuz.in microbuz.out Explicaţii


1 86 Cerinţa este 1. Costul minim total este 86
11 14 18 23 29 36 44 45 53 64
15
2 3 18 Cerinţa este 2. Biletele cumpărate şi preţurile
11 14 18 23 29 36 44 45 53 64 4 23 lor sunt:
15 8 45 3 km parcurşi, preţ 18
4 km parcurşi, preţ 23
8 km parcurşi, preţ 45
2 7 25 Cerinţa este 2. Biletele cumpărate şi preţurile
13 17 18 19 21 22 25 28 31 37 7 25 lor sunt:
39 8 28 7 km parcurşi, preţ 25
8 28 7 km parcurşi, preţ 25
9 31 8 km parcurşi, preţ 28
8 km parcurşi, preţ 28
9 km parcurşi, preţ 31
3 163 Cerinţa este 3. Preţul maxim comun este 163
11 14 18 23 29 36 44 45 53 64 2 3 4 7 10 Setul 1: câte un bilet cu preţurile
15 5689 14 18 23 44 64
Setul 2: câte un bilet cu preţurile
29 36 45 53

16.1.1 Indicaţii de rezolvare

Propusă de: Prof. Marinel Şerban


Soluţie prof. Marinel Şerban. - CN E. Racoviţă Iaşi
Observaţii. Problema testează cunoştinţe despre:
(1) lucrul cu vectori
(2) utilizarea unui algoritm de tip succesor
(3) lucru cu baze de numeraţie
(4) determinarea minimului şi maximului unei mulţimi de valori

Cerinţele 1 şi 2. Se vor genera toate combinaţiile posibile de bilete ı̂n mod succesiv.
Pentru ı̂nceput, vom reţine ı̂n tabloul max cnti min 3, ni , pentru i de la 1 la 10, numărul
maxim de bilete de tip i pe care le putem cumpăra fără a depăşi n kilometri.
În continuare, vom genera fiecare combinaţie cu ajutorul tabloului cnti = numărul de bilete
de tip i din combinaţie, ı̂n mod succesiv utilizând un algoritm de tip succesor. Pentru fiecare
combinaţie vom calcula distanţa parcursă:
distanta 1 cnt1  2 cnt2  ...  10 cnt10
CAPITOLUL 16. ONI 2022 16.1. MICROBUZ 207

şi suma costurilor biletelor din acea combinaţie

cost km1 cnt1  km2 cnt2  ...  km10 cnt10

Dacă distanţa este egală cu n, atunci se actualizează răspunsul optim.


Pseudocodul arată astfel:

Existenţa soluţiei pentru cerinţa 3. În condiţiile problemei:


Suma minimă a unei submulţimi este 10 - mulţime cu un bilet de cost 10.
Suma maximă a unei submulţimi este 855 - mulţimea de bilete {91,92,93,94,95,96,97,98,99}.
Deci există 855 - 10 + 1 = 846 sume posibile.
1 2 9
În acelaşi timp există C10  C10  ...  C10 1022 submulţimi.
Conform principiului cutiei lui Dirichlet există cel puţin două submulţimi de sumă egală.
Cerinţa 3. Se vor genera toate combinaţiile posibile de submulţimi de două seturi bilete
disjuncte ı̂n mod consecutiv.
Se va proceda similar ca la primele două cerinţe, dar acum vom encoda starea unui bilet cu
una din cele 3 valori:
0 - biletul nu face parte din nici un set
1 - biletul face parte din setul 1
2 - biletul face parte din setul 2
Pentru fiecare combinaţie se determină cele 2 seturi de bilete. Se reţine maximul şi combinaţia
care produce acest maxim.

Soluţie prof. Claudiu-Cristian Gorea-Zamfir. - Şcoala Gimnazială Alexandru cel Bun, Iaşi
Cerinţele 1 şi 2.

- Se generează toate numerele de 10 cifre, ı̂n baza 4 şi se renţin pentru un număr cifrele sale
ı̂ntr-un tablou cif . Acest tablou va reţine numărul de bilete cumpărate pentru fiecare tip
de bilet i de la 1 la 10;
- Se calculează preţul astfel:

preţ cif1 p1  cif2 p2  ...  cif10 p10

- Se calculează distanţa astfel:

dist cif1 1  cif2 2  ...  cif10 10


CAPITOLUL 16. ONI 2022 16.1. MICROBUZ 208

- Dacă dist n şi pret $ pretmin atunci reţinem configuraţia actuală a tabloului cif şi
actualizăm pretmin pret;

Cerinţa 3.

- Fiecărui bilet ı̂i asociem 3 stări:


0 - biletul nu este ales
1 - biletul este ales ı̂n mulţimea 1
2 - biletul este ales ı̂n mulţimea 2
- Se generează toate numerele de 10 cifre ı̂n baza 3 ı̂n mod similar cu cerinţele anterioare;
- În funcţie de mulţimea asociată, vom calcula sumele sum1 şi sum2 , iar dacă acestea sunt
egale şi maxime, vom reţine suma maximă şi configuraţia.

Soluţie prof. Daniela-Elena Lica. - Centrul Judeţean de Excelenţă Prahova)


Cerinţele 1 şi 2.: Problema acceptă şi o abordare de tip programare dinamică, asemănătoare
cu cea folosită pentru rezolvarea Problemei Rucsacului.
Astfel, considerăm tabloul unidimensional:

costi costul minim necesar pentru a călători i kilometri

Fie:

bileti preţul unui bilet ce asigură parcurgerea unei distanţe de i km

Construirea tabloului cost. Să presupunem că la pasul curent considerăm achiziţionarea unui bilet
ce asigură parcurgerea unei distanţe de d km, cu preţul egal cu biletd ;
Obţinem astfel următoarea relaţie de recurenţă:

costvd min costvd , costv  biletd 

Pentru orice valoare v, parcursă descrescător (pentru a evita suprascrieri incorecte) de la


165  d până la 0.
De remarcat este faptul că fiecare tip de bilet (dintre cele 10) poate fi cumpărat de cel mult
trei ori, astfel că se va efectua adăugarea distanţei de d km ı̂n rucsac de exact trei ori.
Pentru prima cerinţă este suficient să calculăm doar tabloul unidimensional cost cu o complex-
itate de timp egală cu: O n Smax , unde n 10 şi Smax 165 pentru problema noastră.
Pentru a rezolva şi cea de-a doua cerinţă, pe lângă acest tablou, putem reţine, pentru fiecare
valoare v de la 0 la 165, şi modul ı̂n care costv a fost obţinut. Astfel, ı̂n cadrul egalităţii, dacă:

costv  biletd $ costvd


ı̂n adiţie cu actualizarea valorii lui costvd , putem considera că reconstrucţia necesară pentru v  d
km este identică cu cea necesară pentru v km, la care mai trebuie să adăugăm achiziţionarea unui
bilet de d km. Aceste reconstrucţii pot fi efectuate, de exemplu, cu ajutorul unor structuri, ı̂n
O n Smax  memorie - pentru fiecare posibilitate de distanţă, pot exista cel mult 30 de tichete
utilizate ı̂n atingerea acesteia.
e
Cerinţa 3. Folosind scrierea ı̂n baza 2 a numerelor naturale de la 0 la 2  1, putem genera toate
submulţimile unei mulţimi ce are e elemente. Astfel, putem genera toate submulţimile mulţimii
cu 10 elemente din problema de faţă:

rbilet1 , bilet2 , bilet3 , ..., bilet9 , bilet10 x

De exemplu, să considerăm, ı̂n contextul acestei scrieri, numărul binar 0000010111:

ˆ bitul cel mai semnificativ se află ı̂n partea stângă;


ˆ Aici, cel mai semnificativ bit are valoarea 0, este nesetat;
ˆ Au fost selectate numerele:

bilet1 , bilet2 , bilet3 şi bilet5


CAPITOLUL 16. ONI 2022 16.2. RAZA 209

ˆ Fie S bilet1  bilet2  bilet3  bilet5 ;


ˆ La acest pas, ne-ar mai interesa să vedem dacă există o modalitate de a alege nişte numere
din mulţimea rămasă, adică:

rbilet4 , bilet6 , bilet7 , bilet8 , bilet9 , bilet10 x

astfel ı̂ncât ı̂nsumate să dea S. Pentru aceasta, folosim un algoritm identic cu cel folosit ı̂n
cadrul cerinţei 2.

Soluţie stud. Bogdan-Ioan Popa. - Universitatea Bucureşti


În cele ce urmează vom reprezenta o submulţime de bilete ca fiind un număr ı̂n baza 2 scris cu
K biţi. Dacă ı̂n configuraţia (submulţimea) conf bitul i este 1 ı̂nseamnă că biletul cu i  1 km
este luat ı̂n submulţimea conf .
Cerinţele 1 şi 2. Se calculează vectorul de perechi Di perechea cost, conf  unde cost
reprezintă costul minim să obţinem un drum de distanţă exact i, conf reprezintă submulţimea de
bilete cu care se obţine acest cost minim. Ce rămâne să găsim acum este un triplet:

dist1 , dist2 , dist3 pentru care,


dist1  dist2  dist3 N şi
Ddist1 .cost  Ddist2 .cost  Ddist3 .cost este minim.

Pentru reconstrucţia soluţiei ne vom folosi de biletele cumpărate ı̂n configuraţiile Ddist1 .conf ,
Ddist2 .conf respectiv Ddist3 .conf .
K
Complexitatea aceste soluţii este O 2 , unde K este numărul de tipuri de bilete posibile pe
care le putem cumpăra. Pentru această problemă K 10.
Cerinţa 3. Se calculează tabloul bidimensional:

~
„ dacă există o submulţime x a submulţimii conf
„
„
„1.
Aconf,sum cost ‚
„ astfel ı̂ncât suma costurilor biletelor din submulţimea x să fie sum cost
„
„
„0, altf el
€

Ce rămâne de făcut este să iterăm prin toate submulţimile conf posibile ale celor K bilete.
Fie comp conf submulţimea complementară submulţimii conf .
Fie s suma costurilor biletelor din submulţimea conf .
Dacă Acomp conf,s 1 ı̂nseamnă că am găsit două submulţimi disjuncte de bilete care ı̂nsumează
acelaşi cost. Nu uităm ı̂nsă că Acomp conf,s 1 indică faptul că există o submulţime a lui
comp conf pentru care suma costurilor biletelor din ea să fie s şi nu ı̂nseamnă neapărat că suma
costurilor biletelor din comp conf este egală cu s.
K
Complexitatea soluţiei este O 2 Smax , unde Smax reprezintă suma totală a costurilor
biletelor.

16.1.2 *Cod sursă

16.1.3 *Rezolvare detaliată

16.2 raza
Problema 2 - Raza 100 de puncte
Pe planeta Quadratia, locuitorii folosesc forme pătrate ı̂n tot ceea ce construiesc. Ei au trimis
pe solul planetei N maşini speciale de apărare, numite rovere, care se deplasează pe traiectorii ce
descriu pătrate.
CAPITOLUL 16. ONI 2022 16.2. RAZA 210

Harta planetei poate fi privită ca un tablou bidimensional cu număr infinit de linii şi coloane,
numerotarea acestora ı̂ncepând cu 1. Fiecare element al acestui tablou se identifică prin numărul
liniei, respectiv numărul coloanei pe care se găses, te.
Fiecare rover este descris prin 3 numere naturale nenule L, C şi R, reprezentând poziţia (linia
L şi coloana C) elementului din colţul din stânga-sus al pătratului ce descrie traiectoria, respectiv
lungimea R a laturii acestuia. Toate roverele pornesc iniţial din elementul din colţul stânga-sus al
pătratului ce descrie traiectoria, pe care o parcurg ı̂n sensul acelor de ceasornic, traversând câte
un element pe secundă. Ele sunt programate să parcurgă această traiectorie fără a se opri. Este
posibil ca mai multe rovere să se afle, la un moment dat la aceeaşi poziţie de pe hartă.
Rectilinienii sunt singurii duşmani ai quadratienilor, ei deosebindu-se de aceştia prin folosirea
consecventă a liniilor drepte ı̂n tot ceea ce construiesc. Rectilinienii au construit o armă laser de
mare putere, pe care vor să o folosească la distrugerea roverelor quadratiene. Arma a fost adusă
ı̂n poziţia 1, 1 de pe harta planetei, adică elementul de pe prima linie şi prima coloană. Arma
poate emite o singură rază distructivă, timp de o secundă, dezafectând ı̂n acest timp toate roverele
aflate ı̂n secunda respectivă pe diagonala principală a tabloului care descrie harta planetei. Arma
nu poate fi detectată ı̂n primele S secunde. Traiectoria roverelor poate trece prin poziţia ı̂n care
a fost amplasată arma, fără a o putea detecta ı̂n primele S secunde.
În imaginea alăturată avem două rovere, acestea fiind iden-
tificate prin tripletele de numere 4, 2, 6, respectiv 6, 9, 4.
Traiectoria primului rover ı̂ncepe din poziţia 4, 2 şi are
latura 6. Numerele de pe traiectorie reprezintă secunda la
care se parcurge respectivul element al tabloului. Roverul
va ajunge din nou ı̂n poziţia iniţială la secundele 21, 41,
61, . . . . Primul rover intersectează, la prima sa trecere,
diagonala principală ı̂n două puncte: 4, 4 la secunda 3 şi
7, 7 la secunda 9. Al doilea rover intersectează diagonala
principală ı̂ntr-un singur punct 9, 9 la secundele 10, 22, 34, . . . .

Cerinţe

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

1. Determină numărul roverelor a căror traiectorie se intersectează cu diagonala principală a


tabloului ce descrie harta.
2. Determină numărul maxim de rovere, care pot fi distruse simultan, ı̂ntr-o singură secundă,
ı̂nainte de trecerea celor S secunde, precum şi secunda minimă ı̂n care se poate realiza acest
lucru.

Date de intrare

Datele de intrare se citesc din fişierul raza.in, care are următoarea structură:
ˆ pe prima linie se află numărul natural T " r1, 2x, semnificând numărul cerinţei de rezolvat.
ˆ pe a doua linie se află numerele naturale nenule N şi S, separate printr-un spaţiu,
reprezentând numărul roverelor, respectiv numărul de secunde ı̂n care arma nu este de-
tectată.
ˆ pe următoarele N linii se află câte 3 numere naturale nenule, L C R, separate prin câte
un spaţiu, reprezentând coordonatele poziţiei iniţiale, respectiv lungimea laturii traiectoriei
fiecărui rover.

Date de ieşire

Datele de ieşire se vor afişa ı̂n fis, ierul raza.ouţ care are următoarea structură ı̂n funcţ ie de
cerinţă:
ˆ dacă T 1, pe prima linie se va afişa numărul roverelor a căror traiectorie se intersectează
cu diagonala principală;
ˆ dacă T 2, pe prima linie se vor afişa două numere naturale, separate printr-un spaţiu,
reprezentând numărul maxim de rovere dezactivate şi secunda minimă ı̂n care se poate
realiza acest lucru.
CAPITOLUL 16. ONI 2022 16.2. RAZA 211

Restricţii şi precizări

ˆ 1 & N & 10 000


ˆ 1 & S & 100 000
ˆ 1 & L, C & 50 000
ˆ 3 & R & 1 000
ˆ 1 & Numărul maxim de rovere care se intersectează cu raza armei & 1 000.

# Punctaj Restricţii
1 28 T 1
2 72 T 2

Exemple:

raza.in raza.out Explicaţii


1 2 Cerinţa este 1.
2 100 Exemplul reprezintă desenul din enunţ.
426
694
2 13 Cerinţa este 2.
25 Este distrus doar primul rover, momentul ˆn care se distruge
426 acest rover este 3.
694
1 4 Cerinţa este 1.
5 10000 Sunt patru rovere care intersectează diagonala.
333
474
643
923
975
2 23 Cerinţa este 2.
5 10000 Numărul maxim de rovere, care pot fi distruse este 2.
333 Acest lucru se poate realiza cel mai devreme la secunda 3.
474
643
923
975

16.2.1 Indicaţii de rezolvare

Propusă de: prof. Alin Burţa


Soluţie prof. Alin Burţa. - Colegiul National ”B. P Hasdeu”, Buzău
Cerinţa 1. Încă de la citirea datelor de intrare, se verifică dacă traiectoria roverului curent se
intersectează cu diagonala principală şi contorizăm numărul roverelor care ı̂ndeplinesc condiţia.
Avem 3 cazuri:

ˆ Roverul curent nu intersectează diagonala, caz care nu ne interesează;


ˆ Roverul curent intersectează diagonala ı̂ntr-o singură poziţie;
ˆ Roverul curent intersectează diagonala ı̂n două poziţ ii.

Fie x, y  linia respectiv coloana colţului din stânga-sus al traiectoriei roverului curent iar r
lungimea laturii traiectoriei. Condiţia de intersecţie a traiectoriei cu diagonala este:

x & y  r  1 şi x  r  1 ' y

Cerinţa 2. Concomitent cu determinarea roverelor, care intersectează diagonala principală


vom construi 3 vectori, cu următoarele semnificaţii:
CAPITOLUL 16. ONI 2022 16.3. TEXT 212

ˆ unui secunda ı̂n care roverul i intersectează prima dată diagonala ı̂n cadrul primei par-
curgeri a traiectoriei;
ˆ doii secunda ı̂n care roverul i intersectează a doua oară diagonala ı̂n cadrul primei par-
curgeri a traiectoriei sau 0, dacă roverul NU intersectează diagonala ı̂n două poziţii;
ˆ prdi 4 r  4, perioada roverului i (numărul de secunde necesare parcurgerii ı̂ntregii
traiectorii);
Numărul de secunde S este relativ mic, deci putem calcula, pentru fiecare secundă ı̂ntre 1 şi S,
câte rovere se găsesc pe diagonala principală ı̂n acel moment. Acest calcul presupune marcarea,
pentru fiecare rover, a tuturor momentelor de timp ı̂n care roverul atinge diagonala principală,
folosind cei trei vectori construiţi anterior. Reţinem la fiecare pas numărul maxim curent şi
momentul de timp corespunzător.

Soluţie prof. Gorea-Zamfir Claudiu-Cristian. - Şcoala Gimnazială Alexandru cel Bun, Iaşi
La citirea roverelor, identificăm 5 situaţii bazate pe valorile de plecare, raza atinge traiectoria
astfel:
ˆ 2 colţuri (l c) - momentele de timp sunt r0, 2r, 4r, 6r, ...x;
ˆ 1 colţ, dreapta-sus (c  r l) - momentele de timp sunt r1r, 5r, 9r, ...x;
ˆ 1 colţ, stânga-jos (l  r c) - momentele de timp sunt r3r, 7r, 11r, ...x;
ˆ 2 laturi nord şi est (c $ l şi l $ c  r) - momentele de timp sunt r1  l  c, 1  l  c  4r, 1 
l  c  8r, ...x şi r2r  1  l  c, 6r  1  l  c, 10r  1  l  c, ...x;
ˆ 2 laturi sud şi vest (l $ c şi c $ l  r) - momentele de timp sunt r1  c  l  2r, 1  c  l 
6r, 1  c  l  10r, ...x şi r1  c  l  4r, 1  c  l  8r, 1  c  l  12r, ...x.
În fiecare situaţie contorizăm faptul ca raza intersectează traiectoria acestui rover. Numărul
de rovere intersectate, fiind răspunsul cerinţei 1.
Pentru cerinţa 2, ı̂n fiecare dintre cele 5 situaţii, pe baza relaţiilor de tip progresie aritmetică
obţinute din periodicitatea ı̂ntâlnirii roverului cu raza, vom folosi un vector de frecvenţă pentru a
marca la fiecare moment care rovere ar fi afectate. La final determinăm maximul din acest vector
şi momentul ı̂n care raza va emite.

16.2.2 *Cod sursă

16.2.3 *Rezolvare detaliată

16.3 text
Problema 3 - Text 100 de puncte
Alexandra citeşte un text format din litere mici şi mari ale alfabetului englez şi spaţii. Fiind
interesată de criptografie, ea elimină toate spaţiile şi apoi ı̂ncadrează literele, ı̂n ordinea ı̂n care
acestea apar ı̂n text ı̂ntr-un tablou bidimensional, ı̂n care numărul de linii este mai mic sau egal
decât numărul de coloane.
Întrucât pot exista mai multe moduri de ı̂ncadrare a textului, Alexandra ı̂l alege pe cel pentru
care diferenţa absolută dintre numărul liniilor şi al coloanelor tabloului este minimă. Completarea
cu literele textului a tabloului bidimensional se face ı̂n ordinea crescătoare a liniilor şi ı̂n cadrul
fiecărei linii, ı̂n ordinea crescătoare a coloanelor.

Cerinţe

1. Notând cu N numărul de linii şi cu M numărul de coloane ale tabloul bidimensional obţinuţ
afişaţi elementele acestuia pe primele N linii ale fişierului de ieşire, fiecare linie conţinând
exact M litere fără spaţii ı̂ntre ele. Afişarea se va face ı̂n ordinea crescătoare a indicilor
liniilor şi pe fiecare linie ı̂n ordinea crescătoare a indicilor coloanelor.
CAPITOLUL 16. ONI 2022 16.3. TEXT 213

# Punctaj Restricţii
1 16 C 1 şi numărul de caractere din şirul iniţ ial & 10 000
2 36 C 2 şi numărul de caractere din şirul iniţ ial & 1 000
3 16 C 3 şi numărul de caractere din şirul iniţ ial & 1 000
4 16 C 3 şi numărul de caractere din şirul iniţ ial & 50 000
5 16 C 3, fără restricţii suplimentare

2. Determinaţi cel mai lung palindrom de pe o linie sau de pe o coloană a tabloului obţinut. În
cazul ı̂n care există mai multe palindroame de aceeaşi lungime, se va afişa cel care este cel
mai mare din punct de vedere lexicografic conform codului ASCII.
3. Determinaţi care este numărul maxim de elemente dintr-un subtablou dreptunghiular, ce
conţine doar vocale.

Date de intrare

Datele de intrare se citesc din fişierul text.in, care are următoarea structură:
ˆ pe prima linie se află numărul natural C care poate avea doar valorile 1, 2 sau 3, semnificând
numărul cerinţei de rezolvat;
ˆ pe a doua linie se află textul pe care ı̂l va prelucra Alexandra.

Date de ieşire

Datele de ieşire se vor afişa ı̂n fişierul text.out care are următoarea structură:
ˆ dacă C 1, N linii, fiecare conţinând câte M litere, repezentând elementele tabloului obţinut;
ˆ dacă C 2, o singură linie conţinând cel mai lung palindrom determinat;
ˆ dacă C 3, o singură linie conţinând un număr natural reprezentând numărul de elemente
din subtabloul determinat.

Restricţii şi precizări

ˆ 2 & numărul total de caractere din textul iniţ ial al Alexandrei & 200 000
ˆ vocalele sunt: A, E, I, O, U, a, e, i, o, u
ˆ se garantează că N, numărul de linii ale tabloului obţinut este % 1

Exemple:

text.in text.out Explicaţii


1 Anan Numărul de caractere după eliminare spaţiilor este
Ana nu are mere uare 12. Textul plasat ı̂n tablou conţine N 3 linii şi
mere M 4 coloane.
1 Afostod Numărul de caractere după eliminare spaţiilor este
A fost odata ca niciodata atacani 21. Textul plasat ı̂n tablou conţine N 3 linii şi
ciodata M 7 coloane.
2 oao Afostod
A fost odata ca niciodata atacani
ciodata
Tabloul are pe coloana 3 palindromul oao iar pe
liniile 2 şi 3 palindromul ata.
3 6 Acea
Aceasta oaie e alba stao
aiee
alba
CAPITOLUL 16. ONI 2022 16.3. TEXT 214

16.3.1 Indicaţii de rezolvare

Propusă de: prof. Vlad-Laurenţiu Nicu


Soluţie prof. Daniela-Elena Lica. - Centrul Judeţean de Excelenţă Prahova
Cerinţa 1. Presupunând că şirul de caractere citit are o lungime de L caractere după elim-
inarea spaţiilor,
Ó se poate determina numărul de linii, respectiv de coloane, ı̂ntr-o complexitate
egală cu O L, prin parcurgerea tuturor divizorilor lui L.
Cerinţa 2. Pentru uşurinţă, se poate implementa o funcţie ce primeşte ca parametru un şir
2
de caractere să zicem, de lungime v, şi determină ı̂ntr-o complexitate pătratică O v  care, ı̂n
practică, se comportă foarte bine, care este cea mai lungă secvenţă palindromică a acestuia.
Pentru determinarea unei secvenţe maximale palindromice, se poate alege care să fie caracterul
din mijloc pentru palindroame de lungime impară, respectiv care să fie cele două caractere identice
din mijloc pentru palindroame de lungime pară, şi apoi se ı̂ncearcă extinderea simultană a capetelor
palindromului, spre stânga cât şi spre dreapta.
Astfel, putem apela această funcţie de N  M ori, unde N numărul de linii ale matri-
cei obţinute, M numărul de coloane ale matricei obţinute, transmiţând, de fiecare dată, ca
parametru al funcţiei, şirul obţinut prin parcurgerea unei linii, respectiv a unei coloane.
Cerinţa 3. Problema se poate rezolva ı̂ntr-o complexitate de timp O N M  O L.
Astfel, ı̂ncercăm să găsim răspunsul parcurgând linie cu linie matricea obţinută.
Presupunem că, la pasul curent ne aflăm pe linia i. Introducem notaţia:
Hj lungimea maximă a unei subsecvenţe de vocale ce se termină ı̂n celula i, j . Astfel, Hj
poate lua doar valori naturale, cuprinse ı̂ntre 1 şi M .
În mod particular, dacă celula i, j  conţine o consoană, atunci Hj 0.
Încercăm, pe rând, să fixăm fiecare coloană j ca fiind coloana cu ı̂nălţimea minimă dintr-un
subtablou dreptunghiular ce se termină pe linia i.
Definim:
lef tj cea mai din stânga poziţie pentru care Hlef tj 1 ' Hj , Hlef tj 2 ' Hj , ..., Hj 1 ' Hj
rightj cea mai din dreapta poziţie pentru care Hj 1 ' Hj , ..., Hrightj 2 ' Hj , Hrightj 1 ' Hj
Putem observa că pentru orice j subtabloul dreptunghiular ı̂n cauză are: l Hj linii şi
c rightj  1  lef tj  1  1 coloane, deci o arie egală cu: l c elemente.
Tablourile unidimensionale lef t şi right pot fi calculate ı̂n complexitate de timp O M , cu
ajutorul a două parcurgeri, una de la stânga la dreapta şi una de la dreapta la stânga. Pentru
a afla răspunsul, respectiv aria maximă a unui dreptunghi ce conţine doar vocale, se calculează
maximul tuturor numerelor l c descrise anterior.

Soluţie prof. Vlad-Laurenţiu Nicu. - Liceul Teoretic Mihail Kogălniceanu, Vaslui


Cerinţa 1. Se citeşte textul şi se elimină spaţiile formându-se o matrice de caractere de
dimensiune divizori ai lungimii şi e afişează matricea obţinută.
Cerinţa 2. Pentru determinarea palindromului de lungime maximă se poate folosi algoritmul
28
lui Manacher .
Acest algoritm constă ı̂n modificarea şirului iniţial prin inserarea unui caracter oarecare (care
sigur nu apare ı̂n sir, exemplu caracterul *) ı̂ntre oricare două caractere. Nu e nevoie de trans-
formarea efectivă a şirului, se poate lucra cu indicii şirului astfel ı̂ncât să considerăm ca acest
caracter a fost inserat. De exemplu şirul abccba devine *a*b*c*c*b*c*.
Pentru a găsi cea mai lungă secvenţă palindrom va trebui să extindem ı̂n jurul fiecărui caracter
ti , astfel ı̂ncât secvenţa dintre tid şi tid să fie palindrom.
Vom păstra ı̂n vectorul Li - cel mai lung palindrom cu centrul ı̂n i. Valoarea maximă din
vectorul L va reprezenta lungimea celui mai lung palindrom.
28
https://cp-algorithms.com/string/manacher.html
CAPITOLUL 16. ONI 2022 16.3. TEXT 215

Cerinţa 3. Pentru determinarea dreptunghiului de arie maximă folosim un algoritm cu his-


tograme (o funcţie in trepte).
Se fixează două linii i1 şi i2 . Pentru fiecare linie se determină numărul minim de zerouri spre
dreapta pe liniile i1 , i1  1, ..., i2 .
Se determină astfel aria maximă.

Soluţie stud. Bogdan-Ioan Popa, stud. Tulbă-Lecu Theodor-Gabriel. - Universitatea


din Bucures, ti, Universitatea Politehnica Bucures, ti
Cerinţa 3. Se iterează prin fiecare pereche de linii i1 , i2 , i1 & i2 . Pentru o pereche i1 , i2 
fixată vom dori să calculăm care este dreptunghiul de arie maximă ce conţine doar vocale care
are linia de sus pe linia i1 şi linia de jos pe linia i2 . Pentru a reuşi acest lucru vom considera
vectorul Cj 1, dacă pe coloana j ı̂ntre liniile i1 şi i2 sunt doar vocale sau 0 dacă nu. Calcularea
acestui vector se poate face cu sume parţiale sau folosindu-ne de vectorul C calculat anterior
pentru perechea i1 , i2  1.
Ce rămâne de făcut este să găsim cea mai lungă secvenţă de valori consecutive, fie ea de
lungime K. Atunci dreptunghiul de arie maximă care conţine doar vocale cu linia sus pe i1 şi
linia de jos pe i2 are
Ó aria egală cu K i2  i1  1. Complexitatea timp a acestei soluţii este
2
O M N  O L L.

Soluţie prof. Şerban Marinel. - Colegiul Naţional Emil Racoviţă, Iaşi


Cerinţa 1. Citirea se face caracter cu caracter, reţinându-se ı̂ntr-un vector caracterele diferite
de spaţiu. Evident concomitent se determină numărul de caractere, apoi se calculează numărul
de linii L şi numărul de coloane C al matricei pentru cerinţa 1.
Cerinţa 2. Pentru a determina palindromul maxim se parcurge matricea linie cu linie,
căutându-se palindroame de lungime lung din mulţimea C, C  1, C  2, ....
Dacă lung devine mai mic decât lungimea palindromului detectat până ı̂n acel moment se
trece la linia următoare. Dacă se determină un palindrom de lungime egală cu lungimea maximă
detectată până ı̂n acel moment se reţine palindromul cel mai mare lexicografic. Acelaşi procedeu
se repetă apoi coloană cu coloană, plecând de acestă dată de la lung L, apoi L  1, ...
Cerinţa 3. Pentru acestă cerinţă construim o matrice ı̂n care am ı̂nlocuit vocalele cu 1 şi
consoanele cu 0 - acestă matrice poate fi calculată ı̂ncă de la cerinţa 1. Pornind de la acestă
matrice construim o matrice de sume parţiale. În acestă matrice am căutat dreptunghiuri de
sumă maximă. Pentru datele de test foarte mari acestă metodă depăşeşte timpul de execuţie dat.

16.3.2 *Cod sursă

16.3.3 *Rezolvare detaliată


Capitolul 17

ONI 2021

17.1 Cat2Pal
Problema 1 - Cat2Pal 100 de puncte
Prin concatenarea a două numere naturale A şi B se pot obţine numerele naturale AB şi BA.
De exemplu, dacă A 8 şi B 8, atunci prin concatenare se poate obţine numărul 88, iar dacă
A 7 şi B 17, atunci prin concatenare se pot obţine numerele 717 şi respectiv 177.

Cerinţe

Scrieţi un program care să rezolve următoarele două cerinţe:

1. Pentru un număr natural nenul A dat, să se calculeze P1 , numărul numerelor naturale
distincte X, unde 1 & X & 10 A, astfel ı̂ncât X concatenat cu A sau A concatenat cu X
este palindrom.
2. Date fiind numărul natural N şi un şir de N numere naturale v 1, v 2, ..., v N , să se
calculeze P2 , numărul de numere palindrom distincte care se pot obţine prin concatenarea
numerelor din perechile v i, v j , unde 1 & i & N şi 1 & j & N .

Date de intrare

Fişierul de intrare cat2pal.in conţine pe prima linie numărul natural C, reprezentând cerinţa
care urmează sa fie rezolvată (1 sau 2).
Dacă C 1, atunci pe linia a doua se află numărul natural A.
Dacă C 2, atunci pe linia a doua se află numărul natural N şi pe linia a treia se află N
numere naturale separate prin spaţiu, reprezentând ş irul v.

Date de ieşire

Fişierul de ieşire cat2pal.out va conţine o singură linie pe care se va scrie un singur număr
natural, reprezentând rezultatul pentru cerinţa C din fişierul de intrare.

Restricţii şi precizări

ˆ 1 & A $ 100 000 000


ˆ 1&N & 10 000
ˆ 0 & v i $ 100 000 ¾i " r1, 2, ..., N x
ˆ Pentru cazul C 2, trebuie luate ı̂n considerare şi perechile v i, v i, adică concatenarea
unui element cu el ı̂nsuşi.
ˆ Pentru teste valorând 20 de puncte: C 1 şi A $ 100 000
ˆ Pentru alte teste valorând 30 de puncte: C 1 şi nu există restricţii suplimentare.
ˆ Pentru teste valorând 15 puncte: C 2 şi N & 1 000
ˆ Pentru teste valorând 35 puncte: C 2 şi nu există restricţii suplimentare.

216
CAPITOLUL 17. ONI 2021 17.1. CAT2PAL 217

Exemple:

cat2pal.in cat2pal.out Explicaţii


1 3 C 1, A 2, numerele X care concatenate cu A produc numere
2 palindrom sunt 2, 12 şi 20, deci P1 3;
2 4 C 2, N 3, v r2, 12, 21x, numerele palindrom distincte care
3 pot fi obţinute sunt 22, 212, 1221, 2112, deci P2 4.
2 12 21

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

17.1.1 Indicaţii de rezolvare

Propunător: prof. Ionel-Vasile Piţ-Rada, Colegiul Naţional ”Traian”, Drobeta-Turnu Severin


Soluţie brute-force - 35 puncte
Prima cerinţa se poate rezolva printr-o metodă brute-force. Putem itera X prin toate numerele
de a 1 la 10 A şi verifica prin construcţia numerelor X ¶A şi A¶X (prin ¶ am notat operaţia de
concatenare). Se pot obţine astfel 20 puncte, ı̂n funcţie şi de constanta implementării.
Complexitate temporală: O A log A
A doua cerinţă se poate rezolva tot folosind o metodă brute-force. Se pot analiza toate perechile
de valori date. Se pot obţine astfel 15 puncte, ı̂n funcţie şi de constanta implementării.
2
Complexitate temporală: O N log M axV al
Soluţ ie oficială - 100 de puncte
Pentru prima cerinţă se poate observa că este suficient a fi analizate:

ˆ pentru concatenările X ¶A prefixele oglinditului numărului A şi prefixele concatenate la


dreapta cu o cifra r0, 1, 2, ..., 9x
ˆ pentru concatenările A¶X sufixele oglinditului numărului A şi sufixele concatenate la stânga
cu o cifra r0, 1, 2, ..., 9x

Astfel se pot depune aceste numere ı̂ntr-un vector şi apoi se verifică şi se numără valorile
distincte care corespund cerinţelor.
2
Complexitate temporală: O log A
Pentru a doua cerinţa se vor parcurge pentru fiecare valoare (asemănător cu rezolvarea de la
prima cerinţă) prefixele şi sufixele valorii oglindite şi pentru fiecare dintre acestea se va ı̂ncerca
obţinerea unui palindrom.
Pentru fiecare palindrom obţinut se va construi un sufix identificator, pentru palindrom cu k
cifre sufixul va avea k  k2 cifre, care va fi marcat ı̂ntr-un vector de poziţie. La final vor fi numărate
poziţiile marcate.
2
Complexitate temporală: O N log M axV al

17.1.2 *Cod sursă

17.1.3 *Rezolvare detaliată


CAPITOLUL 17. ONI 2021 17.2. VIRUS 218

17.2 virus
Problema 2 - virus 100 de puncte
Un laborator specializat studiază mutaţiile unui virus pandemic pentru a găsi cel mai bun
vaccin pentru combaterea acestuia.
Codul unui virus este un şir format din litere (mari şi mici) ale alfabetului englez. Numim
mutaţie a virusului pandemic un şir de caractere care are aceeaşi lungime cu codul virusului şi
care conţine o singură poziţie pentru care litera din şir este diferită de litera situată pe poziţia
respectivă ı̂n codul virusului pandemic.
De exemplu, pentru virusul pandemic având codul abac, şirul Bbac reprezintă o mutaţie,
deoarece are aceeaşi lungime şi diferă doar prin litera de pe prima poziţie.
Laboratorul primeşte o listă conţinând codurilor mai multor viruşi descoperiţi ı̂n urma
testărilor.

Cerinţe

Scrieţi un program care, cunoscând codul virusului pandemic şi lista codurilor viruşilor
descoperiţi ı̂n urma testărilor, rezolvă următoarele cerinţe:

1. Determină numărul de mutaţii ale virusului pandemic existente ı̂n listă, mutaţii nu neapărat
distincte;
2. Determină mutaţia cu număr maxim de apariţii ı̂n listă; dacă există mai multe mutaţii cu
acelaşi număr maxim de apariţii, se va determina prima mutaţie, ı̂n ordine lexicografică.

Date de intrare

Fişierul de intrare virus.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ă codul virusului pandemic. Pe
a treia linie se află un număr natural N , reprezentând numărul de viruşi existenţi ı̂n lista primită
de laborator. Pe următoarele N linii se află codurile viruşilor din listă, câte un cod pe o linie.

Date de ieşire

Fişierul de ieşire virus.out va conţine o singură linie:

ˆ Dacă C 1, pe prima linie va fi scris un număr natural care reprezintă câte elemente din
listă sunt mutaţii ale virusului pandemic.
ˆ Dacă C 2, pe prima linie va fi scris un şir de caractere care reprezintă mutaţia cu număr
maxim de apariţii. Dacă există mai multe mutaţii cu număr maxim de apariţii, va fi afişată
prima (cea mai mică), n̂ ordine lexicografică.

Restricţii şi precizări

ˆ 2&N & 50 000


ˆ Lungimea maximă a codului unui virus este 200

ˆ Dacă a şi b sunt două şiruri de lungime lg, spunem că şirul a a0 a1 a2 ...alg1 este mai
mic din punct de vedere lexicograficc decât şirul b b0 b1 b2 ...blg1 , dacă există o poziţie
k " r0, 1, 2, ..., lg  1x astfel ı̂ncât ai bi pentru orice 0 & i $ k şi ak $ bk .
ˆ În codul ASCII codurile literelor mari sunt mai mici decât codurile literelor mici.

ˆ Pentru teste valorând 31 de puncte: C 1

ˆ Pentru alte teste valorând 16 de puncte: C 2, N le500 şi lungimea maxima a codului unui
virus este 40.
ˆ Pentru alte teste valorând 53 de puncte: C 2 şi nu există restricţii suplimentare.

Exemple:
CAPITOLUL 17. ONI 2021 17.2. VIRUS 219

virus.in virus.out Explicaţii


1 3 mutaţiile sunt Zbac, aBac şi Zbac.
abac
5
Abbbq
Zbac
abbC
aBac
Zbac

2 XbcD mutaţiile XbcD şi aXcD apar fiecare de câte 2 ori, prima ı̂n
abcD ordine lexicografică fiind XbcD.
8 XbcD este mai mic lexicografic decât aXcD, deoarece ı̂n codul
abcdD ASCII:
XbcD ’A’ ¡ ’B’ ¡ ... ¡ ’Z’ ¡ ’a’ ¡ ’b’ ¡ ... ¡ ’z’
Xc
XbcD
aXcD
aXcD
aXc
ab

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

17.2.1 Indicaţii de rezolvare

Propunător: prof. Adrian Pintea, Colegiul Naţional ”Andrei Mureşanu” Dej


Cerinţa 1 - 31 de puncte
Se vor determina numărul de mutaţii ale virusului pandemic existente ı̂n listă, mutaţii nu
neapărat distincte după următorul algoritm:
ˆ Citim codul virusul pe care ı̂l notăm cu v şi cu nr - numărul de mutaţii care iniţial este 0.
ˆ Citim codurile pe rând din fişier şi comparăm fiecare cod citit cu v, numărând poziţiile pe
care se află caractere distincte. În cazul ı̂n care codul citit are aceeaşi lungime egală cu
lungimea codului virusului v şi diferă printr-o singură poziţie, incrementăm nr.
ˆ La final scriem numărul nr ı̂n fişierul de ieşire.
Complexitate temporală: O N Lmax 
Cerinţa 2 - 69 de puncte
Soluţia brute-force - 16 puncte
Putem itera prin toate codurile date, iar dacă un cod este mutaţie, iterăm din nou prin toate
codurile pentru a număra de câte ori apare această mutaţie ı̂n listă. Dacă găsim o mutaţie care
apare de mai multe ori sau apare de la fel de multe ori ca cel mai bun răspuns de până acum, dar
este mai mică din punct de vedere lexicografic, o actualizăm.
2
Complexitate temporală: O N Lmax 
Soluţia oficială
Pentru a obţine o complexitate mai bună vom construi un tabel bidimensional f r cu maxim L
linii (numărul de litere din codul virusului) şi 52 coloane (asociate literelor mici şi mari).
La citirea unui cod din fişier se verifică dacă este mutaţie şi ı̂n caz afirmativ incrementăm
f rposch, unde pos este poziţia pe care apare litera diferită faţă de virusul v, iar ch este litera
prin care diferă faţă de virusul v.
Pentru determinarea maximului se parcurge tabelul bidimensional şi se ţine cont de modificarea
valorii maxime şi de ordinea lexicografică pentru mutaţie.
Complexitate temporală: O N Lmax  Lmax
2
<

CAPITOLUL 17. ONI 2021 17.3. ZID 220

17.2.2 *Cod sursă

17.2.3 *Rezolvare detaliată

17.3 zid
Problema 3 - zid 100 de puncte
Un zid ornamental de formă dreptunghiulară este alcătuit din N rânduri de cărămizi, fiecare
rând având câte M cărămizi identice, aşezate una lângă alta. Fiecare cărămidă este colorată
ı̂ntr-una dintre culorile r0, 1, 2, ..., Cmax .
Un pătrat de latură L ı̂n acest zid este constituit din cărămizile situate pe L rânduri consecutive
şi L coloane consecutive.
Spunem că un pătrat este colorat uniform dacă el conţine acelaşi număr de cărămizi din fiecare
culoare care apare ı̂n pătratul respectiv.

Cerinţe

Scrieţi un program care, ând configuraţia zidului, determină ı̂n acest zid un pătrat de latură
maximă, colorat uniform.

Date de intrare

Fişierul de intrare zid.in conţine pe prima linie numerele naturale N M Cmax , reprezentând
numărul de rânduri de cărămizi, numărul de cărămizi de pe fiecare rând, respectiv culoarea
maximă.
Pe următoarele N linii este descrisă configuraţia zidului, de sus ı̂n jos; pe fiecare linie dintre
cele N se află câte M numere naturale, reprezentând culorile cărămizilor de pe rândul respectiv,
ı̂n ordine, de la stânga la dreapta. Valorile scrise pe aceeaşi linie sunt separate prin câte un spaţiu.

Date de ieşire

Fişierul de ieşire zid.out va conţine o singură linie pe care vor fi scrise 3 numere naturale
N r R C, separate prin câte un singur spaţiu, reprezentând numărul de cărămizi existente ı̂ntr-un
pătrat colorat uniform de latură maximă, respectiv rândul şi cărămida de pe rând situată ı̂n colţul
din stânga-sus al pătratului colorat uniform de latură maximă.

Restricţii şi precizări

ˆ 2 & N, M & 250


ˆ 1 & Cmax &9
ˆ Rândurile sunt numerotate de sus ı̂n jos de la 1 la N . Cărămizile situate pe un rând sunt
numerotate de la stânga la dreapta de la 1 la M .
ˆ Dacă există mai multe pătrate colorate uniform de latură maximă se va alege pătratul pentru
care numărul rândului este minim. Dacă există mai multe pătrate colorate uniform de latură
maximă care au colţul din stânga-sus pe acelaşi rând minim, se va alege pătratul cel mai
din stânga.
ˆ Pentru teste valorând 22 de puncte: 2 & N, M & 30
ˆ Pentru alte teste valorând 23 de puncte: 30 $ N, M & 100
ˆ Pentru alte teste valorând 10 de puncte, cărm̆izile sunt vopsite doar ı̂n două culori: Cmax 1
ˆ Pentru alte teste valorând 45 de puncte: 100 $ N, M & 250

Exemple:
CAPITOLUL 17. ONI 2021 17.3. ZID 221

zid.in zid.out Explicaţii


685 924 Pătratul colorat uniform de latură maximă, situat pe rândul cel
1235 1 2 3 5 mai de sus, cel mai ı̂n stânga este:
1212 3 5 3 5 235
1112 2 3 3 3 223
1235 5 3 2 1 553
3311 2 2 5 5 El conţine 9 cărămizi, ı̂n care apar culorile 2, 3, 5 de câte 3 ori-
2123 2 5 2 2 fiecare. Acest pătrat are colţul din stânga-sus situat pe rândul
al doilea, ı̂n a patra cărămidă de pe rând (a patra coloană).
Există şi alte pătrate colorate uniform formate din 9 cărămizi,
de exemplu:
123
331
212
dar acesta are colţul din stânga-sus pe rândul 4.

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

17.3.1 Indicaţii de rezolvare

Propunător: prof. Nistor Moţ, Şcoala ”Dr. Luca” Brăila


Vom memora configuraţia zidului ı̂ntr-o matrice Z cu N linii şi M coloane, unde Z ij 
reprezintă culoarea cărămizii din poziţia i, j  ı̂n zid.
Soluţia 1 - 22 de puncte
Parcurgem toate pătratele posibile şi verificăm pentru fiecare pătrat dacă este uniform colorat,
parcurgând pătratul şi contorizând culorile care apar ı̂n pătrat ı̂ntr-un vector de frecvenţă.
Pentru a construi toate pătratele posibile fixăm colţul stânga-sus al pătratului ı̂n toate mod-
urile posibile, apoi fixăm latura pătratului ı̂n toate modurile posibile (preferabil ı̂n ordine de-
screscătoare, astfel ı̂ncât prima soluţie găsită să fie de latură maximă).
Iterarea prin toate pătratele are complexitatea O N M min N, M , iar pentru fiecare pătrat
trebuie să facem şi o verificare prin parcurgerea pătratului.
În final, complexitatea temporală este: O N M min N, M  N M  C .
Soluţia 2 - 45 de puncte
Optimizăm soluţia 1, ı̂ncercând să evităm să parcurgem ı̂ntreg pătratul pentru a calcula
frecvenţele culorilor.
Construim un tablou tridimensional F r, unde F rij k  reprezintă numărul de apariţii ale
culorii k pe rândul i al zidului, considerând primele j cărămizi.
Precalculând acest tablou, putem afla frecvenţa de apariţie a fiecărei culori ı̂ntr-un pătrat de
latură L şi colţul stânga-sus ı̂n poziţia i, j  parcurgând doar liniile de la i la i  L  1 şi determinând
frecvenţele de apariţie ale culorilor pentru coloanele j, ..., j  L  1 prin scădere (pentru a determina
frecvenţa de apariţie a culorii k pe linia x scădem F rxj  L  1k   F rxj  1k .
Complexitatea temporală: O N M min N, M  M C .
3.1 Soluţia oficială - 100 de puncte
Optimizăm ı̂n continuare soluţia 2, pentru a determina frecvenţa de apariţie a unei culori
ı̂ntr-un pătrat ı̂n complexitate O 1.
Pentru aceasta vom construi un tablou tridimensional F r, unde F rij k  reprezintă numărul
de apariţii ale culorii k ı̂n submatricea cu colţul stânga-sus ı̂n poziţia 1, 1 şi colţul dreapta-jos
ı̂n poziţia i, j . Precalcularea acestui tablou se poate face ı̂n O N M C  astfel:
CAPITOLUL 17. ONI 2021 17.3. ZID 222

Pentru a memora frecvenţa de apariţie a fiecărei litere ı̂ntr-un pătrat cu colţul stânga-sus ı̂n
poziţia i, j  şi colţul din dreapta-jos ı̂n poziţia i  L  1; j  L  1 vom utiliza un vector uz,
unde uz k  reprezintă frecvenţa de apariţie a culorii k. Calcularea vectorului uz se poate face ı̂n
O C , ı̂n următorul mod:

Complexitatea temporală: O N M min N, M  C .

17.3.2 *Cod sursă

17.3.3 *Rezolvare detaliată


Capitolul 18

ONI 2020 - suspendat !!!

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

223
Capitolul 19

ONI 2019

19.1 domino
Problema 1 - domino 100 de puncte
Într-un joc de domino, fiecare piesă este ı̂mpărţită ı̂n două zone, ı̂n fiecare zonă fiind ı̂nscris
un număr natural. Dacă jocul are dimensiunea d, ı̂n joc vor exista toate piesele distincte care se
pot forma cu numere cuprinse ı̂ntre 0 şi d. Două piese sunt considerate identice dacă au ı̂nscrise
aceleaşi numere, indiferent de ordinea lor. Astfel, piesele 3, 7şi 7, 3 sunt identice. De exemplu,
jocul de dimensiune d 2 va avea 6 piese distincte:

Figura 19.1: domino

Suma tuturor numerelor de pe aceste piese este 12. Problema are două cerinţe:
1. Dat fiind un şir format din N numere naturale nenule reprezentând dimensiunile unor jocuri
de domino, să se determine pentru fiecare joc suma tuturor numerelor ı̂nscrise pe piesele din jocul
respectiv.
2. Dat fiind un şir format din N numere naturale nenule reprezentând sumele tuturor numerelor
de pe piesele unor jocuri de domino, se construieşte mai ı̂ntâi un şir de cifre, notat cu A, scriind
ı̂n ordine toate numerele din şirul dat, fără spaţii ı̂ntre ele. Se cere să se construiască un şir strict
crescător de numere naturale, notat cu B, parcurgând alternativ cifrele din şirul A de la stânga
la dreapta şi de la dreapta la stânga după cum urmează:
- primul număr din B este format din prima cifră din şirul A;
- al doilea număr din B se construieşte concatenând (alipind) cifrele din A, ı̂ncepând de la
dreapta către stânga, până când obţinem un număr strict mai mare decât primul număr din B;
- al treilea număr din B se construieşte concatenând cifrele din A de la stânga către dreapta
(ı̂ncepând cu prima cifră care nu a fost deja utilizată), până când obţinem un număr strict mai
mare decât precedentul din B;
- al patrulea număr din B se construieşte concatenând din nou cifrele din A de la dreapta la
stânga (ı̂ncepând cu cea mai din dreapta cifră care nu a fost deja utilizată), până când obţinem
un număr strict mai mare decât al treilea din B;
- se continuă astfel alternativ, până când nu se mai poate forma un număr strict mai mare
decât ultimul număr adăugat ı̂n B.

Cerinţe

Scrieţi un program care rezolvă cerinţele 1 şi 2 descrise ı̂n enunţ.

Date de intrare

Fişierul de intrare domino.in conţine pe prima linie un număr natural C reprezentând cerinţa
care trebuie rezolvată (1 sau 2). Pe a doua linie se află numărul natural N . Pe a treia linie se află
N numere naturale nenule separate prin câte un spaţiu d1 d2 ... dN .

Date de ieşire

224
CAPITOLUL 19. ONI 2019 19.1. DOMINO 225

Fişierul de ieşire domino.out va conţine o singură linie. Dacă C 1, pe prima linie se vor afişa
N numere naturale separate prin câte un spaţiu; al i-lea număr afişat reprezintă suma numerelor
din jocul de domino având dimensiunea di (1 & i & N ). Dacă C 2, pe prima linie se vor afişa ı̂n
ordine, separate prin câte un spaţiu, valorile din şirul B determinat conform regulilor din enunţ.
Restricţii şi precizări
a 1 & N & 104
a Dacă C 1, 1 & di & 1000, iar dacă C 2, 1 & di & 109, pentru 1 & i & N .
a Numerele din şirul B vor fi afişate fără zerouri nesemnificative (de exemplu, dacă ı̂n urma
aplicării regulilor din enunţ ı̂n şirul B se obţine numărul 0204 se afişează 204).
a Pentru teste ı̂n valoare de 30 de puncte cerinţa este 1.

Exemple
domino.in domino.out Explicaţii
1 12 30 2040 60 252 Cerinţa este 1, deci trebuie să determinăm sumele numerelor
5 din jocurile de dimensiune 2, 3, 15, 4 şi 7.
2 3 15 4 7

Figura 19.2: domino

Timp maxim de executare/test: 0.2 secunde


Memorie: total 8 MB din care pentru stivă 4 MB
Dimensiune maximă a sursei: 10 KB
Sursa: domino.cpp, domino.c sau domino.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 4 ore.
Punctajul maxim cumulat pentru cele trei probleme este de 300 de puncte.

19.1.1 Indicaţii de rezolvare


Descrierea unei solutii posibile

Prof. Maria NITA - Colegiul National ’’Emanuil Gojdu’’, Oradea

Cerinta 1:

Pentru un set de dominouri de dimensiune d, determinarea valorii cerute se poate


realiza generand toate sumele de forma (i,j), cu 0 <= i <= d si 0 <= j <= i.

Generarea sumelor nu se incadreaza in timp pentru datele mari.

Daca termenii sumei se grupeaza se poate obtine:


00
10 11
20 21 22
30 31 32 33
...
i0 i1 i2 ... ... ii
...
d0 d1 d2 ... ... di ... dd
CAPITOLUL 19. ONI 2019 19.1. DOMINO 226

Pe coloana 0: apare 0 de (d+1) ori si suma 0+1+2+...+d = d*(d+1)/2


Pe coloana 1: apare 1 de d ori si suma 1+2+...+d = d*(d+1)/2
Pe coloana 2: apare 2 de d-1 ori si suma 2+3+..+d = (1+2+3+...+d) -1 = d*(d+1)/2 -1
Pe coloana 3: apare 3 de d-2 ori si suma 3+4+...+d = (1+2+3+...+d)-1-2 = d*(d+1)/2
...
Pe coloana i: apare i de d-i+1 ori si suma i+(i+1)+...+d = 1+2+..+(i-1) + i + (i+1)
-(1+2+...+i-1) = d*(d+1)/2 -(1+2+..+i-1)
Pe coloana d: d apare de 1 ori si d = 1+2+..+(d-1)+d - (1+2+...+d-1) = d*(d+1)/2 -(

Adunam aceste sume si obtinem:

(d+1)*(d*(d+1)/2) + 1*d + 2*(d-1) + 3*(d-2) + ... + d*1 -1-(1+2)-(1+2+3)-...-(1+2+.


(d+1)* (d*(d+1)/2) +1*d +2*(d-1) + 3*(d-2) + ... + d*1-1*(d-1)-2*(d-2)-3*(d-3)-..-(
(d+1) * (d*(d+1)/2) + 1+2+3+..+d = (d+1) * (d*(d+1)/2) + d*(d+1)/2 =(d+1+1)* d*(d+1
d*(d+1)*(d+2)/2

Deci suma este pentru fiecare d in parte devine: d*(d+1)*(d+2)/2

Cerinta 2:

O modalitate de rezolvare poate fi:


- se ataseaza fiecarei valori v[i] valoarea p[i] = 10 numarul_de_cifre_v[i] - 1
- se parcurge vectorul v[], luand alternative valorile v[i] ( i=1, i<=j) si v[j] (j
- pentru v[i] cifrele se genereaza astfel:
cif = v[i]/p[i]; v[i] %= p[i]; p[i] /=10;
- pentru v[j] cifrele se genereaza astfel:
cif = v[j]%10; v[j]/=10;
- din cauza dimensiunii numerelor ce se pot obtine se vor folosi siruri de caracter
sir_anterior, sir_urmator
- in sir_urmator se introduc cifre pana la prima valoarea strict mai mare decat sir
- erau necesare prelucrari pentru situatia: prima cifra din sir_urmator este 0

Testele au fost alese astfel incat se putea obtine punctaj si in situatia folosirii
tip long long.

Exista teste in care valorile pe baza carora se obtin sirurile sunt formate din cif

19.1.2 Cod sursă

Listing 19.1.1: Domino 1.cpp


//Maria Nita 100
#include <fstream>
#include <cstring>

#define M 10005

using namespace std;

ifstream cin("domino.in");
ofstream cout("domino.out");

int n, v[M], p[M];

int main()
{
int pr;
cin >> pr;

if (pr == 1)
{
int i, x, a, b, c;
cin >> n;
for(i=1; i<=n; i++)
CAPITOLUL 19. ONI 2019 19.1. DOMINO 227

{
cin >> x;
a = x;
b = x+1;
c = x+2;
if(a%2 == 0)
a /= 2;
else
b /= 2;
v[i] = a * b * c;
}

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


cout << v[i] << ’ ’;
cout << v[n];
cout << ’\n’;
}
else
{
char aux[1000], nr[1000];
int i, j, s, unde, cif;
bool exista;

cin >> n;
for(i=1; i<=n; i++)
cin >> v[i];

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


{
s = v[i];
p[i] = 1;
while(s > 9)
{
p[i] = p[i]*10;
s /= 10;
}
}

i = 1; j = n;
exista = true;
unde = 2; //urmeaza dreapta - stanga
cif = v[1]/p[1];
nr[0] = (char)(cif+’0’);
nr[1] = ’\0’;
v[1] = v[1] % p[1];
p[1] = p[1] / 10;
if(p[1] == 0)
i = 2;
cout << nr << ’ ’; // nr format din prima cifra
while(i<=j && exista)
{
if(unde == 2)
{
s = 0;
while(v[j] > 0 && j >= i)
{
cif = v[j]%10;
v[j] /= 10;
p[j] /= 10;
if(cif == 0 && s == 0)
continue;
aux[s++] = (char)(cif+’0’);
aux[s] =’\0’;
if(v[j]==0 && j >= i && p[i] != 0)
j--;
if(strlen(aux) > strlen(nr) ||
(strcmp(aux, nr)>0 && strlen(aux) == strlen(nr)))
{
unde = 1;
break;
}
}

if(v[j] == 0 && j >= i && p[i] != 0)


j --;
}
CAPITOLUL 19. ONI 2019 19.1. DOMINO 228

else
{
s = 0;
while(p[i] > 0 && i <= j)
{
cif = v[i] / p[i];
v[i] = v[i] % p[i];
p[i] = p[i] / 10;
if(cif == 0 && s == 0)
{
if(p[i] == 0 && i <= j && p[j] != 0)
i++;
continue;
}

aux[s++] = (char)(cif+’0’);
aux[s] = ’\0’;

if(p[i] == 0 && i <= j && p[j] != 0)


i ++;

if(strlen(aux) > strlen(nr) ||


(strcmp(aux, nr)>0 && strlen(aux) == strlen(nr)))
{
unde = 2;
break;
}
}

if(p[i] == 0 && i <= j && p[j] != 0)


i++;
}

if(strlen(aux) < strlen(nr) ||


(strcmp(aux, nr)<0 && strlen(aux) == strlen(nr)))
exista = false;
else
{
cout << aux << ’ ’;
strcpy(nr, aux);
}

if(i == j && p[i] == 0)


exista = false;
}
}

return 0;
}

Listing 19.1.2: domino 2 assert.cpp


//Em. Cerchez 100
#include <fstream>
#include <cstring>
#include <cassert>

#define NMAX 100008

using namespace std;

ifstream fin("domino.in");
ofstream fout("domino.out");

int x, n, cerinta, lglast, lgcrt;


char s[NMAX];
char crt[NMAX];
char last[NMAX];
int v[12];

int compar (char a[], int lga, char b[], int lgb);

int main()
{int i, nr, poz, st, dr, lg, j;
long long int x;
CAPITOLUL 19. ONI 2019 19.1. DOMINO 229

fin>>cerinta>>n;
assert(cerinta==1 || cerinta==2);
assert(0<n && n<=10000);
if (cerinta==1)
{
i=0;
while (fin>>x)
{
i++;
assert(0<x && x<=1000);
fout<<x*(x+1)*(x+2)/2<<’ ’;
}
assert(i==n);
fout<<’\n’; fout.close(); return 0;
}

poz=0;
i=0;
while (fin>>nr)
{
i++;
assert(0<nr && nr<=1000000000);
for (x=1; x<=1000; x++) if (x*(x+1)*(x+2)/2==nr) break;
assert (x<=1000);
lg=0;
while (nr)
{v[lg++]=nr%10; nr/=10; }
for (j=lg-1; j>=0; j--) s[poz++]=v[j]+’0’;
}
assert(i==n);

fout<<s[0]<<’ ’;
last[0]=s[0]; last[1]=0; st=1; dr=poz-1; lglast=1;
while (1)
{
while (s[dr]==’0’ && st<=dr) dr--;
if (st<=dr) {crt[0]=s[dr--]; crt[1]=0; lgcrt=1;}
else break;
while (compar(last,lglast,crt,lgcrt)>=0 && st<=dr)
{crt[lgcrt++]=s[dr--]; crt[lgcrt]=0; }
if (compar(last,lglast,crt,lgcrt)<0)
{
fout<<crt<<’ ’;
strcpy(last,crt); lglast=lgcrt;
}
else break;
while (s[st]==’0’ && st<=dr) st++;
if (st<=dr) {crt[0]=s[st++]; crt[1]=0; lgcrt=1;}
else break;

while (compar(last,lglast,crt,lgcrt)>=0 && st<=dr)


{crt[lgcrt++]=s[st++]; crt[lgcrt]=0; }
if (compar(last,lglast,crt,lgcrt)<0)
{
fout<<crt<<’ ’;
strcpy(last,crt); lglast=lgcrt;
}
else break;
}
fout<<’\n’;
fout.close();

return 0;
}

int compar (char a[], int lga, char b[], int lgb)
{int i;
if (lga<lgb) return -1;
if (lga>lgb) return 1;
for (i=0; i<lga && a[i]==b[i]; i++);
if (i==lga) return 0;
if (a[i]<b[i]) return -1;
return 1;
}
CAPITOLUL 19. ONI 2019 19.1. DOMINO 230

Listing 19.1.3: domino 3.cpp


//Florentina Ungureanu 100

#include <fstream>
#include <cstring>

const int MAXN = 10005;


const int MAXS = 1010;
const int MAXB = 100050;

using namespace std;

ifstream in("domino.in");
ofstream out("domino.out");

typedef unsigned long long nat;

int n,C,v[MAXN];
nat S[MAXS];
char s[MAXB],c[12],a[MAXB],b[MAXB];

void citire()
{
in>>C>>n;
if(C==1)
{ for (int i=1; i<=n; ++i)
in>>v[i];
for (int i=1; i<=1000; ++i) S[i]=S[i-1]+nat(i)*(i+1)/2*3;
}
else
for (int i=1; i<=n; ++i)
{in>>c;
strcat(s,c);
}
}

void P1()
{
for (int i=1; i<=n; ++i)
out<<S[v[i]]<<’ ’;
}
void P2()
{
int i=1,j=strlen(s)-1,p=2,nc=1;
a[0]=s[0];a[1]=’\0’;
out<<a<<’ ’;

while(i<=j){
if(p%2){
while(s[i]==’0’)i++;
if(i+nc<=j+1)
{ strncpy(b,s+i,nc);b[nc]=’\0’;
if(strcmp(a,b)>=0)
{ nc++;strncpy(b,s+i,nc);b[nc]=’\0’;}
}else return;
i+=nc;
}else {
//out<<i<<’*’<<j<<’\n’;
while(j>=i&&s[j]==’0’)j--;
if(i+nc<=j+1)
{ for(int k=0;k<nc;++k)
b[k]=s[j--];
b[nc]=’\0’;//out<<’*’<<b<<’\n’;
if(strcmp(a,b)>=0)
{ if(i>j) return;
b[nc++]=s[j--];b[nc]=’\0’;
}
}
}
if(strcmp(a,b))out<<b<<’ ’;
strcpy(a,b);
p++;
}
}
int main()
CAPITOLUL 19. ONI 2019 19.2. TUBURI 231

{
citire();
if(C==1) P1();
else P2();
return 0;
}

19.1.3 *Rezolvare detaliată

19.2 tuburi
Problema 2 - tuburi 100 de
puncte
Pe un perete au fost montate n  m piese pe n
rânduri (numerotate de sus ı̂n jos, de la 1 la n) şi m
coloane (numerotate de la stânga la dreapta, de la 1 la
m). Piesele sunt tuburi sau coturi având unul dintre
tipurile 1, 2, ..., 6, conform imaginii alăturate.
Ionel poate introduce o bilă ı̂ntr-o piesă situată pe
rândul 1, doar dacă piesa este de tip 2, 4 sau 6. Bila
poate coborı̂ un nivel sau se poate deplasa pe orizontală Figura 19.3: joc1
ı̂ntr-o piesă alăturată, dacă ı̂mbinarea pieselor permite aceasta, dar nu poate urca, din cauza
gravitaţiei. Bila nu poate trece de două ori prin aceeaşi piesă şi se blochează atunci când nu se
mai poate deplasa ı̂ntr-o altă piesă.

Cerinţe

Se citesc două numere naturale n, m şi apoi n  m numere din mulţimea r1, 2, 3, 4, 5, 6x
reprezentând dispunerea pieselor pe perete. Scrieţi un program care să rezolve următoarele cerinţe:
1. determină numărul maxim de piese prin care poate trece până la blocare o bilă introdusă
ı̂n una dintre piesele de pe rândul 1, având tipul 2, 4 sau 6;
2. pentru un rând k dat, determină numerele c şi t, unde c este coloana minimă pentru care,
ı̂nlocuind piesa existentă pe rândul k şi coloana c cu o piesă de tipul t, se obţine un număr cât
mai mare posibil de piese prin care poate trece, până la blocare, o bilă introdusă ı̂n una dintre
piesele de pe rândul 1 având tipul 2, 4 sau 6; dacă există mai multe soluţii de a ı̂nlocui piesa de
pe rândul k şi coloana c, se alege varianta cu t minim.

Date de intrare

Fişierul de intrare tuburi.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 numerele naturale n, m, reprezentând
dimensiunile peretelui. Pe fiecare dintre următoarele n linii se află câte m numere aparţinând
mulţimii r1, 2, 3, 4, 5, 6x reprezentând ı̂n ordine tipurile pieselor de pe perete. Dacă cerinţa este
C 2, fişierul de intrare conţine ı̂n plus, pe a n  3-a linie, un număr natural k reprezentând
numărul unui rând de piese. Valorile scrise pe aceeaşi linie sunt separate prin câte un spaţiu.

Date de ieşire

Fişierul de ieşire tuburi.out va conţine o singură linie.


Dacă C 1, atunci pe prima linie a fişierului se va scrie un număr natural reprezentând
rezultatul de la cerinţa 1.
Dacă C 2, atunci pe prima linie a fişierului se vor scrie două numere naturale c şi t, separate
printr-un spaţiu, cu semnificaţia din enunţ.

Restricţii şi precizări

a 2 & n, m & 500


a Pentru teste valorând 40 de puncte cerinţa este 1.
CAPITOLUL 19. ONI 2019 19.2. TUBURI 232

Exemple

Figura 19.4: tuburi

Timp maxim de executare/test: 0.2 secunde


Memorie: total 8 MB din care pentru stivă 4 MB
Dimensiune maximă a sursei: 10 KB
Sursa: tuburi.cpp, tuburi.c sau tuburi.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 4 ore.
Punctajul maxim cumulat pentru cele trei probleme este de 300 de puncte.

19.2.1 Indicaţii de rezolvare


Descrierea unei solutii posibile

Prof. Florentina Ungureanu - Colegiul National de Informatica, Piatra Neamt

Cerinta 1: se determina pentru fiecare coloana j din multimea de indici {1, 2, ...,
numarul de piese prin care poate trece bila introdusa in piesa de pe randul 1, colo
daca aceasta este de tipul 2, 4 sau 6 si apoi maximul dintre valorile obtinute. In
rezolvarii cerintei doi, pe parcursul determinarii traseelor posibile, pentru fieca
pozitie (i,j) se retine intr-o matrice suplimentara coloana in care a fost introdus
pe randul 1, pentru a ajunge in acea pozitie, respectiv, valoarea 0 daca niciun tra
trece prin acea piesa.

Cerinta 2:

Pentru k=1 se plaseaza in fiecare coloana j, pe rand cate o piesa de orice tip si s
determina lungimea maxima a unui traseu posibil al bilei pana la blocare.

Pentru k diferit de 1 se identifica pentru fiecare coloana j din multimea de indici


{1, 2, ..., m}, de pe randul k tipul piesei care poate fi conectata cu fiecare dint
piesele aflate pe in una dintre pozitiile sus, stanga sau dreapta si se determina
lungimea traseului corespunzator conexiunii facute, recalculand lungimea traseului
bilei pe baza matricei construite pe parcursul rezolvarii cerintei 1.

19.2.2 Cod sursă

Listing 19.2.1: tuburi 100.cpp


//Florentina Ungureanu 100
#include <bits/stdc++.h>
CAPITOLUL 19. ONI 2019 19.2. TUBURI 233

#define Nmax 505

using namespace std;

ifstream in("tuburi.in");
ofstream out("tuburi.out");

int n,m,lm,p,r,k=0;
int a[Nmax][Nmax],C[Nmax][Nmax];

void citire()
{
in>>p>>n>>m;
for (int i=1; i<=n; ++i)
for (int j=1; j<=m; ++j)
if(in>>a[i][j])++k;
for (int j=1; j<=m; ++j)
if (a[1][j]%2==0)
C[1][j]=j;
}

int lungime (int i,int j,int k)


{
int l=1,jj=0,g,col;
if(j==0||j>m)return 0;
do
{
g=0;
col=C[i][j];
if (a[i][j]&&a[i][j]<4)
{
if (a[i+1][j] && a[i+1][j]%2==0)
{i++;l++;g=1;}
}
else switch(a[i][j])
{
case 4:
if (a[i][j+1]==5 || a[i][j+1]==3)
{jj=j;j++;l++;g=1;}break;
case 5:
if (jj==j-1)
{
if (a[i][j+1]==3 || a[i][j+1]==5)
{jj=j;j++;l++;g=1; }
}
else
{
if (a[i][j-1]==1 || a[i][j-1]==5)
{jj=j;j--;l++;g=1;}
}break;
case 6:
if (a[i][j-1]==5 || a[i][j-1]==1)
{jj=j;j--;l++;g=1;}
}
if(k==0) C[i][j]=col;
}
while (g);
return l;
}

int main()
{
int j, l;
citire();
for (j=1; j<=m; j++)
if (a[1][j]%2==0)
{
l=lungime(1,j,0);
if (l>lm) lm=l;
}
if(p==1) out<<lm<<’\n’;
else
{
int t,tc,c,aux,caz;
in>>r;
CAPITOLUL 19. ONI 2019 19.2. TUBURI 234

for (j=1; j<=m; j++)


{ aux=a[r][j];
for (tc=1; tc<=6; ++tc)
{ a[r][j]=tc;
if(tc%2==0)
{C[0][j]=j;l=lungime(1,C[r-1][j],r);}
else if(tc==1)
l=lungime(1,C[r][j+1],r);
else if(tc==3)
l=lungime(1,C[r][j-1],r);
else
{ l=lungime(1,C[r][j-1],r);
if (l>lm)
{lm=l;c=j;t=tc;}
l=lungime(1,C[r][j+1],r);
}
if (l>lm)
{lm=l;c=j;t=tc;}
}
a[r][j]=aux;
}
out<<c<<’ ’<<t<<’\n’;
} return 0;
}

Listing 19.2.2: tuburiOficial.cpp


//Florentina Ungureanu 100
#include <bits/stdc++.h>

#define Nmax 505

using namespace std;

ifstream in("tuburi.in");
ofstream out("tuburi.out");

int n,m,lm,p,r,k=0;
int a[Nmax][Nmax],C[Nmax][Nmax];

void citire()
{
in>>p>>n>>m;
for (int i=1; i<=n; ++i)
for (int j=1; j<=m; ++j)
if(in>>a[i][j])++k;
for (int j=1; j<=m; ++j)
if (a[1][j]%2==0)
C[1][j]=j;
}

int lungime (int i,int j,int k)


{
int l=1,jj=0,g,col;
if(j==0||j>m)return 0;
do
{
g=0;
col=C[i][j];
if (a[i][j]&&a[i][j]<4)
{
if (a[i+1][j] && a[i+1][j]%2==0)
{i++;l++;g=1;}
}
else switch(a[i][j])
{
case 4:
if (a[i][j+1]==5 || a[i][j+1]==3)
{jj=j;j++;l++;g=1;}break;
case 5:
if (jj==j-1)
{
if (a[i][j+1]==3 || a[i][j+1]==5)
{jj=j;j++;l++;g=1; }
}
CAPITOLUL 19. ONI 2019 19.3. VENUS 235

else
{
if (a[i][j-1]==1 || a[i][j-1]==5)
{jj=j;j--;l++;g=1;}
}break;
case 6:
if (a[i][j-1]==5 || a[i][j-1]==1)
{jj=j;j--;l++;g=1;}
}
if(k==0) C[i][j]=col;
}
while (g);
return l;
}

int main()
{
int j, l, lmv;
citire();
for (j=1; j<=m; j++)
if (a[1][j]%2==0)
{
l=lungime(1,j,0);
if (l>lm) lm=l;
}
lmv=lm;
if(p==1) out<<lm<<’\n’;
else
{
int t,tc,c,aux,caz;
in>>r;
lm=0;
for (j=1; j<=m; j++)
{ aux=a[r][j];
for (tc=1; tc<=6; ++tc)
{ a[r][j]=tc;
if(tc%2==0)
if(r>1) l=lungime(1,C[r-1][j],r);
else l=lungime(1,j,r);
else if(tc==1) l=lungime(1,C[r][j+1],r);
else if(tc==3) l=lungime(1,C[r][j-1],r);
else
{ l=lungime(1,C[r][j-1],r);
if (l>lm)
{lm=l;c=j;t=tc;}
l=lungime(1,C[r][j+1],r);
}
if (l>lm)
{lm=l;c=j;t=tc;}
}
a[r][j]=aux;
}
out<<c<<’ ’<<t<<’\n’;
} return 0;
}

19.2.3 *Rezolvare detaliată

19.3 venus
Problema 3 - venus 100 de puncte
Casa de Modă Venus a decis să se modernizeze şi, ı̂ncepând cu 1 ianuarie 2020 ora 00  00,
l-a angajat pe robotul Vasile. Vasile poate executa orice comandă ı̂n exact T ore, indiferent de
complexitatea acesteia (mai exact, dacă Vasile ı̂ncepe să lucreze la comandă ı̂n momentul x, la
momentul x  T ore comanda va fi gata de predare). Foarte ı̂ncrezătoare ı̂n calităţile robotului
Vasile, Casa de Modă Venus a lansat o campanie publicitară cu sloganul ”Dacă am ı̂ntârziat,
primeşti produsul comandat gratis!”. Campania şi-a atins scopul, ca urmare Casa de Modă a
primit deja N comenzi pentru ı̂ntreg anul 2020. Pentru fiecare comandă sunt specificate valoarea
acesteia, precum şi data şi ora până la care produsul comandat trebuie să fie gata de predare.
CAPITOLUL 19. ONI 2019 19.3. VENUS 236

Dacă Vasile predă produsul exact la data şi ora specificată ı̂n comandă (sau ı̂nainte) el ı̂ncasează
valoarea comenzii. Dacă nu, el tot trebuie să execute comanda respectivă, dar nu va primi suma
reprezentând valoarea ei.
Deşi lucrează fără nicio pauză, Vasile estimează că este posibil să nu poată preda la timp
toate comenzile, dar ı̂şi planifică lucrul, astfel ı̂ncât pierderea să fie minimă (adică suma valorilor
comenzilor care nu vor fi predate la timp să fie cât mai mică). Numim planificare optimală
succesiunea ı̂n care Vasile trebuie să execute cele N comenzi, astfel ı̂ncât pierderea să fie minimă.
Cerinţe
Scrieţi un program care, cunoscând informaţiile referitoare la cele N comenzi, determină
pierderea minimă, precum şi o planificare optimală.
Date de intrare
Fişierul de intrare venus.in conţine pe prima linie numărul natural N , reprezentând numărul
de comenzi şi numărul natural T , reprezentând numărul de ore necesare lui Vasile pentru a executa
o comandă. Pe următoarele N linii se află informaţiile despre comenzi, câte o comandă pe o linie,
sub forma:
V zi luna ora
unde V este valoarea comenzii, zi este ziua ı̂n care trebuie predată comanda (un număr natural
cuprins ı̂ntre 1 şi numărul de zile ale lunii), luna este denumirea lunii, iar ora este un număr natural
cuprins ı̂ntre 0 şi 23. Valorile scrise pe aceeaşi linie sunt separate prin câte un spaţiu.
Comenzile se consideră numerotate de la 1 la N ı̂n ordinea din fişierul de intrare.
Date de ieşire
Fişierul de ieşire venus.out va conţine pe prima linie numărul natural pmin, reprezentând
pierderea minimă. Pe a doua linie vor fi scrise N numere naturale distincte cuprinse ı̂ntre 1 şi N ,
separate prin câte un spaţiu, reprezentând o planificare optimală.
Restricţii şi precizări
a 1 & N & 1000
a 1 & T & 500
a 1 & V & 10000
a Numele lunilor vor fi scrise cu litere mici. Anul 2020 este an bisect, adică luna februarie are
29 de zile.
a Dacă există mai multe planificări optimale, se va accepta orice soluţie corectă.
a Se acordă 50% din punctajul pentru fiecare test pentru determinarea valorii pmin. Punctajul
integral pentru fiecare test se acordă pentru rezolvarea ambelor cerinţe (pmin şi o planificare
optimală).

Exemple

venus.in venus.out Explicaţii


4 25 50 Începând cu 1 ianuarie ora 0, Vasile execută
90 10 ianuarie 20 4132 comanda 4 şi termină pe 2 ianuarie la ora 1.00.
50 2 ianuarie 8 Execută apoi comanda 1 pe care o termină pe 3
20 4 ianuarie 3 ianuarie la ora 2.00.
70 2 ianuarie 9 Execută apoi comanda 3 pe care o termină pe 4
ianuarie la ora 3.00.
Execută apoi comanda 2 pe care o termină pe 5
ianuarie la ora 4.00, ceea ce ı̂nseamnă că a depăşit
termenul de predare, deci pierde valoarea ei (50).
Există şi alte planificări optimale posibile.
De exemplu, 4 3 1 2.

Timp maxim de executare/test: 0.1 secunde


Memorie: total 8 MB din care pentru stivă 4 MB
Dimensiune maximă a sursei: 10 KB
Sursa: venus.cpp, venus.c sau venus.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 4 ore.
Punctajul maxim cumulat pentru cele trei probleme este de 300 de puncte.
CAPITOLUL 19. ONI 2019 19.3. VENUS 237

19.3.1 Indicaţii de rezolvare


Descrierea unor solutii posibile

prof. Emanuela Cerchez, Colegiul National "Emil Racovita" Iasi

Solutia 1, prof. Emanuela Cerchez, Colegiul National "Emil Racovita" Iasi

In primul rand observam ca daca o comanda nu poate fi onorata la timp, putem sa o l


la final, pentru oricum nu vom primi valoarea ei. Deci comenzile neonorate la timp
toate plasate la sfarsitul planificarii optimale, nu conteaza in ce ordine.
Pierderea va fi suma valorilor acestor comenzi.
Ca urmare ne intereseaza in mod special lista comenzilor care pot fi onorate la tim
(sa o numim pe scurt Lista)
Observam de asemenea ca este convenabil sa onoram mai intai comenzile cele mai valo
deci vom ordona comenzile descrescator dupa valoare.
Vom parcurge comenzile in ordinea descrescatoare a valorii lor si pentru fiecare co
analizam daca o putem sau nu onora la timp. Pentru aceasta vom cauta o pozitie in L
in care comanda respectiva ar putea fi inserata (fara a afecta alte comenzi, adica
comenzilor existente deja in Lista trebuie sa ramana la timp, deoarece aceste comen
mai valoroase).
Daca o astfel de pozitie nu este gasita vom considera ca aceasta comanda este neono
Pentru a putea verifica daca inserarea este posibila, vom retine comenzile in Lista
ordinea crescatoare a timpului lor de predare. Parcurgem comenzile secvential in or
inversa si cautam pozitia in care putem insera comanda curenta, astfel incat Lista
ramana ordonata dupa timpul de predare, iar comenzile parcurse pot fi amanate cu ti
de predare al comenzii curente.
Deoarece este complicat sa lucram cu date si cu ore, chiar de la citire vom calcula
de predare in ore.

Solutie alternativa,

prof. Florentina Ungureanu, Colegiul National de Informatica, Piatra Neamt

Se declara un vector p cu maximum 366*24 elemente, pozitiile 1, 2, ... , 366*24/t


corespunzand perioadelor t, 2*t, 3*t s.a.m.d.

Se transforma timpii de finalizare ai comenzilor in ore si se retine pentru fiecare


intr-un camp nt numarul intreg de perioade t dupa care aceasta trebuie finalizata.

Se sorteaza descrescator dupa valoare comenzile.

Se parcurge sirul obtinut si pentru fiecare comanda v[i]:

- se verifica daca p[v[i].nt] este 0, caz in care aceasta se planifica executarea c


in perioada v[i].nt, iar p[v[i].nt] = v[i].nr (nr. initial al comenzii);

- daca p[v[i].nt] este diferit de 0, se cauta pozitia k (k din multimea {v[i].nt-1,


p[k]=0, caz in care se planifica executarea comenzii in perioada k, se bifeaza intr
boolean comenzile executate. in cazul nu se gaseste o pozitie k conform specificati
de mai sus, comanda ramane restanta;

- valoarea totala a comenzilor restante se determina scazand din valoarea totala a


valoarea fiecarei comenzi planificate;

- se afiseaza comenzile planificate parcurgand sirul p construit, iar cele restante


crescatoare a numarului initial (ordinea executarii acestora nefiind importanta).
CAPITOLUL 19. ONI 2019 19.3. VENUS 238

19.3.2 Cod sursă

Listing 19.3.1: venus 1.cpp


//Em. Cerchez 100
#include <fstream>
#include <cstring>

#define NMAX 1002

using namespace std;

ifstream fin("venus.in");
ofstream fout("venus.out");

char Luni[12][50]={"ianuarie","februarie", "martie", "aprilie" ,"mai","iunie",


"iulie","august","septembrie","octombrie","noiembrie","decembrie"};

int zl[12]={31,29,31,30,31,30,31,31,30,31,30,31};
int sz[12];

struct Comanda
{short int v, p, nr, tc; };
///v = valoare
///p=timpul pana la care trebuie predata comanda
///nr de ordine al comenzii
///tc=-1 daca comanda nu poate fi onorata la timp sau timpul curent de predare altfel
int n, lg, t;
Comanda C[NMAX];
int pmin;
int L[NMAX];

int main()
{int i, zi, ora, k, j, sch;
char luna[50];
Comanda aux;
fin>>n>>t;

for (i=1; i<12; i++) sz[i]=sz[i-1]+zl[i-1];


for (i=1; i<=n; i++)
{
fin>>C[i].v>>zi>>luna>>ora;
for (j=0; j<12; j++)
if (!strcmp(Luni[j],luna)) break;
C[i].p=(sz[j]+zi-1)*24+ora;
C[i].nr=i;
}

///sortez descrescator comenzile dupa v


do
{sch=0;
for (i=1; i<n; i++)
if (C[i].v<C[i+1].v)
{
aux=C[i]; C[i]=C[i+1]; C[i+1]=aux;
sch=1;
}
}
while (sch);

for (i=1; i<=n && t>C[i].p; i++) C[i].tc=-1;

if (i<=n)
{L[1]=i; lg=1; C[i].tc=t;
for (i++; i<=n; i++)
{///verific daca pot onora la timp comanda C[i]
///parcurg comenzile existente si verific care pot fi amanate
for (j=lg;
j>=1 && C[L[j]].tc+t<=C[L[j]].p && C[L[j]].p>=C[i].p;
j--);

///putem insera comanda i pe pozitia j+1?


if (j==0&&C[i].p>=t || C[L[j]].p<=C[i].p && C[L[j]].tc+t<=C[i].p)
//onorez comanda i
CAPITOLUL 19. ONI 2019 19.3. VENUS 239

{
for (k=lg; k>j; k--) {C[L[k]].tc+=t; L[k+1]=L[k];}
lg++;
L[j+1]=i;
C[i].tc=C[L[j]].tc+t;
}
else C[i].tc=-1;
}
}

pmin=0;
for (i=1; i<=n; i++)
if (C[i].tc==-1) pmin+=C[i].v;
fout<<pmin<<’\n’;

for (i=1; i<=lg; i++) fout<<C[L[i]].nr<<’ ’;


for (i=1; i<=n; i++)
if (C[i].tc==-1) fout<<C[i].nr<<’ ’;

fout<<’\n’;
fout.close();
return 0;
}

Listing 19.3.2: venus 2.cpp


//Florentina Ungureanu 100
#include <fstream>
#include <cstring>
#include <algorithm>

using namespace std;

ifstream cin("venus.in");
ofstream cout("venus.out");

struct comanda
{
int nr, v, h,nt;
} v[1001];

char luni[][15]= {"","ianuarie","februarie","martie","aprilie","mai","iunie",


"iulie","august","septembrie","octombrie", "noiembrie", "decembrie"};

int n,m,p[8800],k,tlm,L[]= {0,31,29,31,30,31,30,31,31,30,31,30,31},mg,tot,t;


bool b[1001];

int lunaZ(char* luna)


{
int i=1;
while(strcmp(luni[i],luna)) i++;
return L[i-1];
}

bool comp(comanda x, comanda y)


{
return x.v>y.v;
}

int main()
{
int zi,l,j,i,dmax=0;
char luna[15];
cin>>n>>t;

for (int i=1; i<12; ++i)


L[i]+=L[i-1];

for (int i=1; i<=n; ++i)


{
v[i].nr=i;
cin>>v[i].v>>zi>>luna>>v[i].h;
l=lunaZ(luna);
v[i].h+=(l+zi-1)*24;
if(v[i].h<t)v[i].nt=0;
CAPITOLUL 19. ONI 2019 19.3. VENUS 240

else v[i].nt=v[i].h/t;
tot+=v[i].v;
}

sort(v+1,v+n+1,comp);

mg=0;
tlm=366*24;
p[0]=1;
for(i=1;i<=n;++i)
if(!p[v[i].nt])
{
p[v[i].nt]=v[i].nr;
b[i]=true;
mg+=v[i].v;
}
else
{
j=v[i].nt;
while(j>0&&p[j])
j--;
if(j)
{
p[j]=v[i].nr;
b[i]=true;
mg+=v[i].v;
}
}

cout<<tot-mg<<’\n’;
for (i=1; i<=tlm; ++i)
if (p[i])
cout << p[i]<<’ ’;

for (int i=1;i<=n;++i)


if(!b[i])
cout << v[i].nr<<’ ’;

return 0;
}

19.3.3 *Rezolvare detaliată


Capitolul 20

ONI 2018

20.1 evip
Problema 1 - evip 100 de puncte
Un număr natural n se numeşte număr VIP dacă este format din cel puţin două cifre, conţine
cel puţin o cifră impară şi cel puţin o cifră pară, iar toate cifrele impare sunt scrise ı̂naintea tuturor
celor pare. (VIP=Valori Impare Pare). De exemplu, 352, 7546 sunt numere VIP, iar 35, 468, 5483,
387 nu sunt numere VIP. Se numeşte SECVENŢĂ VIP ı̂ntr-un şir de cifre, o succesiune de cifre
(aflate pe poziţii consecutive ı̂n şir) care formează, ı̂n ordine, un număr VIP.

Cerinţe

Pentru un şir de cifre nenule, se cere să se determine:


1. Numărul de SECVENŢE VIP din şir.
2. Lungimea minimă a unui şir de cifre care conţine acelaşi număr de SECVENŢE VIP ca
şirul dat şi are toate cifrele impare situate ı̂naintea celor pare.
3. Suma tuturor numerelor ce se pot forma, astfel ı̂ncât fiecare număr să conţină toate cifrele
distincte ale celui mai mare număr VIP din şirul dat, fiecare cifră fiind folosită exact o dată, şi
nicio altă cifră diferită de acestea.

Date de intrare

Fişierul evip.in conţine pe prima linie un număr natural c reprezentând cerinţa care trebuie
să fie rezolvată (1, 2 sau 3). Pe cea de a doua linie se află un şir de cifre nenule, neseparate prin
spaţiu, reprezentând, ı̂n ordine, elementele şirului.

Date de ieşire

Dacă cerinţa este c 1, atunci, pe prima linie a fişierului evip.out va fi scris un număr natural
reprezentând numărul de SECVENŢE VIP din şir.
Dacă cerinţa este c 2, atunci, pe prima linie a fişierului evip.out va fi scris un număr natural
reprezentând lungimea minimă a unui şir de cifre care conţine acelaşi număr de SECVENŢE VIP
ca şirul dat şi are toate cifrele impare situate ı̂naintea celor pare.
Dacă cerinţa este c 3, atunci, pe prima linie a fişierului evip.out va fi scris un număr natural
reprezentând suma tuturor numerelor ce se pot forma, astfel ı̂ncât fiecare număr să conţină toate
cifrele distincte ale celui mai mare număr VIP din şirul dat, fiecare cifră fiind folosită exact o dată,
şi nicio altă cifră diferită de acestea.

Restricţii şi precizări

a Numărul de cifre de pe linia a doua a fişierului de intrare este cel puţin 2 şi cel mult 10 000.
a şirul conţine cel puţin o SECVENŢĂ VIP.
a Pentru teste valorând 30% din punctaj cerinţa este 1. Pentru teste valorând 30% din punctaj
cerinţa este 2. Pentru teste valorând 40% din punctaj cerinţa este 3.

241
CAPITOLUL 20. ONI 2018 20.1. EVIP 242

Exemple
evip.in evip.out Explicaţii
1 6 Sunt 6 SECVENŢE VIP ı̂n şirul dat:
413643623 136 (4 136 43623), 1364 (4 1364 3623), 36 (41364 36 23),
364 (41 364 3623), 36(41364 36 23), 362 (41364 362 3).
2 5 Şirul dat conţine 6 SECVENŢE VIP. Cel mai mic număr de cifre
413643623 dintr-un şir care conţine 6 SECVENŢE VIP şi are toate cifrele impare
situate ı̂naintea celor pare, este 5. Un exemplu de astfel de şir este
13246
3 1776 Cel mai mare număr VIP din şir este 1344. Cifrele distincte ale
413443623 acestui număr sunt {1,3,4}. Suma tuturor numerelor ce se pot scrie,
folosind, o singură dată, toate cifrele {1,3,4}, şi nicio altă cifră diferită
de acestea, este 1776. 134+143+314+341+413+431=1776
Timp maxim de executare/test: 0.1 secunde
Memorie: total 8 MB din care pentru stivă 8 MB
Dimensiune maximă a sursei: 10 KB

20.1.1 Indicaţii de rezolvare

Cerinţa 1)
Se identifică secvenţele maximale, de cifre impare (impark ).
Fiecare astfel de secvenţă conţine impark ˜ park numere VIP.
Fie m numărul acestor secvenţe. Se calculează şi se afişează <
m
k 1 impark ˜ park .
Cerinţa 2)
Fie v numărul numerelor VIP aflate ı̂n şirul dat.
Lungimea celui mai scurt şir ce are toate cifrele impare ı̂naintea celor pare şi conţine axact
Ó
v numere VIP este dk  dv , unde dk reprezintă cel mai mare divizor al lui v, 1 & dk & v,
k

v dk ˜  dv
k
Demonstraţie: ...
Cerinţa 3)
Se identifică cel mai mare număr VIP din şir şi se determină mulţimea cifrelor distincte utilizate
ı̂n scrierea acestuia rx1 , x2 , ..., xk x. ı̂n fiecare număr, trebuie să apară fiecare dintre cele k cifre
distincte.
Fiecare dintre cifrele acestei mulţimi se poate afla pe oricare dintre cele k poziţii.
Mai ı̂ntâi se calculează câte numere se pot forma cu cele k cifre. Dacă fixăm prima cifră pe
una dintre cele k poziţii, atunci cea de-a doua cifră poate ocupa oricare dintre cele k  1 poziţii
rămase ş.a.m.d. Penultimei cifre ı̂i rămân două poziţii, iar ultima cifră se aşează pe ultimul loc
rămas.
Notam acest număr cu nr k ˜ k  1 ˜ k  2 ˜ ... ˜ 2 ˜ 1
Aşezând numerele unele sub altele, se observă că suma cifrelor de pe fiecare ordin este aceeaşi.
Notăm această sumă cu s. Mai mult, toate cele k cifre apar de acelaşi număr de ori pe oricare
ordin (al unităţilor, al zecilor, al sutelor, etc). Fiind nr numere ı̂n total 
fiecare cifră va apărea
de nrk
ori ı̂n s.
Aşadar

=x  s =x  s
k k
=x
k
nr k ˜ k  1 ˜ ... ˜ 2 ˜ 1
s ˜ k k k  1 ˜ ... ˜ 2 ˜ 1 ˜ k
k i 1
k i 1 i 1

Suma tuturor numerelor = <k 1


i 0
i
s ˜ 10 s ˜ 111...1
ÍÒÒ Ò Ò Ò Ò Ò Ò ÑÒÒ Ò Ò Ò Ò Ò Ò Ï
dekori
Cea mai mare sumă se obţine atunci când cel mai mare număr VIP conţine toate cele 9 cifre
distincte (201599999798400)
k
<
Exemplu. Pentru mulţimea r1, 3, 4x, k 3, i 1 1  3  4 8 s 2 ˜ 1 ˜ 8 16  
Suma tuturor numerelor = 16 ˜ 1  16 ˜ 10  16 ˜ 100 16 ˜ 111 1776

20.1.2 *Cod sursă


CAPITOLUL 20. ONI 2018 20.2. NXY 243

20.1.3 *Rezolvare detaliată

20.2 nxy
Problema 2 - nxy 100 de puncte
Se consideră N , un număr natural nenul. Dorim să-l scriem pe N ca suma a două numere
naturale nenule x şi y, astfel ı̂ncât suma cifrelor numerelor x şi y să fie maximă.

Cerinţe

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


1. să determine suma maximă a cifrelor a două numere x şi y cu proprietatea că x  y N ;
2. să determine două numere naturale nenule xmax şi ymax cu proprietatea că xmax ' ymax,
xmax  ymax N , suma cifrelor lor este maximă, iar diferenţa xmax  ymax este maximă;
3. să determine două numere naturale nenule xmin şi ymin cu proprietatea că xmin ' ymin,
xmin  ymin N , suma cifrelor lor este maximă, iar diferenţa xmin  ymin este minimă.

Date de intrare

Fişierul de intrare nxy.in conţine pe prima linie numărul natural c, reprezentând cerinţa (1,
2 sau 3). Pe cea de a doua linie se află numărul natural N .

Date de ieşire

Fişierul de ieşire nxy.out va conţine o singură linie. Dacă c 1, pe prima linie va fi scris
un număr natural s, reprezentând suma maximă a cifrelor a două numere naturale nenule x şi y
pentru care x  y N . Dacă c 2 sau c 3, pe prima linie vor fi scrise două numere naturale
nenule separate printr-un singur spaţiu, reprezentând soluţia pentru cerinţa respectivă (xmax
ymax pentru c 2, respectiv xmin ymin pentru c 3).

Restricţii şi precizări

a 1 $ N & 1018
a Pentru teste valorând 20% din punctaj cerinţa este 1. Pentru teste valorând 40% din punctaj
cerinţa este 2. Pentru teste valorând 40% punctaj cerinţa este 3.

Exemple
nxy.in nxy.out Explicaţii
1 16 Suma maximă care se poate obţine adunând cifrele a două numere x
25 şi y pentru care x+y=25 este 16.
2 19 6 Suma maximă care se poate obţine adunând cifrele a două numere x şi
25 y pentru care x  y 25 este 16. Perechea de numere xmax ' ymax
pentru care xmax  ymax este maximă (13) şi pentru care suma
cifrelor este maximă este xmax 19 şi ymax 6.
3 16 9 Suma maximă care se poate obţine adunând cifrele a două numere x
25 şi y pentru care x  y 25 este 16. Perechea de numere xmin ' ymin
pentru care xmin  ymin este minimă (7) şi pentru care suma cifrelor
este maximă este xmin 16 şi ymin 9.

Timp maxim de executare/test: 0.2 secunde


Memorie: total 8 MB din care pentru stivă 8 MB
Dimensiune maximă a sursei: 10 KB
CAPITOLUL 20. ONI 2018 20.3. VIITOR 244

20.2.1 Indicaţii de rezolvare

Metoda de programare utilizată este Greedy.


Vom extrage cifrele numărului N şi le vom plasa ı̂ntr-un vector.
Pentru a maximiza suma, ı̂n numărul x vom plasa câte cifre de 9 este posibil, cifrele din y fiind
determinate astfel ı̂ncât x  y să fie N .
O atenţie specială trebuie să acordăm la determinarea primei cifre a lui x (care probabil nu va
mai fi 9) şi la faptul că y ar putea avea o cifră mai puţin decât x.
Pentru xmin ymin procedăm similar, doar că plasăm cifrele egale cu 9 ı̂n y şi calculăm cifrele
din x.
Avem grijă ı̂nsă când ajungem la cifra dominantă să o calculăm astfel ı̂ncât cifra dominantă
din xmin să fie cât mai apropiată de cifra dominantă din ymin (cu atenţie specială ca xmin să
nu fie mai mic decât ymin).
În problemă N este de tip long long int, dar algoritmul poate fi cu uşurinţă adaptat şi pentru
numere mari.

20.2.2 *Cod sursă

20.2.3 *Rezolvare detaliată

20.3 viitor
Problema 3 - viitor 100 de puncte
Staţiunea Xoni de pe insula Ixos are N magazine de ı̂ngheţată, aşezate unul lângă altul, pe
aceeaşi parte a străzii pietonale. Acestea sunt numerotate cu valori naturale de la 1 la N , ı̂n
ordinea aşezării pe stradă.
Magazinele sunt deţinute de K acţionari (numerotaţi de la 1 la K), fiecare dintre aceştia fiind
proprietarul unor magazine numerotate consecutiv. Un magazin poate avea mai mulţi acţionari.
Odată cu venirea verii, ı̂ncepe şi perioada concediilor, toţi acţionarii luându-şi concediul
ı̂mpreună, timp de M zile. ı̂nainte de a pleca ı̂n concediu, ei au discutat cu Transportel pentru a se
ocupa de aprovizionarea cu ı̂ngheţată a magazinelor lor. Acesta a decis, singur, să facă ı̂n fiecare
dintre cele M zile aprovizionarea unor magazine, numerotate şi acestea cu numere consecutive.

Cerinţe

Determinaţi, pentru fiecare acţionar numărul de magazine deţinute de către acesta care nu au
fost aprovizionate cu ı̂ngheţată ı̂n nicio zi din concediu.

Date de intrare

Prima linie a fişierului viitor.in conţine trei numere naturale N , M şi K separate prin câte
un spaţiu, cu semnificaţia de mai sus.
Fiecare dintre următoarele M linii conţine câte două numere naturale x şi y, separate printr-un
spaţiu. Numerele x y aflate pe cea de a i-a linie dintre cele M semnifică faptul că ı̂n ziua i sunt
aprovizionate cu ı̂ngheţată toate magazinele cu numerele de ordine x, x  1, ..., y.
Fiecare dintre următoarele K linii conţine câte două numere naturale a şi b, separate printr-un
spaţiu. Numerele a b situate pe a i-a linie dintre cele K semnifică faptul că proprietarul i este
acţionar la magazinele cu numerele a, a  1, a  2, ..., b.

Date de ieşire

Fişierul viitor.out va conţine K linii. Pe cea de a i-a linie se află numărul de magazine
deţinute de către acţionarul i, care nu au fost aprovizionate cu ı̂ngheţată ı̂n nicio zi din concediu.

Restricţii şi precizări


CAPITOLUL 20. ONI 2018 20.3. VIITOR 245

a 1 & N & 2 000 000 000


a 1 & M & 100 000
a 1 & K & 100 000
a Pentru teste valorând 30 de puncte, 1 & N, M, K & 1 000.
a 1 & x & y & N, 1 & a & b & N
a Se garantează că fiecare dintre cele N magazine are cel puţin un acţionar.
a Fiind foarte departe ı̂n viitor, observăm că oamenii trăiesc mai mult, se deplasează mai
repede, dar au şi concedii mai lungi.

Exemple
viitor.in viitor.out Explicaţii
10 3 2 1 Magazinul 7 nu a fost aprovizionat ı̂n nicio zi, fiind singu-
14 2 rul dintre cele deţinute de primul acţionar care nu primeşte
9 10 ı̂ngheţată ı̂n nicio zi.
36 Dintre magazinele deţinute de acţionarul 2, cele cu numerele 7
17 şi 8 nu primesc ı̂ngheţată ı̂n nicio zi.
4 10

Timp maxim de executare/test: 1.3 secunde


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

20.3.1 Indicaţii de rezolvare

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

Formal, problema spune că se dă un prim set de intevale pe axa Ox (fiecare dintre ele acoperă
valori naturale consecutive) şi se cer apoi informatii despre intervale dintr-un alt set: câte numere
naturale de pe axa Ox, incluse ı̂n intervalul dat, nu se află ı̂n niciun interval din primul set.
O soluţie poate fi următoarea: odată cu citirea unui interval din primul set, se marchează
ı̂ntr-un vector valorile naturale care se află ı̂n acel interval. Când avem intervale din al doilea
set, putem fie parcurge element cu element pentru a determina câte valori sunt nemarcate, fie
putem afla direct dacă ı̂n prealabil folosim un vector de sume parţiale pentru intervalul ı̂n care
am marcat. Această soluţie obţine punctaj parţial pentru că nu se ı̂ncadrează nici ı̂n timp nici ı̂n
memorie pentru datele maxime de la intrare.
O alta optimizare poate fi utilizarea tehnicii numită ”şmenul lui Mars” pentru evitarea par-
curgerii tuturor numerelor dintr-un interval dat din primul set.
O altă soluţie care permite obţinerea punctajului maxim este următoarea: Dupa ce intervalele
din primul set se sortează după extremitatea stângă, se poate determina unui alt şir de intervale,
disjuncte, reunind pe cele care se intersectează. Pentru aceasta se foloseşte un algoritm simplu:
avem setat un interval curent, iar intervalul la care am ajuns (ı̂n ordinea dată la intrare) produce
următoarele cazuri:
- intersectează intervalul curent şi nu ı̂l extinde la dreapta (doar acolo se poate extinde) - ı̂n
acest caz trecem la următorul interval;
- intersectează intervalul curent şi ı̂l extinde la dreapta - actualizăm extremitatea dreaptă a
intervalului curent şi trecem la următorul interval;
- nu intersectează intervalul curent (automat ı̂ncepe după extremitatea dreaptă a acestuia),
caz ı̂n care trecem ı̂n soluţie (reuniune) intervalul curent şi reiniţializăm intervalul curent cu acela
la care am ajuns cu parcurgerea.
Având intervalele disjuncte din reuniune, la fiecare interval din al doilea set putem localiza
intervalele din reuniune ı̂n jurul cărora se află extremităţile celui de la interogarea curentă prin
căutări binare. Folosindu-ne şi de un vector de sume parţiale, putem răspunde apoi direct la fiecare
interogare.

20.3.2 *Cod sursă


CAPITOLUL 20. ONI 2018 20.3. VIITOR 246

20.3.3 *Rezolvare detaliată


Capitolul 21

ONI 2017

21.1 carte
Problema 1 - carte 100 de puncte
În timpul activităţilor din ”Săptămâna Altfel” elevii clasei a VII-a doresc să ajute la organizarea
cărţilor din biblioteca şcolii. Fiecare carte este etichetată cu un cod care este exprimat printr-
un un şir de caractere distincte. Acestea pot fi cifrele 0, 1, ..., 9 şi primele zece litere mici ale
alfabetului englez a, b, ..., j.
Codul identifică ı̂n mod unic fiecare carte, adică nu vor exista două cărţi cu acelaşi cod, dar
şi genul literar din care acestea face parte. Cărţile din acelaşi gen literar au codul de identificare
format din aceleaşi caractere, distincte, dispuse ı̂n altă ordine.
Numim coduri pereche două coduri de identificare care au acelaşi număr de caractere şi care
diferă printr-un caracter. De exemplu, codurile 42a8 şi 2c8a sunt coduri pereche. Pe de altă parte,
codurile 42a8 şi 248a, respectiv 42ab şi 248c, nu sunt coduri pereche.

Cerinţe

Fiind dat şirul celor N coduri de identificare, scrieţi un program care să rezolve următoarele
cerinţe:
1) determină numărul de cărţi din cel mai numeros gen literar şi numărul de genuri literare
care au acest număr maxim de cărţi.
2) determină numărul de coduri, din şirul celor N , care sunt coduri pereche cu ultimul cod din
şir.

Date de intrare

Fişierul de intrare carte.in conţine pe prima linie un număr natural C. Pentru toate testele,
C poate lua numai valorile 1 sau 2. Pe a doua linie se află numărul N de cărţi din biblioteca şcolii,
iar pe următoarele N linii, câte un şir de caractere pe fiecare linie, ce reprezintă codul pentru
identificarea unei cărţi.

Date de ieşire

Dacă valoarea lui C este 1, se va rezolva numai cerinţa 1. ı̂n acest caz, fişierul de ieşire
carte.out conţine pe prima linie numărul maxim de cărţi de acelaşi gen literar, M AX, iar pe a
doua linie numărul de genuri literare care au exact M AX cărţi.
Dacă valoarea lui C este 2, se va rezolva numai cerinţa 2. ı̂n acest caz, fişierul de ieşire
carte.out conţine pe prima linie numărul de coduri pereche cu ultimul cod din şirul celor N .

Restricţii şi precizări

a 1 & N & 1 000 000


a Pentru rezolvarea corectă a primei cerinţe se obţin 60 de puncte, iar pentru rezolvarea corectă
a celei de a doua cerinţe se acordă 40 de puncte

Exemple

247
CAPITOLUL 21. ONI 2017 21.1. CARTE 248

carte.in carte.out Explicaţii


1 32 Sunt maxim 3 cărţi de acelaşi gen literar.
8 Sunt 2 genuri cu număr maxim de cărţi:
1289f5 { 128905, 129805, 219805 } şi { 1e2, 12e, e21 }
128905
129805
219805
12
1e2
12e
e21
2 5 Sunt 5 coduri pereche cu 2189e5:
10 1289f5, 128905, 129805, 219805, 218905.
1289f5
128905
5
12
129805
219805
218905
132
312
2189e5

Timp maxim de executare/test: 2.0 secunde


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

21.1.1 Indicaţii de rezolvare

prof. Pintea Adrian Doru, Colegiul Naţional ”Andrei Mureşanu” DEJ

Pentru fiecare cod de identificare se construieşte un număr natural ı̂n care pentru fiecare
caracter din codul de identificare se ia poziţia p din şirul ”0123456789abcdefghij” şi se adaugă
p
la număr valoarea 2 , astfel codurile de identificare care sunt formate din aceleaşi caractere vor
avea asociat acelaşi număr şi ı̂n continuare se foloseşte un vector de frecvenţe [0, 1, ..., 1 100 000],
1.100.000 % 2 .
20

2 12 3
De exemplu, pentru codul ”2b3” se formează numărul 2  2  2 4  4096  8 4108 (ı̂n
baza 2 este 1000000001100), deci codurile ”2b3” şi ”23b” au asociat acelaşi număr.
Cerinta 1
Se parcurge vectorul de frecvente, se găseşte valoarea maximă şi la final printr-o parcurgere
liniara a acestui vector se numără pe câte poziţii apare valoarea maximă.
Cerinta 2
Pentru ultimul cod de identificare se construieşte numărul asociat conform precizărilor descrise
anterior şi comparând acest număr cu celelalte numere asociate celorlalte coduri se verifică dacă
sunt coduri pereche cu ultimul cod (au aceeaşi lungime şi diferă prin exact un caracter).

21.1.2 Cod sursă

Listing 21.1.1: carte.cpp


#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <cctype>
CAPITOLUL 21. ONI 2017 21.1. CARTE 249

#define Nmax 1300000

using namespace std;

int A[Nmax], poz, j, P, N, K, l, k, x,Max, nMax, y, S=0;


int i, mx, n, cx, ed, nrC=0, C, mp, np, ngen, nm, Vmax, nr, nc, cmx, mu, nu;
char s[25];

int main()
{
freopen("carte.in", "r", stdin);
freopen("carte.out","w", stdout);

scanf("%d\n%d\n",&C,&N);

assert(0 < N && N<=1000000);

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


{
scanf("%s\n", &s);
n = strlen(s);
assert(0 < n && n <= 20);

mx = 0;
j = 0;
while(j < n)
{
assert(isdigit(s[j]) || isalpha(s[j]) && s[j]<=’j’);
if(isdigit(s[j]))
x=s[j]-’0’;
else
x=s[j]-’a’ + 10;
mx |= 1<<x;
++j;
}

A[mx]++;
if(A[mx]==1)
ngen++;

if(A[mx] > Max)


Max = A[mx], nMax=1;
else
if(A[mx] == Max)
nMax++;

if(i==N) {mu = mx; nu = n;}


if(mx>Vmax) Vmax=mx;
}

if(C==1)
printf("%d\n%d\n", Max, nMax);
else
{
for(mx = 0; mx <= Vmax; mx++)
if(A[mx])
{
ed = mu ˆ mx;
nc=0;
do
{
ed &= ed-1;
nc++;
} while (ed);

nr=0;
cmx = mx;
do
{
cmx&= cmx-1;
nr++;
} while (cmx);

if(nr==nu && nc==2)


nrC+=A[mx];
}
CAPITOLUL 21. ONI 2017 21.2. GHINDE 250

printf("%d\n", nrC);
}

return 0;
}

21.1.3 *Rezolvare detaliată

21.2 ghinde
Problema 2 - ghinde 100 de puncte
Scrat şi Scratte sunt două veveriţe devoratoare de ghinde. Ele trăiesc ı̂ntr-un stejar ı̂nalt şi
culeg ghinde din cele N ramuri ale acestuia. Veveriţele vor organiza un concurs: cine culege cele
mai multe ghinde ı̂n K ture. ı̂ntr-o tură, fiecare veveriţă se va deplasa de la vizuină până la o
ramură a stejarului, de unde va culege cât mai multe ghinde, dar nu mai mult de M ghinde, după
care va reveni ı̂n vizuină. Veveriţele vor efectua alternativ fiecare câte K ture, prima care ı̂ncepe
fiind Scratte.
Supărat că la concurs nu va ı̂ncepe primul, Scrat decide să se antreneze separat şi să vadă câte
ghinde ar culege ı̂n K ture, dacă ar fi singur.

Cerinţe

Să se realizeze un program care determină:


1) Câte ghinde culege Scrat ı̂n timpul antrenamentului;
2) Câte ghinde a cules fiecare veveriţă pe durata concursului.

Date de intrare

Pe prima linie a fişierului ghinde.in se află un număr natural C. Pentru toate testele, C poate
lua numai valorile 1 sau 2.
Pe a doua linie se găsesc numerele N , M şi K reprezentând numărul de ramuri ale stejarului,
numărul maxim de ghinde culese la o tură, respectiv numărul de ture.
Pe următoarele N linii se găsesc numărul de ghinde de pe fiecare ramură ı̂n parte.

Date de ieşire

Dacă valoarea lui C este 1, se va rezolva numai punctul 1) din cerinţe. ı̂n acest caz, fişierul de
ieşire ghinde.out va conţine numărul total de ghinde cules ı̂n timpul antrenamentului de Scrat.
Dacă valoarea lui C este 2, se va rezolva numai punctul 2) din cerinţe. ı̂n acest caz, fişierul
de ieşire ghinde.out va conţine pe aceeaşi linie două numere naturale, separate printr-un spaţiu,
reprezentând ı̂n ordine, numărul de ghinde culese de Scratte respectiv Scrat, pe durata concursului.

Restricţii şi precizări

a 1 & N & 500 000


a 1 & K & 2 000 000 000
a 1 & M & 500 000
a 0 & numărul de ghinde de pe o ramură & 500 000
a Pentru rezolvarea corectă a primei cerinţe se obţin 20 de puncte, iar pentru rezolvarea corectă
a celei de a doua cerinţe se obţin 80 puncte.

Exemple
CAPITOLUL 21. ONI 2017 21.2. GHINDE 251

ghinde.in ghinde.out Explicaţii


1 29 Scart culege 10 ghinde de pe prima ramura, apoi 10 de pe a doua
3 10 3 ramura şi alte 9 de pe a doua ramura, adică 10+10+9 = 29
13
19
4
2 23 20 Scratte: 10 de pe ramura a doua;
4 10 3 Scrat: 10 de pe ramura unu;
13 Scratte: 9 de pe ramura doi;
19 Scrat: 7 de pe ramura patru;
4 Scratte: 4 de pe ramura trei;
7 Scrat: 3 de pe ramura unu;
Scratte: 10+9+4=23
Scrat: 10+7+3=20

Timp maxim de executare/test: 0.7 secunde


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

21.2.1 Indicaţii de rezolvare

prof. Mircea Rotar - Lic. Voc. Ped. N. Bolcaş, Beiuş

Considerăm M numărul maxim de ghinde culese la o tură.


Aflăm câte ture se realizează cu M ghinde culese la fiecare tură. Pentru acest lucru la toate cele
N ramuri reţinem câtul dintre numărul de ghinde de pe ramură şi M , pe urmă ı̂nsumăm toate
aceste valori. Pentru ghindele rămase vom construi un vector de frecvenţe care reţine resturile
ı̂mpărţirii anterioare (restul ı̂mpărţirii dintre numărul de ghinde de pe ramură şi M ) şi le vom
distribui ı̂n ordine descrescătoare a valorilor pentru resturi.

21.2.2 Cod sursă

Listing 21.2.1: ghinde.cpp


///prof. Rotar Mircea
#include <bits/stdc++.h>

using namespace std;

constexpr int Nmax=500005;


constexpr int Mmax=500005;

ifstream f("ghinde.in");
ofstream g("ghinde.out");

long long rez, nr, rez1, rez2;


int M, N, K, i, C, a[Mmax], j, x;

int main()
{
f>>C>>N>>M>>K;

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


{
f>>x;
nr += x/M;
a[x%M]++;
}

if(C == 1)
{
if(nr>=K)
{
g<<1LL*K*M<<’\n’ ;
CAPITOLUL 21. ONI 2017 21.3. SUBMAT 252

return 0;
}

K -= nr;
rez =1LL * nr * M;

for(j= M-1; j && K;)


if(a[j])
rez += j, K--, a[j]--;
else
j--;

g<< rez <<’\n’ ;


return 0;
}

if(nr >= 2*K)


{
g<< 1LL*K*M <<" "<< 1LL*K*M <<’\n’ ;
return 0;
}

rez1 = (nr+1)/2*M;
rez2 = nr/2*M;
K = 2*K - nr;

for(j=M-1; j>0 && K;)


if(K%2==0)
if(a[j])
rez1 += j, a[j]--, K--;
else
j--;
else
if(a[j])
rez2 += j, a[j]--, K--;
else
j--;

g<< rez1 <<" "<< rez2 <<’\n’ ;

return 0;
}

21.2.3 *Rezolvare detaliată

21.3 submat
Problema 3 - submat 100 de puncte
Se consideră o matrice A având N linii şi N coloane. Elementele acesteia aparţin mulţimii
r0, 1, 2x. Pe fiecare linie şi pe fiecare coloană valorile elementelor sunt dispuse crescător.
Fie două elemente din matrice situate pe linia i1 şi coloana j1 respectiv i2 şi j2 , unde i1 & i2
şi j1 & j2 . O submatrice a lui A, având colţurile stânga-sus şi dreapta-jos ı̂n i1 , j1  şi i2 , j2 ,
este formată din toate elementele situate pe linii cuprinse ı̂ntre i1 şi i2 , inclusiv, şi coloane ı̂ntre
j1 şi j2 , inclusiv. Numim submatrice constantă o submatrice a matricei A, având toate elementele
egale.
Cerinţe
Realizaţi un program care determină numărul maxim K de elemente pe care ı̂l are o submatrice
constantă a lui A şi numărul submatricilor constante formate din K elemente.
Date de intrare
În fişierul submat.in pe prima linie se găseşte numărul natural N . Pe următoarele N linii
câte o pereche de numere naturale, despărţite printr-un spaţiu:
- Primul număr de pe linia i  1 din fişier reprezintă numărul de ordine al primei coloane de
pe linia i din matricea A, unde elementul este egal cu 1. Dacă pe linia i nu apare niciun element
egal cu 1, acest număr are valoarea 0.
CAPITOLUL 21. ONI 2017 21.3. SUBMAT 253

- Al doilea număr de pe linia i  1 din fişier reprezintă numărul de ordine al primei coloane de
pe linia i din matricea A, unde elementul este egal cu 2. Dacă pe linia i nu apare niciun element
egal cu 2, acest număr are valoarea 0.

Date de ieşire

Fişierul de ieşire submat.out va conţine pe prima linie o pereche de numere naturale separate
printr-un spaţiu, reprezentând, ı̂n ordine, numărul maxim de elemente pe care ı̂l are o submatrice
constantă a lui A, respectiv numărul submatricilor constante formate din acest număr maxim de
elemente determinat.

Restricţii şi precizări

a 1 & N & 5 000


a Considerăm liniile şi coloanele matricei A numerotate de la 1 la N .

Exemple
submat.in submat.out Explicaţii
8 12 6 Matricea corespunzătoare fişiereului de intrare este:
40 00011111
48 00011112
48 00011112
37 00111122
36 00111222
35 00112222
23 01222222
02 02222222
Numărul maxim de elemente al unei submatrici constante este
12.
Sunt 6 submatricile constante formate din 12 elemente, respec-
tiv cele având colţurile ı̂n: (1,1) şi (6,2); (1,4) şi (4,6); (1,4) şi
(3,7); (5,6) şi (8,8); (7,3) şi (8,8); (6,5) şi (8,8).

Timp maxim de executare/test: 0.1 secunde


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

21.3.1 Indicaţii de rezolvare

Stelian Ciurea

Pentru ı̂nceput determinăm submatricea cu număr maxim de elemente egale cu 2, apoi subma-
tricea cu număr maxim de elemente egale cu 0. Datorită particularităţii matricei (valori crescătoare
pe fiecare linie şi pe fiecare coloană), aceste determinări se pot face ı̂n O n, parcurgând cele două
şiruri de intrare.
Pentru a determina submatricea cu număr maxim de elemente egale cu 1, fixăm o linie - fie
ea linia i, apoi pentru toate liniile j, unde j ia valori de la i la n, calculăm numărul de elemente
egale cu 1 din submatricea aflată ı̂ntre liniile i si j şi coloanele determinate de prima valoare cu
un elemente egal cu 1 de pe linia i şi ultima coloană cu un element egal cu 1 de pe coloana j care
este coloana anterioara primei coloane care conţine un 2. Aceste două coloane se deduc din datele
de intrare referitoare la linia i, respectiv la linia j.
2
Pentru ı̂ncadrarea ı̂n timp pentru această etapă a algoritmului, care are complexitatea O n ,
sunt necesare anumite optimizări care ţin cont de dimensiunea submatricei de arie maximă deter-
minată până ı̂n acel moment şi de particularităţile matricei.
CAPITOLUL 21. ONI 2017 21.3. SUBMAT 254

21.3.2 Cod sursă

Listing 21.3.1: submat cpp.cpp


//#include <cstdio>
#include <fstream>
#include <algorithm>

#define nmax 5002

using namespace std;

ifstream f("submat.in");
//FILE * f = fopen("submat.in","rt");

ofstream g("submat.out");
//FILE * g = fopen("submat.out","wt");

int primacoloana1[nmax];
int primacoloana2[nmax];

int n,nrelemax=1,i,ctmax,nrelemcrt,j,poz;
int inaltime,latime;
int xi1, xi2, xj1, xj2;

int main()
{
f >> n;
//fscanf(f,"%ld",&n);

for (i=1;i<=n;i++)
f >> primacoloana1[i] >> primacoloana2[i];
//fscanf(f,"%ld%ld",&primacoloana1[i],&primacoloana2[i]);

for (i=1;i<=n;i++)
{
if (primacoloana2[i]==0)
continue;

latime = n - primacoloana2[i] + 1;
inaltime = n - i + 1;
nrelemcrt = inaltime * latime;

if (nrelemcrt > nrelemax)


{
nrelemax = nrelemcrt;
ctmax=0;
}

if (nrelemcrt == nrelemax)
{
ctmax++;
}
}
//cout << nrelemax << ’ ’ << ctmax << endl;

for (i=n; i>=1;i--)


{
if (primacoloana1[i]==0 && primacoloana2[i]==0)
latime = n;
else
if (primacoloana1[i]==0 && primacoloana2[i]!=0)
latime = primacoloana2[i]-1;
else
latime = primacoloana1[i]-1;

inaltime = i;
nrelemcrt = inaltime * latime;
//cout << i << ’ ’ << nrelemcrt << endl;

if (nrelemcrt > nrelemax)


{
nrelemax = nrelemcrt;
poz = i;
CAPITOLUL 21. ONI 2017 21.3. SUBMAT 255

ctmax=0;
}

if (nrelemcrt == nrelemax)
{
ctmax++;
}
}
//cout << nrelemax << ’ ’ << ctmax << endl;

primacoloana1[i]=0;
for (i=1;i<=n;i++)
{
if (primacoloana1[i]==primacoloana1[i-1])
continue;

if (primacoloana1[i]==0)
continue;

xi1 = primacoloana1[i];

if (primacoloana2[i]!=0)
xi2 = primacoloana2[i] - 1;
else
xi2 = n;

int hmin = nrelemax / (xi2 - xi1 + 1);


// cout << i << ’ ’ << hmin << endl;

for (j=max(i,i+hmin-1);j<=n;j++)
{
if (primacoloana1[j]==0)
break;

inaltime = j - i + 1;
xj1 = primacoloana1[j];
if (primacoloana2[j]!=0)
{
if (primacoloana2[j]<primacoloana1[i])
break;
xj2 = primacoloana2[j] - 1;
}
else
xj2 = n;

//cout << i << ’ ’ << j << ’ ’<< xi1 << ’ ’<< xi2 << ’ ’
// << xj1 << ’ ’ << xj2 <<endl;

latime = min(xi2,xj2) - max (xi1, xj1) + 1;


//cout << inaltime << ’ ’<< latime << endl;

nrelemcrt = inaltime * latime;


if (nrelemcrt > nrelemax)
{
nrelemax = nrelemcrt;
ctmax=0;
}
if (nrelemcrt == nrelemax)
{
ctmax++;
}
}
}

//cout << nrelemax << ’ ’ << ctmax << endl;


g << nrelemax << ’ ’ << ctmax << endl;
//fprintf(g,"%ld %ld\n",nrelemax,ctmax);
}

21.3.3 *Rezolvare detaliată


Capitolul 22

ONI 2016

22.1 birouri
Problema 1 - birouri 100 de puncte
Arhi şi-a propus să extindă clădirea de birouri pe care a proiectat-
o iniţial pe un singur nivel numerotat cu 1, ı̂mpărţit ı̂n n ˜ n zone
pătratice de latură 1, fiecare corespunzând unui birou, prin constru-
irea mai multor niveluri. În colţurile tuturor birourilor se construiesc
grinzi de rezistenţă. Pentru a asigura rezistenţa ı̂ntregii clădiri, Arhi
va proiecta niveluri noi, numerotate cu 2, 3, ... atât timp cât conţin
cel puţin un birou şi sunt respectate următoarele patru reguli:
R1: fiecare nivel nou va fi proiectat sub forma unui dreptunghi sau Figura 22.1: birouri
pătrat de arie maximă pentru nivelele cu număr impar, respectiv, sub forma unui pătrat de arie
maximă pentru nivelele cu număr par;
R2: fiecare dintre colţurile zidurilor unui nivel nou trebuie plasat pe câte o grindă de rezistenţă
dintre două sau mai multe birouri de pe nivelul precedent;
R3: oricare două dintre colţurile zidurilor unui nivel nou vor fi plasate pe ziduri diferite (un
zid nu se poate suprapune ı̂n totalitate pe alt zid) şi cel puţin două vârfuri opuse ale unui nivel
nou se vor afla pe ziduri opuse ale nivelului precedent;
R4: orice porţiune de zid de pe nivelul k (k % 1), construită deasupra unui birou de pe nivelul
k  1, se va suprapune exact peste una dintre laturile biroului, sau ı̂l va străbate ı̂n diagonală.
Birourile de pe nivelul k (k % 1), vor fi construite exact deasupra celor de pe nivelul precedent,
astfel, nivelurile 2, 4 etc. vor avea lângă ziduri spaţii triunghiulare care nu vor aparţine niciunui
birou.
Numerele inscripţionate pe birouri ı̂n imaginea de mai sus, indică nivelul corespunzător
birourilor vizibile de deasupra clădirii.

Cerinţe

Cunoscându-se lungimea n a laturii primului nivel al clădirii, să se determine:


1. numărul maxim de niveluri pe care le poate avea clădirea;
2. numărul total de birouri ale clădirii cu număr maxim de niveluri.

Date de intrare

Fişierul de intrare birouri.in conţine pe prima linie una dintre valorile 1 sau 2, reprezentând
cerinţa 1, dacă se cere determinarea numărului maxim de niveluri pe care le poate avea clădirea,
respectiv cerinţa 2, dacă se cere determinarea numărului total de birouri al clădirii cu număr
maxim de niveluri.
Linia a doua conţine un număr natural n (reprezentând lungimea fiecărui zid al primului nivel
al clădirii).

Date de ieşire

256
CAPITOLUL 22. ONI 2016 22.1. BIROURI 257

Fişierul de ieşire birouri.out conţine pe prima linie un număr natural reprezentând numărul
maxim de niveluri pe care le poate avea clădirea, dacă cerinţa a fost 1, respectiv un număr natural
reprezentând numărul total de birouri ale clădirii cu număr maxim de niveluri, dacă cerinţa a fost
2.

Restricţii şi precizări

a 3 & n & 32768


a Pentru rezolvarea corectă a cerinţei 1 se acordă 30% din punctaj, iar pentru rezolvarea
corectă a cerinţei 2 se acordă 70% din punctaj.

Exemple
birouri.in birouri.out Explicaţii
1 5 Exemplul corespunde imaginii de mai sus. Clădirea cu nivelul
10 de la bază de latură 10 va avea 5 niveluri.
Nivelul 6 nu se mai construieşte, deoarece nu ar conţine niciun
birou.
2 172 Clădirea cu 5 niveluri şi latura de la bază de lungime 10 are:
10 - pe primul nivel 100 birouri;
- pe nivelul doi 40 birouri;
- pe nivelul trei 24 birouri;
- pe nivelul patru 4 birouri;
- pe nivelul cinci 4 birouri.
100 + 40 + 24 + 4 + 4 = 172

Timp maxim de executare/test: 0.2 secunde


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

22.1.1 Indicaţii de rezolvare

prof. Florentina Ungureanu, Colegiul Naţional de Informatică Piatra-Neamţ

O soluţie se poate obţine astfel:


Se observă că nivelurile de ordin impar cu zidurile de lungime n şi m (pe nivelul 1 m n) sunt
formate din n ˜ m birouri.
Pentru fiecare nivel niv de ordin par:
- determinăm k min n, m (n, m lungimile zidurilor de pe nivelul precedent), iar numărul
de birouri de pe acest nivel va fi k ˜ k ©2  k; dacă numărul de birouri obţinut este 0, nivelul nu
se va construi;
- dacă valoarea k este divizibilă cu patru, următorul nivel va fi de formă pătratică, cu lungimile
zidurilor n m k ©2, iar ı̂n caz contrar, lungimile zidurilor devin n n©2  1; m n©2  1.
Algoritmul se finalizează când nu mai poate fi construit un nivel nou.

22.1.2 Cod sursă

Listing 22.1.1: ABbirouri.cpp


#include <iostream>
#include <fstream>

#define in "birouri.in"
#define ou "birouri.out"

using namespace std;

long long NrBirouri;


long long NrNiv;
long long n, lat, lun, drept, tmp;

int main()
CAPITOLUL 22. ONI 2016 22.1. BIROURI 258

{
short c;
int i, ok;
ifstream f(in);
f>>c>>n;
f.close();

NrNiv = NrBirouri = 0;
lat = n;
lun = n;
ok = 1;
drept = 1;
do
{
NrNiv++;
if(drept)
{
if(lun == lat)
{
if(lat % 2 == 1)
ok = 0;
NrBirouri += lat*lat;
lat /= 2; lun = lat;
}
else
{
NrBirouri += lat*lun;
if(lun < lat) tmp = lun, lun = lat, lat = tmp;
if(lat % 2 == 1 || lun % 2 == 1)
ok = 0;
lat /=2; lun = lat;
}
}
else
{
NrBirouri += 2*(lat-1)*lat;
if(lat % 2 == 1)
lat = lat - 1, lun = lun + 1;
}

drept = !drept;
if(lat == 1 || lun == 1)
ok = 0;
} while(ok);

ofstream g(ou);

if(c == 1)
g<<NrNiv<<endl;
else
g<<NrBirouri<<endl;

g.close();
return 0;
}

Listing 22.1.2: CSbirouri.cpp


#include <fstream>

using namespace std;

ifstream f("birouri.in");
ofstream g("birouri.out");

int n,v,h,m;
long long p;

int main()
{
f>>v>>n;
m=n;
if(n&1)
{
h=1;
CAPITOLUL 22. ONI 2016 22.1. BIROURI 259

p=n*n;
}
else
while((n&1)+(m&1)<2)
{
p+=n*m;
h++;
n=min(n,m);
m=n=n/2;
if(n>=2)
{
p+=2*(n-1)*n;
h++;
if(n&1)
{ m=n+1;
n--;
}
}
}

if(v==1)
g<<h<<’\n’;
else
g<<p<<’\n’;

f.close();
g.close();
return 0;
}

Listing 22.1.3: FUbirouri.cpp


#include <fstream>

using namespace std;

int n,m,k, c, niv, b,p;

ifstream in("birouri.in");
ofstream out("birouri.out");

int main()
{
in>>c>>n;m=n;
do
{
niv++;
if(niv%2)
b+=n*m;
else
{
k=n>m?m:n;
p=k*k/2-k;b+=p;
if(!p)niv--;
if (k%4==0)
n=m=k/=2;
else
{
n=n/2-1;
m=n+2;
}
}
} while(n&&n%2==0);

if(c==1)
out << niv << endl;
else
out << b << endl;

return 0;
}
CAPITOLUL 22. ONI 2016 22.2. CRISTALE 260

22.1.3 *Rezolvare detaliată

22.2 cristale
Problema 2 - cristale 100 de puncte

Pietrele preţioase au fascinat omenirea ı̂ncă din timpuri străvechi iar cele mai
renumite dintre ele, cristalele, au devenit atât simbolul durităţii cât şi al eternităţii.
În urma unui studiu ştiinţific, pe un eşantion de formă dreptunghiulară se pot ob-
serva diferite tipuri de molecule, dispuse ı̂ntr-o geometrie perfectă, pe M rânduri
a câte N molecule fiecare, aliniate una lângă alta. O formaţiune cristalizabilă este
alcătuită din 3 molecule de acelaşi tip, ı̂nvecinate două câte două, având una dintre cele patru
forme din imaginea alăturată (fig.1).
Fiecare formaţiune este ı̂nconjurată de jur-ı̂mprejur, ca ı̂n fig.2, de un ı̂nveliş
special format şi el din molecule identice, de alt tip decât cele din formaţiunea
cristalizabilă pe care o ı̂nconjoară şi o izolează de restul formaţiunilor moleculare. În
acest fel, fiecare moleculă din formaţiunea cristalizabilă se ı̂nvecinează la N ord, Sud,
Est şi V est cu o moleculă din aceeaşi formaţiune cristalizabilă sau cu o moleculă
din ı̂nvelişul special.
Fiecare formaţiune cristalizabilă se bombardează cu raze X şi ı̂n acest fel are loc
cristalizarea, proces prin care ı̂nvelişul special se extinde peste formaţiunea cristal-
izabilă pe care o ı̂nconjoară, formând o singură structură din care se va dezvolta
cristalul.

Cerinţe

1. Determinaţi numărul formaţiunilor cristalizabile ce pot fi identificate pe


eşantionul analizat.
2. Afişaţi eşantionul rezultat după cristalizare.

Date de intrare

Fişierul de intrare cristale.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ă două numere naturale M şi
N , separate printr-un spaţiu, având semnificaţia din enunţ. Pe următoarele M linii se află câte N
numere naturale, separate prin câte un spaţiu, reprezentând moleculele din eşantionul analizat.

Date de ieşire

Dacă valoarea lui c este 1, atunci se va rezolva numai cerinţa 1, caz ı̂n care pe prima linie a
fişierului cristale.out va fi scris un număr natural reprezentând numărul formaţiunilor cristaliz-
abile identificate pe eşantionul analizat.
Dacă valoarea lui c este 2, atunci se va rezolva numai cerinţa 2. ı̂n acest caz, fişierul de ieşire
cristale.out va conţine pe fiecare dintre primele M linii, câte N numere naturale separate prin
câte un spaţiu, reprezentând moleculele eşantionului rezultat după cristalizare.

Restricţii şi precizări

a 4 & M, N & 800 şi tipul fiecărei molecule este exprimat printr-un număr natural din intervalul
1, 16;
a pe marginea eşantionului nu pot fi identificate formaţiuni cristalizabile;
a există cel puţin o formaţiune cristalizabilă pe eşantionul analizat;
a eşantionul nu conţine formaţiuni cristalizabile lipite (cu celule vecine pe una din cele patru
direcţii);
a pentru rezolvarea corectă a cerinţei 1 se acordă 30% din punctaj, iar pentru rezolvarea corectă
a cerinţei 2 se acordă 70% din punctaj.
CAPITOLUL 22. ONI 2016 22.2. CRISTALE 261

Exemple
cristale.in cristale.out Explicaţii
2 Se va rezolva cerinţa 1 a problemei.
Eşantionul are 6 rânduri cu câte 8 molecule pe fiecare
rând. Pe acest eşantion observăm o formaţiune cristal-
izabilă cu celule de tip 9, izolată de ı̂nvelişul special for-
mat din celule identice, toate de tip 6 şi o formaţiune
cristalizabilă cu celule de tip 7, izolată de ı̂nvelişul spe-
cial format din celule identice, toate de tip 4.
Formaţiunea de 3 molecule de tip 8 nu este o formaţiune
cristalizabilă ı̂ntrucât se află pe marginea eşantionului.
Se va rezolva cerinţa 2 a problemei.
După cristalizare, peste fiecare din cele două formaţiuni
cristalizabile identificate se extinde ı̂nvelişul din jurul
lor.

Timp maxim de executare/test: 1.0 secunde


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

22.2.1 Indicaţii de rezolvare

prof. Cristina Iordaiche, Liceul Teoretic ”Grigore Moisil”, Timişoara

Identificarea unei formaţiuni cristalizabile presupune analizarea elementelor de pe patru rânduri


consecutive ale eşantionului. Astfel, datele pot fi memorate ı̂ntr-un tablou bidimensional cu patru
linii şi N coloane.
Acest tablou memorează câte patru rânduri succesive din eşantion.
Se identifică apoi, fiecare formaţiune cristalizabilă, conform definiţiei din enunţ.
Pentru fiecare dintre cele patru tipuri de formaţiuni posibil cristalizabile, se verifică dacă cele
şapte molecule care formează ı̂nvelişul sunt de acelaşi tip, diferit de cel al formaţiunii pe care o
ı̂nconjoară.
Pentru cerinţa 1 se numără formaţiunile cristalizabile identificate iar pentru cerinţa 2, se
actualizează valorile din tablou, conform fenomenului de cristalizare definit.
Afişarea tabloului se realizează succesiv, linie cu linie, pe măsură ce sunt actualizate valorile
acestuia.

22.2.2 Cod sursă

Listing 22.2.1: ABcristale.cpp


#include <iostream>
#include <fstream>

#define Nmax 1501


#define in "cristale.in"
#define ou "cristale.out"

using namespace std;

short A[5][Nmax], m, n, c, i, j, tmp;


int NrC;

int main()
{
ifstream f(in);
CAPITOLUL 22. ONI 2016 22.2. CRISTALE 262

ofstream g(ou);

int cate;
f>>c>>m>>n;
for(i=1; i<=4; ++i)
for(j=1; j<=n; ++j)
f>>A[i][j];

cate = m-4;
/*
if(c == 2)
{
for(j=1; j<=n; ++j)
g<<A[1][j]<<’ ’;
g<<’\n’;
}
*/
while(cate>=0)
{
for(i=2; i<=3; ++i)
for(j=2; j<=n-2; ++j)
{
if( A[i][j] == A[i][j+1] &&
A[i][j] == A[i+1][j+1] &&
A[i][j] != A[i-1][j] ) //1
{
tmp = A[i-1][j];
if(A[i-1][j+1] == tmp && A[i][j-1] == tmp &&
A[i][j+2] == tmp && A[i+1][j] == tmp &&
A[i+1][j+2] == tmp && A[i+2][j+1] == tmp )
{
NrC++;
if(c <= 2) A[i][j] = A[i][j+1] = A[i+1][j+1] = tmp;
}

}
else
if( A[i][j] == A[i][j+1] && A[i][j] == A[i+1][j] &&
A[i][j] != A[i-1][j] ) //2
{
tmp = A[i-1][j];
if(A[i-1][j+1] == tmp && A[i][j-1] == tmp &&
A[i][j+2] == tmp && A[i+1][j-1] == tmp &&
A[i+1][j+1] == tmp && A[i+2][j] == tmp )
{
NrC++;
if(c <= 2) A[i][j] = A[i][j+1] = A[i+1][j] = tmp;
}
}
else
if( A[i][j] == A[i+1][j] && A[i][j] == A[i+1][j+1] &&
A[i][j] != A[i-1][j] ) //3
{
tmp = A[i-1][j];
if(A[i][j-1] == tmp && A[i][j+1] == tmp &&
A[i+1][j-1] == tmp && A[i+1][j+2] == tmp &&
A[i+2][j] == tmp && A[i+2][j+1] == tmp )
{
NrC++;
if(c <= 2) A[i][j] = A[i+1][j] = A[i+1][j+1] = tmp;
}
}
else
if( A[i][j+1] == A[i+1][j+1] && A[i][j+1] == A[i+1][j] &&
A[i][j+1] != A[i-1][j+1] ) //4
{
tmp = A[i-1][j+1];
if(A[i][j] == tmp && A[i][j+2] == tmp &&
A[i+1][j-1] == tmp && A[i+1][j+2] == tmp &&
A[i+2][j] == tmp && A[i+2][j+1] == tmp )
{
NrC++;
if(c <= 2) A[i][j+1] = A[i+1][j+1] = A[i+1][j] = tmp;
}
}
}
CAPITOLUL 22. ONI 2016 22.2. CRISTALE 263

if(c == 2)
{
for(j=1; j<=n; ++j)
g<<A[1][j]<<’ ’;
g<<’\n’;
}

for(i=2; i<=4; ++i)


for(j=1; j<=n; ++j)
A[i-1][j] = A[i][j];

for(j=1; j<=n; ++j)


f>>A[4][j];
cate--;
};

if(c == 1)
g<<NrC<<’\n’;
else
{
for(i=1; i<=3; i++)
{
for(j=1; j<=n; ++j)
g<<A[i][j]<<’ ’;
g<<’\n’;
}
}

f.close();
g.close();
return 0;
}

Listing 22.2.2: CIcristale.cpp


#include <fstream>

#define ML 801
#define MC 801

using namespace std;

short int p[5][MC],lc[MC],lu[MC];


int m,n,np;

ifstream f("cristale.in");
ofstream g("cristale.out");

int f1(int li, int co)


{
int cp,cf;
cp=p[li][co];
cf=p[li][co-1];
if((cp==p[li][co+1])&&
(cp==p[li+1][co+1])&&
(cp!=cf)&&
(cf==p[li-1][co])&&
(cf==p[li-1][co+1])&&
(cf==p[li][co+2])&&
(cf==p[li+1][co+2])&&
(cf==p[li+2][co+1])&&
(cf==p[li+1][co]) )
{
p[li][co]=p[li][co+1]=p[li+1][co+1]=cf;
return 1;
}

return 0;
}

int f2(int li, int co)


{
int cp,cf;
cp=p[li][co];
CAPITOLUL 22. ONI 2016 22.2. CRISTALE 264

cf=p[li][co-1];
if((cp==p[li][co+1])&&
(cp==p[li+1][co])&&
(cp!=cf)&&
(cf==p[li-1][co])&&
(cf==p[li-1][co+1])&&
(cf==p[li][co+2])&&
(cf==p[li+1][co+1])&&
(cf==p[li+2][co])&&
(cf==p[li+1][co-1]))
{
p[li][co]=p[li][co+1]=p[li+1][co]=cf;
return 1;
}

return 0;
}

int f3(int li, int co)


{
int cp,cf;
cp=p[li][co];
cf=p[li][co-1];
if((cp==p[li+1][co+1])&&
(cp==p[li+1][co])&&
(cp!=cf)&&
(cf==p[li-1][co])&&
(cf==p[li][co+1])&&
(cf==p[li+1][co+2])&&
(cf==p[li+2][co+1])&&
(cf==p[li+2][co])&&
(cf==p[li+1][co-1]))
{
p[li][co]=p[li+1][co+1]=p[li+1][co]=cf;
return 1;
}

return 0;
}

int f4(int li, int co)


{
int cp,cf;
cp=p[li][co];
cf=p[li][co-1];
if((cp==p[li+1][co])&&
(cp==p[li+1][co-1])&&
(cp!=cf)&&
(cf==p[li-1][co])&&
(cf==p[li][co+1])&&
(cf==p[li+1][co+1])&&
(cf==p[li+2][co])&&
(cf==p[li+2][co-1])&&
(cf==p[li+1][co-2]))
{
p[li][co]=p[li+1][co]=p[li+1][co-1]=cf;
return 1;
}

return 0;
}

void afis4_linii()
{
int i,j;
for(i=2;i<=2;i++)
{
g<<’\n’;//fprintf(g,"\n");
for(j=1;j<=n-1;j++)
g<<p[i][j]<<’ ’;//fprintf(g,"%d ",p[i][j]);

g<<p[i][n];//fprintf(g,"%d",p[i][n]);
}
}

void copiere_linii_peste_cele_vechi()
CAPITOLUL 22. ONI 2016 22.2. CRISTALE 265

{
int j;
for(j=1;j<=n;j++)
{
p[1][j]=p[2][j];
p[2][j]=p[3][j];
p[3][j]=p[4][j];
}
}

int main()
{
int i,j,idf,k,c;

f>>c>>m>>n;
for(i=1;i<=4;i++)
for(j=1;j<=n;j++)
f>>p[i][j];

int nr_linii=3;

if(c==2)
{
for(j=1;j<=n-1;j++)
g<<p[1][j]<<’ ’;
g<<p[1][n];
}

do
{
i=2;
for (k=2;k<=n-1;k++) lu[k]=1;
{
for (k=2;k<=n-1;k++)
{
lc[k]=lu[k];
lu[k]=1;
}

for(j=2;j<=n-1;j++)
{
if(lc[j])
{
idf=1;
if(idf&&(j<n-1)&&f1(i,j))
{
np++;
lu[j]=lu[j+1]=lu[j+2]=0;
j+=2;
idf=0;
}

if(idf&&(j<n-1)&&f2(i,j))
{
np++;idf=0;
lu[j-1]=lu[j]=lu[j+1]=0;
j+=2;
}

if(idf&&(j<n-1)&&f3(i,j))
{
np++;idf=0;
lu[j-1]=lu[j]=lu[j+1]=lu[j+2]=0;
j+=1;
}

if(idf&&(j>2)&&f4(i,j))
{
np++;idf=0;
lu[j-2]=lu[j-1]=lu[j]=lu[j+1]=0;
j+=1;
}
}
}
}
CAPITOLUL 22. ONI 2016 22.2. CRISTALE 266

if(c==2)
afis4_linii();

copiere_linii_peste_cele_vechi();

for(j=1;j<=n;j++)
f>>p[4][j];

nr_linii++;
i=1;
} while(nr_linii<m);

if(c==2)
{
for(i=2;i<=3;i++)
{
g<<’\n’;
for(j=1;j<=n-1;j++)
g<<p[i][j]<<’ ’;
g<<p[i][n];
}

g<<’\n’;
}
else
if(c==1)
g<<np<<’\n’;

return 0;
}

Listing 22.2.3: CMcristale.cpp


#include <iostream>
#include <fstream>

using namespace std;

char a[805][805],val;
int n,m,rez1,p;

ifstream f("cristale.in");
ofstream g("cristale.out");

void citeste()
{
f>>p>>n>>m;
int i,j,x;
for(i=1; i<=n;i++)
for(j=1;j<=m;j++)
{
f>>x;
a[i][j]=char(x+’0’);
}
}

int tip1( int i, int j)


{ /// y y
/// y x x y
/// y x y
/// y

//short int val;


val=a[i+1][j];
if(a[i][j]!=val && a[i][j]==a[i][j+1] && a[i][j]==a[i+1][j+1]
&& a[i-1][j]==val && a[i-1][j+1]==val && a[i][j-1]==val
&& a[i][j+2]==val && a[i+1][j+2]==val && a[i+2][j+1]==val)
{
a[i][j]=a[i][j+1]=a[i+1][j+1]=a[i+1][j]=val;
return 1;
}

return 0;
}
CAPITOLUL 22. ONI 2016 22.2. CRISTALE 267

int tip2( int i, int j)


{ /// y y
/// y x x y
/// y x y
/// y

// short int
val=a[i+1][j+1];
if ( a[i][j]==a[i][j+1] && a[i][j]==a[i+1][j] && a[i][j]!=val
&& a[i-1][j]==val && a[i-1][j+1]==val && a[i][j-1]==val
&& a[i][j+2]==val && a[i+1][j-1]==val && a[i+2][j]==val )
{
a[i][j]=a[i][j+1]=a[i+1][j+1]=a[i+1][j]=val;
return 1;
}

return 0;
}

int tip3( int i, int j)


{ /// y
/// y x y
/// y x x y
/// y y

// short int
val=a[i][j-1];
if ( a[i][j]==a[i+1][j-1] && a[i][j]==a[i+1][j] && a[i][j]!=val
&& a[i-1][j]==val && a[i][j+1]==val&& a[i+1][j-2]==val
&& a[i+1][j+1]==val && a[i+2][j]==val && a[i+2][j-1]==val )
{
a[i][j]=a[i+1][j]=a[i][j-1]=a[i+1][j-1]=val;
return 1;
}

return 0;
}

int tip4( int i, int j)


{ /// y
/// y x y
/// y x x y
/// y y

// short int
val=a[i][j+1];
if ( a[i][j]==a[i+1][j+1] && a[i][j]==a[i+1][j] && a[i][j]!=val
&& a[i-1][j]==val && a[i][j-1]==val && a[i+1][j-1]==val
&& a[i+1][j+2]==val && a[i+2][j]==val && a[i+2][j+1]==val )
{
a[i][j]=a[i][j+1]=a[i+1][j+1]=a[i+1][j]=val;
return 1;
}

return 0;
}

void sol()
{
int i,j;
citeste();

for(i=2; i<=n-1;i++)
for(j=2;j<=m-1;j++)
if(tip1(i,j))
rez1++;
else
if(tip2(i,j))
rez1++;
else
if(tip3(i,j))
rez1++;
else
if(tip4(i,j))
rez1++;
CAPITOLUL 22. ONI 2016 22.2. CRISTALE 268

if(p==1)
g<<rez1<<endl;
else
for(i=1; i<=n;i++)
{
for(j=1;j<=m;j++)
g<<a[i][j]-’0’<<" ";
g<<endl;
}
}

int main()
{
sol();
return 0;
}

Listing 22.2.4: CScristale.cpp


#include <fstream>

using namespace std;

int v,m,n,i,j,cr;

ifstream f("cristale.in");
ofstream g("cristale.out");

short a[4][1502];

void muta()
{
int i,j;

for(i=0;i<4;i++)
for(j=0;j<n;j++)
a[i][j]=a[i+1][j];

for(j=0;j<n;j++)
f>>a[3][j];
}

void afis(int i)
{
for(int j=0;j<n;j++)
g<<a[i][j]<<’ ’;
g<<’\n’;
}

int este_cristal(int i, int j, int k)


{
int x=a[i-1][j];

switch(k)
{
case 1: return (j<n-2 && a[i][j]!=x && a[i-1][j+1]==x &&
a[i][j+2]==x && a[i+1][j+1]==x && a[i+2][j]==x &&
a[i+1][j-1]==x && a[i][j-1]==x);
case 2: return (j<n-2 && a[i][j]!=x && a[i-1][j+1]==x &&
a[i][j+2]==x && a[i+1][j+2]==x && a[i+2][j+1]==x &&
a[i+1][j]==x && a[i][j-1]==x);
case 3: return (j<n-2 && a[i][j]!=x && a[i][j+1]==x &&
a[i+1][j+2]==x && a[i+2][j+1]==x && a[i+2][j]==x &&
a[i+1][j-1]==x && a[i][j-1]==x);
case 4: return (j>1 && a[i][j]!=x && a[i][j+1]==x &&
a[i+1][j+1]==x && a[i+2][j]==x && a[i+2][j-1]==x &&
a[i+1][j-2]==x && a[i][j-1]==x);
}

return -1; // !!!


}

void numara(int i)
{
for(int j=1;j<n-1;j++)
CAPITOLUL 22. ONI 2016 22.2. CRISTALE 269

if(a[i][j]-a[i][j+1]==0&&a[i][j]-a[i+1][j]==0)
cr+=este_cristal(i,j,1);
else
if(a[i][j]-a[i][j+1]==0 && a[i][j]-a[i+1][j+1]==0)
cr+=este_cristal(i,j,2);
else
if(a[i][j]-a[i+1][j]==0 && a[i][j]-a[i+1][j+1]==0)
cr+=este_cristal(i,j,3);
else
if(a[i][j]-a[i+1][j]==0 && a[i][j]-a[i+1][j-1]==0)
cr+=este_cristal(i,j,4);
}

void cristalizare(int i, int j, int k)


{
int x=a[i-1][j];

switch(k)
{
case 1:
{
if(j<n-2 && a[i][j]!=x && a[i-1][j+1]==x && a[i][j+2]==x &&
a[i+1][j+1]==x && a[i+2][j]==x && a[i+1][j-1]==x && a[i][j-1]==x)
a[i][j]=a[i][j+1]=a[i+1][j]=x;
break;
}
case 2:
{
if(j<n-2 && a[i][j]!=x && a[i-1][j+1]==x && a[i][j+2]==x &&
a[i+1][j+2]==x && a[i+2][j+1]==x && a[i+1][j]==x && a[i][j-1]==x)
a[i][j]=a[i][j+1]=a[i+1][j+1]=x;
break;
}
case 3:
{
if(j<n-2 && a[i][j]!=x && a[i][j+1]==x && a[i+1][j+2]==x &&
a[i+2][j+1]==x && a[i+2][j]==x && a[i+1][j-1]==x && a[i][j-1]==x)
a[i][j]=a[i+1][j]=a[i+1][j+1]=x;
break;
}
case 4:
{
if (j>1 && a[i][j]!=x && a[i][j+1]==x && a[i+1][j+1]==x &&
a[i+2][j]==x && a[i+2][j-1]==x && a[i+1][j-2]==x && a[i][j-1]==x)
a[i][j]=a[i+1][j]=a[i+1][j-1]=x;
break;
}
}
}

void cristalizeaza(int i)
{
for(int j=1;j<n-1;j++)
if(a[i][j]-a[i][j+1]==0&&a[i][j]-a[i+1][j]==0)
cristalizare(i,j,1);
else
if(a[i][j]-a[i][j+1]==0 && a[i][j]-a[i+1][j+1]==0)
cristalizare(i,j,2);
else
if(a[i][j]-a[i+1][j]==0 && a[i][j]-a[i+1][j+1]==0)
cristalizare(i,j,3);
else
if(a[i][j]-a[i+1][j]==0 && a[i][j]-a[i+1][j-1]==0)
cristalizare(i,j,4);
}

void solve1()
{
int i,j;

for(i=0;i<4;i++) for(j=0;j<n;j++)
f>>a[i][j];

numara(1);

while(i<m)
CAPITOLUL 22. ONI 2016 22.3. PARCHET 270

{
muta();
numara(1);
i++;
}

g<<cr<<’\n’;
}

void solve2()
{
int i, j;

for(i=0;i<4;i++)
for(j=0;j<n;j++)
f>>a[i][j];

cristalizeaza(1);
afis(0);
afis(1);

while(i<m) // i = ??? !!!


{
muta();
cristalizeaza(1);
afis(1);
i++;
}

afis(2);
afis(3);
}

int main()
{
f>>v>>m>>n;

if(v==1)
solve1();
else
solve2();

f.close();
g.close();
return 0;
}

22.2.3 *Rezolvare detaliată

22.3 parchet
Problema 3 - parchet 100 de puncte
Meseria de parchetar a devenit mai uşoară de când a apărut parchetul laminat. Acesta se
2
livrează ı̂n plăci pătratice de câte 1 m şi montarea lui este destul de uşoară. Gigel este convins
că este suficient de priceput să facă această operaţie ı̂n propria locuinţă. El dispune de planul
2
locuinţei şi a cumpărat o anumită cantitate reprezentând X m de parchet laminat. Planul
locuinţei este descris printr-un tablou bidimensional de dimensiuni N x M, fiecare element al
2
tabloului reprezentând exact 1 m . Pereţii sunt reprezentaţi prin caracterul ’P’ iar suprafeţele
camerelor prin caracterul ’S’ (spaţiu).
În planul din figura alăturată este descrisă o locuinţă cu 5 camere acestea având
2
respectiv, suprafeţele de 10, 2, 1, 3, 5 m .
Gigel nu este sigur de faptul că parchetul cumpărat ı̂i ajunge. Din această
cauză a hotărât iniţial să pună parchetul ı̂ncepând cu camera cea mai mare, apoi
ı̂n următoarea, ı̂n ordinea descrescătoare a suprafeţei şi aşa mai departe, până ı̂n
momentul ı̂n care parchetul rămas nu mai este suficient pentru acoperirea suprafeţei
următoarei camere. Nu va lăsa neparchetată o cameră pentru a parcheta una cu
CAPITOLUL 22. ONI 2016 22.3. PARCHET 271

o suprafaţă mai mică. Gigel se mai gândeşte şi la posibilitatea de a acoperi complet un număr
maxim de camere folosind ı̂ntreaga cantitate de parchet.

Cerinţe

Fiind date N , M , X şi planul locuinţei să se determine:


2
1. numărul C de camere pe care a reuşit să le acopere Gigel şi numărul R de m de parchet
care ı̂i rămân, procedând aşa cum a hotărât iniţial;
2. numărul de posibilităţi de parchetare a unui număr maxim de camere, folosind ı̂ntreaga
cantitate de parchet.

Date de intrare

Fişierul de intrare parchet.in conţine pe prima linie un număr natural p reprezentând cerinţa
care trebuie să fie rezolvată (1 sau 2). Linia a doua a fişierului de intrare conţine numerele naturale
N şi M separate printr-un spaţiu. Pe linia a treia se află numărul natural X. Următoarele N linii
¬ ¬ ¬ ¬
conţin câte M caractere P şi S descriind planul locuinţei.

Date de ieşire

Dacă valoarea lui p este 1, atunci fişierul de ieşire parchet.out conţine pe prima linie două
numere naturale C şi R separate printr-un spaţiu, reprezentând respectiv numărul de camere
2
acoperite cu parchet şi cantitatea de parchet rămasă, exprimată ı̂n m . Dacă valoarea lui p este
2, atunci pe prima linie a fişierului de ieşire se va scrie numărul de posibilităţi de parchetare a
unui număr maxim de camere folosind ı̂ntreaga cantitate de parchet, respectiv valoarea 0 ı̂n cazul
ı̂n care acest lucru nu este posibil.

Restricţii şi precizări

a 2 & N , M & 250


a În casă sunt maxim 20 de camere şi casa are pereţi spre exterior.
2
a Suprafaţa unei camere nu depăşeşte N  2 ˜ M  2 m .
a Pentru rezolvarea corectă a cerinţei 1 se acordă 50% din punctaj, iar pentru rezolvarea
corectă a cerinţei 2 se acordă 50% din punctaj.

Exemple
parchet.in parchet.out Explicaţii
1 31 Se va rezolva doar cerinţa 1.
2
79 Locuinţa are 5 camere având suprafeţele de 10, 2, 1, 3, 5 m .
19 Pot fi parchetate complet 3 camere consumând 18 = 10+5+3
2 2
PPPPPPPPP m . Rămâne 1 m de parchet nefolosit.
PSSSPSPSP
PSSSPSPPP
PSSPPPPSP
PSPPSSPSP
PSPSSSPSP
PPPPPPPPP
2 1 Se va rezolva doar cerinţa 2.
79 Dacă se aleg camerele cu suprafeţele 10, 1, 3, 5 va fi folosită
19 ı̂ntreaga suprafaţă de parchet. Există o singură posibilitate de
PPPPPPPPP a selecta un număr maxim de camere.
PSSSPSPSP
PSSSPSPPP
PSSPPPPSP
PSPPSSPSP
PSPSSSPSP
PPPPPPPPP

Timp maxim de executare/test: 0.5 secunde


Memorie: total 2 MB din care pentru stivă 2 MB
Dimensiune maximă a sursei: 5 KB
CAPITOLUL 22. ONI 2016 22.3. PARCHET 272

22.3.1 Indicaţii de rezolvare

Soluţie 1 (prof. Marinel Şerban)


1. Se determină suprafaţa fiecărei camere.
2. Se ordonează descrescător vectorul care conţine suprafeţele camerelor.
3. Pentru prima cerinţă se utilizează un algoritm de tip ”umplerea rucsacului”.
4. Pentru cerinţa a doua se utilizează un algoritm pentru a determina o submulţime de sumă
dată - se numără, evident, submulţimile de cardinal maxim. Algoritmul poate fi de tip succesor,
care se poate implementa elementar, pe vector, sau utilizând operatorii pe biţi sau folosind tehnica
backtracking.
Soluţie 2 (prof. Alin Burţa)
Se procedează la fel ca la soluţia 1, dar determinarea suprafeţei fiecărei camere se face astfel:
Vom memora ı̂ntr-un tablou bidimensional A, pentru fiecare poziţie liberă, numărul camerei
din care face parte acea poziţie.
Pas 1. Se identifică prima poziţie liberă - neparchetată - din tablou, fie aceasta i, j 
Pas 2. Se parchetează i, j , precum şi poziţiile din dreapta, până la ı̂ntâlnirea peretelui
(marcăm acest fapt prin memorarea pe poziţiile parchetate a numărului camerei curente). Pe
parcurs, identificăm prima pozţie liberă de pe linia următoare, fie acesta i  1, jj .
Pas 3. Dacă există o poziţie neparchetată pe linia i  1, vom parcheta poziţia i  1, jj  precum
şi toate poziţiile libere din stânga şi din dreapta acesteia, până la ı̂ntâlnirea unui perete, aflând
pe parcurs poziţia de unde va ı̂ncepe parchetarea poziţiilor de pe următoarea linie. ı̂n acest mod
vom parcheta toate poziţiile libere ı̂ncepând cu linia i şi până la peretele sudic al camerei curente.
Repetăm paşii 1-3 până la parchetarea completă a camerelor.
În cazul ı̂n care o cameră are o formă neregulată (nu este un simplu dreptunghi), constatăm că
aceasta va conţine mai multe zone, marcate cu numere diferite. Va trebui să realizăm o operaţie
de lipire a acestor zone pentru a calcula suprafaţa totală a camerei.
Putem utiliza un tablou unidimensional C, unde C i reprezintă valoarea folosită la marcarea
camerei i, iniţial C i i.
Printr-o simplă parcurgere a elementelor tabloului A, dacă identificăm ı̂n aceeaşi cameră două
poziţii vecine marcate diferit, atunci vom trece la comasarea celor două zone, modificând valorile
corespunzătoare din tabloul C.
Procedeul se realizează ı̂n complexitate pătratică şi nu necesită utilizarea unor structuri de
date complexe ori a unor algoritmi de umplere recursivi.

22.3.2 Cod sursă

Listing 22.3.1: ABparchet.cpp


//prof. Alin Burta
#include <fstream>
#include <cstring>
#include <iostream>

#define Nmax 252


#define in "parchet.in"
#define ou "parchet.out"

using namespace std;

unsigned int A[Nmax][Nmax], n, m, c, p, v;


unsigned int C[1001], ST[1001];
unsigned int S[1000]; // suprefetele camerelor
int x[21]; // pt. generare de submultimi
unsigned int cam; // cate camere am acoperit
unsigned int maxim, nrmaxim, NrCam;

void uneste(unsigned int a, unsigned int b)


{
CAPITOLUL 22. ONI 2016 22.3. PARCHET 273

unsigned int q, cop, t;


if (a > b)
cop = a, a = b, b = cop;
cop = b;
t = S[b] + S[a];

for(q = 1; q <= NrCam; ++q)


if(C[q] == cop)
C[q] = a;

for(q = 1; q <= NrCam; ++q)


if(C[q] == a)
S[q] = t;
}

int main()
{
char linie[Nmax];
unsigned int i,j, k, ok, ii, jj,t, nr;
unsigned int sup, pus, putere;

ifstream f(in);
f>>v>>n>>m>>p;
f.get();

sup = 0;
for(i=1; i<=n; ++i)
{
f.get(linie,m+1);
f.get();
//cout<<linie<<endl;
for(j=0; j<m; ++j)
if(linie[j] == ’P’)
A[i][j+1] = -1;
else
A[i][j+1] = 0;
}
f.close();

NrCam = 0;

//caut primul colt de camera neacoperit


for(i=1; i<=n-1; ++i)
for(j=1; j<=m-1; ++j)
if( A[i][j]==0 ) //am gasit un colt
{
//umplu linia cu nrcamerei
ok = 1;
NrCam++;
ii = i;
jj = j;
sup = 0;

while(ok)
{
ok = 0; //cout<<ii<<’ ’<<jj<<endl;
if( A[ii+1][jj] == 0) ok = jj ;

for(k=jj-1; A[ii][k]==0; k--)


{
if(A[ii][k] == 0)
A[ii][k] = NrCam, sup++;
if(A[ii+1][k] == 0)
ok = k;
}

for(k=jj+1; A[ii][k]==0; k++)


{
if(A[ii][k] == 0)
A[ii][k] = NrCam, sup++;
if(A[ii+1][k] == 0)
ok = k;
}

A[ii][jj] = NrCam;
sup++;
CAPITOLUL 22. ONI 2016 22.3. PARCHET 274

ii++;
jj = ok;
}

S[NrCam] = sup;
}

ofstream g(ou);

//unesc camerele
for(i=1; i<=NrCam; ++i)
C[i] = i;
for(i=2; i<n; ++i)
for(j=2; j<m; ++j)
{
if( A[i][j] != -1 && A[i-1][j] != -1 &&
A[i][j] != A[i-1][j] && C[ A[i][j] ] != C [ A[i-1][j] ] )
uneste( C[ A[i][j] ], C[ A[i-1][j] ]);

if( A[i][j] != -1 && A[i][j+1] != -1 &&


A[i][j] != A[i][j+1] && C[ A[i][j] ] != C [ A[i][j+1] ] )
uneste( C[ A[i][j] ], C[ A[i][j+1] ]);

if( A[i][j] != -1 && A[i+1][j] != -1 &&


A[i][j] != A[i+1][j] && C[ A[i][j] ] != C [ A[i+1][j] ] )
uneste( C[ A[i][j] ], C[ A[i+1][j] ]);

if( A[i][j] != -1 && A[i][j-1] != -1 &&


A[i][j] != A[i][j-1] && C[ A[i][j] ] != C [ A[i][j-1] ] )
uneste( C[ A[i][j] ], C[ A[i][j-1] ]);
}

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


ST[i] = 0;
for(i=1; i<=NrCam; ++i)
ST[C[i]] = S[i];

//sortez descrescator
for(i=1; i<1000; ++i)
for(j=i+1; j<=1000; ++j)
if(ST[i]<ST[j])
t = ST[i], ST[i] = ST[j], ST[j] = t;

while( ST[NrCam] == 0)
NrCam--;

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


S[i] = ST[i];

//cerinta 1
pus = 0;
cam = 0;
while(pus <= p && S[cam+1] <= p-pus)
pus +=S[cam+1], cam++;

if( v == 1)
{
if(NrCam == 1 && S[1]<=p)
g<<1<<’ ’<<p-pus<<’\n’;
else
g<<cam<<’ ’<<p-pus<<’\n’;
}

if( v == 2)
{
//cerinta 2
putere = 1;
pus = 0;
for(i=1; i<=NrCam; ++i)
x[i] = 0, putere *=2, pus +=S[i] ;

putere/=2;

if(pus == p)
g<<"1\n";
else
CAPITOLUL 22. ONI 2016 22.3. PARCHET 275

{
maxim = 0; nrmaxim = 0;
for(k = 1; k<putere; ++k)
{
t = 1;
pus = 0;
for(j=1; j<=NrCam && t; ++j)
ii = x[j] + t, x[j] = ii % 2, t = ii / 2;

nr = 0;
for(j=NrCam; j>=1; --j)
if(x[j]==1)
pus += S[j], nr++;

if(pus == p)
if(nr > maxim)
maxim = nr, nrmaxim = 1;
else
if(nr == maxim)
nrmaxim++;

nr = 0;
pus = 0;
for(j=NrCam; j>=1; --j)
if(x[j]==0)
pus += S[j], nr++;

if(pus == p)
if(nr > maxim)
maxim = nr, nrmaxim = 1;
else
if(nr == maxim)
nrmaxim++;
}

g<<nrmaxim<<endl;
}
}

g.close();
return 0;
}

Listing 22.3.2: CSparchet.cpp


//prof. Cristina Sichim
#include <fstream>
#include <algorithm>

using namespace std;

ifstream f("parchet.in");
ofstream g("parchet.out");

int n,m,p,P,i,j,k,cate,numar;
char a[255][255];
int c[21],sum[21],nr[21],nrp,di[]={-1,0,1,0},dj[]={0,1,0,-1},cer;

void sterge(int i, int j)


{
int inou,jnou;
cate++;
a[i][j]=’P’;
for(int t=0;t<4;t++)
{
inou=i+di[t];
jnou=j+dj[t];
if(a[inou][jnou]==’S’)
sterge(inou, jnou);
}
}

void numara(int t)
{
for(int v=0;v<=1;v++)
CAPITOLUL 22. ONI 2016 22.3. PARCHET 276

{
nr[t]=nr[t-1]+v;
sum[t]=sum[t-1]+v*c[t];
if(sum[t]==P)
if(nr[t]>numar)
{
numar=nr[t];
cate=1;
}
else
{
if(nr[t]==numar)
cate++;
}
else
if(t<k && sum[t]<P)
numara(t+1);
}
}

int main()
{
f>>cer>>n>>m>>P;

for(i=0;i<n;i++)
{
f>>a[i];
for(j=1;j<m-1;j++)
if(a[i][j]==’S’)
nrp++;
}

if(nrp==(n-2)*(m-2))
if(cer==1)
{
g<<1<<’ ’;
if(P>=nrp)
g<<P-nrp<<’\n’;
else
g<<P<<’\n’;
}
else
if(P!=nrp)
g<<0<<’\n’;
else
g<<1<<’\n’;
else
{
for(i=1;i<n-1;i++)
for(j=1;j<m-1;j++)
if(a[i][j]==’S’)
{
cate=0;
sterge(i,j);
c[++k]=cate;
}

sort(c+1,c+k+1);

i=k;
while(i>0 && p+c[i]<=P)
{
p=p+c[i];
i--;
}

if(cer==1)
g<<k-i<<’ ’<<P-p<<’\n’;
else
{
cate=0;
while(k>0 && c[k]>P)
k--;
numara(1);
g<<cate<<’\n’;
}
CAPITOLUL 22. ONI 2016 22.3. PARCHET 277

f.close();
g.close();
return 0;
}

Listing 22.3.3: MSparchetBiti.cpp


//prof. Marinel Serban
#include <bits/stdc++.h>

using namespace std;

ifstream fin("parchet.in");
ofstream fout("parchet.out");

int Casa[255][255], Camere[210], N, M, S, nr_camere, Cerinta;

void Citire()
{
int i, j;
char X[255];

fin >> Cerinta;


fin >> N >> M; // dimensiuni casa
fin >> S; // suprafata parchet
fin.get(); // ENTER

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


{
fin.getline(X, 255); //citesc o linie
for (j = 0; j < M; ++j)
Casa[i][j+1] = (X[j] == ’P’ ? -1 : 0);//-1 perete; 0 spatiu
}
}

int Suprafata(int l, int c, int nr)


{
struct element
{
short int lin, col;
} C[100000], X;

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


int dc[] = {0, 1, 0, -1};
int ic = 0, sc = -1, k, l9, c9, S = 1;

Casa[l][c] = nr;
sc++;
C[sc].lin = l;
C[sc].col = c;

while (ic <= sc)


{
X = C[ic++];
for (k = 0; k < 4; k++)
{
l9 = X.lin + dl[k];
c9 = X.col + dc[k];
if (Casa[l9][c9] == 0)
{
S++;
Casa[l9][c9] = nr;
sc++;
C[sc].lin = l9;
C[sc].col = c9;
}
}
}

return S;
}

void Suprafete_camere()
CAPITOLUL 22. ONI 2016 22.3. PARCHET 278

{
int i, j;
for (i = 2; i < N; ++i)
for (j = 2; j < M; ++j)
if (Casa[i][j] == 0) //camera goala - inca neprelucrata
{
nr_camere++;
Camere[nr_camere] = Suprafata(i, j, nr_camere);
}
}

int Criteriu(int A, int B)


{
return A > B;
}

void Cerinta_a()
{
int SS = S, i = 1;
int Cate_se_parcheteaza = 0;

sort(Camere + 1, Camere + nr_camere + 1, Criteriu);

while (i <= nr_camere && SS >= Camere[i])


{
Cate_se_parcheteaza++;
SS -= Camere[i];
i++;
}

fout << Cate_se_parcheteaza << ’ ’ << SS << ’\n’;


}

void Cerinta_b()
{
int Contor = 0, nrsel, NRSEL = 0, Suprafata, i;
int b2 = 0, b2max = 1 << nr_camere;

while (b2 < b2max)


{
b2++;
Suprafata = 0;
nrsel = 0;

for (i = 0; i < nr_camere; i++)


if (b2 & (1 << i))
{
Suprafata += Camere[i + 1];
nrsel++;
}

if (Suprafata == S)
{
if (nrsel > NRSEL)
{
NRSEL = nrsel;
Contor = 1;
}
else
if (nrsel == NRSEL)
Contor++;
}
}

fout << Contor << ’\n’;


}

int main()
{
Citire();
Suprafete_camere();
if (Cerinta == 1)
Cerinta_a();
else
Cerinta_b();
return 0;
CAPITOLUL 22. ONI 2016 22.3. PARCHET 279

Listing 22.3.4: MSparchetVector.cpp


//prof. Marinel Serban
#include <bits/stdc++.h>

using namespace std;

ifstream fin("parchet.in");
ofstream fout("parchet.out");

int Casa[255][255], Camere[210], N, M, S, nr_camere, Cerinta;

void Citire()
{
int i, j;
char X[255];

fin >> Cerinta;


fin >> N >> M; // dimensiuni casa
fin >> S; // suprafata parchet
fin.get(); // ENTER

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


{
fin.getline(X, 255); // citesc o linie
for (j = 0; j < M; ++j)
Casa[i][j+1] = (X[j] == ’P’ ? -1 : 0);// -1 perete; 0 spatiu
}
}

int Suprafata(int l, int c, int nr)


{
struct element
{
short int lin, col;
} C[100000], X;

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


int dc[] = {0, 1, 0, -1};
int ic = 0, sc = -1, k, l9, c9, S = 1;

Casa[l][c] = nr;
sc++;
C[sc].lin = l;
C[sc].col = c;

while (ic <= sc)


{
X = C[ic++];
for (k = 0; k < 4; k++)
{
l9 = X.lin + dl[k];
c9 = X.col + dc[k];
if (Casa[l9][c9] == 0)
{
S++;
Casa[l9][c9] = nr;
sc++;
C[sc].lin = l9;
C[sc].col = c9;
}
}
}

return S;
}

void Suprafete_camere()
{
int i, j;
for (i = 2; i < N; ++i)
for (j = 2; j < M; ++j)
if (Casa[i][j] == 0) // camera goala - inca neprelucrata
CAPITOLUL 22. ONI 2016 22.3. PARCHET 280

{
nr_camere++;
Camere[nr_camere] = Suprafata(i, j, nr_camere);
}
}

int Criteriu(int A, int B)


{
return A > B;
}

void Cerinta_a()
{
int SS = S, i = 1;
int Cate_se_parcheteaza = 0;

sort(Camere + 1, Camere + nr_camere + 1, Criteriu);

while (i <= nr_camere && SS >= Camere[i])


{
Cate_se_parcheteaza++;
SS -= Camere[i];
i++;
}

if (Cerinta == 1)
fout << Cate_se_parcheteaza << ’ ’ << SS << ’\n’;
}

void Cerinta_b()
{
int Contor = 0, nrsel, NRSEL = 0, Suprafata, i;
int b2[100] = {0};

while (!b2[0])
{
i = nr_camere;

while (b2[i])
b2[i--] = 0;

b2[i] = 1;
Suprafata = 0;
nrsel = 0;
for (i = 1; i <= nr_camere; i++)
if (b2[i])
{
Suprafata += Camere[i];
nrsel++;
}

if (Suprafata == S)
{
if (nrsel > NRSEL)
{
NRSEL = nrsel;
Contor = 1;
}
else
if (nrsel == NRSEL)
Contor++;
}
}

if (Cerinta == 2)
fout << Contor << ’\n’;
}

int main()
{
Citire();
Suprafete_camere();

Cerinta_a();
Cerinta_b();
CAPITOLUL 22. ONI 2016 22.3. PARCHET 281

return 0;
}

22.3.3 *Rezolvare detaliată


Capitolul 23

ONI 2015

23.1 cript
Problema 1 - cript 100 de puncte
Ilinca a citit despre criptarea mesajelor, acum doreşte să comunice cu prietena ei Miruna numai
prin mesaje criptate folosind un sistem propriu de criptare.
Ilinca ştie că fiecare caracter se reprezintă ı̂n memoria calculatorului pe 8 biţi, ı̂n care se
memorează scrierea ı̂n baza 2 a codului ASCII al caracterului respectiv.
Pentru a cripta caracterul, Ilinca foloseşte o matrice pătratică având 8 linii (numerotate de la
0 la 7 de sus ı̂n jos) şi 8 coloane (numerotate de la 0 la 7 de la stânga la dreapta). Pe prima linie
a matricei Ilinca scrie cei 8 biţi reprezentând scrierea ı̂n baza 2 a codului ASCII al caracterului,
pe poziţia 0 fiind scrisă cifra cea mai semnificativă (corespunzătoare lui 27). Pe fiecare dintre
următoarele 7 linii din matrice scrie permutarea circulară cu o poziţie la stânga a liniei anterioare.
Ordonează lexicografic liniile matricei formate şi defineşte cript-ul caracterului ca fiind succesiunea
de biţi reprezentată de ultima coloană din matrice, parcursă de sus ı̂n jos, urmată de indicele liniei
din matrice pe care a ajuns după ordonarea lexicografică reprezentarea ı̂n baza 2 a codului ASCII
al caracterului. Dacă există linii egale ı̂n matrice, după ordonarea lexicografică acestea ı̂şi vor
păstra ordinea relativă iniţială, astfel că linia conţinând reprezentarea ı̂n baza 2 a codului ASCII
al caracterului va fi prima dintre acestea.
Pentru a cripta un mesaj, Ilinca scrie ı̂n ordine cript-urile caracterelor din mesajul respectiv.
Miruna cunoaşte sistemul de criptare al Ilincăi, ca urmare ştie să decripteze mesajele primite.

Cerinţe

Scrieţi un program care să rezolve următoarele două cerinţe:


1. citeşte un mesaj şi afişează mesajul criptat conform sistemului Ilincăi;
2. citeşte un mesaj criptat conform sistemului Ilincăi şi determină mesajul decriptat.

Date de intrare

Fişierul de intrare cript.in conţine pe prima linie un număr natural c, care poate fi 1 sau
2, reprezentând cerinţa ce urmează a fi rezolvată. Pe a doua linie a fişierului se află un şir de
caractere.

Date de ieşire

Fişierul de ieşire cript.out va conţine o singură linie pe care va fi scrisă criptarea şirului din
fişierul de intrare (dacă c 1), respectiv decriptarea şirului din fişierul de intrare (dacă c 2).

Restricţii şi precizări

a Lungimea mesajului necriptat este nenulă şi nu depăşeşte 30000.


a Caracterele din şirul citit au coduri cuprinse ı̂ntre 32 şi 127.
a Spunem că şirul s1 precedă ı̂n ordinea lexicografică şirul s2 dacă există o poziţie k astfel
ı̂ncât s1i s2i pentru orice i $ k şi s1k  $ s2k .
a Pentru teste valorând 50% din punctaj cerinţa este 1.

282
CAPITOLUL 23. ONI 2015 23.1. CRIPT 283

Exemple

cript.in cript.out Explicaţii


1 100010004101000004 Caracterul ’A’ are codul ASCII 65 reprezentarea
AB pe 8 biţi fiind 01000001
Matricea permutărilor circulare este ı̂n stânga, iar
ı̂n dreapta este matricea cu liniile ordonate lexi-
cografic

Linia reprezentării lui ’A’ are indicele 4. Cript-ul


caracterului ’A’ este 100010004
Se procedează analog pentru ’B’
2 VI 101110001-cript-ul caracterului ’V’
101110001111000002 111000002-cript-ul caracterului ’I’
Tabelul 23.1: cript

Timp maxim de executare/test: 0.15 secunde


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

23.1.1 Indicaţii de rezolvare

prof. Lucia Miron - Colegiul ”C. Negruzzi” Iasi

Soluţia 1 - (100 puncte)


Rezolvarea primei cerinţe, presupune simularea algoritmului de criptare descris ı̂n enunţul
problemei. Reprezentam pe 8 biţi, adică ı̂n baza 2 codul ASCII al fiecarui caracter din mesaj.
Memorăm codul binar ı̂ntr-o matrice de caractere, memorăm ı̂ntr-un vector ord, indicele fiecărei
linii din matrice, ordonăm lexicografic liniile matricei de caractere, construim cript-ul luând ı̂n
ordine caracterele ultimei coloane şi poziţia ı̂n vectorul ord a valorii 0.
Rezolvarea celei de a doua cerinţe presupune determinarea codului binar al unui caracter pe
baza informaţiilor primite: ultima coloana a matricei permutarilor circulare ordonate lexicografic
şi poziţia liniei pe care se află codul binar al caracterului ı̂n cadrul acestei matrice.
Pasul 1: Vom determina prima coloană din matricea obţinută: este evident că prima coloana
este ultima coloană ordonată lexicografic, pentru ordonare determin numărul de elemente 0 de pe
ultima coloană şi numărul de elemente 1 de pe ultima coloană. Primul 0 de pe prima coloană va fi
primul 0 de pe ultima coloană, al doilea 0 de pe prima coloană va fi al doilea 0 de pe ultima coloană,
şamd, la fel procedam cu elementele 1, astfel putem determina un vector p, cu proprietatea că
pi=poziţia celei de a i-a valori din coloana 1 ı̂n ultima coloana
Pasul 2: Pe baza vectorului p, determinat anterior vom determina linia din matricea ordonată
lexicografic al cărui indice l-am primit ı̂n ultimul caracter din cript, fie acest indice t, parcurgem
ultima coloană ı̂n ordinea pt, ppt, pppt, şamd.
Soluţia 2 - Octavian Dumitraşcu (100 puncte)
Vom determina codificările pentru cele 96 de caractere (de la 32 la 127) şi memorăm ı̂ntr-un
vector de cuvinte solc hari codificarea caracterului din codul ASCII i şi ı̂n soli memorăm un
număr unic conform codificării, mai exact primele 8 caractere ale codului sunt ı̂n ordine ultimii 8
biţi (poziţiile 0, 1, ..., 7) şi caracterul de pe poziţia 9 este reprezentat ı̂n baza 2 şi ocupă poziţiile
8, 9, 10, 11.
La citirea textului necriptat, pentru fiecare caracter vom afişa direct criptul acestuia. Pentru
decodificare, vom căuta caracterul având criptul egal cu cel citit.
CAPITOLUL 23. ONI 2015 23.1. CRIPT 284

23.1.2 Cod sursă

Listing 23.1.1: cript emcerchez 100.cpp


//Emanuela Cerchez - 100 puncte
#include <fstream>
#include <cstring>
#include <cassert>

#define LGMAX 270001

using namespace std;

int c;
char s[LGMAX];

ifstream fin("cript.in");
ofstream fout("cript.out");

char M[8][9];
int ord[8];
char uc[9];

void criptare();
void decriptare();

int main()
{int lg;
fin>>c; fin.get();
assert(c==1 || c==2);
fin.getline(s,LGMAX);
lg=strlen(s);
if (c==1) {assert(lg<=30000); criptare();}
else {assert (lg<=270000 && lg%9==0);decriptare();}
fout<<’\n’; fout.close();
return 0;
}

void criptare()
{int i, j, k, aux, poz;
for (i=0; s[i]; i++)
{ assert (s[i]>=32 && s[i]<=127);
for (j=7; j>=0; j--)
{M[0][j]=(s[i]&1)+’0’; s[i]>>=1;}
for (k=1; k<8; k++)
{for (j=0; j<7; j++) M[k][j]=M[k-1][j+1];
M[k][7]=M[k-1][0];}
for (k=0; k<8; k++) ord[k]=k;
for (k=0; k<8; k++)
for (j=k+1; j<8; j++)
if (strcmp(M[ord[k]],M[ord[j]])>0)
{aux=ord[k]; ord[k]=ord[j]; ord[j]=aux;}
for (k=0; k<8; k++)
for (j=k+1; j<8; j++)
if (strcmp(M[ord[k]],M[ord[j]])==0&&ord[k]>ord[j])
{aux=ord[k]; ord[k]=ord[j]; ord[j]=aux;}
for (k=0; k<8; k++)
{fout<<M[ord[k]][7];
if (!ord[k]) poz=k;}
fout<<poz;
}
}

void decriptare()
{int i, j, poz, nr0, cod;
char *q;
for (i=0; s[i]; i++)
{ nr0=0; cod=0;
for (j=0; j<8; j++, i++)
{assert(s[i]==’0’|| s[i]==’1’);
uc[j]=s[i]; nr0+=(s[i]==’0’);}
q=uc;
for (j=0; j<nr0; j++)
{ord[j]=strchr(q,’0’)-uc; q=ord[j]+1+uc;}
CAPITOLUL 23. ONI 2015 23.1. CRIPT 285

q=uc;
for (j=nr0; j<8; j++)
{ord[j]=strchr(q,’1’)-uc; q=ord[j]+1+uc; }
assert(s[i]>=’0’ && s[i]<=’7’);
poz=s[i]-’0’;
for (j=0; j<8; j++)
{poz=ord[poz];
cod=(cod<<1)|(uc[poz]-’0’);
}
fout<<(char)cod;
}
}

Listing 23.1.2: cript LuciaMiron.cpp


//Lucia Miron, 100 puncte
#include <fstream>
#include<cstring>

using namespace std;

ifstream fin("cript.in");
ofstream fout("cript.out");

char s[30001], ss[10],t[270001],css;


int c,i,j;

void cript(char c, char cod[10])


{
int n,ord[10],ax,i,j;
char s[10][10],aux[10];

n=c;
for(i=7; i>=0; i--)
{
s[0][i]=’0’+n%2;
n=n/2;
}

s[0][8]=’\0’;
for(i=1;i<=7;i++)
{
for(j=0;j<7;j++)
s[i][j]=s[i-1][j+1];
s[i][7]=s[i-1][0];
s[i][8]=’\0’;
}

//ordonez lexicografic sirul s[]


for(i=0; i<=7; i++)
ord[i]=i;

for(i=0; i<7; i++)


for(j=i+1; j<=7; j++)
if(strcmp(s[i],s[j])>0)
{
strcpy(aux,s[i]);
strcpy(s[i],s[j]);
strcpy(s[j],aux);
ax=ord[i];
ord[i]=ord[j];
ord[j]=ax;
}

for(i=0; i<7; i++)


for(j=i+1; j<=7; j++)
if(strcmp(s[i],s[j])==0&&ord[i]>ord[j])
{
strcpy(aux,s[i]);
strcpy(s[i],s[j]);
strcpy(s[j],aux);
ax=ord[i];
ord[i]=ord[j];
ord[j]=ax;
}
CAPITOLUL 23. ONI 2015 23.1. CRIPT 286

for(i=0; i<=7; i++)


cod[i]=s[i][7];

for(i=0; i<=7; i++)


if(ord[i]==0)
break;

cod[8]=’0’+i;
cod[9]=’\0’;
}

void decript(char ss[10], char & css)


{
int p[10],i,k=0,n0=0,n1=0,j=0,t,cod=0,a[10];
char ds[10];

for(i=0; i<=7; i++)


if(ss[i]==’0’)
n0++;
else
n1++;

for(i=0; i<=7; i++)


if(ss[i]==’0’)
a[k++]=i;
else
a[n0+j++]=i;

t=ss[8]-’0’;
j=a[t];
for(i=0; i<=7; i++)
{
ds[i]=ss[j];
j=a[j];
}

for(i=0; i<=7; i++)


cod=cod*2+(ds[i]-’0’);

css=(char)cod;
}

int main()
{
fin>>c;

if(c==1)
{
fin.get();
fin.getline(s,40001);
for(i=0; s[i]; i++)
{
cript(s[i],ss);
fout<<ss;
}
}
else
{
fin>>t;
for(i=0; t[i]; i=i+9)
{
for(j=0;j<=8;j++)
ss[j]=t[i+j];
ss[9]=’\0’;
decript(ss,css);
fout<<css;
}
}

return 0;
}

Listing 23.1.3: cript odumitrascu.cpp


CAPITOLUL 23. ONI 2015 23.1. CRIPT 287

//sursa cript Octavian Dumitrascu - 100 puncte


#include <fstream>
#include <cstring>
#include <algorithm>

#define LGMAX 300001

using namespace std;

int s, i, p, n, j, k, r , y , z, q, t, q1, a[15];


char sir[ 30005 ], b[15];
char cript[ 300005 ];
char sol_char[140][15];
int sol[140];

ifstream fin("cript.in");
ofstream fout("cript.out");

int main()
{
char caracter;
fin>>p; fin.get(caracter);
for ( i = 32 ; i <= 127 ; i++)
{
z = y = i ;
if (i == 65)
i = i;

for (j = 0; j < 8; j++)


{
a[ j ] = y;
r = y / 128;
y = ( y << 1 )%256 + r;
}

sort (a, a + 8);

for (j = 0; j < 8; j++)


if (a[ j ] == z)
{
q = j;
break;
}

q1 = q;
q = (q << 8);
s = 0;
for (j = 0; j < 8; j++)
{
s = s * 2 + (a[ j ] & 1);
if ( (a[ j ] & 1) == 0)
b[ j ] = ’0’;
else
b[ j ] = ’1’;
}

sol[ i ] = s + q;
b[ 8 ] = q1 + 48;
b[ 9 ] = ’\0’;
strcpy(sol_char[i], b);
}

if (p == 1)
{
fin.getline(sir, LGMAX);
n = strlen(sir);
for (i = 0; i < n; i++)
{
fout<<sol_char[ sir[ i ] ];
}
}
else
{
fin.getline(cript, LGMAX);
n = strlen(cript);
s = 0;
CAPITOLUL 23. ONI 2015 23.1. CRIPT 288

for (i = 0; i < n; i++ )


if (i % 9 < 8)
s = s * 2 + cript[ i ] - 48;
else
{
z = cript[ i ] - 48;
z = (z << 8);
s += z;
for ( j = 32 ; j <= 127; j++)
if (s == sol[ j ] )
fout<<(char)j;
s = 0;
}
}

fout<<’\n’;
fout.close();
return 0;
}

Listing 23.1.4: cript rcostineanu 100.cpp


//Raluca Costineanu 100 puncte
#include <fstream>
#include <cstring>

using namespace std;

ifstream f("cript.in");
ofstream g("cript.out");

int c;
char s[9*30001];
int codif[200][10];

int cmp(int a[10],int b[10])


{
int i;
for(i=0;i<8;i++)
if(a[i]<b[i])
return -1;
else
if(a[i]>b[i])
return 1;
return 0;
}

void codific(char c, int cod[10])


{
int x=c, i, j, a[10][10];

for(i=7; i>=0; i--)


{
a[0][i]=x%2;
x=x/2;
}

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


{
for(j=0;j<7;j++)
a[i][j]=a[i-1][j+1];
a[i][7]=a[i-1][0];
}

int nr[10], aux, k;

// for(i=0;i<=7;i++)
// {for(j=0;j<8;j++)
// g<<a[i][j];
// g<<’\n’;}

for(i=0;i<8;i++)
nr[i]=i;

for(i=0;i<7;i++)
CAPITOLUL 23. ONI 2015 23.1. CRIPT 289

for(j=i+1; j<=7; j++)


if(cmp(a[i], a[j])>0)
{
for(k=0;k<=7;k++)
{
aux=a[i][k];
a[i][k]=a[j][k];
a[j][k]=aux;
}

aux=nr[i];
nr[i]=nr[j];
nr[j]=aux;
}

for(i=0;i<7;i++)
for(j=i+1; j<=7; j++)
if(cmp(a[i], a[j])==0 && nr[i]>nr[j])
{
for(k=0;k<=7;k++)
{
aux=a[i][k];
a[i][k]=a[j][k];
a[j][k]=aux;
}

aux=nr[i];
nr[i]=nr[j];
nr[j]=aux;
}

//g<<’\n’;
// for(i=0;i<=7;i++)
// {for(j=0;j<8;j++)g<<a[i][j];g<<’\n’;}

for(j=0;j<=7;j++)
cod[j]=a[j][7];

for(i=0;i<=7;i++)
if(nr[i]==0)
cod[8]=i;
}

char decodific(int cod[10])


{
int i;

for(i=32;i<=127;i++)
if(cmp(codif[i], cod)==0 && codif[i][8]==cod[8])
return i;
return -1; // ... !!!
}

int main()
{
f>>c;
f.get();
f.getline(s,9*30001);

if(c==1)
{
int i, j, cod[10];
char codul[10];

for(i=32; i<=127; i++)


{
codific(char(i), cod);
for(j=0;j<9;j++)
codif[i][j]=cod[j];
}

for(i=0; s[i]; i++)


{
for(j=0;j<=8;j++)
g<<codif[s[i]][j];
//g<<’\n’;
CAPITOLUL 23. ONI 2015 23.2. SCADERE 290

}
}
else
{
int n=strlen(s), i, j;
int cod[10];
char codul[10];

for(i=32; i<=127; i++)


{
codific(char(i), cod);
for(j=0;j<9;j++)
codif[i][j]=cod[j];
}

for(i=0;i<n;i=i+9)
{
strncpy(codul, s+i, 9);
codul[9]=0;
for(j=0;j<9;j++)
cod[j]=codul[j]-’0’;
g<<decodific(cod);
}
}

return 0;
}

23.1.3 *Rezolvare detaliată

23.2 scadere
Problema 2 - scadere 100 de puncte
Fie n un număr natural nenul.
Să considerăm o expresie de forma: x1  x2  x3  ...  xn
Se ştie că scăderea nu este o operaţie asociativă, adică x1  x2  x3  j x1  x2   x3 .
Ca urmare, prin plasarea unor perechi de paranteze ı̂n expresie, putem obţine diferite valori.
Pentru problema noastră, vom denumi scădere o expresie de forma de mai sus ı̂n care pot apărea
şi paranteze rotunde care se ı̂nchid corect. Valoarea unei scăderi se obţine efectuând operaţiile de
scădere ı̂n ordine de la stânga la dreapta; dacă apar paranteze, se efectuează mai ı̂ntâi operaţiile
din paranteze.

Cerinţe

Date fiind valorile variabilelor x1 , x2 , ..., xn care intervin ı̂n scădere, scrieţi un program care să
rezolve următoarele două cerinţe:
1. să se determine valoarea maximă a unei scăderi (obţinută prin inserarea convenabilă a unor
paranteze rotunde ı̂n expresia x1  x2  x3  ... xn ), precum şi o scădere având valoare maximă.
2. să se determine valoarea unei scăderi specificate.

Date de intrare

Fişierul de intrare scadere.in conţine pe prima linie un număr natural c indicând cerinţa care
trebuie să fie rezolvată (1 sau 2).
Pe a doua linie este scris numărul natural n, care reprezintă numărul de variabile care intervin
ı̂n scădere. Variabilele sunt numerotate de la 1 la n ı̂n ordinea ı̂n care intervin ı̂n scădere. Pe
următoarele n linii sunt scrise ı̂n ordine valorile variabilelor x1 , x2 , ..., xn , câte o valoare pe o linie.
Dacă cerinţa este 2, fişierul mai conţine o linie pe care este scris un şir de caractere reprezentând
o scădere.

Date de ieşire
CAPITOLUL 23. ONI 2015 23.2. SCADERE 291

Fişierul de ieşire scadere.out va conţine pentru c 1 două linii; pe prima linie va fi scris un
număr ı̂ntreg reprezentând valoarea maximă a unei scăderi (obţinută prin inserarea convenabilă
a unor paranteze rotunde ı̂n expresia x1  x2  x3  ...  xn ), iar pe a doua linie o scădere având
valoare maximă. Dacă c 2 fişierul de ieşire va conţine o singură linie pe care va fi scris un număr
ı̂ntreg reprezentând valoarea scăderii specificate pe ultima linie a fişierului de intrare.

Restricţii şi precizări

a 3 & n & 5000


a Valorile variabilelor x1 , x2 , ..., xn sunt numere ı̂ntregi din intervalul 100, 100.
a Scăderea din fişierul de intrare, respectiv scăderea de valoare maximă afişată ı̂n fişierul de
¬ ¬
ieşire vor avea maxim 40000 de caractere care pot fi doar cifre, litera mică x , paranteze rotunde
¬ ¬
şi operatorul  (minus).
a Pentru teste valorând 50% din punctaj cerinţa va fi 1. Pentru afişarea corectă a valorii
maxime se acordă 40% din punctajul pe test. Punctajul integral se acordă pentru afişarea corectă
a valorii maxime şi a unei scăderi de valoare maximă.

Exemple
scadere.in scadere.out Explicaţii
1 17 Parantezarea care conduce la valoarea maximă este:
4 x1-x2-(x3-x4) x1-x2-(x3-x4)=-7-5-(-10-19)=-12-(-29)=-12+29=17
-7
5
-10
19
2 -3 x1-((x2-x3)-x4)=
4 -7-((5-10)-19)=
-7 -7-(15-19)=-7-(-4)=
5 -7+4=-3
-10
19
x1-((x2-x3)-x4)

Timp maxim de executare/test: 0.1 secunde


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

23.2.1 Indicaţii de rezolvare

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

Observăm că prin parantezare nu putem schimba semnul termenului x1, iar semnul termenului
x2 va fi ı̂ntotdeauna schimbat.
Pentru orice alt termen, plasând o paranteză ı̂n faţa termenului precedent păstrăm semnul
acestuia.
x1  x2  x3
x1  x2  x3 x1  x2  x3
Pentru a obţine suma maximă intenţionăm să schimbăm semnul termenilor negativi şi să
păstrăm semnul termenilor pozitivi.
Pentru cerinta 2 trebuie să evaluăm o expresie dată. Acest lucru se poate realiza ı̂n mai multe
moduri.
O varianta ar fi de a utiliza o stivă (implementată ca un vector: inserarea se realizează la vârful
stivei, adică la sfârşitul vectorului, iar extragerea se realizează de asemenea de la vârful stivei, adică
se elimină ultimul element din vector). ı̂n acest vector se vor reţine rezultatele parţiale obţinute
pe parcursul evaluării expresiei.
Parcurgem şirul care conţine expresia.
În cazul ı̂n care caracterul curent este ’(’, inserăm la sfârşitul vectorului valoarea INFINIT
pentru a marca faptul că nu am plasat ı̂ncă pe stivă nicio valoare pe acel nivel.
În cazul ı̂n care caracterul curent este ’)’, vom elimina ultimul element din vector (adică vom
coborı̂ ı̂n stivă), transmiţând pe nivelul precedent valoarea elementului eliminat (această valoare
CAPITOLUL 23. ONI 2015 23.2. SCADERE 292

va fi scăzută din nivelul precedent sau copiată pe nivelul precedent, ı̂n funcţie de caz - dacă pe
nivelul precedent se află o valoare sau nu).
În caz contrar, determin numărul variabilei care urmează ı̂n şir, aflu valoarea acestei variabile
şi o scad din valoarea aflată la vârful stivei (dacă există o astfel de valoare) sau o copiez la vârful
stivei (dacă la vârful stivei este plasată valoarea INFINIT).

23.2.2 Cod sursă

Listing 23.2.1: scadere 100 emcerchez.cpp


//Emanuela Cerchez 100 puncte
#include <fstream>
#include <cstring>

#define NMAX 5001


#define LGMAX 40001
#define INF 1000000000

using namespace std;

ifstream fin("scadere.in");
ofstream fout("scadere.out");

char s[LGMAX], snr[10];


int x[NMAX];
int st[LGMAX];
int n, cerinta;
int rez;
void sir(int x);
int evalexp();

int main()
{int i, nrp=0;
fin>>cerinta>>n;
for (i=1; i<=n; i++) fin>>x[i];
if (cerinta==1)
{
rez=x[1]-x[2];
for (i=3; i<=n; i++)
if (x[i]>=0) rez+=x[i];
else rez-=x[i];
fout<<rez<<’\n’;
fout<<"x1-";
for (i=3; i<=n; i++)
{
if (x[i]>=0)
{if (nrp%2==0)
{fout<<"("; nrp++;}
}
else
{if (nrp%2)
{fout<<"("; nrp++;}
}
fout<<"x"<<i-1<<"-";
}
fout<<"x"<<n;
for (i=0; i<nrp; i++) fout<<")";
fout<<’\n’;
fout.close();
return 0;}

fin>>s;
rez=evalexp();
fout<<rez<<’\n’;
fout.close();
return 0;
}

int evalexp()
{int vf=0, i, nr;
st[0]=INF;
for (i=0; s[i]; )
CAPITOLUL 23. ONI 2015 23.2. SCADERE 293

if (s[i]==’(’)
{st[++vf]=INF; i++;}
else
if (s[i]==’)’)
{if (st[vf-1]==INF) st[vf-1]=st[vf];
else st[vf-1]-=st[vf];
vf--;i++;}
else
{
if (s[i]==’-’)i++;
if (s[i]==’x’)
{i++;//sar x
nr=0;
while (s[i]>=’0’&& s[i]<=’9’) {nr=nr*10+s[i]-’0’; i++;}
if (st[vf]==INF) st[vf]=x[nr];
else st[vf]-=x[nr];
}
}
return st[0];
}

Listing 23.2.2: scadere 100 odumitrascu.cpp


//scaderi sursa Octavian Dumitrascu 100 puncte
#include <cstdio>
#include <cstring>

using namespace std;

int m, t, nr_minus, paranteze, q, j, p, i, s, n, s1, start, stop,


x[ 5005 ], semn[ 5005 ], y[ 5005 ], st[ 5005 ];
char sir[ 40005 ];

int main()
{
freopen("scadere.in", "r", stdin);
freopen("scadere.out", "w", stdout);

scanf("%d\n",&p);
scanf("%d\n", &n);
for (i = 1; i <= n; i++)
scanf("%d\n",&x[ i ]);

if (p == 1)
{
s = x[1];
start = 2;
stop = 2;
s1 = 0;
j=2;

for (i = 2; i <= n; i++)


if (x[ i ] > 0)
{
s1 -= x[ i ];
stop ++;
}
else
{
j = i - 1;
break;
}

if (i > n)
{
semn[start] = 1;
semn[stop] = -1;
s = s - s1 - 2 * x[2];
}
else
{
if (j == 2)
s = s - x[ 2 ],j++ ;
else
if (j > 2)
CAPITOLUL 23. ONI 2015 23.2. SCADERE 294

{
s = s - (s1 + 2 * x[ 2 ]);
semn [ start ] = 1;
semn [ stop - 1 ] = -1;
j++;
};

if ( j == 1)
s += x[2];

start = stop = j ;
s1 = 0;
for (i = j ; i <= n; i++)
if (x[ i ] >= 0)
{
s1 += x[ i ];
stop ++;
}
else
{
if (stop - start >= 1)
{
s += s1;
semn[ start ] = 1;
semn[ stop ] = -1;
}
else
s += s1;

s1 = -x[ i ];
start = stop = i;
}

if (stop - start >= 1)


{
semn[ start ] = 1;
semn[ stop ] = -1;
}

s = s + s1;
}

printf("%d\n", s);
if (semn[1] == 1)
printf("(");
printf("x1-");

for (i = 2; i < n; i++)


{
if (semn[ i ] == 1 )
printf("(");
printf("x%d",i);
if (semn [ i ] !=- 1)
printf("-");
if (semn[ i ] == -1)
printf(")-");
}

printf("x%d",n);
if (semn[ n ] == -1 || semn[ n + 1 ] == -1)
printf(")");
}
else
{
gets ( sir );
m = strlen(sir);
y[ 1 ] = 1;
t = 1;
paranteze = 0;
q = 0;
nr_minus = 0;

for ( i = 0; i < m; i++)


if (sir[ i ] == ’(’ )
q++,st[q]=nr_minus;
else
CAPITOLUL 23. ONI 2015 23.3. TV 295

if (sir[ i ] == ’)’)
q--,nr_minus = st[q];
else
if (sir[ i ] ==’-’ )
nr_minus =st[q], nr_minus++;
else
if (sir[ i ] == ’x’)
if (nr_minus % 2 == 0)
y[ t++ ] = 1;
else
y[ t++ ] = -1;
else
while(i+1<m && sir[i+1]>=’0’ && sir[i+1]<=’9’)
i++;
s = 0;
for (i = 1;i <= n; i++)
s = s + y[ i ] * x[ i ];

printf("%d\n",s);
}

return 0;
}

23.2.3 *Rezolvare detaliată

23.3 tv
Problema 3 - tv 100 de puncte
Comisia Naţională a Audiovizualului (CNA) este autoritatea care coordonează activitatea
posturilor media din România. şeful CNA-ului doreşte o statistică referitoare la publicitatea
transmisă de posturile de televiziune. ı̂n acest scop, el primeşte pentru fiecare zi informaţii ı̂n
următorul format:
d hh  mm  ss
unde d este durata exprimată ı̂n secunde a publicităţii, iar hh  mm  ss este momentul de start
al publicităţii (hh este ora, mm este minutul, iar ss este secunda). Observaţi că d este separat de
hh printr-un singur spaţiu, iar următoarele valori sunt separate prin caracterul ’:’.
De exemplu o linie de forma:
150 05  02  45
se interpretează astfel: există un post TV care a transmis publicitate cu durata de 150 secunde,
ora de ı̂nceput fiind 5, 2 minute şi 45 de secunde.
”Secunda de aur” este o secundă ı̂n care se difuzează cât mai multă publicitate, adică pe un
număr maxim de posturi ı̂n acea secundă se transmite publicitate. Dacă sunt mai multe astfel
de secunde, ”secunda de aur” este considerată prima secundă cu această proprietate ı̂n derularea
zilei.
Şeful CNA primeşte ı̂n fiecare dimineaţă lista cu activitatea din ziua anterioară ca o succesiune
de linii, fiecare linie având forma descrisă mai sus.

Cerinţe

Scrieţi un program care, cunoscând lista din ziua anterioară, să rezolve următoarele cerinţe:
1. să determine durata totală ı̂n care niciun post de televiziune nu a difuzat publicitate;
2. să determine care este ”secunda de aur”.

Date de intrare

Fişierul de intrare tv.in conţine pe prima linie numărul natural c, care poate fi 1 sau 2,
reprezentând cerinţa care urmează să fie rezolvată. Pe a doua linie se află numărul natural N ,
reprezentând numărul de linii din lista cu informaţii primită de şef. Pe următoarele N linii sunt
descrise informaţiile, ı̂n formatul specificat ı̂n enunţ.

Date de ieşire
CAPITOLUL 23. ONI 2015 23.3. TV 296

Fişierul de ieşire tv.out va conţine o singură linie pe care vor fi scrise 3 numere naturale
separate prin caracterul ’:’ ı̂n formatul următor:
hh  mm  ss
semnificând durata totală exprimată ı̂n ore (hh), minute (mm) şi secunde (ss) pe parcursul
căreia niciun post de televiziune nu a difuzat publicitate ı̂n ziua respectivă (dacă c 1), respectiv
”secunda de aur” (dacă c 2).

Restricţii şi precizări

a 1 & N & 100000


a hh va fi un număr de exact două cifre, cuprins ı̂ntre 00 şi 23
a mm, respectiv ss vor fi numere de exact două cifre, cuprinse ı̂ntre 00 şi 59
a Durata d este nenulă şi sfârşitul transmisiei publicităţii se află ı̂n cadrul zilei curente.
a Pentru teste valorând 60% din punctaj cerinţa este 1.

Exemple
tv.in tv.out Explicaţii
1 23:18:40 Pentru exemplul 1, cerinţa este 1.
6 Pe parcursul zilei, timp de 23 de ore, 18 minute şi 40 de secunde
120 12:00:00 nu s-a difuzat publicitate.
200 12:01:50
1000
13:00:00
2000
13:01:00
100 14:05:05
10 23:59:49
2 12:01:50 Pentru exemplul 2, cerinţa este 2.
6 Secunda de aur este 12:01:50 pentru că există un număr maxim
1200 posturi care difuzează publicitate (3 posturi).
12:00:00
2000
12:01:50
1000
12:00:00
2000
13:01:00
100 14:05:05
10 23:59:49

Timp maxim de executare/test: 0.2 secunde


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

23.3.1 Indicaţii de rezolvare

Octavian Dumitraşcu, Colegiul Naţional ”Dinicu Golescu” Câmpulung Muscel

Vom transforma timpii ı̂n secunde.


Vom construi un vector h cu 24x60x60 componente. Pentru fiecare moment de timp vom reţine
ı̂n hi=numărul de posturi care ı̂ncep să emită publicitate la momentul i.
Pentru fiecare interval de timp din listă incep, sf  (unde sf incep  d) vom incrementa
hincep şi voi decrementa hsf .
Suma elementelor de la 1 la x reprezintă câte posturi transmit publicitate la momentul x.
Soluţia este optimă având complexitate O N .
CAPITOLUL 23. ONI 2015 23.3. TV 297

23.3.2 Cod sursă

Listing 23.3.1: tv odumitrascu 100.cpp


//Octavian Dumitrascu - 100 puncte
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

int sf, liber, yy, m1, s1, h1, d, c, ss, n, ma, p,


timp, start, stop, i, h[100005];
char s[10];

int main()
{
freopen("tv.in", "r", stdin);
freopen("tv.out", "w", stdout);

scanf("%d\n%d\n", &p, &n);

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


{
scanf("%d ",&d);
gets(s);
h1=(s[0]-48)*10+s[1]-48;
m1=(s[3]-48)*10+s[4]-48;
s1=(s[6]-48)*10+s[7]-48;
start = s1 + m1*60 + h1*60*60;
stop = start + d;
h[start]++;
h[stop]--;
}

ss = 0;
liber = 0;
ma = 0;
for (i = 0; i < 60*60*24; i++)
{
ss += h[i];
if (ss > ma)
{
ma = ss;
timp = i;
}

if (ss == 0)
liber++;
}

if (p == 1)
yy = liber;
else
yy = timp;

strcpy(s,"");
h1 = yy/3600;
m1 = yy%3600/60;
s1 = yy%60;
s[0]=h1/10+48;
s[1]=h1%10+48;
s[2]=’:’;
s[3]=m1/10+48;
s[4]=m1%10+48;
s[5]=’:’;
s[6]=s1/10+48;
s[7]=s1%10+48;
s[8]=’\0’;

printf("%s\n", s);

return 0;
}
CAPITOLUL 23. ONI 2015 23.3. TV 298

23.3.3 *Rezolvare detaliată


Capitolul 24

ONI 2014

24.1 codat
Problema 1 - codat 100 de puncte
Se consideră un şir de N numere naturale, notate x1 , x2 , x3 , ..., xN . Definim pentru orice
pereche de indici i, j, 1 & i & j & N , distanţa ı̂ntre elementele xi şi xj ca fiind egală cu j  i.
Acest şir va fi codificat după următoarele reguli:
a fiecare element din şir este ı̂nlocuit cu indicele celui mai apropiat element din şir (cel faţă de
care distanţa este minimă) strict mai mare decât el;
a dacă pentru un element din şir există două elemente care respectă regula de mai sus, atunci
el va fi ı̂nlocuit cu indicele mai mare, adică al elementului strict mai mare decât el, aflat ı̂n dreapta
lui;
a elementele de valoare maximă din şir vor fi ı̂nlocuite cu -1.

Cerinţe

Scrieţi un program care codifică un şir de N valori, după regulile descrise.

Date de intrare

Fişierul codat.in conţine:


- pe prima linie numărul natural N
- pe următoarea linie N numere naturale nenule, separate prin câte un spaţiu, reprezentând
şirul x1 , x2 , x3 , ..., xN

Date de ieşire

Fişierul codat.out va conţine pe prima linie N numere ı̂ntregi nenule, separate prin câte un
spaţiu, reprezentând şirul codificat.

Restricţii şi precizări

a 1&N & 1000000


a 1 & xi & 2000000000, 1 & i & N
Exemple
codat.in codat.out Explicaţii
7 2 -1 4 2 4 7 4 x1 2: cel mai apropiat element strict mai mare decât el este x2
2935114 x2 9: nu are nici un element mai mare decât el
x3 3: elementele mai mari strict decât el, sunt aflate la distanţă
egală, deci va fi ı̂nlocuit cu indicele mai mare adică 4
x4 5: cel mai apropiat element strict mai mare decât el este x2
x5 1: cel mai apropiat element strict mai mare decât el este x4
x6 1: cel mai apropiat element strict mai mare decât el este x7
x7 4: cel mai apropiat element strict mai mare decât el este x4

Timp maxim de executare/test: 2.0 secunde


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

299
CAPITOLUL 24. ONI 2014 24.1. CODAT 300

24.1.1 Indicaţii de rezolvare

???-???

Considerăm şirul numerelor citite memorat ı̂n vectorul A1..N . Algoritmul construieşte ı̂n
O N  un vector St1..N , ı̂n care elementul Sti memorează indicele celui mai apropiat element
mai mare decât Ai situat ı̂n vectorul A de la poziţia 1..i  1. În mod identic ı̂n urma unei noi
parcurgeri a vectorului A, se va construi vectorul Dr1..N , ı̂n care elementul Dri memorează
indicele celui mai apropiat element mai mare decât Ai situat ı̂n vectorul A de la poziţia i  1..N .
Pentru fiecare element Ai soluţia o reprezintă cel cel mai apropiat dintre indicii elementului
maxim aflat ı̂n stânga, salvat ı̂n Sti, şi cel mai mare element aflat ı̂n dreapta lui, salvat ı̂n Dri.
Pentru construcţia vectorului St elementele lui A sunt parcurse de la stânga la dreapta. Vom
folosi un vector suplimentar Cnd pentru pastrarea doar a elementelor ce candidează la a fi maxime
pentru unul dintre elementele următoare. În fapt ı̂n vectorul Cnd vom plasa indicii acestora.
Pentru exemplificare vom construi vectorul St pentru vectorul iniţial A 2, 7, 4, 8, 6.
Notăm cu nr, numărul de elemente din vectorul candidaţilor Cnd. Când indicele i este introdus
ı̂n vectorul Cnd, toate elementele ai caror indici sunt deja salvaţi vor fi extrase dacă sunt mai mici
decât Ai. Operaţia se realizează decrementând valoare indicelui nr atâta timp cât ACndnr &
Ai. La final, Cndnr  1 reprezintă indicele primului element mai mare decât Ai. Iniţializăm
Cnd0 1.
Pentru asta vom gestiona vectorul Cnd astfel:
A1 2 este plasat ı̂n vectorul Cnd1 1, nr 1. Elementul St1 1 deoarece nu are
element mai mare situat ı̂n stânga lui.
A2 7 Elementul ACndnr fiind mai mic decât A2, este scos din şirul canditaţilor şi
ı̂nlocuit cu indicele acestuia (Cndnr 2), nr 1.
Deasemenea, St2 1 deoarece nu are element mai mare situat ı̂n stanga lui A3 4.
Acesta poate reprezenta maximul unui element aflat ı̂n vectroul A după el, deci este salvat ı̂n
vectorul Cnd pe poziţia a doua.
Acesta conţine acum indicii 2, 3. Deasemenea St3 Cnd1 2.
A4 8, acest element este mai mare decât ambele elemente aflate ı̂n Cnd, deci le va extrage
pe toate. Vectorul Cnd va conţine doar indicele (4). Fiind plasat pe prima poziţie ştim că
St4 1 deoarece nu are element mai mare situat ı̂n stânga lui.
A5 6, acesta poate reprezenta maximul unui element aflat după el, deci este salvat ı̂n Cnd
pe poziţia a doua. Vectorul Cnd conţine acum indicii 4, 5. Deasemenea St5 Cnd1 4.
Astfel indicele celui mai apropiat maxim din stânga este primul element din Cnd care nu a fost
extras la introducerea lui 5.

24.1.2 Cod sursă

Listing 24.1.1: codat mn.cpp


#include <fstream>

#define DIM 1000002

using namespace std;

ifstream fin("codat.in");
ofstream fout("codat.out");

int v[DIM], s[DIM], L[DIM], R[DIM], minim, k, n, i, maxim, pminim;

int main()
{
fin>>n;
for (i=1;i<=n;i++)
{
fin>>v[i];
if (v[i] > maxim)
CAPITOLUL 24. ONI 2014 24.1. CODAT 301

maxim = v[i];
while (k!=0 && v[i] >= v[s[k]])
{
k--;
}
L[i] = s[k];
s[++k] = i;
}

k = 0;
s[k] = n+1;
for (i=n;i>=1;i--)
{
while (k!=0 && v[i] >= v[s[k]])
{
k--;
}
R[i] = s[k];
s[++k] = i;

for (i=1;i<=n;i++)
{
if (v[i] == maxim)
{
v[i] = -1;
continue;
}

minim = n+2;
if (L[i] != 0 && i-L[i]<minim)
{
minim = i-L[i];
pminim = L[i];
}
if (R[i]!=n+1 && R[i]-i <= minim)
{
minim = R[i]-i;
pminim = R[i];
}
v[i] = pminim;
}

for(i=1;i<=n;i++)
fout<<v[i]<<" ";

return 0;
}

Listing 24.1.2: codat patratic.cpp


#include <fstream>

#define MAXN 100002

using namespace std;

ifstream in("codat.in");
ofstream out("codat.out");

int v[MAXN];
int idx[MAXN];

int abs(int x)
{
if(x<0) x*=-1;
return x;
}

int main()
{
int N;
in>>N;
CAPITOLUL 24. ONI 2014 24.1. CODAT 302

for(int i=1;i<=N;++i)
in>>v[i];

for(int i=1;i<=N;++i)
{
idx[i]=-1;
for(int j=i-1;j>=1;--j)
if(v[j]>v[i])
{
idx[i]=j;
break;
}

for(int j=i+1;j<=N;++j)
if(v[j]>v[i])
{
if(idx[i]==-1||abs(idx[i]-i)>=abs(j-i))
idx[i]=j;
break;
}
}

for(int i=1;i<=N;++i)
out<<idx[i]<<’ ’;

out<<’\n’;

in.close();
out.close();

return 0;
}

Listing 24.1.3: codat stack.cpp


#include <fstream>
#include <stack>

#define MAXN 100001

using namespace std;

ifstream in("codat.in");
ofstream out("codat.out");

int abs(int x)
{
if(x<0) x*=-1;
return x;
}

stack <int> st;

int v[MAXN];
int idx[MAXN];

int main()
{
int N;
in>>N;

for(int i=1;i<=N;++i) in>>v[i];

for(int i=1;i<=N;++i)
{
while(!st.empty()&&v[st.top()]<=v[i])
st.pop();

if(st.empty())
idx[i]=-1;
else
idx[i]=st.top();

st.push(i);
}
CAPITOLUL 24. ONI 2014 24.2. NOD 303

while(!st.empty())
st.pop();

for(int i=N;i>=1;--i)
{
while(!st.empty()&&v[st.top()]<=v[i])
st.pop();

if(!st.empty())
if(idx[i]==-1||abs(idx[i]-i)>=abs(st.top()-i))
idx[i]=st.top();

st.push(i);
}

for(int i=1;i<=N;++i)
out<<idx[i]<<’ ’;
out<<’\n’;

in.close();
out.close();

return 0;
}

24.1.3 *Rezolvare detaliată

24.2 nod
Problema 2 - nod 100 de puncte
Pe vremea maurilor, transmiterea unor mesaje codificate ı̂ntre două persoane se făcea folosind
un cifru numit nod.
Cele două persoane alegeau ı̂n secret o poveste. Aceasta era scrisă ı̂ntr-o carte folosind litere
mici şi mari ale alfabetului englez, pe P pagini, numerotate de la 1 la P , fiecare conţinând exact
R rânduri, numerotate ı̂n cadrul fiecărei pagini de la 1 la R, iar fiecare rând fiind format din exact
C cuvinte, numerotate ı̂n cadrul fiecărui rând de la 1 la C.
Un cuvânt al mesajului de transmis era codificat prin poziţia sa ı̂n povestea aleasă de cei
doi, folosind trei numere scrise cu cifre romane, ce indicau ı̂n ordine: numărul paginii, numărul
rândului ı̂n cadrul paginii, respectiv al cuvântului ı̂n cadrul rândului.
Mesajul astfel codificat era scris pe trei linii. Pe prima linie erau scrise numerele paginilor, pe
a doua linie numerele rândurilor, iar pe a treia linie erau scrise numerele de ordine ale cuvintelor.
Presupunem că mesajul este format din primul cuvânt de pe al cincilea rând al celei de a doua
pagini şi din al patrulea cuvânt de pe rândul al doilea al primei pagini. Mesajul putea fi transmis
pe trei linii ı̂n modul următor:
II I (numerele paginilor)
V II (numerele rândurilor)
I IV (numerele cuvintelor)
Cifrele romane sunt scrise cu majusculele M, D, C, L, X, V, I, iar valorile corespunzătoare
lor sunt ı̂n ordine: 1000, 500, 100, 50, 10, 5, 1. Valoarea unui număr scris cu cifre romane se
calculează parcurgând de la stânga la dreapta cifrele numărului astfel:
- cifra curentă se adună la valoarea obţinută până ı̂n acel moment, dacă cifra următoare este
mai mică sau egală cu ea;
- cifra curentă se scade din valoarea obţinută până ı̂n acel moment, dacă cifra următoare este
mai mare decât ea;
- ultima cifră se adună ı̂ntotdeauna la valoarea obţinută până ı̂n acel moment.
De exemplu pentru numărul MCDXLVI scris cu cifre romane, se obţine valoarea 1446 ı̂n sistem
zecimal, astfel: 1000-100+500-10+50+5+1, iar pentru numărul XXI scris cu cifre romane se obţine
valoarea 21 ı̂n sistemul zecimal astfel: 10+10+1.
CAPITOLUL 24. ONI 2014 24.2. NOD 304

Cerinţe
Cunoscându-se textul poveştii ales de cei doi şi mesajul codificat de ei scrieţi un program care
rezolvă următoarele două cerinţe:
a) Rescrie mesajul codificat folosind scrierea cu cifre din sistemul zecimal.
b) Afişează toate cuvintele mesajului decodificat ı̂n ordinea ı̂n care acestea apar ı̂n poveste.
Date de intrare
Fişierul nod.in conţine:
a pe prima linie numărul 1, dacă se cere rezolvarea doar a cerinţei a) sau numărul 2, dacă se
cere rezolvarea cerinţei b);
a pe următoarele trei linii mesajul codificat după regulile descrise ı̂n enunţ;
a dacă primul număr din fişier este 2 atunci a cincea linie conţine trei numere naturale P , R
şi C, separate ı̂ntre ele prin câte un spaţiu, cu semnificaţia din enunţ;
a pe următoarele P  R linii este scris textul poveştii, fiecare linie conţinând C cuvinte, separate
prin câte un spaţiu.
Date de ieşire
Dacă primul număr din fişierul de intrare este 1 atunci fişierul nod.out va conţine, ı̂n aceeaşi
ordine, pe trei linii, numerele din mesajul codificat scrise ı̂n sistem zecimal. Numerele vor fi
despărţite ı̂n cadrul liniilor prin câte un spaţiu.
Dacă primul număr din fişierul de intrare este 2 atunci fişierul nod.out va conţine pe o singură
linie cuvintele mesajului decodificat, ı̂n ordinea din poveste. Cuvintele vor fi separate prin câte
un spaţiu.
Restricţii şi precizări
a 1 & P & 2000; 1 & R & 25; 1 & C & 15
a 1 & lungimea unui cuvânt din poveste & 12
Exemplul 1:
nod.in nod.out Explicaţii
1 321 Testul de intrare indică rezolvarea primei cerinţe, adică cerinţa a).
III II I 252 Numerele de pe fiecare linie sunt scrise ı̂n aceeaşi ordine, ı̂n sistemul
II V II 614 zecimal.
VI I IV
Exemplul 2:
nod.in nod.out
2 La Olimpiada comisia decide prima
I III II I II
I I II I II
I II II II IV
324
La Olimpiada problemele pot
avea una sau mai
multe cerinte Pentru unele
probleme comisia poate decide
ca prima cerinta sa
fie evaluata si separat

Explicaţii:
Testul de intrare indică rezolvarea celei de a doua cerinţe, adică cerinţa b).
Cuvintele identificate ı̂n poveste sunt:
La - prin (I,I,I)
prima - prin (III,I,II)
comisia - prin (II,II,II)
Olimpiada - prin (I,I,II)
decide - prin (II,II,IV)
Cuvintele mesajului decodificat, ı̂n ordinea din poveste, sunt:
La Olimpiada comisia decide prima
CAPITOLUL 24. ONI 2014 24.2. NOD 305

Timp maxim de executare/test: 0.5 secunde


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

24.2.1 Indicaţii de rezolvare

??? - ???

Definesc o structură care să reţină pagina, rândul, poziţia cuvântului pe rând.
Definesc un tablou de tipul structurii descrise mai sus, care va reţine pentru fiecare cod, pagina,
rândul şi numărul cuvântului de pe rând.
Citim primul număr din fişier, care reprezintă cerinţa, apoi citim pe rând următoarele trei linii
din fişierul de date, care conţin respectiv paginile, rândurile şi numărul cuvântului pe linie.
Pentru cerinţa 1
De pe fiecare linie separăm câte un cuvânt, care reprezintă un număr scris cu cifre romane şi
transformăm numărul scris cu cifre romane ı̂n număr ı̂ntreg scris ı̂n sistem zecimal:
Parcurgem cifrele numărului de la stânga la dreapta câte una, o transformăm ı̂n sistem zecimal
şi

daca(cif[i]<cif[i+1])
nr=nr-cif[i];
else
nr=nr+cif[i+1];

La sfârşit adunăm ultima cifră.


Memorăm numerele astfel obţinute ı̂n tablou.
Scriem tripletele pagină, rând, număr caracter pe câte un rând ı̂n fişierul de ieşire.
Pentru cerinţa 2
După parcurgerea etapelor de la cerinţa 1.
Ordonăm tabloul obţinut, crescător, după trei criterii: pagină, rând şi numărul cuvântului.
Citim din fişier cele trei valori P, R, C, respectiv numărul paginilor cărţii, numărul de rânduri
de pe o pagină şi numărul de cuvinte de pe un rând.
Citim textul linie cu linie, separăm cuvintele şi le numărăm. Calculăm pentru fiecare cod
transmis poziţia pe care o ocupă cuvântul ı̂n text, respectiv: (pagina-1)*R+(rând-1)*C+cuvânt.
În momentul ı̂n care ajungem la cuvântul din text, cu numărul de ordine egal cu numărul
calculat pentru codul transmis, scriem cuvântul ı̂n fişierul de ieşire.

24.2.2 Cod sursă

Listing 24.2.1: nod.cpp


#include <iostream>
#include <fstream>
#include <string.h>

using namespace std;

ifstream f("nod.in");
ofstream g("nod.out");

struct cuvinte
{
int pag, rand, cuv;
};

char sir[600]="", numar[11]="",*p, sep[]=" ";


int n,k,P,R,C;
cuvinte t[26];
CAPITOLUL 24. ONI 2014 24.2. NOD 306

void extrage_nr();
int calculeaza(int x);
void transforma(char numar[11],int k);
void ordonare();
void cauta_cuvinte();
void afisare();

int main()
{
f>>k; f.get();
extrage_nr();
if(k==1)
{
for(int d=1;d<=3;d++)
for(int i=1; i<=n;i++)
{
switch (d)
{
case 1:g<<t[i].pag; break;
case 2:g<<t[i].rand; break;
case 3:g<<t[i].cuv; break;
}

if(i<n)
g<<" ";
else
g<<’\n’;
}
}
else
{
ordonare();
cauta_cuvinte();
}

f.close();
g.close();
return 0;
}

void extrage_nr()
{
int i;
for(i=1;i<=3;i++)
{
n=0;
f.getline(sir,600);//cout<<sir<<endl;
p=strtok(sir,sep);
while (p)
{
n++;
strcpy(numar, p);
transforma(numar,i);
p=strtok(NULL,sep);
}
}
}

void transforma(char numar[11],int d)


{
int i,l,cif1,cif2,nr=0;
l=strlen(numar);
for(i=0;i<l-1;i++)
{
cif1=calculeaza(numar[i]);
cif2=calculeaza(numar[i+1]);
if(cif1<cif2)
nr=nr-cif1;
else
nr=nr+cif1;
}

if(l==1)
nr=calculeaza(numar[i]);
else
CAPITOLUL 24. ONI 2014 24.2. NOD 307

nr=nr+cif2;

switch (d)
{
case 1:t[n].pag=nr;break;
case 2:t[n].rand=nr;break;
case 3:t[n].cuv=nr;break;
}
}

int calculeaza(int x)
{
int cif;
switch (x)
{
case ’M’:cif=1000;break;
case ’D’:cif=500;break;
case ’C’:cif=100;break;
case ’L’:cif=50;break;
case ’X’:cif=10;break;
case ’V’:cif=5; break;
case ’I’:cif=1;break;
}

return cif;
}

void ordonare()
{
int i,sw; cuvinte aux;
do
{
sw=1;
for(i=1;i<n;i++)
if(t[i].pag>t[i+1].pag ||
(t[i].pag==t[i+1].pag && t[i].rand>t[i+1].rand) ||
(t[i].pag==t[i+1].pag &&
t[i].rand==t[i+1].rand &&
t[i].cuv>t[i+1].cuv))
{
aux=t[i];
t[i]=t[i+1];
t[i+1]=aux;
sw=0;
}
} while (sw==0);
}

void cauta_cuvinte()
{
int i=1,nrct=0, nrc=0;
char *p;

f>>P>>R>>C;
f.get();

nrct=(t[1].pag-1)*R*C + (t[1].rand-1)*C + t[1].cuv;

while(f.getline(sir,600) && i<=n)


{
p=strtok(sir,sep);
while (p)
{
nrc++;

if(nrc==nrct)
{
if (i<n)
g<<p<<" ";
else
g<<p;

i++;
nrct=(t[i].pag-1)*R*C+(t[i].rand-1)*C + t[i].cuv;
}
CAPITOLUL 24. ONI 2014 24.2. NOD 308

p=strtok(NULL,sep);
}
}
}

void afisare()
{
int i;
for(i=1;i<=n;i++)
{
g<<t[i].pag<<" "<<t[i].rand<<" "<<t[i].cuv<<’\n’;
}
}

Listing 24.2.2: nod dl pct1 2.cpp


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <cassert>

#define Pmax 2010


#define Rmax 300
#define Cuvmes 30

using namespace std;

struct tag
{
short int pg,rd,cv;
};

char s[Rmax],Pag[Rmax], Rand[Rmax], Cuv[Rmax],romane[10]="MDCLXVI ";

tag L[Cuvmes];

int p, r, c, P[Cuvmes], C[Cuvmes], R[Cuvmes], np, nc, nr;


int i, j, k, test, ii=1;

int cif(char x)
{
if(x==’M’) return 1000;
if(x==’D’) return 500;
if(x==’C’) return 100;
if(x==’L’) return 50;
if(x==’X’) return 10;
if(x==’V’) return 5;
if(x==’I’) return 1;

return -1; // ... !!!


}

int NrRo(char s[])


{
int n=strlen(s);
int Nr=cif(s[n-1]);

for(int i=0; i<n-1; i++)


if (cif(s[i])>=cif(s[i+1]))
Nr+=cif(s[i]);
else
Nr-=cif(s[i]);

return Nr;
}

void PuneNr(int V[], int &n, char s[1000])


{
char x[1000], *p;
p=strtok(s," ");

while (p)
{
CAPITOLUL 24. ONI 2014 24.2. NOD 309

strcpy(x,p);
V[++n]=NrRo(x);
p=strtok(NULL," ");
}
}

bool cmp(tag a, tag b)


{
if(a.pg < b.pg) return true;
if(a.pg == b.pg && a.rd<b.rd) return true;
if(a.pg == b.pg && a.rd==b.rd && a.cv<b.cv) return true;
return false;
}

int main()
{
freopen("nod.in", "r", stdin);
freopen("nod.out", "w", stdout);

scanf("%d\n", &test);
gets(Pag);

int x=strlen(Pag);

for(int t=0; t<x; t++)


assert(strchr(romane,Pag[t]));

PuneNr(P, np, Pag);


gets(Rand);
x=strlen(Rand);

for(int t=0; t<x; t++)


assert(strchr(romane,Rand[t]));

PuneNr(R,nr,Rand);
gets(Cuv);
x=strlen(Cuv);

for(int t=0; t<x; t++)


assert(strchr(romane,Cuv[t]));

PuneNr(C,nc,Cuv);

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


{
L[i].pg=P[i];
L[i].rd=R[i];
L[i].cv=C[i];

assert(np>=1&&np<=20);

if(test==1)
{
for(i=1; i<=np;i++)
{
printf("%d", L[i].pg);
if(i<np)
printf(" ");
else
printf("\n");
}

for(i=1; i<=np;i++)
{
printf("%d", L[i].rd);
if(i<np)
printf(" ");
else
printf("\n");
}

for(i=1; i<=np;i++)
{
printf("%d", L[i].cv);
CAPITOLUL 24. ONI 2014 24.2. NOD 310

if(i<np)
printf(" ");
else
printf("\n");
}
}
else
{
scanf("%d %d %d\n", &p, &r, &c);
assert(p>=1&&p<=2000&&r>=1&&r<=25&&c>=1&&c<=15);

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


{
assert(L[i].pg<=p && L[i].pg>=1);
assert(test==1 || L[i].rd<=r && L[i].rd>=1);
assert(test==1 || L[i].cv<=c && L[i].cv>=1);
}

sort(L + 1, L + np + 1, cmp);

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


for(j=1; j<=r; j ++)
{
for(k=1; k<=c; k++)
{
scanf("%s", &s);
x=strlen(s);
assert(x<=12);

for(int t=0; t<x; t++)


assert(isalpha(s[t])||s[t]==’ ’);

if(L[ii].pg==i && L[ii].rd==j && L[ii].cv==k)


{
if(ii<np)
printf("%s ", s);
else
printf("%s\n", s);
ii++;
}
}

scanf("\n");
}
}

return 0;
}

Listing 24.2.3: nod dt ok.cpp


#include <fstream>
#include<string.h>

using namespace std;

ifstream f("nod.in");
ofstream g("nod.out");

int p[256],r[30],c[25],poz[100103];
char *q1,*q2,*q, sir[]="MDCLXVI",b[256],w[256],s[256],x[2];
int nr_pag=1,nr_r=1,nr_c=1,v[8]={1000, 500, 100, 50, 10, 5,1};

void citire_pagini()
{
int i,j,k;

f.getline(b,255);
q=strtok(b," ");

while(q)
{
strcpy(w,q);
q1=strchr(sir,w[0]);
i=q1-sir;
CAPITOLUL 24. ONI 2014 24.2. NOD 311

if(strlen(w)==1)
p[nr_pag]=v[i];
else
{
for(k=1;k<strlen(w);k++)
{
q2=strchr(sir,w[k]);
j=q2-sir;

if(j>=i)
p[nr_pag]=p[nr_pag]+v[i];
else
p[nr_pag]=p[nr_pag]-v[i];

q1=q2;i=j;
}

q1=strchr(sir,w[strlen(w)-1]);
i=q1-sir;
p[nr_pag]=p[nr_pag]+v[i];
}

nr_pag++;
q=strtok(NULL," ");
}

nr_pag--;
if(x[0]==’1’)
{
for(i=1;i<=nr_pag;i++)
g<<p[i]<<’ ’;
g<<’\n’;
}
}

void citire_randuri()
{
int i,j,k;

f.getline(b,255);
q=strtok(b," ");

while(q)
{
strcpy(w,q);
q1=strchr(sir,w[0]);
i=q1-sir;

if(strlen(w)==1)
r[nr_r]=v[i];
else
{
for(k=1;k<strlen(w);k++)
{
q2=strchr(sir,w[k]);
j=q2-sir;

if(j>=i)
r[nr_r]=r[nr_r]+v[i];
else
r[nr_r]=r[nr_r]-v[i];

q1=q2;i=j;
}

q1=strchr(sir,w[strlen(w)-1]);
i=q1-sir;
r[nr_r]=r[nr_r]+v[i];
}

nr_r++;
q=strtok(NULL," ");
}

nr_r--;
if(x[0]==’1’)
CAPITOLUL 24. ONI 2014 24.2. NOD 312

{
for(i=1;i<=nr_r;i++)
g<<r[i]<<’ ’;
g<<’\n’;
}
}

void citire_cuv()
{
int i,j,k;
f.getline(b,255);
q=strtok(b," ");

while(q)
{
strcpy(w,q);
q1=strchr(sir,w[0]);
i=q1-sir;

if(strlen(w)==1)
c[nr_c]=v[i];
else
{
for(k=1;k<strlen(w);k++)
{
q2=strchr(sir,w[k]);
j=q2-sir;

if(j>=i)
c[nr_c]=c[nr_c]+v[i];
else
c[nr_c]=c[nr_c]-v[i];

q1=q2;i=j;
}

q1=strchr(sir,w[strlen(w)-1]);
i=q1-sir;
c[nr_c] =c[nr_c]+v[i];
}

nr_c++;
q=strtok(NULL," ");
}

nr_c--;
if(x[0]==’1’)
{
for(i=1;i<=nr_c;i++)
g<<c[i]<<’ ’;
g<<’\n’;
}
}

int main()
{
int i,P,R,C,nr=0,j,aux;

f.getline(x,2);
citire_pagini();
citire_randuri();
citire_cuv();

if(x[0]==’2’)
{
f>>P>>R>>C;

for(i=1;i<=nr_pag;i++)
poz[i]=(p[i]-1)*R*C+(r[i]-1)*C+c[i];

for(i=1;i<=nr_pag-1;i++)
for(j=i+1;j<=nr_pag;j++)
if(poz[i]>poz[j])
{
aux=poz[i];
poz[i]=poz[j];
CAPITOLUL 24. ONI 2014 24.3. PLACA 313

poz[j]=aux;
}

j=1;
nr=1;
for(i=1;i<=R*P;i++)
{
f.getline(b,255);
q=strtok(b," ");
while(q)
{
strcpy(w,q);
if(nr==poz[j])
{
g<<w<<’ ’;j++;
}
nr++;
q=strtok(NULL," ");
}
}
}

f.close();
g.close();
return 0;
}

24.2.3 *Rezolvare detaliată

24.3 placa
Problema 3 - placa 100 de puncte
Un gard este format din mai multe plăci dreptunghiulare. Fiecare placă este, la rândul ei,
construită din N  M cărămizi. Una dintre plăci ridică o problemă, deoarece este deteriorată.
Placa este reprezentată pe hârtie cu ajutorul unei matrice cu N linii şi M coloane, numerotate
de la 1 la N , respectiv de la 1 la M . Matricea conţine doar valori 0 şi 1, şi respectă următoarele
reguli:
- un element egal cu 1 indică prezenţa ı̂n aceea poziţie a unei cărămizi, iar un element egal cu
0 indică absenţa ei;
- linia 1 şi linia N conţin numai valori egale cu 1, pentru că marginea de sus şi cea de jos a
plăcii este intactă;
- din orice element egal cu 1, situat ı̂n interiorul matricei, se poate ajunge pe linia 1 sau pe
linia N sau pe amândouă, mergând doar ı̂n sus sau doar ı̂n jos, parcurgând numai valorile egale
cu 1;
- există cel puţin o coloană stabilă (formată numai din elemente egale cu 1).
Se doreşte modificarea plăcii şi pentru aceasta se pot şterge din matrice maximum K coloane
alăturate. După ştergere se alipesc coloanele rămase şi se deplasează pe verticală partea de sus a
plăcii spre cea de jos, până când se va forma o coloană stabilă.

Cerinţe

Să se determine ı̂nălţimea minimă Hmin pe care o poate avea placa ştergând cel mult K
coloane alăturate. Identificaţi numărul minim de coloane alăturate care trebuie şterse pentru a
obţine ı̂nălţimea Hmin.

Date de intrare

Din fişierul placa.in se citesc de pe prima linie 3 numere naturale N , M , K separate prin câte
un spaţiu, având semnificaţia din enunţ.
Pe fiecare dintre următoarele M linii ale fişierului se găsesc perechi de numere naturale N 1,
N 2, separate printr-un spaţiu. Astfel pe linia i  1 a fişierului de intrare numărul N 1 reprezintă
numărul de elemente de 1 situate pe coloana i, ı̂ncepând cu linia 1, deplasându-ne ı̂n ”jos” până la
ı̂ntâlnirea unei valori egale cu 0, sau până se ajunge pe linia N ; numărul N 2 reprezintă numărul de
CAPITOLUL 24. ONI 2014 24.3. PLACA 314

elemente de 1 situate pe coloana i, ı̂ncepând cu linia N , deplasândune ı̂n ”sus” până la ı̂ntâlnirea
unei valori egale cu 0, sau până se ajunge pe linia 1.

Date de ieşire

În fişierul placa.out se va scrie pe prima linie ı̂nălţimea minimă cerută Hmin, iar pe a doua
linie numărul minim de coloane ce trebuie eliminate pentru a obţine ı̂nălţimea Hmin.

Restricţii şi precizări

a 1 & N & 100000; 1 & M & 100000; 1 & K $ M ;


a se garantează că pe liniile ce conţin informaţii referitoare la cele M coloane ale matricei
există cel puţin o linie pe care se află valoarea N de 2 ori, ı̂n rest suma celor două valori este strict
mai mică decât N ;
a toate valorile din fişier sunt strict pozitive;
a se acordă 30% din punctajul pe test dacă doar prima valoare e corectă şi 70% din punctajul
pe test dacă doar a doua valoare e corectă.

Exemple
placa.in placa.out Explicaţii
563 3 Matricea iniţială:
11 2 111111
21 010100
12 000110
55 001110
13 111111
11 ı̂nălţimea minimă este 3 şi se poate obţine eliminând, de exemplu,
coloanele 3, 4, 5 rezultând matricea:
111
010
111
O altă modalitate de a obţine aceeaşi ı̂nălţime dar prin ştergerea
unui număr minim de coloane (4 şi 5) conduce la:
1111
0110
1111

Timp maxim de executare/test: 0.5 secunde


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

24.3.1 Indicaţii de rezolvare

prof. Marius Nicoli, C.N. ”Fraţii Buzeşti”, Craiova

Se memoreaza numărul total de 1 de pe fiecare coloană ı̂ntr-un verctor V . Pentru fiecare


poziţie i calculăm maxS i ca fiind cea mai mare valoare din V aflată ı̂ntre poziţiile 1 şi i. De
asemenea, calculăm maxDi = cea mai mare valoare din V care se găseşte ı̂ntre poziţiile i şi N .
Cei 2 vectori se calculează printr-un algoritm de determinare a maximului dintr-un vector.
Pentru o secvenţă K fixată ı̂ncepând cu poziţia i, ı̂nălţimea plăcii rămase după eliminarea
secvenţei este cea mai mare dintre valorile maxS i  1 şi maxDi  K . Stabilind ı̂nceputul
secvenţei ı̂n fiecare poziţie posibilă determinăm valoarea Hmin cerută la prima cerinţă.
Pentru a calcula numărul minim de coloane pentru care obţinem această valoare ı̂n timp liniar
putem proceda astfel: determinăm cea mai din dreapta poziţie pentru care maxS i ¡= Hmin şi
cea mai din stânga poziţie pentru care maxDi ¡= Hmin; acestea sunt prima şi ultima coloană
a zonei care poate fi eliminată pentru a obţine o soluţie optimă pentru a doua cerinţă.
Altă posibilitate pentru a rezolva a doua cerinţă este să căutăm binar valoarea cerută iar ı̂n
timpul verificării se procedează ca la determinarea primei cerinţe.
CAPITOLUL 24. ONI 2014 24.3. PLACA 315

24.3.2 Cod sursă

Listing 24.3.1: placa.cpp


#include <fstream>
#define DIM 1000002

using namespace std;

int v[DIM], a[DIM], b[DIM], c[DIM], d[DIM];


int n, m, k, x, y, i, j, sol, solc, p, u, mid;

int main()
{
ifstream fin("placa.in");
ofstream fout("placa.out");

fin>>n>>m>>k;
for (i=1;i<=m;i++)
{
fin>>x>>y;
if (x!=n)
v[i] = x+y;
else
v[i] = n;
}

a[1] = v[1];
for (i=2;i<=m;i++)
if (a[i-1] > v[i])
a[i] = a[i-1];
else
a[i] = v[i];

b[m] = v[m];
for (i=m-1;i>=1;i--)
if (v[i] > b[i+1])
b[i] = v[i];
else
b[i] = b[i+1];

sol = m+2;
for (i=1,j=k;j<=m;i++,j++)
{
if (a[i-1] > b[j+1])
x = a[i-1];
else
x = b[j+1];

if (x < sol)
sol = x;
}

fout<<sol<<"\n";

for (i=1;i<=m;i++)
{
if (a[i] > sol)
c[i] = 1;
else
c[i] = c[i-1];
}

for (i=m;i>=1;i--)
{
if (b[i] > sol)
d[i] = 1;
else
d[i] = d[i+1];

c[i] = c[i] && d[i];


if (c[i] == 1)
p++;
}
CAPITOLUL 24. ONI 2014 24.3. PLACA 316

fout<<p<<"\n";

return 0;
}

Listing 24.3.2: placa1.cpp


#include <fstream>
#define DIM 1000002

using namespace std;

int v[DIM], a[DIM], b[DIM];


int n, m, k, x, y, i, j, sol, solc, p, u, mid;

int main()
{
ifstream fin("placa.in");
ofstream fout("placa.out");

fin>>n>>m>>k;
for (i=1;i<=m;i++)
{
fin>>x>>y;
if (x!=n)
v[i] = x+y;
else
v[i] = n;
}

a[1] = v[1];
for (i=2;i<=m;i++)
if (a[i-1] > v[i])
a[i] = a[i-1];
else
a[i] = v[i];

b[m] = v[m];
for (i=m-1;i>=1;i--)
if (v[i] > b[i+1])
b[i] = v[i];
else
b[i] = b[i+1];

sol = m+2;
for (i=1,j=k;j<=m;i++,j++)
{
if (a[i-1] > b[j+1])
x = a[i-1];
else
x = b[j+1];

if (x < sol)
sol = x;
}

fout<<sol<<"\n";
if (sol == n)
{
fout<<"0\n";
return 0;
}

p = 1;
u = k;
while (p<=u)
{
mid = (p+u)/2;

solc = m+2;
for (i=1,j=mid;j<=m;i++,j++)
{
if (a[i-1] > b[j+1])
x = a[i-1];
CAPITOLUL 24. ONI 2014 24.3. PLACA 317

else
x = b[j+1];

if (x < solc)
solc = x;
}

if (solc == sol)
u = mid-1;
else
p = mid+1;
}

fout<<p<<"\n";

return 0;
}

Listing 24.3.3: placa2.cpp


#include <fstream>

using namespace std;

ifstream f("placa.in");
ofstream g("placa.out");

int s[300003],d[300003],m,n;

int calcul(int k)
{
int i,x=n+1,max;

for(i=1;i+k<=m;i++)
{
if(s[i]>d[i+k])
max=s[i];
else
max=d[i+k];
if(x>max)
x=max;
}

return x;
}

int minim(int k, int p)


{
int min1=p;

for(int i=1;i+k<=m;i++)
if(s[i]<=d[i+k] &&min1<=s[i])
min1=s[i];
else
if(s[i]>=d[i+k]&&min1<=d[i+k])
min1=d[i+k];

return min1;
}

int main()
{
int i,j,k,x,min,y,v[300003],st,dr,mij;

f>>n>>m>>k;

for(i=1;i<=m;i++)
{
f>>x>>y;

if(x+y>n)
v[i]=x;
else
v[i]=x+y;
}
CAPITOLUL 24. ONI 2014 24.3. PLACA 318

s[1]=v[1];
for(j=2;j<=m;j++)
if(v[j]<s[j-1])
s[j]=s[j-1];
else
s[j]=v[j];

d[m]=v[m];
for(j=m-1;j>=1;j--)
if(v[j]<d[j+1])
d[j]=d[j+1];
else
d[j]=v[j];

min=minim(k,s[1]);
g<<min<<’\n’;

//a doua cerinta

st=1;dr=k;
mij=(st+dr)/2;
while(st<dr)
{
if(calcul(mij)>min)
st=mij+1;
else
dr=mij;

mij=(st+dr)/2;
}

g<<st-1<<’\n’;
f.close();
g.close();
return 0;
}

Listing 24.3.4: placa3.cpp


#include <iostream>
#include <fstream>

#define MAXN 100003


#define MAXM 500003
#define INF 100000000

using namespace std;

int n,m,k;
int hmin,kmin;
int nr1[MAXM]; //numarul de 1 de pe fiecare coloana
int x,y;
int maxS[MAXM];
int maxD[MAXM];

void solve_good()
{
maxS[0] = 0;
for(int i=1; i<=m; i++)
maxS[i] = max(maxS[i-1],nr1[i]);

maxD[m+1] = 0;
for(int i=m; i>=1; i--)
maxD[i] = max(maxD[i+1],nr1[i]);

for(int i=1; i<=m-k; i++)


hmin = min(hmin,max(maxS[i-1],maxD[i+k]));

int lb=0,ub=k; //lower_bound, upper_bound pentru BinarySearch


int kt;
int hcmin;
while (lb<=ub)
{
hcmin = INF;
CAPITOLUL 24. ONI 2014 24.3. PLACA 319

kt = (lb+ub)/2;

for(int i=1; i<=m-kt; i++)


hcmin = min(hcmin,max(maxS[i-1],maxD[i+kt]));

if (hcmin==hmin)
kmin = min(kmin,kt);

if (hcmin>hmin)
lb = kt+1;
else
if (hcmin==hmin)
ub = kt-1;
}
}

int main()
{
ifstream fin("placa.in");
ofstream fout("placa.out");

fin>>n>>m>>k;

for(int j=1; j<=m; j++)


{
fin>>x>>y;
nr1[j]=x+y;
}

fin.close();

hmin = INF;
kmin = INF;

solve_good();

fout<<hmin<<"\n"<<kmin<<"\n";

fout.close();
return 0;
}

Listing 24.3.5: placa4.cpp


#include <fstream>
#define DIM 1000010

using namespace std;

int a[DIM], b[DIM], v[DIM];

int n, m, k, i, j, x, y, sol, solc, aux;

ifstream fin("placa.in");
ofstream fout("placa.out");

int main()
{
fin>>n>>m>>k;

for (i=1;i<=m;i++)
{
fin>>x>>y;
if (x == n)
v[i] = n;
else
v[i] = x+y;
}

a[1] = v[1];
for (i=2;i<=m;i++)
if (a[i-1] > v[i])
a[i] = a[i-1];
else
a[i] = v[i];
CAPITOLUL 24. ONI 2014 24.3. PLACA 320

b[m] = v[m];
for (i=m-1;i>=1;i--)
if (v[i] > b[i+1])
b[i] = v[i];
else
b[i] = b[i+1];

sol = m+2;

for (i=1, j=k; j<=m; i++, j++)


{
if (a[i-1] > b[j+1])
x = a[i-1];
else
x = b[j+1];

if (x < sol)
sol = x;
}

aux = k;
for (k=1;k<=aux;k++)
{
solc = m+2;

for (i=1, j=k; j<=m; i++,j++)


{
if (a[i-1] > b[j+1])
x = a[i-1];
else
x = b[j+1];

if (x < solc)
solc = x;
}

if (solc == sol)
break;
}

fout<<sol<<"\n"<<k<<"\n";

return 0;
}

Listing 24.3.6: placa5.cpp


#include <fstream>
#define DIM 1000002

using namespace std;

ifstream fin("placa.in");
ofstream fout("placa.out");

int a[DIM];
int x, y, n, m, k, i, st, dr, maximst, maximdr, sol1;

int main()
{
fin>>n>>m>>k;

for(i=1;i<=m;i++)
{
fin>>x>>y;
if (x == n)
{
a[i] = x;

if (st == 0)
st = i;

dr = i;
}
CAPITOLUL 24. ONI 2014 24.3. PLACA 321

else
a[i] = x + y;
}

maximst = 0;
for (i=1;i<st;i++)
if (a[i] > maximst)
maximst = a[i];

maximdr = 0;
for (i=m;i>dr;i--)
if (a[i] > maximdr)
maximdr = a[i];

if (maximst > maximdr)


sol1 = maximst;
else
sol1 = maximdr;

fout<<sol1<<"\n"<<dr-st+1;

return 0;
}

Listing 24.3.7: placa6.cpp


#include <fstream>
#define DIM 1000002

using namespace std;

int v[DIM], a[DIM], b[DIM];


int n, m, k, x, y, i, j, sol, solc, p, u, mid;

int main()
{
ifstream fin("placa.in");
ofstream fout("placa.out");

fin>>n>>m>>k;
for (i=1;i<=m;i++)
{
fin>>x>>y;
if (x!=n)
v[i] = x+y;
else
v[i] = n;
}

a[1] = v[1];
for (i=2;i<=m;i++)
if (a[i-1] > v[i])
a[i] = a[i-1];
else
a[i] = v[i];

b[m] = v[m];
for (i=m-1;i>=1;i--)
if (v[i] > b[i+1])
b[i] = v[i];
else
b[i] = b[i+1];

sol = m+2;
for (i=1,j=k;j<=m;i++,j++)
{
if (a[i-1] > b[j+1])
x = a[i-1];
else
x = b[j+1];

if (x < sol)
sol = x;
}
CAPITOLUL 24. ONI 2014 24.3. PLACA 322

fout<<sol<<"\n";
fout<<k<<"\n";

return 0;
}

Listing 24.3.8: placa8.cpp


#include <iostream>
#include <fstream>

using namespace std;

ifstream f("placa.in");
ofstream g("placa.out");

int c[300000],m,n,k;

void maxcol(int &max,int &colmax1,int &colmax2)


{
int i;max=0;
for(i=1;i<=m;i++)
if(c[i]>max)
{
max=c[i];
colmax1=i;
colmax2=i;
}
else
if(c[i]==max)
colmax2=i;
}

void sterge(int k1,int k2)


{
int i;
for(i=k1;i<=k2;i++)
c[i]=0;
}

void afi(int m)
{
int i;

for(i=1;i<=m;i++)
cout<<c[i]<<" ";

cout<<endl;
}

int main()
{
int hmin,max,colmax1=3000000,colmax2=0,i,x,y,cm1,cm2;

f>>n>>m>>k;

hmin=n;
for(i=1;i<=m;i++)
{
f>>x>>y;
if(x==n)
c[i]=n;
else
c[i]=x+y;
}

// afi(m);
maxcol(max,colmax1,colmax2);
while(colmax2-colmax1+1<=k)
{
sterge(colmax1,colmax2);
hmin=colmax2-colmax1+1;

//afi(m);
maxcol(max,cm1,cm2);
CAPITOLUL 24. ONI 2014 24.3. PLACA 323

if(colmax1>cm1) colmax1=cm1;
if(colmax2<cm2) colmax2=cm2;
}

g<<max<<endl;
g<<hmin<<endl;

return 0;
}

Listing 24.3.9: placa10.cpp


#include <fstream>
#define DIM 1000010

using namespace std;

int a[DIM], b[DIM], v[DIM];

int n, m, k, i, j, x, y, sol, solc, aux;

ifstream fin("placa.in");
ofstream fout("placa.out");

int main()
{
fin>>n>>m>>k;

for (i=1;i<=m;i++)
{
fin>>x>>y;
if (x == n)
v[i] = n;
else
v[i] = x+y;
}

a[1] = v[1];
for (i=2;i<=m;i++)
if (a[i-1] > v[i])
a[i] = a[i-1];
else
a[i] = v[i];

b[m] = v[m];
for (i=m-1;i>=1;i--)
if (v[i] > b[i+1])
b[i] = v[i];
else
b[i] = b[i+1];

sol = m+2;

for (i=1, j=k; j<=m; i++, j++)


{
if (a[i-1] > b[j+1])
x = a[i-1];
else
x = b[j+1];

if (x < sol)
sol = x;
}

aux = k;
for (k=aux;k>=1;k--)
{
solc = m+2;

for (i=1, j=k; j<=m; i++,j++)


{
if (a[i-1] > b[j+1])
x = a[i-1];
else
x = b[j+1];
CAPITOLUL 24. ONI 2014 24.3. PLACA 324

if (x < solc)
solc = x;
}

if (solc != sol)
break;
}

fout<<sol<<"\n"<<k+1<<"\n";

return 0;
}

24.3.3 *Rezolvare detaliată


Capitolul 25

ONI 2013

25.1 secvp
Problema 1 - secvp 100 de puncte
Se consideră un şir cu N numere naturale a1 , a2 , ..., aN . Asupra unui element ai , din şir, se
pot efectua operaţii de incrementare (adunare cu 1: ai ai  1) sau decrementare (scădere cu 1:
ai ai  1). Fiecare element din şir poate fi incrementat sau decrementat de oricâte ori.

Cerinţe

Dat fiind şirul celor N numere naturale, să se determine:


a. numărul total minim de operaţii necesare pentru a transforma toate numerele din şir ı̂n
numere prime;
b. numărul minim de operaţii (incrementări şi decrementări) ce trebuie să fie efectuate asupra
elementelor şirului astfel ı̂ncât să existe o secvenţă de lungime K formată numai din numere prime.

Date de intrare

Fişierul de intrare secvp.in conţine pe prima linie numerele naturale N şi K, iar pe următoarea
linie N numere naturale. Numerele scrise pe aceeaşi linie sunt separate prin spaţii.

Date de ieşire

Fişierul de ieşire secvp.out conţine pe prima linie un număr natural T , reprezentând numărul
total minim de operaţii necesare pentru a transforma toate numerele din şir ı̂n numere prime.
Pe a doua linie vor fi scrise două numere naturale separate prin spaţiu minK nrsK, unde minK
reprezintă numărul minim de operaţii ce trebuie să fie efectuate asupra elementelor şirului astfel
ı̂ncât să existe o secvenţă de lungime K formată numai din numere prime, iar nrsK reprezintă
numărul de secvenţe de lungime K care se pot obţine cu acelaşi număr minK de operaţii de
incrementare/decrementare.

Restricţii şi precizări

a 2 & K & N & 100 000


a 0 & ai & 1 000 000, pentru 1 & i & N
a O secvenţă din şir este formată din elemente aflate pe poziţii consecutive ı̂n şirul dat.
a 1 nu este număr prim.
a Pentru determinarea corectă a valorii T se acordă 30% din punctajul pe test. Pentru deter-
minarea corectă a valorilor T şi minK se acordă 70% din punctajul pe test. Punctajul integral se
acordă pentru determinarea corectă a tuturor celor 3 valori.

Exemple

325
CAPITOLUL 25. ONI 2013 25.1. SECVP 326

secvp.in secvp.out Explicaţii


73 9 Pentru a transforma 15 ı̂n număr prim sunt necesare 2 incrementări
15 3 8 26 22 10 14 32 Pentru a transforma 3 ı̂n număr prim sunt necesare 0 operaţii
Pentru a transforma 8 ı̂n număr prim e necesară 1 decrementare
Pentru a transforma 26 ı̂n număr prim sunt necesare 3 decre-
mentări
Pentru a transforma 22 ı̂n număr prim e necesară 1 incrementare
Pentru a transforma 10 ı̂n număr prim e necesară 1 incrementare
Pentru a transforma 14 ı̂n număr prim e necesară 1 decrementare
Numărul total de operaţii necesare este 9.
Numărul minim de operaţii necesare pentru a obţine o secvenţă de
lungime K este 3.
Cele două secvenţe de lungime K ce necesită 3 operaţii sunt a1 ,
a2 , a3 şi a5 , a6 , a7 .

Timp maxim de executare/test: 0.5 secunde


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

25.1.1 Indicaţii de rezolvare

https://infogenius.ro/problema-secvp-oni-2013-clasa-7/
Pentru prima cerinţă vom parcurge şirul, iar pentru fiecare număr, calculăm modulul diferenţei
dintre el şi cel mai apropiat număr prim de acesta. Pentru asta vom folosi o funcţie minInc ı̂n
care iterăm pe i de la 0 ı̂n sus până când x  i sau x  i este prim. Reţinem valorile găsite ı̂ntr-un
vector inc de dimensiune N M AX (pentru a doua cerinţă). Pe parcurs, pentru a afla răspunsul
primei cerinţe, calculăm suma acestor rezultate.
La a doua cerinţă practic trebuie să determinăm suma minimă pe care o poate avea o secvenţă
de lungime k şi câte astfel de secvenţe există ı̂n vectorul nostru.
Vom folosi Ciurul lui Eratostene pentru a testa mai apoi ı̂n O 1 primalitatea numerelor.

25.1.2 Cod sursă

https://infogenius.ro/problema-secvp-oni-2013-clasa-7/

Listing 25.1.1: secvp.cpp


#include <fstream>

#define NMAX 100005


#define NRMAX 1000050

std::ifstream fin("secvp.in");
std::ofstream fout("secvp.out");

int n, k;
bool sieve[NRMAX]; // ciurul
short int inc[NMAX];

/** Functia care determina cu cat trebuie incrementat


sau decrementat x-ul pentru a deveni prim */

int minInc(int x)
{
// Tratam separat cazul x == 0 deoarece
// nu putem accesa ciur[-1] \c si ciur[-2]:
if (!x)
return 2;

for (int i = 0; ; i++)


if (!sieve[x - i] || !sieve[x + i])
return i;
}

/// Ciurul lui Eratostene


void erat()
CAPITOLUL 25. ONI 2013 25.1. SECVP 327

{
int i, j;
sieve[0] = sieve[1] = true;

for (i = 2; i * i <= NRMAX; i++)


if (!sieve[i])
for (j = i * i; j <= NRMAX; j += i)
sieve[j] = true;
}

/// Functia care citeste datele si rezolva prima cerinta


void read()
{
int i, x, MinInc, sum = 0;
erat();

fin >> n >> k;


for (i = 1; i <= n; i++)
{
fin >> x;
MinInc = minInc(x);
inc[i] = MinInc;
sum += MinInc;
}

fout << sum << ’\n’;


}

/// Functia care rezolva a doua cerinta


void solve()
{
int i, u, apMin, sumMin, sum = 0;

// Calculam suma primelor k elemente:


for (i = 1; i <= k; i++)
sum += inc[i];

sumMin = sum;
apMin = 1;

// u reprezinta indicele ultimului element


// al secventei curente, iar sum suma
// elementelor acesteia.

for (u = k + 1; u <= n; u++)


{
// Actualizam sum in O(1):
sum += inc[u] - inc[u - k];

if (sum < sumMin)


{
sumMin = sum;
apMin = 1;
}
else
if (sum == sumMin)
apMin++;
}

fout << sumMin << ’ ’ << apMin << ’\n’;


fout.close();
}

int main()
{
read();
solve();
return 0;
}

25.1.3 *Rezolvare detaliată


CAPITOLUL 25. ONI 2013 25.2. PATRATE2 328

25.2 patrate2
Problema 2 - patrate2 100 de puncte
Bia şi Ştefan joacă un nou joc. Bia i-a cerut lui Ştefan să se gândească la un număr natural
n
nenul mai mic strict decât V M AX 2 . Apoi, ea a luat un pix şi o foaie şi a desenat n matrice
pătratice (denumite pe scurt pătrate), pe care le-a numerotat de la 1 la n. Apoi a completat
pătratele ı̂n modul următor: ı̂n pătratul cu numărul i au fost scrise ı̂n ordine crescătoare toate
numerele naturale nenule mai mici decât V M AX, care, scrise ı̂n baza 2, au cifra corespunzătoare
i1
lui 2 , egală cu 1; plasarea numerelor s-a făcut parcurgând coloanele de la stânga la dreapta şi
completând fiecare coloană de sus ı̂n jos.
Fiecare pătrat are latura minimă necesară pentru ca numerele pe care trebuie să le conţină să
ı̂ncapă.
Este posibil ca pătratele să nu se umple integral (ı̂n acest caz pătratul se va completa cu
valoarea 0).
După ce a desenat pătratele, Bia ı̂l ı̂ntreabă pe Ştefan ı̂n care dintre pătrate se află numărul
la care s-a gândit el. Apoi face o magie şi ghiceşte numărul la care s-a gândit Ştefan.
În plus, ca să-l impresioneze şi mai tare pe Ştefan, Bia i-a spus linia şi coloana pe care este
plasat acest număr ı̂n primul pătrat spus de Ştefan.

Cerinţe

Fiindcă voi nu credeţi ı̂n magie, scrieţi un program care să determine numărul la care s-a
gândit Ştefan, precum şi poziţia acestui număr ı̂n primul pătrat spus de Ştefan.

Date de intrare

Fişierul de intrare patrate2.in conţine pe prima linie numerele naturale n şi m, separate prin
spaţiu, reprezentând numărul de pătrate pe care le va desena Bia, respectiv ı̂n câte dintre pătratele
desenate se află numărul la care s-a gândit Ştefan. Pe al doilea rând se află, separate prin câte
un spaţiu, m numere naturale cuprinse ı̂ntre 1 şi n, reprezentând cele m pătrate ı̂n care se află
numărul la care s-a gândit Ştefan.

Date de ieşire

Fişierul de ieşire patrate2.out va conţine pe prima linie numărul la care s-a gândit Ştefan.
Pe a doua linie vor fi scrise două numere naturale separate prin spaţiu L C, reprezentând, linia,
respectiv coloana pe care se află numărul respectiv ı̂n primul pătrat menţionat de Ştefan.

Restricţii şi precizări

a 1 $ m & n & 28
a ı̂n fiecare matrice pătratică liniile sunt numerotate de sus ı̂n jos ı̂ncepând cu 1, iar coloanele
de la stânga la dreapta ı̂ncepând cu 1.
a Pentru teste valorând 50% din punctaj, n este & 14.
a Pentru determinarea corectă a numărului la care s-a gândit Ştefan se acordă 40% din punc-
tajul pe test. Punctajul integral se obţine pentru rezolvarea ambelor cerinţe.

Exemple
patrate2.in patrate2.out Explicaţii
43 13
143 13

Timp maxim de executare/test: 0.1 secunde


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

25.2.1 *Indicaţii de rezolvare

25.2.2 *Cod sursă


CAPITOLUL 25. ONI 2013 25.3. CURSA 329

25.2.3 *Rezolvare detaliată

25.3 cursa
Problema 3 - cursa 100 de puncte
O cursă de maşini electrice prevăzute cu panouri solare are loc pe un traseu care traversează
n localităţi, numerotate ı̂n ordinea de pe traseu de la 1 la n. Linia de start se află la kilometrul
zero şi coincide cu ı̂nceputul primei localităţi. Linia de sosire este la sfârşitul ultimei localităţi.
Orice localitate, exceptând localitatea 1, ı̂ncepe la sfârşitul localităţii precedente. Prin urmare,
pentru fiecare localitate i se cunoaşte distanţa di de la linia de start până la sfârşitul localităţii,
exprimată ı̂n km.

Figura 25.1: cursa

În momentul ı̂nceperii competiţiei, din fiecare localitate există exact o maşină aliniată la linia
de start. Maşinile au aceleaşi caracteristici, ca urmare se deplasează cu aceeaşi viteză, cu excepţia
traversării localităţii din care provin unde, datorită avantajelor terenului propriu (suporteri dotaţi
cu oglinzi, lămpi, etc...), ı̂şi dublează instantaneu viteza până la ieşirea din localitate, apoi revin
la viteza iniţială.
La concurs sunt invitate şi televiziunile locale, iar pentru telespectatori, sarea şi piperul sunt
depăşirile, de aceea este important să reţinem informaţii despre acestea, pentru a le putea viziona
ı̂n reluare. Se consideră depăşire situaţia ı̂n care o maşină ajunge din urmă o altă maşină, apoi
trece ı̂n faţa acesteia.

Cerinţe

Cunoscând localităţile de pe traseu, scrieţi un program care tipăreşte ordinea sosirii maşinilor
la linia de sosire, respectiv informaţii despre toate depăşirile efectuate ı̂n timpul concursului.

Date de intrare

Fişierul de intrare cursa.in va conţine pe prima linie numărul de localităţi n. Urmează n linii
care descriu informaţii despre cele n localităţi. Pe linia i  1 din fişier se află două numere naturale
c şi d, separate prin spaţiu, cu semnificaţia că numărul de concurs al maşinii din localitatea i este
c şi că localitatea i se termină la d kilometri faţă de linia de start.

Date de ieşire

Fişierul de ieşire cursa.out va conţine pe prima linie numerele de concurs ale maşinilor ı̂n
ordinea sosirii lor, separate prin câte un spaţiu. ı̂n cazul ı̂n care există mai multe maşini care
sosesc simultan la linia de sosire, acestea vor fi afişate ı̂n ordinea crescătoare a numerelor de
concurs.
Pe următoarele linii sunt descrise depăşirile, ı̂n ordinea crescătoare a localităţilor ı̂n care se
produc. O depăşire este descrisă printr-o succesiune de valori de forma L c k m1 m2 ... mk ,
cu semnificaţia că ı̂n localitatea L maşina cu numărul de concurs c depăşeşte k maşini, maşinile
depăşite fiind, ı̂n ordinea ı̂n care sunt depăşite, m1 m2 ... mk . Dac sunt depşite ı̂n acelaşi moment
două sau mai multe maşini, acestea se vor afişa ı̂n ordinea descresctoare a numerelor de concurs.

Restricţii şi precizări


CAPITOLUL 25. ONI 2013 25.3. CURSA 330

a 2 $ n & 500
a Numerele de concurs ale maşinilor sunt numere naturale nenule distincte de maxim 3 cifre.
a Distanţa dintre linia de start şi linia de sosire (sfârşitul ultimei localităţi) & 30 000
a Dacă prima cerinţă este rezolvată corect, se obţine 40% din punctajul pe test. Dacă prima
cerinţă este rezolvată corect, dar la afişarea depăşirilor maşinile dintr-o localitate nu sunt afişate
ı̂n ordinea solicitată, se acordă 70% din punctajul pe test. Punctajul integral se obţine pentru
rezolvarea corectă a ambelor cerinţe.

Exemple
cursa.in cursa.out Explicaţii
5 70 35 99 10 66 Prima localitate ı̂ncepe de la km.0 şi se termină la km.5 şi are
10 5 3 99 2 66 10 maşina nr. 10.
66 7 4 35 2 66 10 A doua localitate se află ı̂ntre km 5-7 şi are maşina nr. 66.
99 15 5 70 4 66 10 99 35 A treia localitatea se află ı̂ntre km 7-15 şi are maşina nr. 99.
35 23 A patra ı̂ntre km 15-23 are maşina nr. 35 şi ultima ı̂ntre km
70 34 23-34 are maşina 70.
Ordinea de sosire a maşinilor este: 70 35 99 10 66.
Maşinile 35 şi 99 termină cursa deodată, se enumeră ı̂n ordinea
crescătoare a numerelor.
Depăşiri: Localitatea 3: maşina 99 va depăşi 2 maşini, ı̂n or-
dine maşinile 66 apoi 10.
Localitatea 4: maşina 35 depăşeşte 2 maşini, pe 66 şi 10 şi
ajunge pe 99 fără să o depăşească.
Localitatea 5: maşina 70 depăşeşte 4 maşini, ı̂n ordine pe 66,
10, apoi simultan pe 99 şi 35 aflate la egalitate (se enumeră ı̂n
ordine descrescătoare).

Timp maxim de executare/test: 0.1 secunde


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

25.3.1 *Indicaţii de rezolvare

25.3.2 *Cod sursă

25.3.3 *Rezolvare detaliată


Capitolul 26

ONI 2012

26.1 bile
Problema 1 - bile 100 de puncte
Matei a inventat un nou joc cu bile. Terenul de joc este o tablă dreptunghiulară aşezată
vertical. Tabla este ı̂mpărţită ı̂n m ˜ n celule, aşezate ı̂n m linii şi n coloane. În unele dintre celule
se află obstacole.
De sus, din celulele aflate pe prima linie, sunt lăsate să cadă bile. Bilele cad vertical până
la ı̂ntâlnirea unui obstacol sau până ı̂n celula cea mai de jos din coloana pe care se află. Prima
bilă care loveşte un obstacol se deplasează pe orizontală ı̂n coloana alăturată din stânga, apoi
ı̂şi continuă căderea. Fiecare dintre celelalte bile care lovesc acelaşi obstacol se deplasează pe
orizontală, ı̂n coloana alăturată, dar ı̂n direcţie opusă faţă de bila care a lovit acest obstacol exact
ı̂naintea lor, apoi ı̂şi continuă căderea.

Cerinţe

Cunoscând numărul de bile lăsate să cadă de pe fiecare celulă a primei linii şi poziţia obsta-
colelor, determinaţi numărul de bile ajunse ı̂n fiecare celulă a ultimei linii. Poziţiile obstacolelor
sunt indicate prin linia şi coloana lor (colţul din stânga sus corespunde liniei 1 şi coloanei 1).

Date de intrare

Fişierul bile.in conţine pe prima linie, separate prin câte un spaţiu, numerele naturale m, n şi
p (numărul de linii, numărul de coloane şi numărul de obstacole). Următoarele p linii conţin câte
două numere, separate de câte un spaţiu, reprezentând poziţiile celor p obstacole. Ultimele n linii
conţin câte un număr natural, reprezentând numărul bilelor lansate din fiecare celulă a primei
linii (ı̂ncepând cu prima celulă de pe linie).

Date de ieşire

Fişierul de ieşire bile.out va conţine n linii cu câte un număr, acesta reprezentând numărul
de bile din fiecare celulă a ultimei linii (ı̂ncepând cu prima celulă de pe această linie).

Restricţii şi precizări

a 2 & m, n & 2000;


a 0 & p & 10000;
a Se lansează maximum 1000 de bile din fiecare celulă;
a Pe prima şi ultima linie, respectiv prima şi ultima coloană, nu există obstacole;
a Nu există două obstacole alăturate pe linie, coloană sau diagonală.

Exemple

331
CAPITOLUL 26. ONI 2012 26.1. BILE 332

bile.in bile.out Explicaţii


675 8
23 0
25 10
42 0
44 9
56 0
4 8
6
4
5
8
3
5

Timp maxim de executare/test: 0.1 secunde


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

26.1.1 Indicaţii de rezolvare

prof. Nistor Moţ

Se poate simula căderea bilelor folosind o matrice m  n ı̂n care punem, de exemplu, 1 ı̂n
celulele cu obstacole şi un număr pozitiv - numărul de bile ajunse ı̂n celula respectivă. Soluţia nu
se va ı̂ncadra ı̂n timp pentru dimensiuni mai mari.
Se poate ı̂ncerca eliminarea liniilor care nu au obstacole, ceea ce nu va aduce un câştig
substanţial de timp.
Soluţia optimă presupune ordonarea poziţiilor obstacolelor crescător după linii (pe aceeaşi linie
nu contează ordinea obstacolelor, ele neinfluenţându-se reciproc) apoi, considerând şirul bilelor de
pe prima linie b1 , b2 , ..., bn , fiecare obstacol de pe poziţia i, j  va modifica 3 termeni din acest
şir: la bj  1 se adaugă bj   1©2, la bj  1 se adaugă bj ©2 iar bj  devine 0.
Astfel complexitatea algoritmului va fi dată de complexitatea algoritmului de sortare, după
care determinarea numărului de bile de pe fiecare coloană se obţine ı̂n O p.

26.1.2 Cod sursă

Listing 26.1.1: bile.cpp


#include <stdio.h>
#include <stdlib.h>

struct coord
{
int x;
int y;
} a[10002];

int fc(const void *a, const void *b)


{
return ((struct coord *)a)->x - ((struct coord *)b)->x;
}

int main()
{
FILE *fi, *fo;

int m,n,i,j,p,b[5002];
coord t;

fi=fopen("bile.in","r");

fscanf(fi,"%d %d %d",&m,&n,&p);
CAPITOLUL 26. ONI 2012 26.2. PROIECTE 333

for(i=1;i<=p;i++)
fscanf(fi,"%d %d",&a[i].x,&a[i].y);
for(i=1;i<=n;i++)
fscanf(fi,"%d",&b[i]);

fclose(fi);

qsort(a+1,p,sizeof a[0],fc);

for(i=1;i<=p;i++)
{
j=a[i].y;
b[j-1]+=b[j]/2+b[j]%2;
b[j+1]+=b[j]/2;
b[j]=0;
}

fo=fopen("bile.out","w");
for(i=1;i<=n;i++)
fprintf(fo,"%d\n",b[i]);

fclose(fo);
return 0;
}

26.1.3 *Rezolvare detaliată

26.2 proiecte
Problema 2 - proiecte 100 de puncte
În oraşul Iaşi, cele N firme IT derulează ı̂n prezent M proiecte din acest domeniu (printre
care şi ONI 2012). Firmele sunt identificate prin numere naturale de la 1 la N , iar proiectele
sunt identificate prin numere naturale de la 1 la M . Fiecare proiect are una sau mai multe etape,
o etapă fiind executată de o singură firmă IT. Spunem că o firmă coordonează un proiect dacă
execută mai mult de jumătate din etapele proiectului.

Cerinţe

Cunoscând numărul firmelor IT, numărul proiectelor, numărul de etape ale fiecărui proiect
şi firmele ce execută fiecare etapă, să se determine firma/firmele care coordonează cel mai mare
număr de proiecte.

Date de intrare

Fişierul de intrare proiecte.in conţine, pe prima linie, numerele naturale N şi M , sepa-
rate printr-un spaţiu, cu semnificaţia de mai sus. Pe fiecare dintre următoarele M linii se află
informaţii despre câte un proiect, ı̂n ordinea numerelor de identificare a acestora. Astfel, pe linia
corespunzătoare proiectului i (1 & i & M ), se află un număr natural nri , urmat de nri numere
naturale f1 f2 ... fnri , reprezentând numărul de etape ale acestui proiect, respectiv firmele care
execută fiecare etapă din proiect (firma fk execută etapa k, 1 & k & nri ). Numerele de pe aceeaşi
linie sunt separate prin câte un spaţiu.

Date de ieşire

Fişierul de ieşire proiecte.out va conţine o singură linie, pe care va fi scris numărul de iden-
tificare al firmei/firmelor care coordonează cel mai mare număr de proiecte. Dacă sunt mai
multe astfel de firme, numerele de identificare ale acestora se vor afişa pe aceeaşi linie, ı̂n ordine
crescătoare, separate prin câte un spaţiu.

Restricţii şi precizări


CAPITOLUL 26. ONI 2012 26.2. PROIECTE 334

a 1 & M & 200;


a 1 & N & 1000000;
a 1 & nri & 200000 1 & i & M ;
a 1 & M ˜ nri & 320000 1 & i & M ;
a 1 & fk & N , 1 & k & nri ;
a există ı̂ntotdeauna cel puţin un proiect coordonat de o firmă.

Exemple
proiecte.in proiecte.out Explicaţii
54 13 Numărul maxim de proiecte coordonate de aceeaşi firmă este 2:
233 firma 1 coordonează proiectele 2 şi 4, iar firma 3 coordonează
3121 proiectele 1 şi 3.
534331
11

Timp maxim de executare/test: 0.2 secunde


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

26.2.1 Indicaţii de rezolvare

Prof. Lucia Miron

Soluţia are complexitate O M ˜ max nri ; 1 & i & M


Putem descompune problema ı̂n două subprobleme şi anume:
a) Pentru ı̂nceput trebuie să determinăm pentru fiecare proiect, dacă există, firma care ı̂l
coordonează,
b) Trebuie să determinăm toate firmele care coordonează numărul maxim de proiecte
Vom citi pe rând, ı̂ntr-un vector datele corespunzătoare unui proiect, pentru a rezolva prima
subproblemă, trebuie să găsim dacă există un element majoritar ı̂n acest vector.
Pentru determinarea elementului majoritar utilizăm următoul algoritm:
Iniţializăm candidatul la majoritate cu primul element, dacă un element din vector este egal cu
candidatul incrementăm un contor, dacă este diferit, decrementăm contorul. Verificăm contorul
la fiecare pas, dacă este 0, schimbăm candidatul la majoritate cu elementul curent.
In final verificam dacă valoarea memorată ı̂n candidatul la majoritate este element majoritar,
numărând efectiv numărul de apariţii.
Pentru a rezolva a doua subpoblemă, pe măsura ce determinăm un elemnt majoritar ı̂l vom
adăuga la vectorul de firme coordonatoare, dacă este primul proiect coordonat de aceasta sau
incrementăm numărul de proiecte coordonat de aceasta, dacă apare deja ı̂n vectorul V (notăm cu
V vectorul de firme coordonatoare şi cu N R vectorul cu numărul de proiecte coordonate).
Ordonăm vectorul V , apoi determinam maximul din vectorul N R şi elementele din V pentru
care avem valoarea maximă ı̂n vectorul N R, datorită sortării elementele vor fi afişate ı̂n ordine
cerută.

26.2.2 Cod sursă

Listing 26.2.1: proiecte1.cpp


#include<fstream>

using namespace std;

FILE * fin;
ofstream fout("proiecte.out");

int v[201], x[200001],Nr[201];


int n, m,cand,k;
int i,nr,j,nrmax,nrap,ok,aux;
CAPITOLUL 26. ONI 2012 26.2. PROIECTE 335

int main()
{
fin=fopen("proiecte.in","r");
fscanf(fin,"%d%d",&n,&m);

for(i=1;i<=m;i++)
{
fscanf(fin,"%d",&nr);//fin>>nr;

for(j=1;j<=nr;j++)
fscanf(fin,"%d",&x[j]);

cand=x[1];nrap=1;
for(j=2;j<=nr;j++)
if(x[j]==cand)
nrap++;
else
if(nrap>0)nrap--;
else
{
nrap=1;
cand=x[j];
}

if(nrap>0)
{
nrap=0;
for(j=1;j<=nr;j++)
if(x[j]==cand)nrap++;
}

if(nrap>nr/2)
{
ok=0;
for(j=1;j<=k;j++)
if(v[j]==cand)
{
++Nr[j];
ok=1;
break;
}

if(ok==0)
{
v[++k]=cand;
Nr[k]=1;
}
}
}

ok=0;
while(!ok)
{
ok=1;
for(j=1;j<=k-1;j++)
if(v[j]>v[j+1])
{
aux=v[j];
v[j]=v[j+1];
v[j+1]=aux;
aux=Nr[j];
Nr[j]=Nr[j+1];
Nr[j+1]=aux;
ok=0;
}
}

nrmax=Nr[1];
for(j=2;j<=k;j++)
if(nrmax<Nr[j])
nrmax=Nr[j];

for(j=1;j<=k;j++)
if(nrmax==Nr[j])
fout<<v[j]<<’ ’;
CAPITOLUL 26. ONI 2012 26.2. PROIECTE 336

fout<<’\n’;
fout.close();
return 0;
}

Listing 26.2.2: proiecte2.cpp


#include<iostream>
#include<algorithm>

using namespace std;

int f[1000],pr[1000],N,c[210],K;
int a[200002];
int m,n,i,j,k,val,ap,p,q,r,max1;

void sortare()
{
int s;
do
{
s=1;
for(int i=1;i<N;i++)
if(f[i]>f[i+1])
swap(f[i],f[i+1]),s=0;
} while(!s);
}

int main()
{
freopen("proiecte.in","r",stdin);
freopen("proiecte.out","w",stdout);

cin>>n>>m;
for(i=1;i<=m;i++)
{
cin>>k;
for(j=1;j<=k;j++)
//cin>>a[j];
scanf("%d",&a[j]);

sort(a+1,a+k+1);

val=a[(k+1)/2];
a[0]=a[k+1]=0;
ap=1;
p=(k+1)/2;
q=p+1;
r=p-1;
while(a[q]==val)
ap++,q++;
while(a[r]==val)
ap++,r--;
if(ap>=k/2+1)
{
N++;
f[N]=val;
}
}

sortare();

//for(i=1;i<=N;i++) cout<<f[i]<<" ";


//cout<<endl<<N<<endl;
int nr;
f[N+1]=0;
max1=0;
for(i=2;i<=N+1;i++)
{
nr=1;
while(f[i]==f[i-1])
nr++,i++;
if(max1<nr)
max1=nr,K=1,c[K]=f[i-1];
CAPITOLUL 26. ONI 2012 26.3. ZIGZAG 337

else
if(max1==nr)
K++,c[K]=f[i-1];
}

for(i=1;i<=K;i++)
cout<<c[i]<<" ";

return 0;
}

26.2.3 *Rezolvare detaliată

26.3 zigzag
Problema 3 - zigzag 100 de puncte
Rail Fence Cipher, cunoscut sub numele de cifru zig-zag, este o metodă de codificare a mesajelor
folosind un caroiaj ı̂n care textul este scris ı̂ncepând din colţul stânga-sus, diagonal de sus ı̂n jos,
iar apoi, după ce s-a scris caracterul de pe ultima linie, se continuă, diagonal de jos ı̂n sus, ca ı̂n
exemplu. Numărul de linii ale caroiajului este cheia de codificare. După ce textul a fost scris ı̂n
acest mod, mesajul codificat se obţine parcurgând liniile de sus ı̂n jos şi preluând de pe fiecare
linie toate caracterele de la stânga la dreapta. Dacă vrem să codificăm textul ”OLIMPIADA DE
INFORMATICA”, cu cheia de codificare 6, atunci se procedează astfel:
1. Se scrie textul ı̂n zigzag ı̂n caroiaj

Figura 26.1: zigzag

2. Se iau caracterele pe linii şi se formează mesajul codificat: ODTL EAIIA MCMDIRA-
PANOIF
Cerinţe
Scrieţi un program care citeşte cheia de codificare şi un text codificat şi determină mesajul
decodificat.
Date de intrare
Pe prima linie a fişierului zigzag.in se află două numere naturale c şi n, separate printr-
un spaţiu, unde c reprezintă cheia de codificare, iar n numărul de caractere al mesajului, şi pe
următoarea linie un şir format din n caractere ce reprezintă mesajul codificat.
Date de ieşire
Fişierul zigzag.out va conţine o singură linie, pe care se află mesajul decodificat.
Restricţii şi precizări
a 1 $ c $ 5000;
a 1 $ n $ 50000;
a ı̂n mesaj sunt doar caractere cu codul ASCII mai mic ca 127 şi mai mare ca 31.

Exemple
zigzag.in zigzag.out Explicaţii
6 24 OLIMPIADA DE INFORMATICA
ODTL EAIIA MCMDIRAPANOIF
CAPITOLUL 26. ONI 2012 26.3. ZIGZAG 338

Timp maxim de executare/test: 0.1 secunde


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

26.3.1 Indicaţii de rezolvare

prof. Daniel Popa

Pentru a decodifica mesajul se creează un vector de decodificare. Se completează o matrice


(cu c linii şi cu n div (c-1)+1 coloane) cu numerele de ordine exact aşa cum ar trebui puse
literele şi se parcurge pe linii, obţinându-se astfel vectorul de decodificare. Pentru a reduce spaţiul
de memorie necesar NU se completează efectiv o matrice ci doar se deduc regulile de completare:
1. Pe prima coloana vom avea numerele de la 1 la c
2. Pe coloana a doua se pun pe primele c  1 linii diferenţa dintre 2 ˜ c şi numărul liniei
3. Pe celelalte coloane dacă sunt pare se pune valoarea 2*c-i+(j-2) div 2 *dt, unde
dt 2 ˜ c  2, altfel se pune valoarea i+(j-1) div 2*dt.
Este posibil ca unele din aceste valori calculate să fie peste valoarea lui n şi atunci nu se
folosesc.
1 7 13 19
2 6 8 12 14 18 20
3 5 9 11 15 17 21
4 10 16 22

26.3.2 Cod sursă

Listing 26.3.1: zigzag.cpp


#include<fstream>
#include<string>

using namespace std;

FILE *fin;

ofstream fout("zigzag.out");

char s[100010], a[50010],chr;


int n,c,x,p,k,nr,m,i;

int main()
{
fin=fopen("zigzag.in","r");
fscanf(fin,"%d%d",&c,&n);
fscanf(fin,"%c",&chr);

for(i=1;i<=n;i++)
{
fscanf(fin,"%c",&chr);
a[i]=chr;
}

nr = (n%(2*c-2)==0 ? n/(2*c-2) : n/(2*c-2)+1);


m=nr*(2*c-2);

for(i=n+1;i<=m;i++)
s[i]=’X’;
k=1;

//rand 1
for(x=0;x<=nr-1;x++)
if(s[x*(2*c-2)+1]!=’X’)
s[x*(2*c-2)+1]=a[k++];

//randuri de la 2 la c-1
for(p=2;p<=c-1;p++)
CAPITOLUL 26. ONI 2012 26.3. ZIGZAG 339

for(x=0;x<=nr-1;x++)
{
if(s[x*(2*c-2)+p]!=’X’)
s[x*(2*c-2)+p]=a[k++];
if(s[x*(2*c-2)+2*c-p]!=’X’)
s[x*(2*c-2)+2*c-p]=a[k++];
}

//rand c
for(x=0;x<=nr-1;x++)
if(s[x*(2*c-2)+c]!=’X’)
s[x*(2*c-2)+c]=a[k++];

for(i=1;i<=n;i++)
fout<<s[i];

fout<<’\n’;
fout.close();
return 0;
}

26.3.3 *Rezolvare detaliată


Capitolul 27

ONI 2011

27.1 joc
Problema 1 - joc 100 de puncte
Georgel şi Ionel au inventat un joc. Georgel scrie ı̂n fiecare pătrăţel pe o foaie de matematică,
de forma unui tablou bidimensional cu n linii şi m coloane, valori de 0 sau 1. Ionel stabileşte 3
forme distincte pe care Georgel trebuie să le identifice pe foaia de matematică ı̂n poziţia dată sau
rotite ca ı̂n figura 1, 2 sau 3. Cele trei forme propuse sunt:

Figura 27.1: joc

Pentru un joc, Georgel trebuie să le identifice pe foaie sub forma de pătrăţele pline cu 1.

Cerinţe

Scrieţi un program care să identifice numărul de apariţii pentru toate formele precizate.

Date de intrare

Fişierul joc.in conţine pe prima linie valorile lui n şi m separate printr-un spaţiu, pe
următoarele n linii fiind valorile din pătrăţelele de pe foaie (neseparate prin spaţii).

Date de ieşire

Fişierul joc.out va conţine pe prima linie numărul total de forme identificate, de oricare din
cele trei tipuri.

Restricţii şi precizări

a 0 $ m, n $ 100
a Un pătrăţel se poate regăsi ı̂n una sau mai multe forme.

340
CAPITOLUL 27. ONI 2011 27.2. MESAJ 341

Exemple
joc.in joc.out Explicaţii
55 7 Există 2 forme de primul tip, 3 forme de tipul al doilea şi 2 de al treilea tip,
00100 ca ı̂n desen.
00110
01111
00100
00100

Timp maxim de executare/test: 1.0 secunde

27.1.1 Indicaţii de rezolvare

prof. Roxana Tı̂mplaru - Liceul de Informatică ”Ştefan Odobleja” Craiova


Se ı̂ncadrează fiecare formă aflată fie ı̂n poziţia iniţială, fie rotită ı̂ntr-o matrice cu 3 linii şi 3
coloane, pătrăţelele formei fiind elemente egale cu 1. Se formează astfel 12 matrice. Se caută apoi
ı̂n matricea iniţială toate cele 12 matrice, verificându-se doar elementele egale cu 1. Se numără
formele găsite şi se afişează rezultatul obţinut.

27.1.2 *Cod sursă

27.1.3 *Rezolvare detaliată

27.2 mesaj
Problema 2 - mesaj 100 de puncte
Maria şi Ionuţ doresc să comunice ı̂ntre ei prin bileţele. Pentru ca mesajele lor să nu fie
ı̂nţelese şi de ceilalţi colegi, ei se hotărăsc să le codifice. Pentru a codifica un mesaj, Maria şi Ionuţ
procedează astfel:
a aleg ı̂mpreună un cuvânt s numit cheie format din p litere diferite două câte două;
a ı̂mpart mesajul pe care doresc să-l transmită ı̂ntre ei ı̂n secvenţe de caractere alăturate de
lungime p, cu excepţia ultimei secvenţe care poate avea mai puţin de p caractere;
a scriu pe foaie cuvântul cheie ales;
a sub cuvântul cheie ales se scriu secvenţele de lungime p determinate anterior, ı̂n ordinea
obţinerii lor;
a mesajul codificat se obţine astfel:
` se parcurge tabelul obţinut anterior, pe coloane, de sus ı̂n jos;
` ordinea de parcurgere a coloanelor este ordinea alfabetică a literelor din cuvântul cheie;

Cerinţe
Scrieţi un program care să determine litera din mesajul codificat care apare de cele mai puţine
ori, iar dacă sunt mai multe astfel de litere, prima dintre acestea ı̂n ordine alfabetică şi să realizeze
decodificarea unui mesaj codificat ı̂n modul prezentat anterior.
Date de intrare
Fişierul de intrare mesaj.in conţine:
- pe prima linie numărul p de caractere din cheie;
- pe a doua linie cuvântul cheie ales de Maria şi Ionuţ;
- pe a treia linie numărul n de caractere din mesajul codificat;
- pe a patra linie mesajul codificat.
CAPITOLUL 27. ONI 2011 27.2. MESAJ 342

Date de ieşire

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


- pe prima linie litera din mesajul codificat care apare de cele mai puţine ori ı̂n mesajul codificat;
dacă sunt mai multe astfel de litere, prima dintre acestea ı̂n ordine alfabetică;
- pe a doua linie mesajul decodificat.

Restricţii şi precizări

a Cuvântul cheie conţine numai litere mari ale alfabetului englez (A, B, C,...,Z) şi are maxim
26 de litere;
a Mesajul codificat conţine litere mari ale alfabetului englez, cuvintele sunt separate prin unul
sau mai multe spaţii;
a Lungimea mesajului codificat nu depăşeşte 2000 de caractere.
a Se acordă punctaje parţiale:
µ 20% pentru afişarea valorii corecte pe prima linie a fişierului mesaj.out (cerinţa a)
µ 80% pentru afişarea corectă a mesajului pe linia a doua a fişierului mesaj.out (cerinţa
b)

Exemple
mesaj.in mesaj.out
8 COMPUTER F
44 SUCCES LA OLIMPIADA NATIONALA DE INFORMATICA
SAAO T PTDMCOAANCU DNIICL LFALIIEASMA REINAO
Explicaţii:
Maria şi Ionuţ aleg cuvântul cheie COMPUTER
Mesajul pe care doresc să-l codifice este:
SUCCES LA OLIMPIADA NATIONALA DE INFORMATICA
Tabelul obţinut este următorul:

Figura 27.2: mesaj

Litera cerută este F şi mesajul codificat este:


SAAO T PTDMCOAANCU DNIICL LFALIIEASMA REINAO

Timp maxim de executare/test: 1.0 secunde

27.2.1 Indicaţii de rezolvare

prof. Florin Moldovanu - Colegiul Naţional ”Roman Vodă” Roman

- Pentru determinarea literei care apare de cele mai puţine ori se construieşte un vector ı̂n care
se păstrează numărul de apariţii pentru fiecare literă. Se identifică apoi litera care apare ı̂n mesaj
şi are număr minim de apariţii.
- Se construieşte un vector v ı̂n care păstrează ordinea de parcurgere a coloanelor.
Se citeşte mesajul codificat şi, folosind vectorul v, determinat la pasul anterior, se construieşte
matricea necesară pentru decodificare.
Textul decodificat se obţine prin parcurgerea matricei pe linii.

27.2.2 *Cod sursă


CAPITOLUL 27. ONI 2011 27.3. ZAR 343

27.2.3 *Rezolvare detaliată

27.3 zar
Problema 3 - zar 100 de puncte
Maria a primit cadou un joc. Jocul are o tablă sub formă de caroiaj dreptunghiular format
din pătrăţele de latură 1 dispuse ı̂n L linii şi C coloane. Pătrăţelele conţin primele L ˜ C numere
naturale nenule ı̂ncepând cu 1, reprezentând coduri.
Codificarea se face astfel: se ı̂ncepe cu valoarea 1 din pătrăţelul din 9 10 11 12
stânga-jos, se continuă cu valorile 2,3,...C de la stânga la dreapta, continuă 8 7 6 5
pe rândul următor de la dreapta la stânga şi aşa mai departe. ı̂n acest fel 1 2 3 4
ultimul pătrăţel va fi codificat ı̂ntotdeauna cu valoarea L*C, ca ı̂n exemplul din dreapta.
Jocul constă ı̂n plasarea unui jeton ı̂n colţul din stânga jos şi trebuie să se ducă jetonul dincolo
de pătrăţelul codificat cu valoarea L*C ı̂n urma mutărilor realizate. Pentru efectuarea unei mutări,
el aruncă mai ı̂ntâi un zar. Dacă acesta arată valoarea Z şi jetonul se află ı̂n pătrăţelul cu codul
P, jetonul va fi dus ı̂n pătrăţelul cu codul P+Z. Dacă P+Z¿L*C, jocul se termină. ı̂n caz contrar,
există situaţii ı̂n care jetonul nu va rămâne neapărat ı̂n noua poziţie, ı̂ntrucât ı̂n unele poziţii ale
caroiajului sunt indicatoare care precizează unde trebuie dus jetonul care tocmai a ajuns ı̂n acea
poziţie.
4 3 2
Un pătrăţel poate să conţină cel mult un indicator. Indicatoarele sunt de 9
tipuri: pentru primele 8 tipuri se precizează peste câte poziţii se va plasa jetonul 5 X 1
din poziţia curentă, respectiv: pe aceeaşi linie la dreapta(1), ı̂n diagonală pe 6 7 8
direcţia dreapta-sus(2), pe aceeaşi coloană ı̂n sus(3), pe diagonală pe direcţia stânga-sus(4), pe
aceeaşi linie la stânga(5), pe diagonală pe direcţia stânga-jos(6), pe aceeaşi coloană ı̂n jos(7), ı̂n
diagonală pe direcţia dreapta-jos(8). Indicatorul de tipul 9 precizează codul unui pătrăţel ı̂n care
va fi plasat jetonul.
La aplicarea unui indicator se respectă regulile:
a) dacă jetonul iese ı̂n afara tablei, acest indicator va fi neglijat, iar jetonul va rămâne pe loc
(jocul nu se poate termina astfel).
b) dacă se ajunge ı̂ntr-un pătrăţel care conţine un alt indicator, acesta nu va mai fi luat ı̂n
considerare.

Cerinţe

Cunoscând, ı̂n ordine, cele K valori obţinute ı̂n urma aruncărilor cu zarul, să se determine
dacă jocul se poate ı̂ncheia. ı̂n caz afirmativ se va afişa numărul de aruncări cu zarul după care
jocul se ı̂ncheie. ı̂n caz contrar, se va afişa poziţia jetonului după cele K mutări.

Date de intrare

Fişierul zar.in conţine pe prima linie două numere L şi C reprezentând numărul de linii,
respectiv numărul de coloane ale tablei de joc.
Pe linia a 2-a se găseşte un număr natural I reprezentând numărul de pătrăţele ı̂n care se
găsesc indicatoare.
Pe următoarele I linii se găsesc câte 3 numere naturale separate prin câte un spaţiu
reprezentând descrierea câte unui indicator. Primul număr este codul pătrăţelului ı̂n care este
pus indicatorul, al doilea număr este tipul de indicator. Dacă tipul de indicator este 9, al treilea
număr semnifică codul pătrăţelului unde va fi trimis jetonul. Dacă al doilea număr este din
mulţimea {1, 2, 3, 4, 5, 6, 7, 8}, al treilea număr reprezintă numărul de poziţii de pe tablă peste
care se va muta jetonul ı̂n direcţia precizată.
Pe linia următoare se află un număr natural K reprezentând numărul de aruncări cu zarul.
Pe următoarea linie sunt K numere naturale separate prin câte un spaţiu, reprezentând, ı̂n
ordine, valorile obţinute după fiecare aruncare.

Date de ieşire

Fişierul zar.out va conţine pe prima linie două numere naturale separate printr-un spaţiu.
Dacă jocul se termină, prima valoare va fi 1, iar a doua va reprezenta numărul de mutări după
care s-a terminat. Dacă jocul nu se termină, prima valoare va fi 2, iar a doua codul pătrăţelului
unde a rămas jetonul.
CAPITOLUL 27. ONI 2011 27.3. ZAR 344

Restricţii şi precizări

a 1 & L, C & 50; 0 & I & L ˜ C;


a 1 & K & 1000;
a Valorile obţinute ı̂n urma aruncărilor cu zarul sunt numere din mulţimea {1, 2, 3, 4, 5, 6};
a Numărul de poziţii cu care se poate deplasa jetonul pentru un indicator de tip 1-8 este număr
ı̂ntreg nenul & 50;

Exemple
zar.in zar.out Explicaţii
34 2 11 Iniţial jetonul este ı̂n pătrăţelul 1. După prima mutare el ajunge ı̂n
4 pătrăţelul 5 (zarul arată 2 şi jetonul ar ajunge ı̂n pătrăţelul 3 dar
321 indicatorul de acolo ı̂n trimite ı̂n pătrăţelul 5). După mutarea a doua
811 jetonul ajunge ı̂n pătrăţelul 6 (acolo ı̂l trimite zarul şi nu este ı̂ntâlnit
712 un indicator). După mutarea a treia, jetonul ajunge ı̂n pătrăţelul 7
9 9 11 (zarul ı̂l trimite ı̂n 8, iar indicatorul de acolo ı̂n 7 acum nu se tine cont
4 de indicatorul din 7). După mutarea 4 jetonul ajunge la pătrăţelul
2122 11 (după aruncarea cu zarul trebuia dus ı̂n 9 iar indicatorul de acolo
ı̂l trimite ı̂n 11).

Timp maxim de executare/test: 1.0 secunde

27.3.1 Indicaţii de rezolvare

prof. Marius Nicoli, CN ”Fraţii Buzeşti” - Craiova

Se simulează mutările conform regulilor din enunţ. Probabilitatea de a greşi tratând multi-
tudinea de cazuri ce pot apărea poate fi scăzută printr-o codificare eficientă a datelor. Astfel, se
pot pastra structuri de date care să permită determinarea poziţiei pe tabla de joc pentru un cod
dat (un vector de structuri, V k .i şi V k .j sunt poziţiile pe tabla de joc ale pătrăţelului cu codul
k), respectiv pentru determinatea codului pătrăţelului aflat pe poziţia i, j  pe tabla de joc (o
matrice, M ij  = codul pătrăţelului de pe poziţia i, j ).
Aceste structuri se construiesc la citirea datelor. Se poate renunţa la utilizarea structurilor
scriind funcţii care să facă trecerea de la cod la coordonate şi invers. Se pot utilize 2 vectori de
direcţii, di şi dj , deplasarea cu x paşi din poziţia i, j  pe direcţia k putându-se face prin construcţii
de forma i  x ˜ di k , j  x ˜ dj k .

27.3.2 *Cod sursă

27.3.3 *Rezolvare detaliată


Capitolul 28

ONI 2010

28.1 char
Problema 1 - char 100 de puncte
Alex a primit de la Moş Crăciun un joc foarte interesant. Jocul este format dintr-un text cu
n litere mici ale alfabetului englez. Fiecare literă are o anumită putere, dată printr-un număr
natural. Puterea k a unei litere c constă ı̂n faptul că, dacă aceasta este atinsă atunci toate literele
din secvenţa de k litere, din stânga şi din dreapta se transformă ı̂n c. Spre exemplu, dacă litera x
are puterea 2, atunci după atingere, textul abcbxpbrr se transformă ı̂n abxxxxxrr. Cunoscând
puterea fiecărei litere, jocul constă ı̂n determinarea numărului maxim m de litere, care după
atingere să transforme orice literă din text cel mult o dată.
Cerinţe
Scrieţi un program care să citească un text cu n litere, puterea fiecărei litere şi să afişeze
numărul de litere din text cu puterea maximă, notat cu q precum şi numărul m.
Date de intrare
În fişierul char.in se dau:
- pe prima linie: numărul natural n
- pe a doua linie: cele n litere ale textului fără spaţiu ı̂ntre ele
- pe a treia linie: numărul h de litere distincte din text
- pe a patra linie: h numere naturale separate ı̂ntre ele prin câte un spaţiu reprezentând puterea
literelor din text ı̂n ordine alfabetică.
Date de ieşire
Fişierul char.out va conţine pe prima linie numărul q şi pe a doua linie numărul m.
Restricţii şi precizări
a 1 & n & 10000,
a 1 & putere literă & 100
a Dacă ı̂n stânga sau dreapta unei litere sunt mai puţine litere decât puterea, atunci atingerea
ei conduce la transformarea tuturor literelor din stânga, respectiv dreapta.
a Se acordă 30% din punctaj pentru determinarea numărului q şi 70% din punctaj pentru
determinarea numărului m.
a Prima literă din text este pe poziţia 1, a doua literă pe poziţia 2, şi aşa mai departe.

Exemple
char.in char.out Explicaţii
12 6 Litera a are puterea 2, litera b puterea 5, litera c puterea 3,
acbbxacbbbxb 3 respectiv litera x are puterea 2.
4 Litera cu puterea maximă este b şi apare ı̂n secvenţă de 6 ori.
2532 Numărul maxim de litere, care pot fi atinse astfel ı̂ncât oricare
literă a textului să se transforme cel mult o dată este 3 (de
exemplu se pot atinge literele de pe poziţiile 1, 6, 11).
Timp maxim de executare/test: 1.0 secunde

345
CAPITOLUL 28. ONI 2010 28.1. CHAR 346

28.1.1 Indicaţii de rezolvare

Popescu Anastasiu Doru - profesor Colegiul Naţional ”Radu Greceanu” Slatina


’index[person]Doru Popescu Anastasiu

1. Se construieşte un vector v v 1, v 2, ..., v h cu literele distincte din text, astfel fiecărei
litere v i ı̂i corespunde o putere pi, i 1, ..., h.
2. Se determină ı̂n max puterea maximă şi apoi poziţiile literelor cu puterea egală cu max ı̂n
text. Pentru fiecare astfel de literă determinată anterior, se incrementează variabila q.
3. Pentru a doua cerinţă se observă faptul că fiecărei litere ı̂i corespunde o porţiune continuă de
litere din text. O astfel de porţiune este caracterizată prin capătul din stânga, respectiv dreapta.
Determinăm ı̂n doi vectori st st1, ..., stn, dr dr1, ..., drn aceste capete de porţiuni.
4. Acum problema se reduce la determinarea unui număr maxim de porţiuni disjuncte. Acest
număr este numărul căutat m.

28.1.2 Cod sursă

Listing 28.1.1: CHAR.CPP


#include <fstream>

using namespace std;

char x[10003],y[100];
int s[10003],d[10003],i,h,p[100],n,k,nr,t,j,aux,m,maxx,i1,j1;

void afis()
{
ofstream fout("char.out");
fout<<nr<<’\n’<<m;
fout.close();
}

void cit()
{
int i,j,sw;
char aux;

ifstream fin("char.in");
fin>>n;
fin.get();

for (i=1;i<=n;i++)
x[i]=fin.get();
fin.get();

fin>>h;
for (i=1;i<=h;i++)
fin>>p[i];
fin.close();

//lit dist din x


k=0;
for (i=1;i<=n;i++)
{
sw=1;
for (j=1;j<=k;j++)
if (y[j]==x[i])
{
sw=0;
break;
}

if (sw)
{
k++;
y[k]=x[i];
}
CAPITOLUL 28. ONI 2010 28.1. CHAR 347

for (i=1;i<k;i++)
for (j=i+1;j<=k;j++)
if (y[i]>y[j])
{
aux=y[i];
y[i]=y[j];
y[j]=aux;
}

// for(i=1;i<=h;i++) cout<<y[i]<<" "<<p[i]<<’\n’;


}

int putere(char c)
{
int i;
for (i=1;i<=h;i++)
if (y[i]==c)
return p[i];
return 0;
}

int main()
{
cit();

//prima parte
maxx=0;
for (i=1;i<=h;i++)
if (p[i]>maxx)
maxx=p[i];

nr=0;
for (i=1;i<=n;i++)
if (maxx==putere(x[i]))
nr++;

//a doua parte


//cout<<"\n";
for (i=1;i<=n;i++)
{
t=putere(x[i]);
i1=i-t;
j1=i+t;
if (i1<0) i1=0;
if (j1>n) j1=n;
s[i]=i1;
d[i]=j1;
// cout<<i1<<" "<<j1<<’\n’;
}

for (i=1;i<n;i++)
for (j=i+1;j<=n;j++)
if (d[i]>d[j])
{
aux=s[i];
s[i]=s[j];
s[j]=aux;
aux=d[i];
d[i]=d[j];
d[j]=aux;
}
i=1;
m=1;
//cout<<s[1]<<" "<<d[1]<<’\n’;
for (j=2;j<=n;j++)
if (s[j]>d[i])
{
m++;
i=j;
// cout<<s[i]<<" "<<d[i]<<’\n’;
}

afis();
return 0;
CAPITOLUL 28. ONI 2010 28.2. MARATON 348

28.1.3 *Rezolvare detaliată

28.2 maraton
Problema 2 - maraton 100 de puncte
Pentru desfăşurarea probei de maraton a poştaşilor, organizatorii au plasat pe traseu n  2
semafoare, la distanţe egale unul de celălalt. Primul semafor e plasat pe linia de start, iar ultimul
semafor este plasat pe linia de sosire şi ambele vor avea aprinsă culoarea verde din momentul ı̂n
care se dă startul şi până la sfârşitul cursei.
Pentru fiecare semafor ı̂ntâlnit pe traseu,
cele trei culori ale sale: roşu, galben şi verde
se aprind succesiv astfel: ı̂ntotdeuna după roşu
se face galben, după galben se face verde, iar
după verde urmează roşu, şi aşa mai departe.
Culoarea roşie a fiecărui semafor se schimbă ı̂n
galben după 5 secunde, galbenul se schimbă ı̂n Figura 28.1: maraton
verde după 3 secunde, iar verdele ı̂n roşu după
2 secunde.
În momentul ı̂n care se dă startul şi se porneşte cronometrul toate cele n semafoare de pe traseu
se aprind. La unele va fi culoarea roşie, la altele galben, iar la altele verde, nefiind sincronizate la
acest moment.
Fiecare poştaş ı̂nscris la maraton trebuie să parcurgă traseul de la linia de start până la linia de
sosire şi să treacă pe rând de cele n semafoare, doar pe culoarea verde a fiecăruia dintre ele. Dacă
un concurent ajunge ı̂n dreptul semaforului şi acesta este verde va trece obligatoriu mai departe.
Dacă ajunge ı̂n dreptul unui semafor chiar ı̂n secunda ı̂n care se schimbă culoarea acestuia, atunci
concurentul poate trece mai departe doar dacă această schimbare s-a facut de la galben la verde,
nu şi de la verde la roşu sau de la roşu la galben.

Cerinţe

Ştiind că poştaşul Andrei parcurge distanţa dintre două semafoare succesive ı̂n k secunde, să
se scrie un program care să determine numărul minim de secunde necesar pentru ca el să treacă
linia de sosire.

Date de intrare

Din fişierul maraton.in:


- De pe prima linie se citesc două numere naturale n şi k despărţite printr-un spaţiu. Valoarea
n reprezintă numărul de semafoare plasate ı̂ntre cele două linii, cea de start şi cea de sosire, iar
numărul k reprezintă timpul necesar, exprimat ı̂n secunde, pentru parcurgerea distanţei dintre
oricare două semafoare succesive de pe traseu.
- De pe următoarea linie se citesc n valori ı̂ntregi despărţite prin câte un spaţiu, ce reprezintă
culoarea pe care o are fiecare semafor ı̂n momentul startului. Vom codifica cu 2 culoarea roşie,
1 culoarea galbenă şi cu 0 culoarea verde a semaforului.

Date de ieşire

Fişierul maraton.out va conţine o singură linie pe care se va scrie numărul natural s, care
reprezintă numărul minim de secunde necesar pentru ca Andrei să treacă de linia de sosire.

Restricţii şi precizări

a 1 & n & 5000 şi 1 & k & 600


a linia de sosire este plasată imediat după ultimul semafor.
CAPITOLUL 28. ONI 2010 28.2. MARATON 349

Exemple
maraton.in maraton.out Explicaţii
32 25 Se dă startul şi după 2 secunde poştaşul ajunge ı̂n dreptul
0 0 -1 primului semafor. La acesta tocmai se schimbă culoarea din
verde ı̂n roşu şi ca urmare poştaşul nu poate trece. Aşteaptă
8 secunde, se face verde iar după alte 2 secunde ajunge ı̂n
dreptul celui de-al doilea semafor (au trecut 12 secunde de la
start). Aici mai aşteaptă 8 secunde, se face verde şi poate
trece. Parcurge ı̂n 2 secunde distanţa până ı̂n dreptul celui
de-al treilea semafor. Când ajunge ı̂n dreptul acestui semafor
(după 22 secunde de la start) mai aşteaptă o secundă (1), se
face verde şi peste 2 secunde trece linia de sosire (22+1+2=25
secunde).

Timp maxim de executare/test: 1.0 secunde

28.2.1 Indicaţii de rezolvare

prof. Cristina Iordaiche, Liceul ”Grigore Moisil” Timişoara

Se parcurge traseul dat şi pentru fiecare semafor se determină culoarea pe


care o are acesta ı̂n momentul ı̂n care poştaşul a ajuns ı̂n dreptul lui.
Dacă semaforul are culoarea:
a verde, poştaşul trece mai departe
a galben sau roşu, se calculează numărul de secunde rămase până când
acesta ı̂şi va schimba culoarea ı̂n verde
a tocmai s-a schimbat din verde ı̂n roşu, se aşteaptă 8s până se face din nou
verde şi se poate trece mai departe
a tocmai s-a schimbat din galben ı̂n verde, se trece mai departe

28.2.2 Cod sursă

Listing 28.2.1: MARATON.CPP


# include <fstream>

using namespace std;

ifstream f("maraton.in");
ofstream g("maraton.out");

int main()
{
long n,k,i,t,culoare,mai_sta;
long timp=0;

f>>n>>k;

for(i=1;i<=n;i++)
{
f>>culoare;

timp=timp+k;
t=(timp%10);

switch(culoare)
{
case -2:
if(t==8 || t==9)
mai_sta=0;
else
if(t==10)
mai_sta=8;
else
CAPITOLUL 28. ONI 2010 28.2. MARATON 350

mai_sta=8-t;
break;
case -1:
if (t==4)
mai_sta=0;
else
if (t<=3)
mai_sta=3-t;
else
mai_sta=3+(10-t);
break;
case 0:
if(t==0 || t==1)
mai_sta=0;
else
if(t==2)
mai_sta=8;
else
mai_sta=10-t;
break;
}

timp=timp+mai_sta;
}

g<<(timp+k);
return 0;
}

Listing 28.2.2: MARAton2.CPP


#include<fstream>

using namespace std;

ifstream f("maraton.in");
ofstream g("maraton.out");

int a[6000],n,k;
long s=0;

void citire();

void actualizare(int i,int val)


{
for(int j=i;j<=n;j++)
a[j]=(a[j]+val)%10;
}

void blabla()
{
for (int i=1;i<=n;i++)
{
s=s+k;
actualizare(i,k);

if(a[i]<8)
{
int v=8-a[i];
actualizare(i,v);
s=s+v;
}
}
}

int main()
{
citire();
blabla();
s+=k;
g<<s;
g.close();
return 0;
}
CAPITOLUL 28. ONI 2010 28.3. ROBOTI 351

void citire()
{
f>>n>>k;
int x;

for(int i=1;i<=n;i++)
{
f>>x ;
if(x==-2)
a[i]=0;
else
if(x==-1)
a[i]=5;
else
a[i]=8;
}
}

28.2.3 *Rezolvare detaliată

28.3 roboti
Problema 3 - roboti 100 de puncte
Într-o zonă dreptunghiulară cu p linii şi q coloane se află n roboţi. Celula din stânga sus se
află pe linia 1 şi coloana 1.
Pentru fiecare robot se cunoaşte linia şi coloana pe care se află, precum şi orientarea lui.
Un robot poate fi orientat ı̂n una din cele patru direcţii: nord, sud, est sau vest, codificate cu
caracterele N, S, E, respectiv V. Fiecare robot execută m comenzi. O comandă este codificată
printr-un caracter L, R sau F. La o comandă de tip L, robotul se ı̂ntoarce cu 90 de grade spre
stânga, ı̂n sensul invers acelor de ceasornic. La o comandă de tip R, robotul se ı̂ntoarce cu 90 de
grade spre dreapta, ı̂n sensul acelor de ceasornic. La o comandă de tip F, robotul se deplasează
cu o poziţie ı̂n sensul ı̂n care este orientat robotul.
Roboţii execută simultan prima comandă din şirul lor de comenzi, apoi a doua comandă, etc.
Dacă la un moment dat, doi sau mai mulţi roboţi ajung ı̂n aceeaşi poziţie, aceasta va conduce
la dispariţia lor, iar celula din care dispar se consideră traversată de toţi cei care au dispărut.
Dacă un robot se deplasează ı̂n afara suprafeţei la execuţia unei comenzi, robotul dispare.
Se consideră trecere printr-o celulă vizitarea rezultată ı̂n urma executării unei comenzi de tip
F. Dacă un robot trece de mai multe ori printr-o celulă, se contorizează fiecare trecere a sa. Celula
din care pleacă fiecare robot se consideră trecere pentru robotul respectiv.

Cerinţe

Scrieţi un program care să determine:


a) numărul de roboţi rămaşi după execuţia celor m comenzi,
b) poziţia celulei din zonă prin care s-a trecut cel mai des (dacă există mai multe celule, atunci
se va preciza cea cu indicele de linie cel mai mic, iar dacă există mai multe cu acest indice, cea cu
indicele de coloană cel mai mic) şi numărul de treceri prin această celulă.

Date de intrare

Fişierul roboti.in conţine:


- pe prima linie: p şi q, valori separate printr-un spaţiu şi reprezentând numărul de linii şi de
coloane ale zonei;
- pe a doua linie: valoarea n, reprezentând numărul de roboţi;
- pe următoarele n linii: câte trei valori separate prin câte un spaţiu, reprezentând linia,
coloana şi orientarea fiecărui robot;
- pe următoarea linie: valoarea m reprezentând numărul de comenzi de executat de către
fiecare robot;
- pe următoarele n linii se află câte m caractere reprezentând comenzile pentru fiecare robot
(mai ı̂ntâi cele m comenzi pentru primul robot, apoi cele m comenzi pentru al doilea robot,
ş.a.m.d.). ı̂ntre caracterele unei comenzi nu există niciun spaţiu.
CAPITOLUL 28. ONI 2010 28.3. ROBOTI 352

Date de ieşire

Fişierul roboti.out va conţine:


- pe prima linie numărul de roboţi rămaşi ı̂n final
- pe linia a doua poziţia celulei (linie şi coloană) prin care s-a trecut cel mai des (dacă există
mai multe celule, atunci se va preciza cea cu indicele de linie cel mai mic, iar dacă există mai
multe cu acest indice, cea cu indicele de coloană cel mai mic) şi numărul de treceri prin această
celulă, trei valori separate prin câte un spaţiu

Restricţii şi precizări

a 1 & p, q, m & 50
a 2 & n & 50
a Caracterele cu care se codifică orientările pot fi doar N, S, E, V, iar cele pentru comenzi L,
R sau F (litere mari).
a Iniţial, nu există doi sau mai mulţi roboţi ı̂n aceeaşi celulă.
a Se acordă 50% din punctaj pentru rezolvarea cerinţei a) şi 50% pentru cerinţa b).

Exemple
roboti.in roboti.out Explicaţii
44 0 După prima comandă, roboţii 1 şi 2 ar trebui să ajungă ı̂n aceeaşi
3 122 celulă, deci dispar. Al treilea robot conform primei deplasări va
11E părăsi zona, deci dispare. ı̂n final vor fi 0 roboţi. Doi roboţi au
13V ajuns ı̂n celula de pe linia 1 şi coloana 2, după care au dispărut.
44S Prin celelalte celule s-a trecut maxim o singură dată.
2
FL
FF
FF

Timp maxim de executare/test: 1.0 secunde

28.3.1 Indicaţii de rezolvare

prof. Tı̂mplaru Roxana - profesor Liceul de Informatică ”Ştefan Odobleja” Craiova

Se execută mai ı̂ntâi prima comandă de către toţi roboţii, identificând mai ı̂ntâi situaţiile ı̂n
care aceştia părăsesc zona (se verifică dacă după o deplasare de tip F indicele de linie pentru noua
poziţie nu se află ı̂n intervalul 1, p, sau indicele de coloană nu se află ı̂n intervalul 1, q .
Se identifică apoi situaţiile ı̂n care ajung ı̂n aceeaşi celulă mai mulţi roboţi, ţinând cont că cei
care fac comenzi de tip L sau R nu părăsesc zona, iar cei cu comenzi F se vor deplasa ı̂ntr-o nouă
celulă.
Se continuă ı̂n acelaşi mod cu toate comenzile. Se contorizează numărul de roboţi dispăruţi,
pentru a se preciza numărul celor rămaşi ı̂n final, conform cerinţei a).
Trecerea printr-o celulă se marchează ı̂ntr-o nouă matrice care va avea ı̂n final numărul de
treceri prin fiecare celulă. Se determină apoi cel mai mare element din matrice şi poziţia primului
element din matrice egal cu acesta (dacă există mai multe elemente se va scrie poziţia celui cu
indicele de linie cel mai mic, iar la egalitate, cel cu indicele de coloană cel mai mic)

28.3.2 Cod sursă

Listing 28.3.1: Roboti.cpp


// Implementare Simoiu Robert - 100 pct. Traducere Pascal -> C++
#include <cstdio>

const char FIN[] = "roboti.in";


const char FOU[] = "roboti.out";
const int MAX = 55;
CAPITOLUL 28. ONI 2010 28.3. ROBOTI 353

int m, n, nr, l, c;
int x[MAX], y[MAX], tr[MAX][MAX];
char ori[MAX], depl[MAX][MAX];
bool exista[MAX];

void date()
{
int i;

scanf("%d %d\n", &l, &c);


scanf("%d\n", &n);
nr = n;
for (i = 1; i <= n; i++)
{
scanf("%d %d %c\n", &x[i], &y[i], &ori[i]);
tr[x[i]][y[i]] = 1;
exista[i] = 1;
}

scanf("%d\n", &m);
for (i = 1; i <= n; i++)
fgets(depl[i] + 1, MAX , stdin);
}

void miscare_l(char c, char *c_nou)


{
switch ( c )
{
case ’E’ :
*c_nou = ’N’;
break ;
case ’V’ :
*c_nou = ’S’;
break ;
case ’S’ :
*c_nou = ’E’;
break ;
case ’N’ :
*c_nou = ’V’;
break ;
}
}

void miscare_r(char c, char *c_nou)


{
switch ( c )
{
case ’E’ :
*c_nou = ’S’;
break ;
case ’V’ :
*c_nou = ’N’;
break ;
case ’S’ :
*c_nou = ’V’;
break ;
case ’N’ :
*c_nou = ’E’;
break ;
}
}

void miscare_f(int l, int c, char ch, int *l_nou, int *c_nou)


{
switch ( ch )
{
case ’E’ :
*c_nou = c + 1;
break ;
case ’V’ :
*c_nou = c - 1;
break ;
case ’S’ :
*l_nou = l + 1;
break ;
case ’N’ :
CAPITOLUL 28. ONI 2010 28.3. ROBOTI 354

*l_nou = l - 1;
break ;
}
}

void deplasare()
{
int i, j, vechil, vechic;
short a[MAX][MAX], b[MAX][MAX], p, q;

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


{
for (p = 1; p <= l; p++)
for (q = 1; q <= c; q++)
a[p][q] = 0;

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


if ( exista[j] )
if ( depl[j][i] == ’L’ || depl[j][i] == ’R’)
a[x[j]][y[j]] = 1, b[x[j]][y[j]] = j;

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


if ( exista[j] )
{
switch ( depl[j][i] )
{
case ’L’ : miscare_l(ori[j], &ori[j]); break ;
case ’R’ : miscare_r(ori[j], &ori[j]); break ;
case ’F’ :
{
miscare_f(x[j], y[j], ori[j], &x[j], &y[j]);
if (x[j] == 0 || y[j] == 0 || x[j] > l || y[j] > c)
exista[j] = 0;
else
{
tr[x[j]][y[j]]++;
if (a[x[j]][y[j]] == 0)
a[x[j]][y[j]] = 1, b[x[j]][y[j]] = j;
else exista[j] = 0, exista[b[x[j]][y[j]]] = 0;
}
break;
}
}
}
}

n = 0;
for (i = 1; i <= nr; i++)
if ( exista[i] ) ++n;
}

void afisare()
{
int i, j, max, p, q;

printf("%d\n", n);

max = tr[1][1];
p = q = 1;
for (i = 1; i <= l; i++)
for (j = 1; j <= c; j++)
if (tr[i][j] > max)
max = tr[i][j], p = i, q = j;

printf("%d %d %d", p, q, max);


}

int main()
{
freopen(FIN,"r",stdin);
freopen(FOU,"w",stdout);

date(), deplasare(), afisare();

return 0;
}
CAPITOLUL 28. ONI 2010 28.3. ROBOTI 355

28.3.3 *Rezolvare detaliată


356
Appendix A

Programa olimpiadei - gimnaziu

A.1 Clasa a V-a


1. Algoritmi elementari
– Tipuri simple de date. Tipul ı̂ntreg (pe 4 octeţi), tipul logic
– Structura liniară, alternativă şi repetitivă
– Prelucrarea cifrelor numerelor naturale scrise ı̂n baza 10
– Divizibilitate (divizorii numerelor naturale, numere prime, determinarea cmmdc prin
algoritmul lui Euclid, cmmmc, numere prime ı̂ntre ele, simplificarea fracţiilor)
– Calculul unor expresii (de exemplu, factorial, ridicare la putere)
2. Generări de şiruri
– Generarea şirurilor pe baza unor reguli
– Şirul lui Fibonacci şi alte şiruri recurente
3. Fişiere text
4. Prelucrări de şiruri de numere citite succesiv, fără memorarea lor
– Prelucrări ce necesită stocarea ultimului element
(de exemplu, determinare maxim/minim, primele două maxime sau minime)
– Prelucrări ce necesită stocarea ultimelor p (p=2, 3, ..) elemente (de exemplu, cea mai
lungă subsecvenţă cu anumite proprietăţi, numărarea secvenţelor cu anumite pro-
prietăţi)

Doar pentru etapa naţională

5. Algoritmi elementari. Tablouri unidimensionale


– Prelucrări elementare (de exemplu, parcurgere, inversare, verificare proprietăţi)
– Căutarea liniară a unor valori
– Vectori caracteristici/ de frecvenţă
– Algoritmi de sortare ı̂n complexitate pătratică (sortarea prin selecţie, sortarea prin
inserţie, metoda bulelor)
– Sortare prin numărare (folosind vectori de frecvenţă)

A.2 Clasa a VI-a


1. Tipuri simple de date (ı̂ntregi, reale, char)
2. Sisteme de numeraţie şi reguli de conversie
3. Aritmetica modulară (adunări, scăderi, ı̂nmulţiri)
4. Divizibilitate
– Ciurul lui Eratostene
– Descompunerea numerelor naturale ı̂n factori primi
5. Tablouri unidimensionale (vectori)
– Operaţii cu mulţimi
– Căutare binară

357
APPENDIX A. PROGRAMA OLIMPIADEI - GIMNAZIU A.3. CLASA A VII-A 358

– Precalcularea unor informaţii pentru prefixe/sufixe ı̂n tablouri unidimensionale


(de exemplu sume parţiale, maxime parţiale)
– Probleme cu secvenţe de valori (de exemplu, determinarea unei secvenţe maximale cu o
anumită proprietate, numărarea secvenţelor, prelucrarea secvenţelor de lungime fixată
ce nu implică stive, cozi sau alte structuri de date avansate)
– Căutarea apariţiilor unei subsecvenţe ı̂ntr-o secvenţă de valori ı̂n timp pătratic
– Interclasarea tablourilor unidimensionale

Doar pentru etapa naţională

6. Tablouri bidimensionale
– Prelucrări elementare ale tablourilor bidimensionale (de exemplu, parcurgeri pe linii/-
coloane/diagonale/ı̂n spirală, generări, transpunere, bordare)
– Prelucrări specifice tablourilor bidimensionale pătratice (de exemplu, diagonale şi zone
determinate de diagonale)
– Căutări secvenţiale ı̂n tablouri bidimensionale (de exemplu, a unui element, a unei
secvenţe de valori, a unei submatrice)
– Utilizarea vectorilor de direcţie
7. Simulări
– reprezentarea sistemului de simulat, starea sistemului
– bucla de evenimente ce modifică starea sistemului

A.3 Clasa a VII-a


1. Funcţii
– Declarare, definire, apel
– Variabile locale, variabile globale
– Transmiterea parametrilor prin valoare şi prin referinţă
2. Tablouri
– Tehnica Two Pointers
– Tablouri de diferenţe - Difference Arrays
– Determinarea secvenţei de sumă maximă
– Determinarea elementului majoritar
– Precalcularea unor informaţii ı̂n tablouri bidimensionale (de exemplu sume parţiale
pe prefixe/sufixe de linii/coloane, suma elementelor dintr-o submatrice cu unul dintre
colţuri fixat ı̂n unul dintre colţurile matricei)
– Tablouri multidimensionale
3. Tipuri de date neomogene (struct)
4. Utilizarea funcţiilor din biblioteca STL pentru sortare şi căutare
5. Metoda Greedy

Doar pentru etapa naţională

6. Operaţii cu numere mari


– Adunarea numerelor mari
– Scăderea numerelor mari
– ı̂nmulţirea unui număr mare cu un număr natural
– ı̂mpărţirea cu rest a unui număr mare la un număr natural nenul
7. Algoritmul de exponenţiere rapidă
8. Stiva. Aplicaţii specifice

A.4 Clasa a VIII-a


1. Şiruri de caractere. Funcţii specifice
2. Generarea elementelor combinatoriale prin algoritmi de tip succesor
– Submulţimi
– Produs cartezian
APPENDIX A. PROGRAMA OLIMPIADEI - GIMNAZIU A.5. BARAJUL DE SELECŢIE
A LOTULUI NAŢIONAL LĂRGIT 359

– Permutări
– Combinări
– Aranjamente
– Utilizarea funcţiilor din biblioteca STL pentru permutări

Doar pentru etapa naţională

3. Coada. Aplicaţii specifice


4. Coada cu dublă prioritate (deque). Aplicaţii specifice
5. Elemente de geometrie
– sistemul de coordonate cartezian ı̂n plan
– puncte ı̂n planul cartezian
– distanţa dintre două puncte
– arii

A.5 Barajul de selecţie a lotului naţional lărgit


1. Operaţii pe biţi
2. Indicatorul lui Euler
3. Tablouri de diferenţe (Difference Arrays) 2D
4. Recursivitate
5. Algoritmul de fill
6. Algoritmul lui Lee
7. Tehnica Square root decomposition
8. Metoda programării dinamice

A.6 Note
ˆ Exceptând clasa a V-a, programa fiecărei clase include şi programele pentru toate clasele
precedente.
ˆ Barajul de selecţie a lotului naţional lărgit include programele pentru clasele V-VIII, precum
şi temele suplimentare specificate.
ˆ Pentru barajele de selecţie a echipelor reprezentative ale României vor fi abordate teme
suplimentare.
Appendix B

main(), cin, cout, fin, fout

B.1 Funcţia main()


Toţi suntem obişnuiţi cu structura unui program C/C++ pe care trebuie să-l scriem la olimpiadă!
29
El arată cam aşa :

Listing B.1.1: sss1.cpp


#include <iostream>
#include <fstream>

using namespace std;

int n, c, sum, L, sol1, sol2, x, k, sc, nr, i;

ifstream fin ("sss.in");


ofstream fout("sss.out");

int main()
{
fin>>c;
fin>>n;
// instructiuni

return 0;
}

Chiar dacă utilizarea funcţiilor definite de utilizator este prevăzută ı̂ncepând cu clasa a VII-a,
este bine să le folosim ı̂ncă din clasa a V-a! Asta pentru că utilizarea lor permite o mai bună
30
lizibilitate a programelor! Şi oricum ... participanţii la olimpiadă citesc mai mult decât este
31
prevăzut ı̂n programa olimpiadei! Vom folosi modelul de la funcţia main().

Listing B.1.2: sss2.cpp


#include <iostream>
#include <fstream>

using namespace std;

int n, c, sum, L, sol1, sol2, x, k, sc, nr, i;

ifstream fin ("sss.in");


ofstream fout("sss.out");

int calcul()
{
// instructiuni

return 0;
}
29
https://sepi.ro/page/oni2022
30
https://ro.wikipedia.org/wiki/Lizibilitate
31
tot ce nu este interzis, este permis!

360
APPENDIX B. MAIN(), CIN, COUT, FIN, FOUT B.1. FUNCŢIA MAIN() 361

int main()
{
fin>>c;
fin>>n;

calcul();

return 0;
}
Appendix C

”Instalare” C++

Ca să putem ”lucra” cu C++ avem nevoie de

ˆ un compilator pentru C++, şi

ˆ un IDE (Integrated Development Enviroment) pentru C++.

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

362
APPENDIX C. ”INSTALARE” C++ C.1. KIT OJI 2017 363

Kitul Kit_OJI_2017 se instalează implicit pe C:¯OJI¯


32
t IDE-ul Code::Blocks ,
t compilatorul pentru C: gcc.exe,
33
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 C.2: CodeBlocks & C++ Reference

Figura C.3: Ce conţine C:¯ OJI ¯

C.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
32
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
33
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 C. ”INSTALARE” C++ C.1. KIT OJI 2017 364

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

C.1.2 Folder de lucru

Figura C.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 C. ”INSTALARE” C++ C.1. KIT OJI 2017 365

Figura C.5: New -¿ Text document

Figura C.6: Schimbare nume fişier şi nume extensie fişier


APPENDIX C. ”INSTALARE” C++ C.1. KIT OJI 2017 366

Figura C.7: Confirmare schimbare extensie ı̂n .cpp

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

C.1.3 Utilizare Code::Blocks

Figura C.9: Pregătit pentru a scrie cod de program C++ ı̂n Code::Blocks
APPENDIX C. ”INSTALARE” C++ C.1. KIT OJI 2017 367

Figura C.10: Primul cod de program C++ ı̂n Code::Blocks

Figura C.11: Build - compilare


APPENDIX C. ”INSTALARE” C++ C.1. KIT OJI 2017 368

Figura C.12: 0 error(s), 0 warning(s)

Figura C.13: Run - execuţie


APPENDIX C. ”INSTALARE” C++ C.1. KIT OJI 2017 369

Figura C.14: Executat corect: a făcut “nimic”

C.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 C.15: Settings  % Compiler


APPENDIX C. ”INSTALARE” C++ C.1. KIT OJI 2017 370

Figura C.16: Toolchain executables

Figura C.17: Unde sunt acele programe


APPENDIX C. ”INSTALARE” C++ C.1. KIT OJI 2017 371

C.1.5 Multe surse ı̂n Code Blocks


Settings Environment: pe calculatorul meu setările sunt:

Figura C.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 C.19: Multe surse in Code Blocks - exemple


APPENDIX C. ”INSTALARE” C++ C.2. WINLIBS 372

C.2 winlibs
C.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 C.20: mingw64 pe D:

C.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 C. ”INSTALARE” C++ C.2. WINLIBS 373

Figura C.21: search path

Apare fereastra “System properties”:

Figura C.22: System properties –¿ Advanced

Fereastra este poziţionată pe tab-ul “Advanced”. Se selectează “Environment Variables”.


APPENDIX C. ”INSTALARE” C++ C.2. WINLIBS 374

Figura C.23: Environment Variables

Se selecteaza “Path” şi click pe “Edit”. Apare fereastra “Edit Environment Variables”

Figura C.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 C. ”INSTALARE” C++ C.2. WINLIBS 375

Figura C.25: Calea şi versiunea pentru gcc

Dacă totul este OK atunci se trece la instalarea IDE-ului preferat (Integrated Development
34
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."

C.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!).
34
https://en.wikipedia.org/wiki/Integrated_development_environment
https://ro.wikipedia.org/wiki/Mediu_de_dezvoltare
APPENDIX C. ”INSTALARE” C++ C.2. WINLIBS 376

Figura C.26: Settings –¿ Compiler

Figura C.27: Toolchain executables –¿ Auto-detect


APPENDIX C. ”INSTALARE” C++ C.2. WINLIBS 377

Figura C.28: New –¿ Text Document

Figura C.29: New text Document.txt

Figura C.30: Schimbare nume şi extensie


APPENDIX C. ”INSTALARE” C++ C.2. WINLIBS 378

Figura C.31: Moore apps

Figura C.32: Look for another app


APPENDIX C. ”INSTALARE” C++ C.2. WINLIBS 379

Figura C.33: Cale pentru codeblocks.exe

Figura C.34: Selectare codeblocks.exe


APPENDIX C. ”INSTALARE” C++ C.2. WINLIBS 380

Figura C.35: Editare test01.cpp

Figura C.36: Compilare test01


APPENDIX C. ”INSTALARE” C++ C.2. WINLIBS 381

Figura C.37: Mesaje după compilare

Figura C.38: Execuţie test01


APPENDIX C. ”INSTALARE” C++ C.2. WINLIBS 382

Figura C.39: Rezultat execuţie test01

Figura C.40: Fişiere apărute după compilare!

Figura C.41: Creare test02.cpp gol! + ¡dublu click¿ sau ¡Enter¿


APPENDIX C. ”INSTALARE” C++ C.2. WINLIBS 383

Figura C.42: Lista programelor de utilizat

Figura C.43: Selectare Code::Blocks IDE pentru fişierele .cpp

Figura C.44: Editare+Compilare+Execuţie pentru test02


APPENDIX C. ”INSTALARE” C++ C.2. WINLIBS 384

Figura C.45: Selectare tab ce conţine test01.cpp


Appendix D

Exponenţiere rapidă

D.1 Analogie baza 2 cu baza 10

Figura D.1: Analogie B2 cu B10

În figura D.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)

385
APPENDIX D. EXPONENŢIERE RAPIDĂ D.2. NOTAŢII, RELAŢII ŞI FORMULE 386

D.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;
„
„
„
‚
„ (D.2.1)
„
„
„
„
„nk nk1 ©2 k ' 1; nk1 j 0 desigur ! ... dar şi nk1 j 1 
„
„
„ ak1 , k ' 1;
2
„
„a k
„
„
„
„
„e k 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 ,

D.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 D. EXPONENŢIERE RAPIDĂ D.4. CODUL 387

D.4 Codul
Codul pentru relaţiile (D.2.1) devine:

Listing D.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 (D.4.2).
APPENDIX D. EXPONENŢIERE RAPIDĂ D.4. CODUL 388

Listing D.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;
„
„
„
„n 1 n;
„
„
„
„
„
„a1 a;
„
„
„
„
„calcul pentru k ' 0:
„
„e
‚
„ k nk1 %2 " r0, 1x; k ' 0 (D.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 D. EXPONENŢIERE RAPIDĂ D.4. CODUL 389

Observaţia 4. Instrucţiunile care sunt ”ı̂n plus” se pot elimina. Codul următor arată acest lucru:

Listing D.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 D.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 D. EXPONENŢIERE RAPIDĂ D.5. CHIAR ESTE RAPIDĂ? 390

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 */

D.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 D.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 D. EXPONENŢIERE RAPIDĂ D.6. REZUMAT INTUITIV! 391

Iar aceasta este metoda ”rapidă”:

Listing D.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)!

D.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 D. EXPONENŢIERE RAPIDĂ D.6. REZUMAT INTUITIV! 392

Asta este tot!


Deci, secvenţa de cod este:

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

35
şi nu mai trebuie ”atâtea formule matematice”!

35
Este o glumă!
Appendix E

Căutare binară

E.1 Mijlocul = ?
Care este poziţia (indicele) ”mijlocului” unei zone dintr-un vector?

Figura E.1: căutare binară: mijlocul zonei ı̂n vector

În figura E.1 sunt trei formule pentru determinarea mijlocului unei zone [i1, i2] din vector:
ˆ mij1 = (i1+i2)/2
ˆ mij2 = i1+(i2-i1)/2
ˆ mijdr = (i1+i2+1)/2
Toate trei dau rezultat corect dacă secvenţa [i1, ..., i2] are un număr impar de elemente.
Când secvenţa [i1, ..., i2] are un număr par de elemente apar două elemente la mijloc ... şi ...
primele două formule dau ”mijlocul din stânga” iar a treia formulă dă ”mijlocul din dreapta”.
Atunci când căutăm o valoare ı̂ntr-un vector avem la dispoziţie căutarea secvenţială. Dacă
ştim că vectorul este sortat (şir crescător de numere) atunci este bine să folosim căutarea binară
pentru că aceasta ”găseste” numărul căutat (sau ”spune” că acel număr nu este ı̂n şir)
ˆ ı̂n 10 paşi dacă şirul are 1000 de elemnte (de fapt 1024 dar ... aproximăm!),
ˆ ı̂n 20 de paşi dacă şirul are 1,000,000 de elemente,
ˆ ı̂n 30 de paşi dacă şirul are 1,000,000,000 de elemente,
ˆ ...
Explicaţia simplă este că noi tot ı̂njumătăţim intervalul de căutare până când acest interval
rămâne cu un singur element ... şi vedem dacă acel element este cel căutat (dacă elementul rămas
nu este cel căutat ... ı̂nseamnă că acel element ”căutat” nu există ı̂n şir).
Putem să ne gândim la drumul invers: plecăm de la o secvenţă cu un singur element şi dublăm
secvenţa (invers faţă de cum am făcut ı̂njumătăţind secvenţa) până ajungem la o secvenţă care
are atâtea elemente câte a avut şirul iniţial (ne oprim imediat dacă am depăşit acel număr!).
Lungimile secvenţelor sunt: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, ...

Observăm că ı̂n 10 paşi, plecând din 1, ajungem să depăşim 1000.

393
APPENDIX E. CĂUTARE BINARĂ E.2. POZIŢIE OARECARE 394

E.2 Poziţie oarecare


E.2.1 Varianta iterativă

Listing E.2.1: cautare binara-v1-iterativ.cpp


1 // are rost NUMAI daca vectorul este SORTAT (aici crescator ... !!!)
2 // gaseste o pozitie oarecare din secventa x...x...x (a doua din stanga!)
3 // daca x nu exista --> v[dr] < x < v[st] unde dr=st-1 (initial st < dr)
4
5 #include<iostream>
6
7 using namespace std;
8
9 int nrc=0; // numarul de cautari
10
11 void afisv(int v[], int st, int dr) // afisez v[] intre st si dr
12 {
13 int i;
14 for(i=st; i<=dr; i++)
15 {
16 cout<<v[i]<<" ";
17 }
18 }
19 // ------------------------------------------------------------------
20 bool cautareBinaraIter1(int x, int v[], int st, int dr)
21 {
22 int mij;
23
24 while(st <= dr)
25 {
26 nrc++;
27 mij=(st+dr)/2; // actualizez zona de cautare
28 cout<<"caut "<<x<<" in ";
29 afisv(v,st,dr);
30 cout<<"\n\n";
31
32 if(x == v[mij])
33 {
34 cout<<"am gasit "<<x<<" pe pozitia mij = "<<mij
35 <<" st="<<st<<" dr="<<dr<<"\n";
36 return true;// l-a gasit pe x
37 }
38 else // aici x != v[mij] ...
39 {
40 if(x < v[mij])
41 dr=mij-1; // caut in stanga lui mij, st ramane neschimbat
42 else
43 st=mij+1; // caut in dreapta lui mij, dr ramane neschimbat
44 }
45
46 cout<<"v["<<st<<"] = "<<v[st]<<" v["<<dr<<"] = "<<v[dr]<<endl;
47 }
48
49 if(st>dr)
50 {
51 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
52 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
53 //return;
54 return false;
55 }
56
57 return true; // sau return false; ... oricum nu ajunge aici ... !!!
58 }
59 // ------------------------------------------------------------------
60
61 int main()
62 {
63 int x; // caut x in vectorul a[]
64
65 //x=1; // secventa pe primele pozitii
66 x=2; // nu exista 2 ... ultima cautare in (dr,st)
67 //x=3; // exista numai un 3
68 //x=4; // exista secventa de 4
69 //x=9; // secventa pe ultimele pozitii
APPENDIX E. CĂUTARE BINARĂ E.2. POZIŢIE OARECARE 395

70
71 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
72
73 int n=sizeof(a)/sizeof(int);
74
75 cout<<"prima pozitie in vector = 0\n";
76 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
77
78 cautareBinaraIter1(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
79
80 cout<<endl;
81 cout<<"nr cautari = "<<nrc<<"\n";
82
83 return 0;
84 }

E.2.2 Varianta recursivă

Listing E.2.2: cautare binara-v1-recursiv.cpp


1 // are rost NUMAI daca vectorul este SORTAT (aici crescator ... !!!)
2 // gaseste o pozitie oarecare din secventa x...x...x (a doua din stanga!)
3 // daca x nu exista --> v[dr] < x < v[st] unde dr=st-1 (initial st < dr)
4
5 #include<iostream>
6
7 using namespace std;
8
9 int nrc=0; // numarul de cautari
10
11 void afisv(int v[], int st, int dr) // afisez v[] intre st si dr
12 {
13 int i;
14 for(i=st; i<=dr; i++)
15 {
16 cout<<v[i]<<" ";
17 }
18 }
19 // ------------------------------------------------------------------
20 void cautareBinaraRec1(int x, int v[], int st, int dr)
21 {
22 nrc++; // o noua cautare,
23
24 cout<<"caut "<<x<<" in ";
25 afisv(v,st,dr);
26 cout<<"\n\n";
27
28 int mij=(st+dr)/2;
29
30 if(st>dr)
31 {
32 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
33 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
34 return;
35 //return false;
36 }
37
38 if(x == v[mij])
39 {
40 cout<<"am gasit "<<x<<" pe pozitia "<<mij<<"\n";
41 return;
42 //return true;
43 }
44 else // mai caut
45 {
46 if(x < v[mij])
47 cautareBinaraRec1(x,v,st,mij-1); // caut in stanga lui mij
48 else
49 cautareBinaraRec1(x,v,mij+1,dr); // caut in dreapta lui mij
50 }
51
52 return;
53 }
54 // ------------------------------------------------------------------
APPENDIX E. CĂUTARE BINARĂ E.3. POZIŢIA DIN STÂNGA 396

55 int main()
56 {
57 int x; // caut x in vectorul a[]
58
59 //x=1; // secventa pe primele pozitii
60 x=2; // nu exista 2 ... ultima cautare in (dr,st) dr=st-1
61 //x=3; // exista numai un 3
62 //x=4; // exista secventa de 4
63 //x=9; // secventa pe ultimele pozitii
64
65 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
66
67 int n=sizeof(a)/sizeof(int);
68
69 cout<<"prima pozitie in vector = 0\n";
70 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
71
72 cautareBinaraRec1(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
73
74 cout<<endl;
75 cout<<"nr cautari = "<<nrc<<"\n";
76
77 return 0;
78 }

E.3 Poziţia din stânga


E.3.1 Varianta iterativă

Listing E.3.1: cautare binara-v2-iterativ.cpp


1 // are rost NUMAI daca vectorul este SORTAT (aici crescator ... !!!)
2 // gaseste pozitia din stanga din secventa x...x...x
3 // daca x nu exista --> st = dr = prima pozitie spre dreapta cu v[st] > x
4
5 #include<iostream>
6
7 using namespace std;
8
9 int nrc=0; // numarul de cautari
10
11 void afisv(int v[], int st, int dr) // afisez v[] intre st si dr
12 {
13 int i;
14 for(i=st; i<=dr; i++)
15 {
16 cout<<v[i]<<" ";
17 }
18 }
19 // ------------------------------------------------------------------
20 bool cautareBinaraIter1(int x, int v[], int st, int dr)
21 {
22 int mij;
23
24 while(st != dr)
25 {
26 nrc++;
27 mij=(st+dr)/2; // actualizez zona de cautare
28
29 cout<<"caut "<<x<<" in ";
30 afisv(v,st,dr);
31 cout<<"\n\n";
32
33 if(x <= v[mij])
34 dr=mij; // caut in stanga
35 else
36 st=mij+1; // caut in dreapta
37
38
39
40 cout<<"v["<<st<<"] = "<<v[st]<<" v["<<dr<<"] = "<<v[dr]<<endl;
41 }
42
APPENDIX E. CĂUTARE BINARĂ E.3. POZIŢIA DIN STÂNGA 397

43 if(st==dr) // am terminat (gasit cel mai din stanga sau nu exista deloc)
44 {
45 if(v[st]==x)
46 {
47 cout<<"am gasit "<<x<<" pe pozitia "<<st<<"\n";
48
49 return true;
50 }
51
52 else
53 {
54 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
55 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
56
57 return false;
58 }
59 }
60
61 return true; // sau return false; ... oricum nu ajunge aici ... !!!
62 }
63
64 // ------------------------------------------------------------------
65
66 int main()
67 {
68 int x; // caut x in vectorul a[]
69
70 //x=1; // secventa pe primele pozitii
71 x=2; // nu exista 2 ... ultima cautare in (dr,st) dr=st-1
72 //x=3; // exista numai un 3
73 //x=4; // exista secventa de 4
74 //x=9; // secventa pe ultimele pozitii
75
76 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
77
78 int n=sizeof(a)/sizeof(int);
79
80 cout<<"prima pozitie in vector = 0\n";
81 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
82
83 cautareBinaraIter1(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
84
85 cout<<endl;
86 cout<<"nr cautari = "<<nrc<<"\n";
87
88 return 0;
89 }

E.3.2 Varianta recursivă

Listing E.3.2: cautare binara-v2-recursiv.cpp


1 // are rost NUMAI daca vectorul este SORTAT (aici crescator ... !!!)
2 // gaseste pozitia din stanga din secventa x...x...x
3 // daca x nu exista --> st = dr = prima pozitie spre dreapta cu v[st] > x
4
5 #include<iostream>
6
7 using namespace std;
8
9 int nrc=0; // numarul de cautari
10
11 // ------------------------------------------------------------------
12
13 void afisv(int v[], int st, int dr) // afisez v[] intre st si dr
14 {
15 int i;
16 for(i=st; i<=dr; i++)
17 {
18 cout<<v[i]<<" ";
19 }
20 }
21
22 // ------------------------------------------------------------------
APPENDIX E. CĂUTARE BINARĂ E.4. POZIŢIA DIN DREAPTA 398

23 void cautareBinaraRec2(int x, int v[], int st, int dr)


24 {
25 nrc++; // o noua cautare,
26 cout<<"nrc = "<<nrc<<": caut "<<x<<" in zona indicilor "
27 <<st<<" ... "<<dr<<" : "<<endl;
28 afisv(v,st,dr);
29 cout<<"\n\n";
30 //getchar(); // while si recursivitatea ... pot "scapa" de sub control!
31
32 int mij=(st+dr)/2;
33
34 if(st==dr) // am terminat (gasit cel mai din stanga sau nu exista deloc)
35 {
36 if(v[st]==x)
37 cout<<"am gasit "<<x<<" pe pozitia "<<st<<"\n";
38 else
39 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
40 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
41 return;
42 //return false;
43 }
44
45 // mai caut
46 if(x <= v[mij])
47 cautareBinaraRec2(x,v,st,mij); // caut in stanga
48 else// aici x > v[mij]
49 cautareBinaraRec2(x,v,mij+1,dr); // caut in dreapta
50
51 return; // oricum nu ajunge aici !
52 }
53
54 // ------------------------------------------------------------------
55
56 int main()
57 {
58 int x; // caut x in vectorul a[]
59
60 //x=1; // secventa pe primele pozitii
61 //x=2; // nu exista 2 ... ultima cautare in (dr,st) dr=st-1
62 //x=3; // exista numai un 3
63 x=4; // exista secventa de 4
64 //x=9; // secventa pe ultimele pozitii
65
66 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
67 int n=sizeof(a)/sizeof(int);
68
69 cout<<"prima pozitie in vector = 0\n";
70 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
71
72 cautareBinaraRec2(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
73
74 cout<<endl;
75 cout<<"nr cautari = "<<nrc<<"\n";
76
77 return 0;
78 }

E.4 Poziţia din dreapta


E.4.1 Varianta iterativă

Listing E.4.1: cautare binara-v3-iterativ.cpp


1 // are rost NUMAI daca vectorul este SORTAT (aici crescator ... !!!)
2 // gaseste pozitia din dreapta din secventa x...x...x
3 // daca x nu exista --> st = dr = prima pozitie spre stanga cu v[st] < x
4
5 #include<iostream>
6
7 using namespace std;
8
9 int nrc=0; // numarul de cautari
10
APPENDIX E. CĂUTARE BINARĂ E.4. POZIŢIA DIN DREAPTA 399

11 void afisv(int v[], int st, int dr) // afisez v[] intre st si dr
12 {
13 int i;
14 for(i=st; i<=dr; i++)
15 {
16 cout<<v[i]<<" ";
17 }
18 }
19
20 // ------------------------------------------------------------------
21
22 bool cautareBinaraIter1(int x, int v[], int st, int dr)
23 {
24 int mij;
25
26 while(st != dr)
27 {
28 nrc++;
29 mij=(st+dr+1)/2; // actualizez zona de cautare
30
31 cout<<"caut "<<x<<" in ";
32 afisv(v,st,dr);
33 cout<<"\n\n";
34
35 if(x >= v[mij])
36 st=mij; // caut in dreapta
37 else
38 dr=mij-1; // caut in stanga
39
40 cout<<"v["<<st<<"] = "<<v[st]<<" v["<<dr<<"] = "<<v[dr]<<endl;
41 //getchar();
42 }
43
44 if(st==dr) // am terminat (gasit cel mai din stanga sau nu exista deloc)
45 {
46 if(v[st]==x)
47 {
48 cout<<"am gasit "<<x<<" pe pozitia "<<st<<"\n";
49 return true;
50 }
51 else
52 {
53 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
54 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
55 return false;
56 }
57 }
58
59 return true; // sau return false; ... oricum nu ajunge aici ... !!!
60 }
61 // ------------------------------------------------------------------
62
63 int main()
64 {
65 int x; // caut x in vectorul a[]
66
67 //x=1; // secventa pe primele pozitii
68 x=2; // nu exista 2 ... ultima cautare in (dr,st) dr=st-1
69 //x=3; // exista numai un 3
70 //x=4; // exista secventa de 4
71 //x=9; // secventa pe ultimele pozitii
72
73 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
74 int n=sizeof(a)/sizeof(int);
75
76 cout<<"prima pozitie in vector = 0\n";
77 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
78
79 cautareBinaraIter1(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
80
81 cout<<endl;
82 cout<<"nr cautari = "<<nrc<<"\n";
83
84 return 0;
85 }
APPENDIX E. CĂUTARE BINARĂ E.4. POZIŢIA DIN DREAPTA 400

E.4.2 Varianta recursivă

Listing E.4.2: cautare binara-v3-recursiv.cpp


1 // are rost NUMAI daca vectorul este SORTAT (aici crescator ... !!!)
2 // gaseste pozitia din stanga din secventa x...x...x
3 // daca x nu exista --> st = dr = prima pozitie spre stanga cu v[st] < x
4
5 #include<iostream>
6
7 using namespace std;
8
9 int nrc=0; // numarul de cautari
10
11 void afisv(int v[], int st, int dr) // afisez v[] intre st si dr
12 {
13 int i;
14 for(i=st; i<=dr; i++) { cout<<v[i]<<" "; }
15 }
16
17 // ------------------------------------------------------------------
18
19 void cautareBinaraRec2(int x, int v[], int st, int dr)
20 {
21 nrc++; // o noua cautare,
22 cout<<"nrc = "<<nrc<<": caut "<<x<<" in zona indicilor "
23 <<st<<" ... "<<dr<<" : "<<endl;
24 afisv(v,st,dr);
25 cout<<"\n\n";
26 //getchar(); // while si recursivitatea ... pot "scapa" de sub control!
27
28 int mij=(st+dr+1)/2;
29 if(st==dr) // am terminat (gasit cel mai din stanga sau nu exista deloc)
30 {
31 if(v[st]==x) cout<<"am gasit "<<x<<" pe pozitia "<<st<<"\n";
32 else
33 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
34 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
35 return;
36 //return false;
37 }
38
39 // mai caut
40 if(x >= v[mij])
41 cautareBinaraRec2(x,v,mij,dr); // caut in dreapta
42 else// aici x < v[mij]
43 cautareBinaraRec2(x,v,st,mij-1); // caut in stanga
44
45 return; // oricum nu ajunge aici !
46 }
47 // ------------------------------------------------------------------
48 int main()
49 {
50 int x; // caut x in vectorul a[]
51
52 //x=1; // secventa pe primele pozitii
53 x=2; // nu exista 2 ... ultima cautare in (dr,st) dr=st-1
54 //x=3; // exista numai un 3
55 //x=4; // exista secventa de 4
56 //x=9; // secventa pe ultimele pozitii
57
58 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
59 int n=sizeof(a)/sizeof(int);
60
61 cout<<"prima pozitie in vector = 0\n";
62 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
63
64 cautareBinaraRec2(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
65
66 cout<<endl;
67 cout<<"nr cautari = "<<nrc<<"\n";
68
69 return 0;
70 }
Appendix F

”Vecini” ...

F.1 Direcţiile N, E, S, V

Figura F.1: ”vecini” ı̂n matrice şi sistem Oxy

În figura F.1 sunt două modele (notaţii) utile ı̂n probleme ı̂n care se folosesc directiile N, E, S şi
V (NE, NV, SE şi SV se deduc uşor!).

Figura F.2: Toţi ”pereţii” sunt la N sau la V ı̂n matricea ”bordată”

În figura F.2 ...

401
APPENDIX F. ”VECINI” ... F.1. DIRECŢIILE N, E, S, V 402

Figura F.3: Toţi ”pereţii” sunt codificaţi

În figura F.3 ...


Index

x&1 1 sau x%2 1, 18 Liceul Militar Dimitrie Cantemir, vi


x&2 4 sau x©4%2 1, 18
x&2 8 sau x©8%2 1, 18 marcaj de sfârşit de linie, 3
x&2 = 1 sau x©2%2 1, 18 matrice, 221
”şmenul lui Mars”, 245 matrice de sume parţiale, 215

32768, 257 ne duce cu gândul la, 198


număr prim, 9
algoritm de sortare, 332 numere mari, 3
algoritm de tip ”umplerea rucsacului”, 272
algoritm de tip succesor, 206, 272 oglinditul numărului, 217
algoritmi de umplere, 272 operaţii pe biţi, 17
algoritmi recursivi, 272 operatori pe biţi, 272
algoritmul lui Manacher, 214 ordine inversată, 3
ordine lexicografică, 218
baza 2, 208, 248 ordonare lexicografic, 282
baza 3, 208 ordonare perechi, 56
baza 4, 207
baze de numeraţie, 206 palindrom, 2, 213, 216
bitul cel mai semnificativ, 208 permutare circulară, 282
bordare matrice, 58 precalcul, 10, 221
brute-force, 217 precalculare, 19
prefix, 217
căutare binară, 56, 198, 245, 314, 393 principiul cutiei lui Dirichlet, 207
căutare secvenţială, 393 problema rucsacului, 208
cel mai defavorabil caz, 3 programare dinamică, 208
cifra dominantă, 244 progresie aritmetică, 212
CINOR, vi
citire caracter cu caracter, 3 reconstrucţia soluţiei, 209
ciurul lui Eratostene, 10, 326 recursivitate, 64, 65, 70
cod ASCII, 213, 282, 337 relaţie de recurenţă, 208
codul ASCII, 218 reprezentarea numerelor ı̂n baza 2, 17
concatenare, 216
scriere ı̂n baza 2, 328
divizori, 71 sortare, 39, 198, 245
sortare prin numărare, 39
generalizare, 201 stivă, 291
generarea submulţimilor, 208 strchr, 313
getline, 313 strcpy, 313
Greedy, 244 strlen, 313
strtok, 313
histogramă, 215 sufix, 3, 217
sume parţiale, 215, 245
I.d.k.:
I don’t know who the author is., i, v tabel bidimensional, 219
identificator pentru palindrom, 217 tablou, 54
interclasare, 198 tablou tridimensional, 221
tablouri
lee, 64 unidimensionale, 17
lexicografic, 213, 215 tablouri

403
INDEX INDEX 404

unidimensionale, 17 vector, 3, 206


tehnica backtracking, 272 vector caracteristic, 39
tip long long int, 3, 244 vector de frecvenţă, 10, 18, 39, 212, 221
trage cu tunul, iv vector de frecvenţe, 248, 251
vector de structuri, 344
Universitatea Ovidius, vi vector de sume parţiale, 245
vector suma, 71
valoarea absolută (modulul), 9 vectori de direcţii, 344
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, Bu-
cureş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.

405
BIBLIOGRAFIE BIBLIOGRAFIE 406

[26] Odăgescu, I., Smeureanu, I., Ştefănescu, I.; Programarea avansată a calculatoarelor person-
ale, 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 407

[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_alternativ
e
[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
[84] *** - https://www.viitoriolimpici.ro/

[85] *** - https://oni2023.ro/blog/


Lista autorilor
problemelor şi indicaţiilor

Ştefan-Cosmin Dăscălescu, 10 Gorea-Zamfir Claudiu-Cristian, 207, 212

Adrian Pintea, 219 Ion Văduva, vi


Alexandru Ioniţă, 19 Ionel-Vasile Piţ-Rada, 217
Alin Burţa, 211, 272
Lucia Miron, 283, 334
Bogdan-Ioan Popa, 18, 39, 209, 215
Maria Niţă, 225
Carmen Mincă, 58, 120 Marinel Şerban, 17, 206, 215, 272
Constantin Tudor, vi Marius Nicoli, 92, 155, 245, 314, 344
Costineanu Raluca, 109 Mircea Rotar, 251
Cristina Iordaiche, 194, 261, 349
Cristina Sichim, 123 Nicoli Marius, 53
Nicu Vad Laurenţiu, 138
Dana Lica, 100 Nistor Moţ, 55, 177, 221, 332
Daniel Popa, 338
Daniela-Elena Lica, 208, 214 Octavian Dumitraşcu, 283, 296
Emanuela Cerchez, 3, 85, 129, 164, 173, 201, Pintea Adrian Doru, 248
237, 291
Eugen Nodea, 167 Roxana Tı̂mplaru, 185, 191, 341, 352

Filonela Balasa, 78 Sanda Junea, 148


Flavius Boian, 71, 198 Stelian Ciurea, 253
Florentina Ungureanu, 232, 257
Florin Moldovanu, 342 Tulbă-Lecu Theodor-Gabriel, 215

Gheorghe Dodescu, vi Vlad-Laurenţiu Nicu, 214

408
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