Sunteți pe pagina 1din 51

Studiu de caz - Numărul de cifre ale unui număr 3763

Continuă
Fie următoarea problemă:

Se dă un număr natural N mai mare decat 0. Să se afișeze câte cifre are. Exemplu: 4321 Se va
afișa 4

Soluția 1
O primă metodă ar fi să verificăm dacă numărul este mai mic decât 10, în acest caz numărul
având o cifră. În caz contrar vom verifica dacă numărul este mai mare sau egal cu 10 și mai mic
decât 100, în acest caz numărul având două cifre. Urmând această metodă, putem să tratăm
fiecare caz până când ajungem la lungimea maxim posibilă a numărului dat.

#include <iostream>
using namespace std;

int main() {
int N;
cin>>N;
if(N < 10) {
cout<<"Numarul dat are o cifra";
}
if(10 <= N && N < 100) {
cout<<"Numarul dat are doua cifre";
}
if(100 <= N && N < 1000) {
cout<<"Numarul dat are trei cifre";
}
// putem continua sa adaugam if-uri pana cand acoperim toate posibilitatile
return 0;
}
Putem să ne folosim de else if și să scurtăm puțin lungimea codului sursă.

#include <iostream>
using namespace std;

int main() {
int N;
cin>>N;
if(N < 10)
cout<<"Numarul dat are o cifra";
else if(N < 100) // intrucat N nu a fost mai mic decat 10 putem
cout<<"Numarul dat are doua cifre";
else if(N < 1000)
cout<<"Numarul dat are trei cifre";
// putem continua sa adaugam if-uri pana cand acoperim toate posibilitatile
return 0;
}
Soluția 2
Pentru a scurta soluția putem să numărăm de câte ori putem împărți numărul la 10. Întrucât în
C++ împărțirea numerelor întregi se face folosind aproximarea prin lipsă, împărțind un număr la
10 vom obține numărul fără ultima sa cifră. De exemplu cout<<5687/10;va afișa 568.

#include <iostream>
using namespace std;

int main() {
int N;
cin>>N;
int numar_cifre = 0;
while (N > 0) {
++numar_cifre;
N = N/10; // eliminam ultima cifra
}
cout<<numar_cifre;
return 0;
}
Exemplu

Să executăm codul de mai sus pentru numărul 4321. La prima intrare în while, N se împarte la
10 și devine 432, iar variabila numar_cifre e incrementată și devine 1. Uite ce se întâmplă după
fiecare rulare a instrucțiunilor din while:

#intrare în while valoarea lui N valoarea lui numar_


1 432 1
2 43 2
3 4 3
4 0 4
După cea de-a 4-a intrare în while, N devine 0, condiția din while nu mai e îndeplinită, iar în
consolă se va afișa valoarea 4
Concatenarea a două numere este operaţia prin care se obţine un nou număr prin alăturarea
cifrelor a două numere. Mai precis, prin concatenarea numerelor a = a1a2...an şi b = b1b2...bm se
obţine numărul c = a1a2...anb1b2...bm.

Cerinţă
Se dau două numere naturale nenule a şi b. Aflaţi numărul rez care se obţine prin concatenarea
celor două numere.

Idee
Pentru a adăuga un număr de o singură cifră la un număr x trebuie doar să înmulţeşti x cu 10 şi
apoi să aduni numărul dorit. De exemplu dacă dorești să-l adaugi pe 3 la finalul lui 12, îl vei
înmulți pe 12 cu 10 obținând 120 la care vei aduna 3.

La fel dacă dorești să îl concatenezi pe 53 cu 12, îl vei înmulți pe 53 cu 100 obținând 5300.
Acum dacă îl aduni pe 12 la 5300 vei obține 5312 care este rezultatul dorit.

Gândeşte-te cum poţi adăuga un număr format din trei cifre, apoi din cinci cifre și apoi gândește-
te cum poți rezolva problema în general.

Soluţie
Să presupunem că numărul a este de forma a1a2...an, iar b este de forma b1b2...bm. Înmulţind
numărul a cu 10m vom obţine a1a2...an000...00. Practic numărul obținut va avea m zerouri la final.

Putem observa că dacă adunăm la acest număr numărul b vom obţine exact rez. Ideea pe care se
bazează această soluţie este să adăugăm zerouri la finalul lui a de atâtea ori câte cifre avem în b.
Astfel prima dată trebuie să aflăm numărul de cifre al lui b, iar apoi să adăugăm la finalul
lui a câte un 0 pentru fiecare cifră a lui b.

Atenție!
Pentru a nu pierde valoarea lui b atunci când îi aflăm numărul de cifre, este nevoie să copiem
valoarea lui b într-o nouă variabilă.

Cod
#include <iostream>
using namespace std;

int main() {
int a, b;
cin>>a>>b;
// Ii facem o copie lui b pentru a nu pierde valoarea sa
// cand ii numaram cifrele
int cb = b;
int cifre_b = 0;
while(b > 0) {
++cifre_b;
b /= 10;
}
int i = 1;
while(i <= cifre_b) {
a *= 10;
++i;
}
int rez = a + cb;
cout<<rez;
return 0;
}
Îmbunătățire
Putem să scurtăm codul sursă puțin dacă în loc să numărăm cifrele lui b, adăugăm câte un 0 la
finalul lui a pentru fiecare cifră a lui b.

#include <iostream>
using namespace std;

int main() {
int a, b;
cin>>a>>b;
// Ii facem o copie lui b pentru a nu pierde valoarea sa
// cand ii numaram cifrele
int cb = b;
while(b > 0) {
a *= 10; // Adaugam un 0 pentru fiecare cifra a lui b
b /= 10;
}
int rez = a + cb;
cout<<rez;
return 0;
}
Se dă un număr pozitiv X. Să se afișeze inversul (sau oglinditul) acestui număr.

Inversul unui număr se obține luând cifrele numărului de la dreapta la stânga. De exemplu,
pentru numărul 1234 se va afișa 4321.

Date de intrare

Se citește la tastatură numărul X.

Date de ieșire

Programul va afișa pe ecran oglinditul lui X.

Restricții

 0 < X < 1.000.000.000


 Se garantează că X nu are ultima cifră 0.

Exemplu

Date de intrare Date de ieșire

7612 2167
Un număr N este prim dacă singurii săi divizori sunt 1 și N. Ni se dă un număr și vrem să
verificăm dacă acesta este prim.

Un număr a este divizor al numărului b dacă b se împarte exact la a.

Idee
Pentru a face acest lucru vom parcurge toate numerele între 2 și N-1 și vom verifica dacă există
cel puțin un număr dintre ele care îl divide pe N.

Soluție
#include <iostream>
using namespace std;

int main() {
int N;
cin>>N;
int i = 2, este_prim = 1;
while (i < N) { // Parcurgem numerele de la 2 la N-1
if (N % i == 0) { // Daca N se divide la i
este_prim = 0; // Atunci N nu este prim
}
++i;
}
// Numarul 1 nu e prim prin conventie, desi nu are
// niciun divizor intre 1 si el insusi
if (N == 1) {
este_prim = 0;
}
if (este_prim == 1) {
cout<<"Numarul dat este prim";
} else {
cout<<"Numarul dat nu este prim";
}
return 0;
}
Pentru a ține minte dacă am găsit cel puțin un număr care să-l dividă pe N, folosim
variabila este_prim. Ea va fi inițializată la început cu 1. De fiecare dată când găsim un divizor
vom inițializa variabila este_prim cu 0. În cazul în care nu găsim nici un divizor, ea va
rămâne 1.

Această idee seamănă cu folosirea unui întrerupător. Inițial întrerupătorul este îndreptat în sus.
Atunci când găsim un divizor, poziția întrerupătorului va fi schimbată, acesta rămânând îndreptat
în jos.
Atunci când avem de scris un program mai complex, acesta poate fi împărțit în mai multe părți
din punct de vedere logic. De exemplu dacă ar trebui să implementez un program pentru un robot
care dă goluri, programul ar trebui să facă mai multe lucruri:

1. Ia mingea de la adversar
2. Depășește o parte din jucătorii din fața ta
3. Șutează pe poartă fără să lovești portarul
4. Dacă nu ai dat gol, revino la pasul 1

Fiecare pas din acest program presupune mai mulți pași intermediari. La fel și când rezolvăm o
problemă mai complexă avem nevoie să rezolvăm probleme mai simple și pe urmă să combinăm
aceste rezolvări pentru a rezolva problema inițială. Practic ca să rezolvăm problema marcării
unui gol, trebuie să rezolvăm prima dată cele 3 probleme mai "mici".

În realitate noi primim doar problema complexă și trebuie să ne dăm singuri seama care sunt
problemele intermediare pe care trebuie să le rezolvăm pentru a reuși să facem problema grea.

Exemplu
Se dau două numere a și b. Să se afișeze numărul care are suma cifrelor mai mare. Dacă cele 2
numere au aceeași sumă a cifrelor, se poate afișa oricare dintre ele. Exemplu: pentru a = 1112
și b = 99 se va afișa 99

Primul lucru pe care trebuie să-l facem pentru a rezolva problema este să ne dăm seama care sunt
problemele mai "mici" pe care trebuie să le rezolvăm pentru a rezolva problema inițială. Înainte
să citești mai departe, gândește-te cum ai împărți tu pe pași problema.

Etape

1. Aflarea sumei cifrelor lui a.


2. Aflarea sumei cifrelor lui b.
3. Compararea celor două sume și afișarea numărului cu sumă mai mare a cifrelor

Ce trebuie să facem acum este doar să implementăm cele 3 etape.

#include <iostream>
using namespace std;

int main() {
int a, b;
cin>>a>>b;
// deoarece atunci cand aflam numarul de cifre ale lui a si b, vom pierde
// valorile acestora, trebuie sa le facem inainte o copie pentru a sti
// valoarea lor initiala
int ca = a, cb = b;
int sumCifA = 0, sumCifB = 0;
// Etapa 1
while (a > 0) {
sumCifA += a % 10; // a += b; inseamna a = a + b;
a /= 10; // a /= b; inseamna a = a / b;
}
// Etapa 2
while (b > 0) {
sumCifB += b % 10;
b /= 10;
}
// Etapa 3
if (sumCifA > sumCifB) {
cout << ca;
} else {
cout << cb;
}
return 0;
}
Continuă

Dându-se un număr natural a, să se verifice dacă a și inversul (oglinditul) lui a sunt ambele
numere prime.

Date de intrare

Se citește de la tastatură numărul natural a.

Date de ieșire

Să se afișeze DA dacă numărul a și inversul său sunt ambele prime sau NU, în caz contrar.

Restricții și precizări

 1 ≤ a ≤ 300000
 a nu are ultima cifră 0

Exemplu

Date de intrare Date de ieșire


5 DA
122 NU
17 DA
Un număr natural se numeşte ABC dacă are exact a cifre, prima cifră este b şi ultima cifră este c.

Cerinţă

Fiind date a,b şi c, să se verifice dacă numărul x este ABC. Dacă condiţia este respectată afişaţi
"DA", altfel afişaţi "NU".

Date de intrare

Se vor citi de la tastatură numerele a, b, c şi x, având semnificaţiile din enunţ.

Date de ieşire

Se va afişa pe ecran doar "DA"(dacă numărul x este ABC) sau "NU"(numărul x nu este ABC).

Restricţii

1 < a < 9

0 < b, c < 10

1 < x < 2000000000

Exemplu

Date de intrare Date de iesire

3 1 2 102 DA

4 2 3 1234 NU
Așa cum putem folosi un if într-un alt if, la fel putem folosi un while într-un while,
un if într-un while, etc.

De exemplu putem avea un program care să aibă următoarea structură:

while (...) {
while (...) {
if (...) {
...
}
}
}
Acest lucru ne ajută să scriem programe mai complexe.

Pentru a înțelege cât mai bine această lecție, construiește tabelele de valori pentru cele două
exemple la fel ca și în lecția despre debug.

Exemplu 1
Pentru a înțelege cât mai ușor cum funcționează înlănțuirea, execută programul de mai jos în
CodeBlocks:

#include <iostream>
using namespace std;

int main() {
int n;
cin>>n;
int i = 1, j;
while (i <= n) {
cout << "i ia valoarea: " << i << "\n";
j = 1;
while (j <= n) {
cout << "j ia valoarea: " << j << "\n";
++j;
}
++i;
}
return 0;
}
Înainte să treci mai departe, asigură-te că înțelegi exact de ce se afișează numerele pe care le vezi
pe ecran. În cazul în care nu îți dai seama, gândește-te că poți să privești separat o instrucțiune
din interiorul altei instrucțiuni. Practic primul while așteaptă după cel de-al doilea.

Exemplu 2
Să se afișeze toate numerele mai mici sau egale cu un n dat care au suma cifrelor pară.
#include <iostream>
using namespace std;

int main() {
int n;
cin>>n;
int i = 1, copie, sum_cif;
while (i <= n) {
// ii facem o copie lui i pentru a nu pierde valoarea lui cand ii
// calculam suma cifrelor
copie = i;
sum_cif = 0;
// calculam suma cifrelor
while (copie > 0) {
sum_cif += copie % 10;
copie /= 10;
}
if (sum_cif % 2 == 0)
cout<<i<<' ';
++i;
}
return 0;
}
Înainte să treci mai departe, gândește-te și răspunde singur la următoarele întrebări:

 De ce a fost necesar să îi copiem valoarea lui i la intrarea în primul while?


 Ce s-ar fi întâmplat dacă nu am fi inițializat sum_cif cu 0 de fiecare dată când copiam
valoarea lui i?
 Ce rol are cel de-al doilea while?
 Ce va afișa cout la fiecare pas?

Dacă ai reușit să răspunzi la întrebări și îți sunt clare cele două exemple, înseamnă că ești pregătit
să treci mai departe.

Se dă un număr N. Să se afișeze următorul triunghi de numere:

N N-1 N-2 ... 2 1


N-1 N-2 ... 2 1
....
2 1
1
Mai exact, pe prima linie se vor afișa toate numerele de la N la 1, pe cea de-a doua linie toate
numerele de la N-1 la 1 etc. Numerele de pe fiecare linie vor fi despărțite prin spații.

Date de intrare

Se citește la tastatură numărul N.

Date de ieșire
Programul va afișa pe ecran triunghiul cerut mai sus.

Restricții

0 < N < 50

În multe probleme apare nevoia de a parcurge numere într-o anumită ordine. Ca să parcurgem
numerele de la 1 la n am putea folosi un while în felul următor:

#include <iostream>
using namespace std;

int main() {
int N;
cin>>N;
int i = 1;
while (i <= N) {
cout<<i<<" ";
++i;
}
return 0;
}

for
Pentru a parcurge mai ușor șiruri de numere în C++, putem să folosim for.

#include <iostream>
using namespace std;

int main() {
int N;
cin>>N;
int i;
for (i = 1; i <= N; ++i)
cout<<i<<" ";//afiseaza numerele de la 1 la N
for (i = N; i >= 1; --i)
cout<<i<<" ";//afiseaza numerele de la N la 1
return 0;
}
Deși nu aduce nimic nou față de instrucțiunea while, folosirea instrucțiunii for ajută la creșterea
clarității programelor. Este mult mai ușor să ne dăm seama ce se întâmplă când vedem

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


//instructiuni
față de

i=1;
while (i <=n) {
//instructiuni
++i;
}

Structura unui for


Instrucțiunea for este împărțită în 3 părți:

for (/* Partea 1 */; /* Partea 2 */; /* Partea 3*/)


Instrucțiunile din Partea 1 sunt executate o singură dată înainte de verificarea condiției.

Partea 2 conține condiția de terminare și este verificată înainte de rularea instrucțiunilor care
aparțin lui for.

Partea 3 se va executa după ce sunt rulate instrucțiunile cuprinse între acolade. După execuția
acestei părți, este verificată condiția din partea a doua, iar în cazul în care condiția nu mai este
îndeplinită, rularea programului continuă cu instrucțiunile de după for.

Practic putem scrie orice for cu ajutorul lui while în felul următor:

/*Partea 1*/;
while(/*Partea 2*/) {
//Instructiunile din for
/* Partea 3 */;
}
Atenție

În cazul instrucțiunilor if, while și for dacă în interiorul acestora se află o singură instrucțiune,
atunci nu mai trebuie să punem acolade care să marcheze instrucțiunile din interiorul acestora.

De exemplu

if (n % 2 == 0)
cout<<"n este par";
Întotdeauna când nu punem acolade, prima instrucțiune de după if, while sau for va fi
considerată ca făcând parte din structura folosită, iar următoarele vor fi considerate afară din
aceasta.

int i;
for (i = 1; i <= 5; ++i)
cout<<i<<" ";
cout<<"test";
După execuția acestui cod se va afișa pe ecran 1 2 3 4 5 test.
Se dă un număr N și apoi un șir de N numere. Se cere să se afișeze cel mai mic număr si cel mai
mare număr dintre cele Nnumere.

Date de intrare

Se citește la tastatură numărul N, urmat de un șir de N numere, ce reprezintă elementele șirului.

Date de ieșire

Programul va afișa pe ecran două numere separate printr-un spatiu intre ele, reprezentând cel mai
mic și cel mai mare număr din șir, în această ordine.

Restricții și precizări

 0 < N < 50
 Elementele șirului sunt numere întregi cu valori mai mari decât -100 și mai mici
decât 100.
 Când testezi programul, poți introduce numerele în program cu spațiu între ele, dar nu
uita să apeși enter după ce ai introdus toate numerele.

Exemplu

Date de intrare Date de ieșire


5 1 9
2 7 9 1 3
Gigel a învăţat că înmulţirea este de fapt o adunare repetată. El nu a înţeles încă acest concept şi,
pentru a înţelege, el are nevoie să îşi scrie fiecare etapă a înmulţirii.

Dându-se două numere naturale a şi b, scrieţi fiecare rezultat parţial al înmulţirii


numerelor a şi b, adică a, 2*a, 3*a, ... ,b*a.

Date intrare

De la tastatură se vor citi două numere, a şi b, cu semnificaţiile din enunţ(întotdeauna se scriu


multiplii primului număr).

Date de ieşire

Pe ecran se vor scrie b numere, fiecare rezultat parţial al înmulţirii celor două numere.

Restricţii

0 < a < 1001

0 < b < 1001

Exemplu

Date de intrare Date de ieşire

5 3 5 10 15

Se dă un număr x. Se cere să se afișeze al x-lea număr prim.

Date de intrare

Se citește la tastatură numărul x.

Date de ieșire

Programul va afișa pe ecran al x-lea număr prim.

Restricții

0 < x < 1 001

Exemplu 4 – 7
Nevoia de șir de numere
Atunci când lucrăm cu multe date, avem nevoie să le stocăm în variabile în așa fel încât să le
putem accesa ușor.

Exemplu
Numărul de apariții
Se dă un număr care conține cifrele 0, 1, 2 și 3. Să se afișeze de câte ori apare fiecare cifră în el.

Am putea să folosim 4 variabile și să verificăm dacă ultima cifră este egală cu 0, 1, 2 sau 3.

#include <iostream>
using namespace std;

int main() {
int n;
int n0 = 0,n1 = 0,n2 = 0,n3 = 0; // numarul de cifre de 0, 1, ...
cin>>n;
while (n > 0) {
if (n % 10 == 0)
++n0;
if (n % 10 == 1)
++n1;
if (n % 10 == 2)
++n2;
if (n % 10 == 3)
++n3;
n /= 10;
}
cout<<"Aparitiile lui 0: "<<n0<<"\n";
cout<<"Aparitiile lui 1: "<<n1<<"\n";
cout<<"Aparitiile lui 2: "<<n2<<"\n";
cout<<"Aparitiile lui 3: "<<n3<<"\n";
return 0;
}
Deja pentru 4 cifre programul este destul de lung și multe instrucțiuni sunt foarte asemănătoare
între ele. În cazul în care ar trebui să rezolvăm problema pentru 10 cifre, programul ar fi și mai
lung și mai repetitiv.

Gestionarea unor depozite


Să presupunem că deținem 100 de depozite pentru ciocolată. În fiecare zi vine un camion și
stochează x cutii de ciocolată în depozitul y. Vrem să știm la finalul fiecărei zile câte cutii de
ciocolată avem în fiecare depozit.
De exemplu să presupunem că în prima zi, vine un camion cu 20 de cutii de ciocolată și le
stochează în depozitul 3. În următoarea zi, camionul aduce 5 cutii de ciocolată în depozitul 23,
iar în a treia zi 2 cutii de ciocolată în depozitul 3.

La finalul celor trei zile vom avea 22 de cutii în depozitul 3 și 5 cutii în depozitul 23. Restul
depozitelor vor fi goale.

În viața reală
Foarte multe probleme din viața reală seamănă cu problema gestionării depozitelor. Ne putem
imagina că in loc de camioane avem utilizatori de pe Facebook, iar ciocolata sunt pozele pe care
aceștia le încarcă pe platformă. În fiecare moment Facebook trebuie să ne poată afișa pozele unui
anumit utilizator.

Același principiu se poate aplica și comentariilor și postărilor.

Așa cum ai văzut în lecția precedentă, avem nevoie de o modalitate ușoară de a accesa multe
variabile.

Pentru a face acest lucru, putem să folosim un șir de variabile. Avându-le într-un șir, putem să-i
dăm calculatorului instrucțiuni de forma:

Spune-mi care e valoarea variabilei de pe poziția 5 din șir


Modifică valoarea variabilei de pe poziția 8
Pentru a declara un șir de variabile este necesar să-i spunem calculatorului câte variabile să
conțină șirul și trebuie să-i dăm un nume șirului. Fiecare variabilă va fi identificată prin numele
șirului și poziția sa în șir.

În C++ putem declara un șir de variabile în felul următor:

int v[8];
Șirul v va conține 8 variabile numere întregi, prima va fi pe poziția 0, următoarea pe poziția 1, iar
ultima pe poziția 7.

Între [ și ] (paranteze drepte) vom specifica dimensiunea șirului. Dimensiunea șirului trebuie să
fie un număr întreg.

Întrucât variabilele se alocă în memoria RAM a calculatorului, trebuie să avem grijă ca atunci
când declarăm un șir de variabile, acesta să nu ocupe prea multă memorie. De exemplu, un
șir int de lungime 2 000 000 000 va ocupa 8 GB de memorie pentru că un int ocupă 4 bytes.

Accesarea și modificarea valorilor dintr-un șir


#include <iostream>
using namespace std;

int main() {
int a=3,b=4,c=5,v[10];
v[4]=5; // Atribuim o valoare variabilei de pe pozitia 4 din v
v[a]=5; // Atribuim elementului de pe pozitia valorii lui a (3)
// din v valoarea 5
v[b]+=v[a]; // Adaugam elementului de pe pozitia valorii lui b (4)
// valoarea de pe pozitia lui a din v
++a;
v[a]-=2; // Scadem 2 din valoarea lui v[a] adica v[4]
cout<<v[3]<<" "<<v[a];
return 0;
}
Așa cum vedem în exemplul de mai sus, variabilele dintr-un șir se folosesc la fel ca și variabilele
normale.

La finalul execuției programului se va afișa pe ecran 5 8

Atenție!

Atunci când accesăm poziții folosindu-ne de o variabilă, trebuie să avem grijă ca acea variabilă
să aibă o valoare care se încadrează în dimensiunea șirului. De exemplu dacă avem un șir
de 8 elemente putem accesa elemente de la poziția 0 până la poziția 7 (de
la 0 la 7 sunt 8 numere).

Exemplu
#include <iostream>
using namespace std;

int main() {
int v[8];
int a=-1;
cout<<v[a];// nu va afisa un element din v!
a=8;
cout<<v[a];// nu va afisa un element din v!
return 0;
}

Pentru a putea lucra cu un șir de numere, trebuie prima dată să inițializăm valorile din șir. În
problemele din acest curs va trebui, în general, să citim un șir de numere și să facem diverse
operații asupra lui, iar la final să-l afișăm.

Șirul de numere este dat, în general, prin numărul de elemente pe care le conține, urmat pe linia
următoare de elementele șirului:

6
12 -3 -6 1 -1 0

Citirea
Pentru a citi șirul de numere vom folosi următorul program:
#include <iostream>
using namespace std;

int main() {
int N, v[101];
int i;
cin>>N;
for (i = 1; i <= N; ++i)
cin>>v[i];
return 0;
}
Deși pozițiile din șir încep de la 0, vom folosi pe parcursul cursului pozițiile începând de la 1.
Facem acest lucru pentru ca primul element citit să fie pe poziția 1, al doilea pe poziția 2, etc.
Din acest motiv este important să declarăm șirurile pe care le folosim să aibă dimensiunea mai
mare decât numărul maxim de elemente citite (un șir de 8 elemente are poziții de la 0 la 7).

Afișarea
Pentru a afișa elementele dintr-un șir de numere este suficient să parcurgem pozițiile unde am
pus numere și să afișăm valoarea de la acea poziție:

#include <iostream>
using namespace std;

int main() {
int N, v[101];
int i;
cin>>N;
// Prima data citim sirul
for (i = 1; i <= N; ++i)
cin>>v[i];
// Si il afisam
for (i = 1; i <= N; ++i)
cout<<v[i]<<' ';
return 0;
}
În cazul în care vrem să afișăm elementele de la dreapta la stânga adică de la cea mai mare
poziție la cea mai mică, trebuie doar să parcurgem pozițiile în ordine inversă:

for (i = N; i >= 1; --i)


cout<<v[i]<<' ';
Fiind dat un șir de N numere întregi, să se afișeze pe ecran șirul cu urmatoarele modificări:

 Numerele de pe poziții pare vor fi înmulțite cu 2


 Numerele de pe poziții impare vor fi scazute cu 1

Pozițiile elementelor în șir sunt numerotate de la 1 până la N.

Date de intrare

Se vor citi:

 De pe prima linie, un număr întreg N


 De pe a doua linie, un șir de N numere întregi, separate prin spații

Date de ieșire

Pe ecran se va afișa șirul modificat. Elementele șirului vor fi separate prin spații.

Restricții

 N < 1000
 Numerele din șir vor fi mai mari decât -10000 și mai mici decât 10000

Exemplu

Date de intrare Date de ieșire


5 0 4 2 8 4
1 2 3 4 5
Să zicem că trebuie să rezolvăm următoarea problemă:

Se citește un vector cu n elemente. Să se determine suma valorilor elementelor cuprinse între


primul și ultimul element par al vectorului, inclusiv acestea.

Soluție
Pentru a afla suma cerută vom afla prima poziție a unui element par precum și ultima poziție a
unui element par. Având aceste poziții vom însuma valorile elementelor aflate pe poziții cuprinse
între cele două poziții aflate.

Aflarea primei poziții pare


Pentru a afla prima poziție a unui element par vom parcurge șirul de la început la sfârșit și atunci
când găsim primul element par, vom inițializa o variabilă cu poziția sa. Secvența de cod care face
acest lucru arată în felul următor. Vom presupune că lungimea șirului este N și că acesta a fost
citit în v.

int i,prima_pozitie;
for (i = 1; i <= N; ++i)
if (v[i] % 2 == 0) // am gasit un numar par
prima_pozitie = i;
Totuși codul este greșit. Înainte să citești mai departe, poți să-ți dai seama de ce este greșit?

Greșeala constă în faptul că variabila prima_pozitie va fi reinițializată de fiecare dată când


găsim un număr par, deci nu va reține prima poziție pară. Pentru a repara greșeala, avem nevoie
să știm dacă variabila a fost deja inițializată pentru a nu o inițializa din nou. Putem face acest
lucru inițializând-o înainte de for cu valoarea -1 și să o inițializăm doar dacă are valoarea -1.

int i,prima_pozitie = -1;


for (i = 1; i <= N; ++i)
if (v[i] % 2 == 0 && prima_pozitie == -1) // am gasit un numar par
prima_pozitie = i;
Folosind operatorul &&, putem să simplificăm programul

int i,prima_pozitie = -1;


for (i = 1; i <= N && prima_pozitie == -1; ++i)
if (v[i] % 2 == 0)
prima_pozitie = i;
În această variantă execuția lui for se va opri la găsirea primului număr par deoarece
variabila prima_pozitie nu va mai fi egală cu -1.

Aflarea ultimei poziții pare


Pentru aflarea ultimei poziții pare, aplicăm aceeași metodă doar că de data asta vom parcurge
șirul de la sfârșit la început.

int i,ultima_pozitie = -1;


for (i = N; i >= 1 && ultima_pozitie == -1; --i)
if (v[i] % 2 == 0)
ultima_pozitie = i;
O altă variantă ar fi să aflăm și prima și ultima poziție pară în cadrul aceluiași for.

int i,prima_pozitie = -1, ultima_pozitie;


for (i = 1; i <= N; ++i)
if (v[i] % 2 == 0) {
if (prima_pozitie == -1)
prima_pozitie = i;
ultima_pozitie = i;
}
În această variantă, vom actualiza ultima_pozitie de fiecare dată când găsim un număr par.

Aflarea sumei

Având prima și ultima poziție calculate, mai trebuie să aflăm doar suma parcurgând pozițiile de
la prima la ultima poziție pară.

int suma = 0;
for (i = prima_pozitie; i <= ultima_pozitie; ++i)
suma += v[i];

___________________________________________________________________-_

In lucrul cu șiruri, de multe ori este nevoie să adăugăm elemente într-un șir. De exemplu atunci
când primim un mail nou, acesta se adaugă la șirul mailurilor primite.

Adăugarea unui număr la finalul unui șir


Atunci când avem un șir de numere v de lungime N și vrem să adăugăm un număr x la finalul lui,
trebuie doar să creștem lungimea șirului cu 1 și să punem pe ultima poziție numărul x.

De exemplu dacă vrem să îl adăugăm pe 6 la finalul șirului 9 7 2 vom obține șirul 9 7 2 6.

Codul pentru a face acest lucru este următorul:

++N;
v[N] = x;
Adăugarea unui număr la o poziție dată
Lucrurile se complică atunci când vrem să adăugăm un număr pe o poziție dată. Pentru șirul 6 4
2 9 8 7 dacă dorim să adăugăm numărul 5 pe poziția 3, șirul obținut va fi 6 4 5 2 9 8 7.
Ca în cazul anterior, trebuie să creștem lungimea șirului. Pe lângă creșterea lungimii, trebuie să
mutăm toate elementele de pe poziții mai mari sau egale cu poziția pe care adăugăm elementul
cu o poziție la dreapta.

#include <iostream>
using namespace std;

int main() {
int N, v[100], i;
cin>>N;
for (i = 1; i <= N; ++i) // Citim sirul
cin>>v[i];
int x,p; // Numarul si pozitia pe care sa fie inserat
cin>>x>>p;
++N; // Crestem lungimea sirului
for (i = N; i > p; --i) // Mutam elementele cu o pozitie la dreapta
v[i]=v[i-1];
v[p]=x;
for (i = 1; i <= N; ++i) // Afisam sirul
cout<<v[i]<<' ';
return 0;
}

Fie că ștergem un comentariu care nu ne place de la o postare sau un mail pe care l-am citit,
acestea sunt exemple de cazuri când ștergem elemente din șiruri.

Ștergerea unui număr de la finalul unui șir


Pentru șirul 1 2 3 4 5 după ștergerea ultimului element șirul va deveni 1 2 3 4.

Pentru a face acest lucru trebuie doar să scădem dimensiunea șirului cu 1. Dacă lungimea șirului
o avem salvată în variabila N, atunci o vom scădea cu 1 în felul următor: --N;. Acum de fiecare
dată când vom lucra cu șirul, vom lucra cu noua lungime așa că ultimul element va fi ignorat.

Ștergerea unui număr de la o poziție dată


Pentru șirul 5 4 1 9 2 3 dacă vrem să ștergem elementul de pe poziția 4 vom obține șirul 5 4
1 2 3.

Când ștergem un element de la o poziție dată, pentru a nu rămâne cu o "gaură" în șir, trebuie să
mutăm toate elementele de la dreapta elementului șters cu o poziție spre stânga și să scădem
lungimea șirului cu 1.
#include <iostream>
using namespace std;

int main() {
int N, v[100], i;
cin>>N;
for (i = 1; i <= N; ++i) // Citim sirul
cin>>v[i];
int p; // Pozitia de pe care vrem sa stergem
cin>>p;
for (i = p; i < N; ++i) // Mutam elementele cu o pozitie la stanga
v[i]=v[i+1];
--N; // Scadem lungimea sirului
for (i = 1; i <= N; ++i) // Afisam sirul
cout<<v[i]<<' ';
return 0;
}
Continuă
Dă quiz-ul din nou
Rezultate quiz

se dă un șir de N numere naturale. Să se afișeze elementele pare în ordine inversă a pozițiilor.

Date de intrare

Pe prima linie se citește numărul natural N, iar pe urmatoarea linie N numere naturale, separate
prin spații.

Date de ieșire

Programul va afișa pe ecran numerele cerute, separate prin spații.

Restricții

N <= 1 000
0 < valorile din șir <= 1 000

Exemplu

Date de intrare Date de ieșire

4 4 2
1 2 3 4
Fiind dat un șir de N numere întregi pozitive, să se afișeze pe ecran numerele șirului inițial, cu
următoarele modificări:

 Numerele pare se vor afla pe primele poziții din șir, în ordine crescătoare a pozițiilor
în șirul inițial.
 Numerele impare se vor afla după numerele pare, în ordine descrescătoare a
pozițiilor în șirul inițial.

Date de intrare

Se vor citi:

 Un număr întreg N
 Un șir de N numere întregi pozitive

Date de ieșire

Pe ecran se va afișa șirul modificat.

Restricții

 N < 1000
 Numerele din șir vor fi mai mici sau egale cu 1000

Exemplu

Date de intrare Date de ieșire


5 8 4 5 7 1
1 8 7 4 5
De multe ori în practică apare nevoia sortării unui șir de numere. Atunci când ne citim mailurile,
acestea sunt sortate în funcție de data și de ora la care le-am primit. La fel și postările de pe
Facebook sunt sortate în funcție de numărul de like-uri și comentarii precum și în funcție de dată.

Se dă un șir de numere. Să se afișeze numerele din șir în ordine crescătoare.

Există mai multe metode de a sorta un șir, iar pe parcursul cursului vor fi prezentate mai multe
dintre ele. În această lecție va fi prezentată cea mai simplă dintre ele: sortarea prin selecție.

Sortarea prin selecție


Având un șir de N numere, știm sigur că cel mai mic element din șir va fi pe prima poziție. Dacă
excludem acest element din șir, al doilea element din șirul sortat va fi cel mai mic element din
șirul rămas.

Să presupunem că avem șirul cu 6 elemente 7 1 3 2 9 3. Algoritmul va funcționa în felul


următor:

1. Alegem minimul din șirul 7 1 3 2 9 3. Minimul va fi 1. Îl punem pe prima poziție în șirul


sortat.
2. Alegem minimul din șirul 7 3 2 9 3. Minimul va fi 2. Îl punem pe a doua poziție în șirul sortat.
3. Alegem minimul din șirul 7 3 9 3. Minimul va fi 3. Îl punem pe a treia poziție în șirul sortat.
4. Alegem minimul din șirul 7 9 3. Minimul va fi 3. Îl punem pe a patra poziție în șirul sortat.
5. Alegem minimul din șirul 7 9. Minimul va fi 7. Îl punem pe a cincea poziție în șirul sortat.
6. Alegem minimul din șirul 9. Minimul va fi 9. Îl punem pe a șasea poziție în șirul sortat.

Dacă am formula algoritmul în limbaj natural, ar suna în felul următor:

Cât timp mai avem elemente în șir


Extragem elementul cel mai mic din șir și îl adăugăm la finalul șirului sortat
Implementare
Dacă am implementa algoritmul în varianta de mai sus, am avea bătăi de cap deoarece ar trebui
să lucrăm cu două șiruri: să ștergem elemente dintr-un șir și să le adăugăm în celălalt șir.

Din fericire există o metodă mult mai simplă de a implementa acest algoritm folosind un singur
șir.

#include <iostream>
using namespace std;

int main() {
int N, v[100], i, j, aux;
cin>>N;
for (i = 1; i <= N; ++i) // Citim sirul
cin>>v[i];
for (i = 1; i < N; ++i)
for (j = i + 1; j <= N; ++j) // Cautam elemente mai mici decat v[i]
if (v[i] > v[j]) { // Am gasit un element mai mic
// Punem valoarea din v[j] in v[i] si din v[i] in v[j]
aux = v[i];
v[i] = v[j];
v[j] = aux;
}
for (i = 1; i <= N; ++i) // Afisam sirul
cout<<v[i]<<' ' ;
return 0;
}
În această variantă pentru fiecare poziție notată cu i de la 1 la N-1 căutăm elementul cu valoare
minimă de pe pozițiile de la ila N, iar de fiecare dată când găsim un element mai mic decât v[i],
îl punem pe acesta în locul lui v[i] și vechea valoare care se afla în v[i] o punem în locul
elementului găsit.

Practic facem un schimb de poziții între cele două elemente ale șirului de numere, folosindu-ne
de o variabilă auxiliară care să rețină valoarea unuia dintre elementele care își schimbă locul.
De multe ori când lucrăm cu date, acestea sunt organizate sub forma unui tabel. De exemplu
putem organiza apelurile de pe telefon în felul următor:

Număr telefon Ziua Luna Anul Ora


0720080571 26 5 2017 12
0724782129 16 6 2017 18
0743882529 4 9 2017 8
Dacă am stoca aceste date într-un șir, acesta ar arăta în felul următor:

0720080571 26 5 2017 12 23 12 0724782129 16 6 2017 18 11 0 0743882529 4 9


2017 8 1 59

Așa cum poți vedea, este foarte greu să ne dăm seama pentru fiecare element pe ce rând și pe ce
coloană s-ar afla.

Pentru a ne ușura munca cu acest gen de date, în C++ putem lucra cu șiruri bidimensionale
(matrice).

Matricele sunt șiruri de numere în care fiecare număr este identificat prin două poziții: indicele
rândului (liniei) și al coloanei pe care se află.

Așa cum se vede în figură, liniile se numără de sus în jos, iar coloanele de la stânga la dreapta.

Numerele de pe linia 0 sunt 1,5, 11 și 8, cele de pe linia 1 sunt 7,2, 3 și 9, iar cele de pe
linia 2 sunt 10,6, 4 și 0.

La fel, numerele de pe coloana 0 sunt 1, 7 și 10 etc.

Fiecare număr din matrice poate fi găsit după indicele liniei și al coloanei de pe care face
parte. 3 este pe linia 1 coloana 2, 0este pe linia 2 coloana 3.
La fel ca în cazul șirurilor, vom folosi elementele din matrice începând de la linia și coloana 1.

Putem declara în C++ o matrice cu 3 linii și 4 coloane în felul următor:

int a[3][4];

Exemplu
În problemele cu matrice vom primi prima dată numărul de linii și numărul de coloane ale
matricei urmate de elementele din ea. Un exemplu de date de intrare care conțin o matrice e
următorul:

5 4
1 2 2 5
6 9 1 1
8 7 6 2
2 2 2 2
3 5 2 7
În exemplul de mai sus este o matrice cu 5 linii și 4 coloane. Exemplul are pe prima linie
numărul de linii, urmat de numărul de coloane. Pe următoarele 5 linii se află câte 4 numere
reprezentând numerele de pe fiecare coloană din linia respectivă din matrice.

Citirea
Pentru a citi o matrice, prima dată vom citi în două variabile N și M numărul de linii și de coloane
ale matricei. Pe urmă vom citi conținutul matricei în felul următor: pentru fiecare linie din
matrice vom parcurge toate numerele de pe ea și le vom citi în coloana corespunzătoare.

#include <iostream>
using namespace std;

int main() {
int N, M, matrice[100][100];
cin>>N>>M;
int i,j;
for (i = 1; i <= N; ++i)
for (j = 1; j <= M; ++j)
cin>>matrice[i][j];
return 0;
}

Afișarea
La fel ca și în cazul șirurilor, felul în care se face afișarea este foarte similar cu cel în care se face
citirea. Pentru a afișa, parcurgem elementele pe linii și le afișăm.

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


for (j = 1; j <= M; ++j)
cout<<matrice[i][j]<<" ";
cout<<"\n"; // face afisarea sa treaca pe rand nou
}
_____________________________________________________________________
-

Transpusa unei matrice este o matrice obținută prin oglindire față de diagonala principală.

În practică, calculăm transpusa unei matrice transformând fiecare linie în coloană. Elementele de
pe linia 1 vor deveni elementele coloanei 1, elementele de pe linia 2 vor deveni elementele
coloanei 2 etc. Același lucru se întâmplă și cu coloanele: coloana 1va deveni linia 1 etc.

Exemplu
Matricea Matricea
inițială transpusă

3 4 4 3
1 2 3 4 1 5 9
5 6 7 8 -> 2 6 1
9 1 2 3 3 7 2
4 8 3
Implementare
Pentru a afișa transpusa unei matrice putem să facem în felul următor

#include <iostream>
using namespace std;

int main() {
int linii, coloane, mat[35][35];
int linii_transpusa, coloane_transpusa, transpusa[35][35];
int i,j;
// Citire
cin>>linii>>coloane;
for (i = 1; i <= linii; ++i)
for (j = 1; j <= coloane; ++j)
cin>>mat[i][j];
// Calculare transpusa
linii_transpusa = coloane;
coloane_transpusa = linii;
for (i = 1; i <= linii; ++i)
for (j = 1; j <= coloane; ++j) {
// linia i devine coloana i, coloana j devine linia j
// asta inseamna ca elementul care a fost pe linia i, coloana j
// va ajunge pe coloana i, linia j
transpusa[j][i] = mat[i][j];
}
for (i = 1; i <= linii_transpusa; ++i) {
for (j = 1; j <= coloane_transpusa; ++j)
cout<<transpusa[i][j]<<" ";
cout<<"\n";
}
return 0;
}
Alege toate secvențele de instrucțiuni care afișează pe ecran transpusa transpusei matricei a,
unde matricea e citită de la tastatură.

a. int a[100][100], n, m, i, j;
b.
c. cin >> m >> n; // matricea are m linii si n coloane
d. for (i = 1; i <= m; i++)
e. for (j = 1; j <= n; j++)
f. cin >> a[i][j];
g. for (i = 1; i <= m; i++) {
h. for (j = 1; j <= n; j++)
i. cout << a[i][j] << " ";
j. cout << "\n";
k. }
l.

m. int a[100][100], n, m, i, j;
n.
o. cin >> m >> n; // matricea are m linii si n coloane
p. for (i = 1; i <= m; i++)
q. for (j = 1; j <= n; j++)
r. cin >> a[i][j];
s. for (i = 1; i <= m; i++) {
t. for (j = 1; j <= n; j++)
u. cout << a[j][i] << " ";
v. cout << "\n";
w. }
x.

y. int a[100][100], n, m, i, j, transpusa[100][100], transpusa2[100][100];


z.
aa. cin >> m >> n; // matricea are m linii si n coloane
bb. for (i = 1; i <= m; i++)
cc. for (j = 1; j <= n; j++)
dd. cin >> a[i][j];
ee.
ff. for (i = 1; i <= m; i++)
gg. for (j = 1; j <= n; j++)
hh. transpusa[j][i] = a[i][j];
ii.
jj. for (i = 1; i <= n; i++)
kk. for (j = 1; j <= m; j++)
ll. transpusa2[j][i] = transpusa[i][j];
mm.
nn. for (i = 1; i <= m; i++) {
oo. for (j = 1; j <= n; j++)
pp. cout << transpusa2[i][j] << " ";
qq. cout << "\n";
rr. }
Se dă o matrice cu N linii și M coloane, și un număr natural K. Să se șteargă linia K din matrice.

Date de intrare

Pe prima linie se citesc la tastatură numerele N, M, respectiv K, cu semnificațiile din enunț. Pe


următoarele N linii se citesc Mnumere naturale.

Date de ieșire

Programul va afișa pe ecran matricea cu a K-a linie ștearsă.

Restricții

1 < K <= N,M < 500

Exemplu

Date de intrare Date de ieșire

332 123
123 789
456
789

Se dă o matrice cu N linii și M coloane, și un număr natural K. Să se ștearga coloana K din matrice.

Date de intrare

Pe prima linie se citesc la tastatură numerele N, M, respectiv K, cu semnificațiile din enunț. Pe


următoarele N linii se citesc Mnumere naturale.

Date de ieșire

Programul va afișa pe ecran matricea cu a K-a coloană ștearsă.

Restricții

1< K < N,M < 401

Exemplu

Date de intrare Date de ieșire

332 13
Date de intrare Date de ieșire

123 46
456 79
789

O matrice pătratică este o matrice în care numărul liniilor este egal cu cel al coloanelor. Un
exemplu de date de intrare în care este dată o matrice pătratică este următorul:

5
3 2 1 9 8
1 1 2 7 7
6 4 3 3 3
1 1 9 8 5
5 2 1 4 2
Citirea
int N, m[101][101], i, j;
cin>>N; // Citim numarul de linii si de coloane
for (i = 1; i <= N; ++i) // Parcurgem fiecare linie
for(j = 1; j <= N; ++j) // si fiecare coloana
cin>>m[i][j]; // citim elementul de pe linia i, coloana j
Afișarea se face în mod similar.

Diagonale
Datorită faptului că matricea are formă pătratică, aceasta are două diagonale. Diagonala care
pornește din colțul din stânga sus și se termină în cel din dreapta jos se numește diagonală
principală, iar cea care începe în dreapta sus și se termină în stânga jos se numește diagonală
secundară.
Pe lângă parcurgerea de sus în jos de la stânga la dreapta mai putem parcurge matricea și în alte
feluri.

Când vrem să parcurgem o matrice, ne ajută să folosim un desen în care să avem marcate
coordonatele fiecărei celule:

Primul număr din fiecare celulă reprezintă indicele liniei, iar al doilea număr reprezintă indicele
coloanei. La fel ca și în lecțiile anterioare, am folosit indici începând de la 1.
Parcurgerea diagonalelor

În figura de mai sus am marcat cu albastru diagonala principală, iar cu roșu diagonala secundară.
Elementul 3, 3 face parte din ambele diagonale, așa că a fost marcat și cu roșu, și cu albastru.

Parcurgerea diagonalei principale

Așa cum se vede în figură, elementele de pe diagonala principală au indicele liniei egal cu cel al
coloanei.

// Afisarea valorilor elementelor de pe diagonala principala


int i;
for (i = 1; i <= N; ++i)
cout<<m[i][i];
Parcurgerea diagonalei secundare

Atunci când vrem să parcurgem diagonala secundară de sus în jos, observăm că indicele coloanei
scade de fiecare dată când indicele liniei crește. La primul pas indicele liniei este egal cu 1, iar
cel al coloanei cu numărul de coloane adică N. Știind acest lucru, putem să deducem o formulă
pentru indicele coloanei în funcție de indicele liniei. Indicele coloanei va fi egal cu N -
indice_linie + 1

// Afisarea valorilor elementelor de pe diagonala secundara


int i;
for (i = 1; i <= N; ++i)
cout<<m[i][N - i + 1];

În acest capitol îți voi arăta probleme mai complicate care se rezolvă cu șiruri de numere. Te-ai
gândit vreodată cum de filmele de pe youtube se încarcă așa de repede deși pe serverele lor sunt
milioane de GB de filme? Un alt lucru interesant este viteza cu care funcționează Facebook, o
rețea socială cu peste 2 miliarde de utilizatori.

Fiecare operație executată de calculator durează un anumit timp, să zicem o milisecundă* (a


1000-a parte dintr-o secundă). Pentru a parcurge un șir cu 2 000 000 000 elemente presupunem
că am face 2 000 000 000 de operații (câte una pentru fiecare element). Toate aceste operații ar
dura în total 2 000 000 000 / 1000 = 2 000 000 secunde adică peste 23 de zile. Așa că ne
întrebăm: cum putem lucra cu șiruri mari de numere fără să așteptăm o lună să aflăm rezultatul
programelor?

*Durata unei operații depinde foarte mult de viteza procesorului pe care e rulat programul tău,
dar și de tipul operației. Unele operații sunt mult mai lente decât altele.

Exemple de probleme
Înainte să treci la lecția următoare citește următoarele probleme și gândește-te la felul în care le-
ai rezolva.

Se dă un șir de numere (presupunem că este deja stocat într-o variabilă de tip șir) în ordine
crescătoare și un element x. Cum poți afla dacă x se află în șir fără să parcurgi tot șirul de la
stânga la dreapta?

La alegeri participă N candidați și M alegători. Pentru fiecare alegător știi cu care dintre
cei N candidați a votat. Cum poți afla ce candidat a câștigat alegerile făcând un număr cât mai
mic de operații? Un candidat câștigă alegerile dacă obține cel puțin M/2voturi.
Hai să revenim la problema din introducerea capitolului.

M candidați participă la alegeri. În ziua votului N alegători și-au exprimat voturile. Să se afle
câte voturi a obținut fiecare candidat.

Putem reformula problema în felul următor: Se dă un șir cu N elemente. Să se afle de câte ori
apare fiecare element de la 1 la M în șirul dat.

Soluţia 1
O primă idee ar fi să parcurgem fiecare element de la 1 la M și să numărăm de câte ori apare în
șir.

#include <iostream>
using namespace std;

int main() {
int N, M, v[1001];
cin>>N>>M;
for (int i = 1; i <= N; ++i)
cin>>v[i];

int nr;// numarul de aparitii ale fiecarui element


for (int i = 1; i <= M; ++i) {
nr = 0;
for (int j = 1; j <= N; ++j)
if(i == v[j])
++nr;
cout<<"Elementul "<<i<<" apare de "<<nr<<" ori\n";
}
return 0;
}
cout<<"\n"; face ca afișarea să treacă pe linia următoare.

Această soluție este foarte lentă. Pentru fiecare număr de la 1 la M sunt parcurse toate
cele N numere din șir deci se vor face N*Mcalcule.

Soluţia 2
Această soluţie se bazează pe construirea unui şir suplimentar, pe care îl vom numi şir de apariții
(sau șir de frecvență). Şirul de apariții va reţine de câte ori apare fiecare element în șirul dat.
Pentru a fi mai expliciţi, vom nota şirul cu AP. Astfel, poziţia i din şirul AP va reţine numărul de
apariţii al numărului i în şirul dat. La început şirul AP conţine doar valori 0. Când întâlnim o
nouă valoare (o vom nota x), va trebui doar să incrementăm poziţia x din AP. După ce am parcurs
toate numerele, vom parcurge şirul AP şi vom afişa fiecare valoare din el.

Să luam un exemplu:

Se dă şirul: 1 2 4 3 2 5
Elementul curent va fi notat cu x, iar şirul AP va conţine 5 elemente, iniţial 0, care vor fi scrise
unul după altul, separate printr-un spaţiu, reprezentând numărul de apariţii ale numerelor 1,2,3,4,
respectiv 5(în această ordine).

x(elementul curent) AP (şirul de apariții)

1 10000

2 11000

4 11010

3 11110

2 12110

5 12111

Un cod care utilizează această soluţie este următorul.

#include <iostream>
using namespace std;

int main()
{
int N, M, v[1001], AP[1001];
cin>>N>>M;
// Initializam cu 0 fiecare pozitie pentru a putea numara aparitiile
for (int i = 1; i <= M; ++i)
AP[i] = 0;

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


cin>>v[i];
// Actualizam sirul de aparitii
for (int i = 1; i <= N; ++i)
++AP[v[i]];// Crestem numarul de aparitii ale elementului v[i]
for (int i = 1; i <= M; ++i)
cout<<"Elementul "<<i<<" apare de "<<AP[i]<<" ori\n";
return 0;
}
Acest cod este mai scurt şi mai simplu de înţeles.

Observaţii

1. Dimensiunea șirului de frecvență trebuie setată în funcție de cât de mari pot fi elementele din șir.
Dacă elementele șirului sunt numere până la 1000, atunci trebuie să setăm dimensiunea șirului la
cel puțin 1001.
2. Această soluție este mai eficientă și face doar 2 parcurgeri de la 1 la M și 2 parcurgeri de la 1 la N.
3. Şirurile de frecvenţă sunt utile în cazul în care trebuie să reţinem numărul de apariţii ale tuturor
numerelor dintr-un şir.
4. Şirurile de frecvenţă se pot folosi în cazul în care numerele care trebuie gestionate fac parte dintr-
un domeniu restrâns. Spre exemplu un şir de frecvenţă care ar ţine minte numărul de apariţii
pentru numere până la un miliard ar ocupa mai mult de 3 GB. De obicei, se folosesc pentru a
reţine numărul de apariţii al unor numere dintr-o mulţime de maxim un milion de elemente.

Se dă un șir sortat și un element x. Să se afle dacă x apare în șirul dat.

Această problemă este dată frecvent la interviuri. De multe ori apare sub forma:

Să se afișeze prima poziție dintr-un șir sortat pe care se află un element mai mare sau egal cu un
element x dat

Soluție 1
Pentru a rezolva prima problemă, putem parcurge șirul de la stânga la dreapta și să verificăm,
pentru fiecare element, dacă este egal cu x.

int gasit = 0, i;
for (i = 1; i <= N; ++i)
if (v[i] == x)
gasit = 1;
if (gasit == 1)
cout<<"x apare in sir";
else
cout<<"x nu apare in sir";
Cu cât șirul devine mai lung, numărul de operații va fi din ce în ce mai mare.

Soluție 2
Idee
Pentru a rezolva mai eficient problema, trebuie să ne folosim de faptul că șirul este sortat.

Dacă alegem o poziție p din șir, iar v[p] este mai mare sau egal decât x, atunci știm că x se va
afla în secvența de elemente de la poziția 1 până la poziția p. Vom nota această secvență [1 ...
p]. În cazul în care x este mai mare, atunci vom căuta în secvența [p + 1 ... N].
Exemplu
Fie șirul 1 2 5 11 12 23 25.

Dorim să aflăm dacă elementul 5 se află în șir. Dacă ne uităm la poziția 4, observăm că putem să
căutăm doar în secvența [1 ... 4] din șir deoarece 11 >= 5.

Acum avem de rezolvat aceeași problemă pentru un șir mai mic: 1 2 5 11. Dacă alegem
poziția 2, 2 < 5, deci va fi suficient să-l căutăm pe 5 în secvența [3 ... 4].

Generalizare
La fiecare pas vom împărți șirul în două subsecvențe care nu au nici un element în comun și care
împreună formează șirul de la care am pornit la pasul curent. În funcție de elementul pe care
vrem să-l găsim în șir, vom alege una dintre secvențe și îl vom căuta acolo folosind același
procedeu.

Eficiență
Eficiența algoritmului depinde de felul în care alegem poziția la care împărțim șirul. Dacă
alegem tot timpul începutul șirului, iar xeste mai mare decât elementul de la început, vom
parcurge tot șirul.

Pentru șirul 1 2 3 4 5 dacă căutăm elementul 5 și alegem prima poziție, la pasul următor, vom
căuta pe 5 în șirul 2 3 4 5, la pasul următor în 3 4 5 și așa mai departe.

Pentru a obține eficiența maximă, ne vom uita la elementul de la mijlocul șirului. Astfel
indiferent dacă trebuie să alegem subsecvența din stânga sau pe cea din dreapta, vom efectua
puțini pași.
Exemplu 2
e dă un șir v cu N numere ordonate crescător și un număr x. Să se afle dacă există un element cu
valoarea x în șir.

Pentru a implementa ideea din lecția precedentă vom ține minte la fiecare pas capătul din stânga
(îl notăm st) și capătul din dreapta (îl notăm dr) al secvenței în care căutăm elementul x.

Pentru a trece la pasul următor, vom compara elementul din mijlocul subsecvenței cu x și vom
actualiza capătul din stânga sau capătul din dreapta.

Dacă x este mai mare decât v[m] (am notat m mijlocul secvenței la care ne aflăm), atunci vom
căuta în subsecvența din dreapta [m+1 ... dr]. În caz contrar vom căuta în subsecvența din
stânga [st ... m].

Pentru a afla mijlocul unei subsecvențe care începe la poziția st și se termină la poziția dr este
suficient să aflăm media celor două capete. Astfel mijlocul m va fi m=(st+dr)/2.

Terminarea algoritmului
Atunci când ajungem să avem o secvență cu un singur element, putem verifica dacă acel element
este egal cu x. Putem face acest lucru deoarece la fiecare pas al algoritmului am avut grijă ca în
stânga subsecvenței să avem tot timpul elemente mai mici sau egale cu elementul căutat, iar in
dreapta elemente mai mari.

Implementare
#include <iostream>
using namespace std;

int main()
{
int N, v[100001], x;
cin>>N;
for (int i = 1; i <= N; ++i)
cin>>v[i];
cin>>x;

// Prima data cautam in tot sirul, de la pozitia 1 pana la N


int st = 1, dr = N, m;
// Cat timp subsecventa are mai mult de un element, o injumatatim alegand
// fie jumatatea din stanga, fie cea din dreapta
while (st < dr) {
m = (st + dr) / 2;
if (v[m] < x)
st = m + 1;
else
dr = m;
}
// am ajuns la o subsecventa cu un singur element
if (v[st] == x)
cout<<"x se gaseste in sir";
else
cout<<"x nu se gaseste in sir";
return 0;
}
Observație Când folosim for (int i = 1; i <= N; ++i) variabila i va fi alocată doar în
interiorul lui for. După terminarea lui, ea nu va mai putea fi accesată.

Exemplu
Pentru șirul 1 2 3 4 5 și elementul 5, se va căuta elementul în următoarele intervale:

pozițiile între care căutăm poziția mijlocu

[1, 5] 3

[4, 5] 4

La final, stânga devine egală cu dreapta, având intervalul [5, 5]. Aici algoritmul se va opri.
Se dă un șir v cu N numere întregi. Să se ordoneze crescător elementele acestuia

Pe lângă metoda de sortare pe care am prezentat-o acum câteva lecții, putem ordona crescător
elementele unui șir folosindu-ne și de alte idei.

Idee
Un lucru care ne poate ajuta când vrem să sortăm șiruri este să ne gândim cum putem să ne dăm
seama că un șir nu este sortat.

Un șir nu este sortat dacă reușim să găsim o poziție i (i merge de la 1 la N-1) în așa fel
încât v[i] > v[i + 1], adică dacă există două elemente consecutive care nu sunt în ordine
crescătoare.

Dacă de fiecare dată când găsim două astfel de numere, le interschimbăm, vom avea la un
moment dat un șir sortat întrucât vom rămâne fără elemente pe care să le interschimbăm.

Exemplu
Implementare
Implementarea ideii este foarte simplă: cât timp găsim elemente care nu sunt în ordine
crescătoare, le căutăm și le interschimbăm.

#include <iostream>
using namespace std;

int main()
{
int N, v[100001];
cin>>N;
for (int i = 1; i <= N; ++i)
cin>>v[i];

// Variabila sortat ne spune daca am gasit elemente in ordine descrescatoare


// la pasul curent. Daca am gasit, atunci va mai trebui sa parcurgem sirul o
// data si sa vedem daca mai exista si alte elemente in ordine descrescatoare
// Initializam sortat cu 0 pentru ca inca nu am facut nici o cautare, deci nu
// stim daca sirul este sortat
int sortat = 0, aux;
while (sortat == 0) {
// vom initializa sortat cu 0 cand gasim numere in ordine descrescatoare
// in cazul in care nu gasim, inseamna ca sirul este sortat si putem sa ne
// oprim
sortat = 1;
for (int i = 1; i < N; ++i)
if (v[i] > v[i + 1]) {
sortat = 0;
// interschimbam pe v[i] cu v[i + 1]
aux = v[i];
v[i] = v[i + 1];
v[i + 1] = aux;
}
}
for (int i = 1; i <= N; ++i)
cout<<v[i]<<' ';
return 0;
}
Se dă un șir v cu N numere întregi. Să se ordoneze crescător elementele acestuia. Fiecare
element al șirului este mai mare ca 0 și mai mic decât 100.

Până acum am văzut două metode de sortare a unui șir de numere. Totuși cele două metode devin
foarte lente atunci când dimensiunea șirului devine mare.

Ca să te convingi de acest lucru, încearcă să folosești Bubble sort pentru a sorta crescător un șir
de numere în care numerele se află în ordine descrescătoare.

Faptul că numerele din șir sunt între 1 și 99 ne permite să găsim un algoritm mai eficient
folosind șirurile de frecvență.

Simplificare
Pentru a găsi algoritmul, să ne gândim cum am rezolva problema dacă toate elementele șirului
sunt distincte (nici o valoare nu apare de două ori).

Putem folosi un șir nou, apar, care să ne spună care elemente apar în șirul inițial. Mai
exact apar[i] va fi egal cu 1 dacă valoarea i apare în șirul dat.

#include <iostream>
using namespace std;

int main()
{
int N, v[100001], apar[100];
cin>>N;
for (int i = 1; i <= N; ++i) {
cin>>v[i];
apar[v[i]] = 1;// marcam ca exista in sir un element cu valoarea v[i]
}
// stim ca numerele din sir sunt < 100, deci putem sa parcurgem toate
// numerele pana la 100 si sa vedem care apar in sir
for (int i = 1; i < 100; ++i)
if (apar[i] == 1)
cout<<i<<' ';
return 0;
}
Soluție
În cazul în care unele numere apar de mai multe ori, în loc să afișăm o singură dată, vom afișa
fiecare număr de atâtea ori de câte apare în șir.

#include <iostream>
using namespace std;

int main()
{
int N, v[100001], fr[100];
cin>>N;
// Initializam intai fiecare pozitie din vectorul de frecventa cu 0
for (int i = 1; i < 100; ++i) fr[i] = 0;
for (int i = 1; i <= N; ++i) {
cin>>v[i];
++fr[v[i]];// crestem frecventa elementului v[i]
}

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


for (int j = 1; j <= fr[i]; ++j)
cout<<i<<' ';
return 0;
}
Eficiență
În acest caz, fiecare valoare posibilă de la 1 la 99 va fi afișată de numărul ei de apariții în șir
(pentru valorile care nu apar în șir, instrucțiunea for (int j = 1; j <= fr[i]; ++j) nu va
executa nici un pas). În concluzie se vor face N + 100 calcule în total. Cele 100de calcule se fac
deoarece trebuie să parcurgem fiecare număr de la 0 la 100 în cadrul primului for.

Atenție!
Dacă valorile din șir sunt prea mari, nu putem aplica acest algoritm deoarece am avea nevoie să
folosim prea multă memorie pentru șirul de frecvență.

Continuă

e dau două șiruri v și w cu N respectiv M elemente întregi în ordine crescătoare. Să se afișeze


elementele celor două șiruri în ordine crescătoare.

Date de intrare

De la tastatură se va citi numărul N și un șir de N numere întregi, reprezentând elementele


șirului v. Pe urmă se va citi numărul Mși un șir de M numere întregi.

Date de ieșire

Pe ecran se vor afișa elementele celor două șiruri în ordine crescătoare.

Restricții și precizări

 1 ≤ N, M ≤ 100 000
 -2 000 000 000 ≤ v[i], w[i] ≤ 2 000 000 000

Exemplu

Date de intrare Date de ieșire


3 -1 0 1 2 4 6 7
146
4
Date de intrare Date de ieșire
-1 0 2 7
Probabil că atunci când ai încercat să rezolvi probleme cu matrice, a fost destul de deranjant când
trebuia să introduci o matrice mai mare. Mai mult, dacă programul tău nu afișa un răspuns
corect, după ce corectai programul trebuia să introduci matricea din nou.

Sunt sigur că ai salvat imagini, documente sau fișiere pe calculator pentru a le putea folosi mai
târziu. Aceste fișiere sunt deschise de diverse programe. Ar fi foarte util dacă am putea să ne
salvăm matricile, vectorii sau numerele în fișiere și să îi spunem programului nostru să citeasca
din fișier, pentru a nu trebui să introducem datele de fiecare dată de la tastatură.

În C++ putem face acest lucru în felul următor:

#include <fstream>
using namespace std;

int main() {
ifstream fin("date-intrare.txt");
ofstream fout("date-iesire.txt");
int a,b;
fin>>a>>b;
fout<<a+b;
return 0;
}
Programul de mai sus citește două numere a și b din fișierul date-intrare.txt și afișează
suma lor în fișierul date-iesire.txt.

În video-ul de mai jos poți vedea cum să folosești fișierele text în CodeBlocks

Explicație
 #include <fstream> - include biblioteca fstream care ne ajută să citim și să afișăm
date din fișiere (la fel cum iostream ne ajută cu citirea datelor de la tastatură și afișarea
lor pe ecran).
 ifstream fin("date-intrare.txt"); - deschide fișierul date-intrare.txt și îl
pregătește pentru când vrem să citim din el. În loc de date-intrare.txt putem scrie
orice nume de fișier.
 Pentru a citi date din acest fișier, în loc de cin>> folosim fin>>. În acest caz fin este o
variabilă pe care o folosim pentru acces la fișier, iar ea respectă regulile de denumire a
variabilelor pe care le cunoști de la variabilele întregi. Astfel, bucata de cod din exemplul
de mai jos e corectă și va citi un număr întreg din fișierul date.txt.
 Exemplu

int z;
ifstream ab1c("date.txt");
ab1c>>z;
 ofstream fout("date-iesire.txt"); - La fel ca și în cazul precedent, informează
calculatorul că urmează să afișăm date în fișierul date-iesire.txt de fiecare dată când
scriem fout<<. Aceleași reguli se aplică pentru denumirea fișierului și pentru fout ca și
la punctul anterior. Diferența între când declarăm un fișier de intrare și când îl declarăm
de ieșire e dată de ifstream sau ofstream. Primul vine de la input file stream, iar
al doilea de la output file stream și specifică tipul variabilei pe care o declarăm.
 fin>> și fout<< - pentru a citi și scrie date se aplică aceleași reguli ca și
pentru cin și cout doar că în acest caz folosim fișiere text.

CodeBlocks
Pentru a folosi fișiere text în cadrul unui proiect în CodeBlocks, trebuie să dai click pe File >
New > Empty file sau să apeși Ctrl + Shift + N, să apeși Yes la Do you want to add this
new file in the active project?. Înainte să dai Save, trebuie să îi dai fișierul un nume și să alegi
opțiunea pentru Save as type să fie ultima din listă, adică All files(.).

Execută pasul de mai sus pentru fiecare fișier pe care vrei să îl folosești în program.

Pentru a deschide fișierele create în CodeBlocks, apasă pe + din stânga folderului Others și dă
dublu click pe fiecare fișier pe care vrei să-l deschizi, alege Open it inside the Code::Blocks
editor și apasă OK.

Acum poți scrie date în fișierele create și să le citești sau să le scrii în alt fișier din main.cpp.

S-ar putea să vă placă și