You are on page 1of 316

Vlad Huţanu Tudor Sorin

INFORMATICĂ

(filiera teoretică, profilul real, specializarea
matematică-informatică) şi
(filiera vocaţională, profil militar MApN, specializarea
matematică-informatică)

Ciclul superior al liceului,
clasa a XI-a

Editura L&S Soft
Bucureşti

Copyright 2006-2016  L&S SOFT
Toate drepturile asupra acestei lucrǎri aparţin editurii L&S SOFT.
Reproducerea integralǎ sau parţialǎ a textului din aceastǎ carte este posibilǎ
doar cu acordul în scris al editurii L&S SOFT.

Manualul a fost aprobat prin Ordinul ministrului Educaţiei şi Cercetării
nr. 4446 din 19.06.2006 în urma evaluării calitative organizate de către
Consiliul Naţional pentru Evaluarea şi Difuzarea Manualelor şi este
realizat în conformitate cu programa analitică aprobată prin Ordin al
ministrului Educaţiei şi Cercetării nr. 3252 din 13.02.2006.

Referenţi ştiinţifici:

Prof. Dr. Victor Mitrana, Facultatea de Matematică, Universitatea Bucureşti
Prof. grad I Valiana Petrişor, Colegiul Naţional Bilingv George Coşbuc

Tiparul executat la S.C. LUMINATIPO s.r.l.
Str. Luigi Galvani nr. 20 bis, sector 2, Bucureşti

Descrierea CIP a Bibliotecii Naţionale a României
HUŢANU, VLAD
Informatică : manual pentru ciclul superior al liceului : clasa a XI-
a - (filiera teoretică, profilul real, specializarea matematică-informatică)
şi (filiera vocaţională, profil militar MApN, specializarea matematică-
informatică) / Vlad Huţanu, Tudor Sorin. - Bucureşti : Editura L & S Soft,
2006
ISBN (10) 973-88037-1-3; ISBN (13) 978-973-88037-1-8

I. Tudor, Sorin

004(075.35)

Editura L&S SOFT:
Adresa: Str. Stânjeneilor nr. 6, Sector 4, Bucureşti;
Telefon: 0722-573701; 0727.731.947;
E-mail: office@ls-infomat.ro
Web Site: www.ls-infomat.ro

3

Cuprins
Capitolul 1. Tablouri ………………………………………………………… 7
1.1. Noţiunea de tablou ………………………………………………………………….. 7
1.2. Cum citim şi cum afişăm un tablou bidimensional?……………………………... 8
1.3. Aplicaţii cu tablouri bidimensionale………………………………………………..10
Probleme propuse……………………………………………………………………….. 16
Răspunsurile la testele grilă……………………………………………………………..19

Capitolul 2. Subprograme ………………………………………………… 20
2.1. Noţiunea de subprogram …………………………………………………………. 20
2.2. Subprograme în Pascal................................................................................... 22
2.2.1. Un exemplu de utilizare a funcţiilor..................................................... 22
2.2.2. Un exemplu de utilizare a procedurilor............................................... 24
2.2.3. Structura unui subprogram................................................................. 25
2.2.3.1. Structura subprogramelor de tip funcţie.............................. 25
2.2.3.2. Structura subprogramelor de tip procedură........................ 26
2.2.4. Definirea şi declararea unui subprogram............................................ 27
2.2.5. Apelul subprogramelor........................................................................ 30
2.2.5.1. Apelul funcţiilor.................................................................... 30
2.2.5.2. Apelul procedurilor.............................................................. 31
2.2.5.3. Transmiterea parametrilor la apel....................................... 31
2.2.5.4. Cum memorează subprogramele parametrii trimişi?.......... 33
2.2.5.5. Transmiterea parametrilor prin valoare............................... 33
2.2.5.6. Transmiterea parametrilor prin referinţă.............................. 35
2.2.6. Variabile locale şi globale................................................................... 36
2.2.7. Greşeli frecvente................................................................................. 38
2.2.8. Unităţi de program............................................................................... 39
2.3. Subprograme în C++....................................................................................... 42
2.3.1. Exemple de utilizare a funcţiilor.......................................................... 42
2.3.2. Structura unei funcţii........................................................................... 44
2.3.3. Declararea variabilelor........................................................................ 46
2.3.4. Transmiterea parametrilor.................................................................. 49
2.3.5. Definirea şi declararea unui subprogram............................................ 53
2.4. Aplicaţii care folosesc subprograme................................................................ 55
Probleme propuse……………………………………………………………………….. 62
Răspunsuri...………………….………………………………………………………….. 72

Capitolul 3. Şiruri de caractere …………………………………………… 73
3.1. Generalităţi …………………………………………………………………………. 73
3.2. Şiruri de caractere în Pascal……………………………………………………….74
3.2.1. Noţiuni introductive............................................................................. 74
3.2.2. Concatenarea şirurilor........................................................................ 76

4 Cuprins

3.2.3. Compararea şirurilor........................................................................... 77
3.2.4. Lungimea şirurilor de caractere.......................................................... 79
3.2.5. Subşiruri.............................................................................................. 80
3.2.6. Conversii de la şiruri la valori numerice şi invers................................ 84
3.2.7. Citirea şi scrierea datelor de tip String din şi în fişiere text................. 88
3.3. Şiruri de caractere în C++…………………………………………………………. 89
3.3.1. Generalităţi………............................................................................... 89
3.3.2. Citirea şi scrierea şirurilor de caractere.............................................. 89
3.3.3. Tipul char*………………………………............................................... 92
3.3.4. Lungimea unui şir de caractere.......................................................... 93
3.3.5. Copierea şi concatenarea şirurilor de caractere................................. 94
3.3.6. Căutarea unui caracter într-un şir....................................................... 95
3.3.7. Compararea şirurilor........................................................................... 97
3.3.8. Subşiruri.............................................................................................. 99
3.3.9. Alte funcţii utile în prelucrarea şirurilor.............................................. 101
3.3.10. Conversia şirurilor în valori numerice şi invers................................ 104
3.3.11. Citirea şi scrierea şirurilor de caractere din şi în fişiere text............ 108
3.3.11.1. Operaţia de citire............................................................. 108
3.3.11.2. Operaţia de scriere.......................................................... 109
3.3.12. O modalitate de conversie de la şir la alt tip.................................... 109
Probleme propuse……………………………………………………………………… 110

Capitolul 4. Structuri de date neomogene …………………………… 112
4.1. Noţiuni introductive……………………………………………………………….. 112
4.2. Structuri neomogene în Pascal………………………………………………….. 112
4.2.1. Tipul Record...…............................................................................... 112
4.2.2. Accesul simplificat la câmpuri........................................................... 114
4.2.3. Înregistrări imbricate.......................................................................... 115
4.2.4. Vectori de înregistrări........................................................................ 115
4.2.5. Înregistrare cu variante...................................................................... 116
4.3. Structuri neomogene în C++….…………………………………………………. 118
4.3.1. Tipul struct...….................................................................................. 118
4.3.2. Înregistrări imbricate.......................................................................... 120
4.3.3. Înregistrări cu structură variabilă....................................................... 121
Probleme propuse……………………………………………………………………… 123

Capitolul 5. Structuri de date …………………………………………… 124
5.1. Conceptul de structură de date………………………………………………….. 124
5.2. Structura de tip listă liniară………………………………………………………..126
5.2.1. Prezentarea structurii........................................................................ 126
5.2.2. Liste alocate secvenţial..................................................................... 127
5.2.3. Liste alocate înlănţuit......................................................................... 128
5.2.4. Implementarea alocării înlănţuite prin utilizarea vectorilor.................129
5.3. Structura de tip stivă…………………………………………………………..….. 133
5.4. Structura de tip coadă……….………………………………………………..….. 138
Probleme propuse……………………………………………………………………… 138
Răspunsuri...………………….………………………………………………………… 140

Manual de informatică pentru clasa a XI-a 5

Capitolul 6. Introducere în recursivitate ……………………………… 141
6.1. Prezentare generală ………………………………………………………………141
6.2. Modul în care se realizează autoapelul….………………………………………141
6.2.1. Realizarea autoapelului în Pascal..................................................... 141
6.2.2. Realizarea autoapelului în C++......................................................... 142
6.3. Mecanismul recursivităţii….……………………………………………………… 143
6.4. Cum gândim un algoritm recursiv?……...……………………………………….147
6.5. Aplicaţii recursive……...………………………………..………………………… 148
6.5.1. Aplicaţii la care se transcrie o formulă recursivă............................... 148
6.5.2. Aplicaţii la care nu dispunem de o formulă de recurenţă.................. 153
Probleme propuse……………………………………………………………………… 159
Indicaţii / Rezolvări…………….………………………………………………………. 166

Capitolul 7. Metoda Divide et Impera ………………………………… 172
7.1. Prezentare generală ………………………………………………………………172
7.2. Aplicaţii ……………………………………………………………………………..172
7.2.1. Valoarea maximă dintr-un vector...................................................... 172
7.2.2. Sortarea prin interclasare................................................................. 174
7.2.3. Sortarea rapidă................................................................................. 176
7.2.4. Turnurile din Hanoi........................................................................... 179
7.2.5. Problema tăieturilor.......................................................................... 180
7.3. Fractali …………………………………………………………………………….. 183
7.3.1. Elemente de grafică.......................................................................... 183
7.3.1.1. Generalităţi (varianta Pascal)............................................ 183
7.3.1.2. Generalităţi (varianta C++)............................................... 185
7.3.1.3. Setarea culorilor şi procesul de desenare (Pascal şi C++)... 186
7.3.2. Curba lui Koch pentru un triunghi echilateral.................................... 188
7.3.3. Curba lui Koch pentru un pătrat........................................................ 191
7.3.4. Arborele.............................................................................................193
Probleme propuse……………………………………………………………………… 195
Răspunsuri…………….……………………………………………………………….. 196

Capitolul 8. Metoda Backtracking ……………………………………… 199
8.1. Prezentarea metodei …………………………………………………………….. 199
8.1.1. Când se utilizează metoda backtracking?........................................ 199
8.1.2. Principiul ce stă la baza metodei backtracking................................. 199
8.1.3. O modalitate de implementare a metodei backtracking.................... 201
8.1.4. Problema celor n dame..................................................................... 204
8.2. Mai puţine linii în programul sursă………………………………………………. 207
8.3. Cazul în care se cere o singură soluţie. Ex.: problema colorării hărţilor……. 210
8.4. Aplicaţii ale metodei backtracking în combinatorică…………………………... 212
8.4.1. O generalizare utilă........................................................................... 212
8.4.2. Produs cartezian............................................................................... 213
8.4.3. Generarea tuturor submulţimilor unei mulţimi................................... 215
8.4.4. Generarea combinărilor.................................................................... 217

6 Cuprins

8.4.5. Generarea aranjamentelor................................................................ 219
8.4.6. Generarea tuturor partiţiilor mulţimii {1,2, ..., n}................................ 221
8.5. Alte tipuri de probleme care se rezolvă prin utilizarea metodei backtracking…. 223
8.5.1. Generalităţi....................................................................................... 223
8.5.2. Generarea partiţiilor unui număr natural........................................... 224
8.5.3. Plata unei sume cu bancnote de valori date..................................... 226
8.5.4. Problema labirintului......................................................................... 228
8.5.5. Problema bilei................................................................................... 231
8.5.6. Săritura calului.................................................................................. 233
Probleme propuse……………………………………………………………………… 235
Indicaţii…………….……………………………………………………………………. 238

Capitolul 9. Grafuri …………………………………………………………239
9.1. Grafuri neorientate…………………………………………………………….….. 239
9.1.1. Introducere........................................................................................ 239
9.1.2. Definiţia grafului neorientat............................................................... 240
9.1.3. Memorarea grafurilor......................................................................... 242
9.1.4. Graf complet...................................................................................... 247
9.1.5. Graf parţial, subgraf.......................................................................... 248
9.1.6. Parcurgerea grafurilor neorientate.................................................... 250
9.1.6.1. Parcurgerea în lăţime (BF – bredth first)........................... 250
9.1.6.2. Parcurgerea în adâncime (DF – depth first)...................... 253
9.1.6.3. Estimarea timpului necesar parcurgerii grafurilor.............. 255
9.1.7. Lanţuri............................................................................................... 255
9.1.8. Graf conex......................................................................................... 259
9.1.9. Componente conexe......................................................................... 260
9.1.10. Cicluri.............................................................................................. 262
9.1.11. Arbori............................................................................................... 264
9.1.11.1. Noţiunea de arbore......................................................... 264
9.1.11.2. Noţiunea de arbore parţial............................................... 266
9.2. Grafuri orientate……………………………………………………………….….. 267
9.2.1. Noţiunea de graf orientat.................................................................. 267
9.2.2. Memorarea grafurilor orientate......................................................... 270
9.2.3. Graf parţial, subgraf.......................................................................... 272
9.2.4. Parcurgerea grafurilor. Drumuri. Circuite.......................................... 273
9.2.5. Graf tare conex. Componente tare conexe....................................... 275
Probleme propuse……………………………………………………………………… 278
Răspunsuri…………….……………………………………………………………….. 286

Anexa 1. Memento ………………………………………………………… 289

Anexa 2. Aplicaţii practice ale grafurilor ……………………………… 309

Anexa 3. Codul ASCII ……………………………………………………… 316

  . valorice..j.  . am −1. .1.  3. . Tabloul se identifică printr-un singur nume. iar componentele sale se identifică prin intermediul unui sistem de indici.. Definiţia 1. Noţiunea de tablou Anul trecut am studiat tablourile unidimensionale numite uneori.n  1. . Putem organiza datele sub formă de tablou astfel:  vom numerota cele n produse cu 1. Un magazin oferă spre vânzare n produse. 2 . . Este esenţial de reţinut faptul că toate  a a3. .n  elementele tabloului au acelaşi tip.   am −1. 2 . ai. . am . Un  a1. n. din fiecare tip de produs.n  element al acestuia. Se doreşte să se reţină vânzările lunare.  elementul ai. vectori. 12. 7 Capitolul 1 Tablouri 1.  lunile le vom numerota cu 1. . . 2 .  coloana j.1 a1. .n     am . . .. câte elemente are tabloul? b) Care sunt indicii de adresare în tablou pentru a afla vânzările din produsul 5 în luna septembrie? c) Cum se poate calcula suma încasată în luna mai? . Alăturat avem reprezentat un tablou. 2. 2 .1 am .. . tot prin analogie cu matematica.. Intrebări posibile a) Dacă magazinul vinde 5 produse.1 am −1.j semnifică valoarea încasată din vânzarea produslui i în luna j a anului .prin urmare. 2. se găseşte pe linia i şi    a2.. 2 . elementele tabloului sunt de tip real. a1.1.1 a2 . . În acest an studiem tablourile bidimensionale numite.a3. matrice. Un tablou este o structură omogenă (formată din elemente de acelaşi fel) cu un număr bine determinat de componente. . .1  A= . prin analogie cu matematica.

Cum citim şi cum afişăm un tablou bidimensional ? În clasa a X-a am învăţat să lucrăm cu masive unidimensionale. sub formă de tablou. 2.….. elementul de pe linia a treia şi coloana a patra se adresează prin a[3. vom folosi liniile şi coloanele matricei începând de la 1.1.9 şi...9] of real.. situaţia la învăţătură a celor m elevi ai unei clase..8 şi.4]. Tot aşa. În cazul matricelor. . .. pentru simplitate. Întrebări posibile a) Dacă în clasă sunt 30 de elevi şi aceştia studiază 8 materii. În aceste condiţii se pierde o linie şi o coloană. . Dacă numerotăm elevii cu 1. anul este împărţit în patru trimestre şi că fiecare trimestru are trei luni. n. de exemplu.10 şi coloanele 1. 2. m şi materiile pe care aceştia le studiază cu 1. sau direct var a:array [1. câte elemente are matricea? b) Care este materia la care elevii au cele mai bune rezultate? c) Care este media generală a elevului i? d) Care este media generală a elevilor unei clase? Şirul exemplelor ar putea continua pentru că sunt foarte multe situaţii în care se utilizează tablouri bidimensionale (matrice).. atunci ai. elementul de pe linia a treia şi coloana a patra se adresează prin a[2][3].2. Mai jos.. Tablouri d) Ştiind că.10. Pentru a adresa un element al unui vector se utilizează un singur indice.2..1. int a[10][9].. se poate memora. Considerăm acest fapt neesenţial.9] of real. cu elemente de tip întreg: Varianta Pascal Varianta C++ type tablou = array[1. puteţi observa cum se declară o matrice cu 10 linii şi 9 coloane... matricea are liniile 1.. din punct de vedere economic.j reprezintă media pe care o are elevul i la materia j.  În C++.10. matricea are liniile 0.8 Capitolul 1..9 şi coloanele 0. cum se poate calcula suma încasărilor din trimestrul patru al anului? e) Cum putem determina produsul din vânzarea căruia s-a încasat anual suma maximă? f) Care este luna cu cele mai mari încasări? 2. vom utiliza doi indici..1.2.. de exemplu.  În Pascal. …. Uneori.1. var a:tablou. ... 1. fiecare de indice 0...

În acest caz. Figura 1. readln(m).i<m..i++) end. În figura alăturată.a[10][9]. writeln. programul nu se va termina cu eroare.j<n. ş.i++) readln(n). begin cin>>a[i][j].j++) cout<<a[i][j]<<' '. .1. main() begin { int m. în aşa fel încât acesta să corespundă oricărui set de date de intrare. dar.i. for (i=0. } readln(a[i. Evident.i. apoi a doua. tablourile sunt reţinute pe linii.. begin } for j:=1 to n do } write (a[i.' for j:=1 to n do <<j+1<<"]=".i<m.']='). Observaţi modul în care am afişat tabloul . într-un anumit caz. end end. utilizăm numai primele 4 linii şi primele 5 coloane..  În memorie.a.10.'.j:integer.de aşa natură încât şi pe ecran să arate ca o matrice. cout<<"n=".n.'.n.m.' ').. adică fiecare linie să fie scrisă pe un rând. { for (j=0.j. write ('A['. aveţi reprezentat grafic un tablou cu 8 linii şi 8 coloane. for (i=0. din care. write ('n=').j.h> a:array[1.d.j]). cin>>m. for i:=1 to m do cout<<endl.9] of integer. cout<<"m=".j++) for i:=1 to m do { cout<<"a["<<i+1<<'. cin>>n. for(j=0. write ('m=').1.Manual de informatică pentru clasa a XI-a 9 De multe ori nu ştim câte linii şi câte coloane va trebui să aibă tabloul.i.j<n. într-un astfel de caz există o risipă de memorie internă. #include <iostream. Varianta Pascal Varianta C++ var m. Aceasta înseamnă că la început este memorată prima linie. Exemplu de tablou Programul care-l utilizează va funcţiona corect dacă avem cel mult 8 linii şi cel mult 8 coloane. Iniţial se citesc numărul de linii şi de coloane ale tabloului (m şi n). În programul următor se citeşte şi se afişează un tablou.j]. tabloul se declară cu un număr maxim de linii şi un număr maxim de coloane.

end. x. for i := 1 to m do for (i=0. {. pentru a interschimba conţinutul a două variabile se utilizează o a treia.'. main() var mat: matrice.j].1. {-citirea datelor-} for(i=0. readln(mat[i. Se cere să se interschimbe linia x cu linia y. i. Am fi tentaţi ca manevra să fie un vector cu n componente.n.i<m. cin>>m. După cum am învăţat. Se citesc.j<n. m.10] of byte. cout<<"y=". analizaţi programul de mai jos: Varianta Pascal Varianta C++ type matrice = array[1.j. man: integer. readln(m). Se citeşte un tablou cu m linii şi n coloane.'. În rest. { int mat[10][10]. { for i := 1 to m do cout<<"mat["<<i+1<<'.h> 1.i. se interschimbă două linii. cuprinse între 1 şi m. y=3:  1 2 3    4 5 6  7 8 9   În urma interschimbării liniilor 2 şi 3 se obţine:  1 2 3    7 8 9  4 5 6    Rezolvare.i++) write ('m= '). Cu puţină atenţie ne dăm seama că putem folosi ca manevră o singură variabilă de acelaşi tip cu cel al componentelor de bază ale tabloului. cin>>y.i<m.' for j := 1 to n do <<j+1<<"]=". şi două numere naturale distincte x şi y. writeln. cin>>x. Aplicaţii cu tablouri bidimensionale  Aplicaţia 1. write('y= '). cu m=3.. readln(n).3. j. cout<<endl. apoi pe cel obţinut prin interschimbarea liniilor x şi y. de asemenea.10 Capitolul 1. cin>>n. Exemplu: Considerăm tabloul de mai jos.j<n. cout<<"x=". cout<<mat[i][j]<<' '. begin cout<<"n=".m. end. La început vom afişa tabloul iniţial. readln(x).tiparesc tabloul initial-} cout<<endl. n=3 şi liniile x=2. De această dată. j. Interschimbare de linii. write('mat['. #include <iostream.' ').x..']='). }.10. begin cin>>mat[i][j].y.i.man.j]). n. cout<<"m=".i++) begin { for j := 1 to n do for (j=0. Tablouri 1.j++) write (mat[i. y. } .j++) write ('n= '). readln(y). write('x= '). for(j=0. de manevră.

j].Manual de informatică pentru clasa a XI-a 11 {-interschimbare linii-} for(j=0.j++) for j:=1 to n do { man=mat[x-1][j].j] := man. begin mat[x-1][j]=mat[y-1][j]. man := mat[x. end.i++) writeln. writeln.2. începând cu primul element din linia 1. mat[x. în sensul acelor de ceas. privim tabloul ca pe un ansamblu alcătuit din mai multe dreptunghiuri "concentrice": * * * * *   * * * * * * * * * *   * * * * *   * * * * * Tabloul de mai sus este un ansamblu format din trei dreptunghiuri "concentrice" . end. După ce stabilim numărul de pătrate (cum?).  Aplicaţia 1.j]. Se citeşte un tablou cu n linii şi n coloane. având grijă ca elementele aflate în colţuri să nu fie afişate de două ori. for (i=0. . {-tiparesc tabloul inversat-} cout<<endl.j++) cout<<mat[i][j]<<' '. } mat[y.ultimul are un singur element şi nu a putut fi reprezentat. afişăm elementele aflate pe fiecare latură a fiecărui pătrat în ordinea cerută. Spirala. Se cere să se afişeze elementele tabloului în ordinea rezultată prin parcurgerea acestuia în spirală.  Rezolvare.i<m. for i := 1 to m do } begin } for j := 1 to n do write(mat[i.j].j] := mat[y. Exemplu. { for (j=0.' '). Pentru a putea rezolva problema. mat[y-1][j]=man. end. Fie tabloul:  1 2 3    4 5 6  7 8 9   Elementele vor fi afişate în ordinea: 1 2 3 6 9 8 7 4 5.j<n. cout<<endl.j<n.

SAU=4. writeln(mat[k.'.. m numere naturale. Ştiind că vocalele au câte un punct. ALTI=6. .j. Tablouri Varianta Pascal Varianta C++ type matrice = array[1.j]). n.. j.i]).n-k+1]). Pentru datele de intrare: 3 5 D. { for(i=k.10. iar consoanele câte două.i--) for i := k+1 to n-k+1 do cout<<mat[i][k]<<endl. se afişează 20 (DA=3. for(i=n-k. k: integer. {-listare-} for(i=k+1.k++) readln(mat[i. Căutarea cuvintelor pe tablă şi cumularea punctelor lor este. #include <iostream.']='). begin for(i=n-k. cin>>n. Algoritmul corespunzător acestei soluţii pare însă destul de complicat.SAU ALTI.h> 1. for i := n-k downto k+1 do writeln(mat[i.k.j.  Rezolvare.i++) {-citirea datelor-} for (j=1.j<=n.i>=k. Exemplu.i]).' for i := 1 to n do <<j<<"]=". prima soluţie la care ne gândim. Valorile n şi m şi configuraţia tablei se citesc din fişierul ”tabla. } for i := n-k downto k do } writeln(mat[n-k+1.i<=n. begin for(i=1.i<=n-k+1. for j := 1 to n do cin>>mat[i][j]..  Aplicaţia 1.m≤30).i. AI=2). i. deoarece se pierde . { cout<<"mat["<<i<<'. stabiliţi valoarea totală a cuvintelor de pe tablă. begin } write('mat['.3.A.'. sunt plasate pe unele poziţii jetoane cu litere.12 Capitolul 1. Pe o tablă cu n linii şi m coloane (n. cout<<"n=".n. readln(n). 1≤n.j++) write ('n= '). conform unui joc corect de SCRABBLE. STA=5.i<=n-k+1.i++) for k := 1 to n div 2 + 1 do cout<<mat[i][n-k+1]<<endl..10] of integer.i--) for i := k to n-k+1 do cout<<mat[n-k+1][i]<<endl. writeln(mat[i.i++) end. de obicei. Simpla parcurgere a matricei de caractere şi adunarea valorilor corespunzătoare literelor nu este o strategie bună.i>=k+1. end.i. { int mat[10][10].k]) end.txt”. conform exemplului (locurile goale de pe tablă sunt memorate sub forma unor caractere punct).k<=n/2+1. for(k=1. cout<<mat[k][i]<<endl. main() var mat: matrice.

n. Varianta Pascal Varianta C++ #include <fstream. Dacă utilizăm artificiul de a borda matricea cu caractere punct: ┌pentru i=0. (a[i.i.j.m).') dir++.i'. for(i=1.') dir++. sau face parte din două cuvinte (are litere vecine şi pe orizontală şi pe verticală) şi atunci se punctează dublu.j++) begin f>>a[i][j].' │ ai. or pt+=p*dir.'.j+1]<>'.i<=n.i++) for i:=1 to n do for(j=1.Manual de informatică pentru clasa a XI-a 13 punctajul literelor care apar într-un cuvânt pe orizontală şi unul pe verticală şi care ar trebui luate de două ori în calcul.dir:byte.j-1]<>'. || i<n && dir:=0. dacă ea face parte dintr-un singur cuvânt (are litere vecine doar pe orizontală sau doar pe verticală) şi se punctează obişnuit.' then a[i][j]=='I'|| begin a[i][j]=='O'|| if (a[i.30.') pt:=0. (a[i.i. writeln(pt) end. a[i+1][j]!='.' └■ .') if (i>1) and (a[i-1. ifstream f("tabla.j<=m. int n.h> var a:array[1.n+1'.txt"). De aceea.j]<>'. or if (j>1 && a[i][j-1]!='. for j:=1 to m do pt=0. readln(f. if (a[i][j]!='.j]='A') or a[i][j]=='U') (a[i. a[i][j+1]!='.j]<>'.j++) end. cout<<pt<<endl.j]).1.m.0'.') if (j>1) and (a[i. pentru fiecare literă.i'.. f>>n>>m. char a[31][31].') || j<m && then dir:=dir+1. { for i:=1 to n do if(a[i][j]=='A'|| for j:=1 to m do a[i][j]=='E'|| if a[i. { reset(f).j]='O') or dir=0.a[i.j]='I') or else p=2..j]<>'. an+1.j. (j<m) and (a[i.'tabla. for(i=1.' else p:=2. pt:=pt+p*dir } end.j]='E') or p=1.m.i<=n. begin void main() assign(f.'. (a[i.pt. n.i++) readln(f) for(j=1. f:text. ai. vom analiza în plus.') } then dir:=dir+1.j<=m.j]='U') then p:=1 if (i>1 && a[i-1][j]!='.30]of char.pt:integer.txt'). read(f.' (i<n) and (a[i+1.p. p.n+1 execută │ a0.dir.

a 2-a clasă. fără a face nici măcar o comparaţie între ele. şirul celor n numere se împarte în 10 clase: prima clasă conţine numerele care se termină cu 0. 25. Elementele din linia 0 reţin numărul de elemente din şir care se găsesc pe coloana respectivă: 0 1 2 3 4 5 6 7 8 9 2 1 0 0 0 1 2 0 0 0 40 41 25 6 30 36 Obţinem din nou şirul de numere. după a 2-a cifră a numerelor: 0 1 2 3 4 5 6 7 8 9 1 0 1 2 2 0 0 0 0 0 6 25 30 40 36 41 . Tablouri atunci testarea vecinilor elementelor de pe margine se realizează întocmai ca şi în cazul celor din interiorul matricei: ┌dacă ai-1. 25.' sau ai. cele care se termină cu 1. Împărţim din nou şirul în 10 clase. 30. 6.  Aplicaţia 1. aşezându-le în ordinea coloanelor şi în ordinea în care le-am pus în fiecare coloană: 40.. cu 10 coloane.' │ dirdir+1 (cel puţin un vecin pe direcţia verticală) └■ ┌dacă ai. în care prima linie are indicele 0. Sortarea fără comparaţii este o metodă de sortare care permite sortarea a n numere naturale. denumită Mat.j≠'. 40 şi 30.14 Capitolul 1. deci nu pot fi amplasate litere izolate. 36. 41.' │ dirdir+1 (cel puţin un vecin pe direcţia orizontală) └■ Se mai poate analiza şi cazul în care o literă nu are nici un vecin pe niciuna dintre direcţiile orizontală sau verticală (caz în care litera nu s-ar puncta deloc). a 10 clasă.j+1≠'.. Iniţial.4.j-1≠'. 36. Pentru memorarea numerelor care aparţin fiecărei clase vom utiliza o matrice. Vom prezenta algoritmul pe un exemplu în care se sortează crescător 6 numere naturale: 6. însă problema precizează că literele sunt aşezate corect conform jocului de scrabble. 41. .j≠'. cele care se termină cu 9.' sau ai+1.

Observaţii  Dacă k este numărul maxim de cifre a numerelor din şir.i. 30. end. for (i=0.i. in vector} } k:=0.Cif]:=Mat[0. n. readln(A[i]) for(NrCif=1. { cout<<"n=". 25.Cif]:=A[i] { k++.i<=n.i. begin Mat[0][Cif]++.Zece: main() integer. } for i:=1 to n do //Extrag din mat. Mat:array[0.h> integer.k. int A[100]..100.i]:=0. k:=k+1. end.i] do for (i=1.i<=9.100] NrCif. şirul este sortat. readln(n). Cif:=(A[i] div Zece) for (i=0.i<=9. Zece:=1.j.Zece.i] end. operaţia de împărţire a numerelor în 10 clase se va relua de k ori. begin for (i=1.i++) write('n= ').Mat[100][100].j.Cif]. begin } write('A['.NrCif<=4.NrCif. 36. for (i=1. Zece=1.i++) mod 10. in vector begin k=0. A[k]=Mat[j][i].  Algoritmul este "mare consumator" de memorie! Varianta Pascal Varianta C++ var A:array [1. for i := 1 to n do cin>>A[i].. 41.100] of #include <iostream. 40.j<=Mat[0][i]. cin>>n. for (j=1.Manual de informatică pentru clasa a XI-a 15 Obţinem din nou şirul de numere. if (Mat[0][i]) Mat[0.i<=n.Cif.0.Cif. aşezându-le în ordinea coloanelor şi în ordinea în care le-am pus în fiecare coloană: 6.i++) for i:=0 to 9 do Mat[0][i]=0.i++) begin cout<<A[i]<<endl..NrCif++) end. of integer. if Mat[0. for i:=1 to n do writeln(A[i]). {Extrag din mat. { cout<<"A["<<i<<"]=".j++) Mat[Mat[0.k. for i:=0 to 9 do Mat[0.i<=n.']= '). Zece:=Zece*10. . end. { if (NrCif>1) Zece*=10. } A[k]:=Mat[j.n.i++) for NrCif:=1 to 4 do {Cif= (A[i] / Zece) % 10.Cif]+1. if NrCif>1 then Mat[Mat[0][Cif]][Cif]=A[i].i]<> 0 then } for j:=1 to Mat[0. De această dată.

n]. Pentru tabloul de mai jos  1 2 3  . Exemplu. numim diagonală secundară. 5 şi 9. Se cere: b1) suma elementelor aflate pe diagonala secundară. numim diagonală principală. 5 şi 3. elementele aflate pe "linia" care uneşte A[1. numere întregi. Se citeşte un tablou cu n linii şi n coloane.1] cu A[n.  în nici un program nu se va folosi instrucţiunea if. 2. Tablouri Probleme propuse 1. a2) suma elementelor aflate deasupra diagonalei principale.n]. Se cere: a1) suma elementelor aflate pe diagonala principală. . Pentru tabloul  1 2 3    4 5 6 . b) Pentru un tablou pătratic A. a) Pentru un tablou pătratic A.1] cu A[1. b3) suma elementelor aflate sub diagonala secundară.16 Capitolul 1. a3) suma elementelor aflate sub diagonala principală.    7 8 9 elementele sunt: 7. b2) suma elementelor aflate deasupra diagonalei secundare. se numeşte tablou pătratic. în care numărul liniilor coincide cu numărul coloanelor. elementele să fie în ordine crescătoare. elementele aflate pe "linia" care uneşte A[n. Exemplu. Cerinţe suplimentare:  pentru fiecare cerinţă se va face un program separat. Un astfel de tablou.  4 5 6    7 8 9 elementele sunt: 1. Interschimbaţi coloanele unei matrice cu m linii şi n coloane astfel încât în linia k.

Elementul A[i. Se va afişa un mesaj corespunzător.b.txt” care să conţină o matrice cu 2n linii şi 2n coloane.a.c. 6. 5. Un teren este dat sub forma unui tablou A cu n linii şi m coloane. Pentru 5 cifre n. cu n linii şi n coloane citită din fişierul ”mat3. se cere să se afişeze elementele în ordinea sugerată în figura de mai jos: Figura 1. Exemplu: m=4. fiecare cu n linii şi n coloane. se cere să se decidă pe care dintre cele două diagonale.  Indicaţie. Fiind dată o matrice cu m linii şi n coloane. Să se afişeze coordonatele "pătrăţelelor vârf" (un pătrăţel este vârf dacă toţi vecinii săi au o altitudine strict mai mică).Manual de informatică pentru clasa a XI-a 17 3. Fereastra.j] reţine altitudinea pătrăţelului de coordonate i şi j.2. una numai cu componente egale cu b. o imagine este mult prea mare şi ea este afişată cu ajutorul unei ferestre. una cu c şi una cu d. Dacă vom considera numai o valoare dintre acestea. de exemplu. se cere să se creeze fişierul ”mat5. suma elementelor constitutive este mai mare. Matricea iniţială şi submatricele sunt prezentate mai jos: 10. 8.txt”. 7.txt”. Atenţie! Maximele sau minimele pe linii sau coloane pot să nu fie unice. Exemplu . Pentru o matrice de numere reale a. 4. cu 5 linii şi 4 coloane. formată din 4 submatrice disjuncte. Determinaţi elementele şa ale unei matrice cu n linii şi m coloane (elemente minime pe linie şi maxime pe coloană sau maxime pe linie şi minime pe coloană). se cere să se afişeze cel mai mare element din matrice şi indicii acestuia. se cere să se afişeze suma elementelor strict pozitive din matrice. citită de la tastatură. s-ar putea să pierdem soluţii. Se cere să se afişeze toate submatricele cu 3 linii şi 3 coloane ale matricei iniţiale. Scrieţi programul pseudocod corespunzător. Este necesară construirea în prealabil a matricei în memorie? 9. Pentru o matrice a. n=4. Un astfel de procedeu este utilizat atunci când. una dintre ele fiind formată numai cu componente egale cu a. cu n linii şi m coloane citită din fişierul ”mat1. Pentru o matrice a.d citite de la tastatură. Fiind dată o matrice cu m linii şi n coloane.

k++) n]:=V[k]. b) if x1-y1=x2-y2 b) if (x1-y1==x2-y2) then writeln('Da') cout<<"Da" else writeln('Nu').y1) şi (x2.  Copierea se realizează prin utilizarea unui singur ciclu for! Varianta Pascal Varianta C++ a) for k:=1 to m*n do Mat[1+k a) for (k=1. else cout<<"Nu". else writeln('Nu'). .k++) div n.. Se dă un vector V cu m*n componente numere întregi.3. Mat[1+(k-1)/n][1+(k-1)% m]=V[k].y2). Tablouri 11. elementele de pe linia m. care dintre relaţiile de mai jos testează dacă elementele se găsesc pe o dreaptă paralelă cu una dintre diagonalele matricei (principală sau secundară)? Varianta Pascal Varianta C++ a) if x1-x2=y1-y2 a) if (x1-x2==y1-y2) then writeln('Da') cout<<"Da".18 Capitolul 1. n]:=V[k]. d) if abs(x1-x2)=abs(y1-y2) d) if (abs(x1-x2)==abs(y1-y2)) then writeln('Da') cout<<"Da".k++) Mat[1+(k-1) div m.1+(k-1) mod d) for (k=1.k++) c) for k:=1 to m*n do Mat[1+(k-1)/n][1+(k-1)% n]=V[k]. c) if abs(x1-y1)=abs(x2-y2) c) if (abs(x1-y1)==abs(x2-y2)) then writeln('Da') cout<<"Da". Mat[1+k/n][1+k%n]=V[k]. Mat[1+(k-1) div n.k<=m*n.k<=m*n. c) for (k=1.k<=m*n. Fiind dată o matrice cu n linii şi n coloane (pătratică) cu numere naturale şi fiind date două elemente ale matricei de coordonate (x1. else writeln('Nu'). d) for k:=1 to m*n do Mat[1+k div n. Figura 1. else cout<<"Nu". else cout<<"Nu". iar la sfârşit... .1+(k-1) mod Mat[1+(k-1)/m][1+(k-1)% n]=V[k]. apoi elementele de pe linia 2. else cout<<"Nu". 13. Exemplu 12.k<=m*n.1+k mod m]:=V[k]. b) for k:=1 to m*n do b) for (k=1. Care dintre secvenţele de mai jos copiază vectorul V în matricea Mat cu m linii şi n coloane? Copierea se realizează completând mai întâi elementele de pe linia 1. Fiind dată o matrice cu m linii şi n coloane se cere să se afişeze elementele în ordinea sugerată în imaginea alăturată. else writeln('Nu').1+k mod n]:=V[k].

iar la sfârşit.i<=m..j<=m.Manual de informatică pentru clasa a XI-a 19 14.j++) V[j+(i-1)*n]:=Mat[i.j<=n. Răspunsurile la testele grilă 12.i]. b) for i:=1 to m do b) for (i=1.j]. . Se dă un vector V cu m*n componente numere întregi şi o matrice Mat cu m linii şi n coloane. d).. V[j+(i-1)*n]=Mat[i. V[j+(i-1)*m]=Mat[i][j].i++) for j:=1 to n do for (j=1. V[i+(j-1)*n]=Mat[i][j]. b). .i<=m.i++) for j:=1 to n do for (j=1.i++) for j:=1 to m do for (j=1.j]. V[j+(i-1)*n]=Mat[i][j]. Varianta Pascal Varianta C++ a) for i:=1 to n do a) for (i=1.j++) V[i+(j-1)*n]:=Mat[i.j++) V[j+(i-1)*m]:=Mat[i.i++) for j:=1 to n do for (j=1.j].j<=n. d) for i:=1 to m do d) for (i=1.. 14. Care dintre secvenţele de mai jos copiază matricea Mat în vectorul V? Copierea se realizează astfel: mai întâi se copiază linia 1.j]. apoi linia 2. c) for i:=1 to m do c) for (i=1. linia m.j<=n. c).j++) V[j+(i-1)*n]:=Mat[j.i<=m.i<=n. 13.

Exemple de astfel de subprograme folosite sunt:  matematice: sin.20 Capitolul 2 Subprograme 2. trunc (Pascal) / floor (C++).1. Se consideră funcţia:  x +1 1 + x 2 . În acest capitol învăţăm să lucrăm cu subprograme. Pentru a înţelege noţiunea de subprogram. cos. Cum procedăm? Prin utilizarea cunoştinţelor dobândite până în prezent. Ce observăm? Că atât pentru calculul valorii funcţiei în punctul a. scriem secvenţa de program care calculează valoarea funcţiei calculată pentru x=a şi se mai scrie o dată (sau se copiază şi se adaptează) secvenţa de program care calculează valoarea funcţiei calculată pentru x=b.  de manipulare a fişierelor: close (Pascal) / . pentru x ∈ (1.close() (C++). pentru x ∈ (-∞. . ansamblu implementat separat şi identificat printr-un nume. ∞ ). Prin subprogram vom înţelege un ansamblu alcătuit din declarări şi instrucţiuni scrise în vederea unei anumite prelucrări.1. vom porni de la două exemple. Până în prezent am fost doar utilizatori de subprograme. abs. cât şi pentru calculul valorii funcţiei în punctul b.  Se citesc două valori reale a şi b. Noţiunea de subprogram Definiţia 2.  f ( x ) =  x + 1. Să se scrie un program care afişează care dintre valorile f(a) şi f(b) este cea mai mare.  6  1+ x . 1.-1). aplicăm acelaşi tip de raţionament: încadrăm valoarea respectivă (a sau b) într-unul dintre cele trei intervale şi aplicăm formula de calcul corespunzătoare. pentru x ∈ [-1. . exp.1].

Prin implementarea modulară. cu ajutorul subprogramelor. Putem acum enumera unele dintre avantajele utilizării subprogramelor:  reutilizarea codului .se apelează subprogramul care tipăreşte vectorul. Se scrie un subprogram care calculează valoarea funcţiei într-un punct x oarecare şi se apelează subprogramul: o dată pentru x=a şi încă o dată pentru x=b. să le copiem în noul program şi să facem eventualele adaptări (numărul de componente şi numele vectorului pot fi altele). vom implementa o metodă cunoscută. programul ar arăta astfel: Pasul 1 . Aceste operaţii sunt destul de greoaie şi necesită multă atenţie.  elaborarea algoritmilor prin descompunerea problemei în altele mai simple .în acest fel. se compară între ele şi se afişează rezultatul cerut. dar vom utiliza subprogramele.  depistarea cu uşurinţă a erorilor .  realizarea unor programe uşor de urmărit (lizibile).Manual de informatică pentru clasa a XI-a 21 Oare nu se poate lucra şi altfel? Răspunsul este afirmativ.odată scris. . sortare şi tipărire). În acest caz. Pasul 3 . şansele de a greşi la scrierea unui subprogram sunt cu mult mai mici decât acelea de a greşi la scrierea unui program mare. Se cere să se afişeze cele n numere în ordinea crescătoare a valorilor lor.se apelează subprogramul care citeşte vectorul. pentru că ştim să memorăm un şir de valori (folosind un vector) şi am studiat mai multe metode prin care se poate obţine ordonarea crescătoare a unui şir de valori (folosind algoritmi de sortare). De această dată. Se citesc apoi n numere reale. un număr natural. aici problema a fost descompusă în trei probleme mai simple (citire. metoda clasică ne permite să alegem din secvenţa de instrucţiuni ce formează programul pe cele ce realizează sortarea. În plus. Se citeşte n.  reducerea numărului de erori care pot apărea la scrierea programelor. ştim să rezolvăm această problemă în mai multe feluri.se apelează subprogramul care sortează vectorul. o problemă complexă se rezolvă mai uşor dacă o descompunem în alte subprobleme mai mici. Valorile calculate la cele două apeluri. cea de sortare. Desigur. 2. În general. Faţă de scrierea clasică. rezolvăm cu mult mai uşor problema. unul care tipăreşte un vector şi un al treilea care sortează vectorul după una din metodele cunoscute. ”preluarea“ se realizează mult mai uşor. Vom scrie un subprogram care citeşte un vector. apoi modul în care le-am asamblat (le-am apelat din cadrul programului). dacă într-un alt program este necesară sortarea altui vector de numere reale. un subprogram poate fi utilizat de către mai multe programe. Pasul 2 .verificăm la început subprogramele. Apoi.

Aceste variabile se numesc variabile locale. În exemplu.  Ea este recunoscută de compilator prin faptul că antetul este precedat de cuvântul cheie “function“. Analiza programului anterior  Antetul funcţiei este “function subp(n:integer):real.  Am văzut că funcţia întoarce un anumit rezultat .  Funcţia are variabile proprii . funcţia a primit ca valoare de retur conţinutul variabilei s. de tip real. subp:=s. Rolul său este important şi anume precizează pentru ce valoare trebuie calculată expresia. E 2 = 1 + + +  +  .în exemplu.  Funcţia are un parametru numit n. Se citeşte n. Aşa cum vom vedea.2.2. Prin atribuirea “subp=s. for i:=1 to n do s:=s+1/i. În continuare. Rezultatul este reţinut de variabila locală s. Calculez expresia în mod obişnuit. Subprograme 2. begin s:=0. tipul este real întrucât expresia calculată este de acest tip.  Funcţia are un anumit tip. Un exemplu de utilizare a funcţiilor  Problema 2.1. de diferite tipuri. var s:real. end.  Funcţia se numeşte “subp“. Funcţia care calculează E1 este: function subp(n:integer):real. i:integer. În exemplu. prezentăm cele două programe care utilizează funcţia: .adică variabile care sunt definite în cadrul ei. care precizează natura rezultatului. număr natural. Subprograme în Pascal 2.“.22 Capitolul 2.1.“. Să se scrie programele care afişează valoarea calculată a expresiilor: n 1 1 1  1 1 1 E1 = 1 + + +  + . există posibilitatea să avem mai mulţi parametri. Observaţi mecanismul prin care am obţinut aceasta. ele sunt s şi i. 2 3 n  2 3 n  Rezolvare.

lucrurile stau altfel: valorile acestora sunt cunoscute. var s:real. iar în al doilea. rez. begin write ('n='). în cazul funcţiilor . Din acest punct de vedere ei se numesc formali. readln(n).2. function subp(n:integer):real. “rez:=subp(n)“. prod:=1.“. i:integer. end. Definiţia 2. subp:=s. În terminologia utilizată în teoria subprogramelor .  Funcţia este apelată întotdeauna din cadrul unei expresii. end. function subp(n:integer):real. oricare ar fi valoarea lor. var s:real. readln(n).se utilizează termenii parametri formali şi parametri efectivi. . _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ var n. end.i:integer.prod:real. write(prod:5:2). Atunci când scriem o funcţie. for i:=1 to n do prod:=prod*rez. for i:=1 to n do s:=s+1/i. Funcţia trebuie să întoarcă rezultatul corect.3. write(subp(n):5:2). for i:=1 to n do s:=s+1/i. begin s:=0. begin write ('n='). Parametrii care se utilizează la apel se numesc parametri efectivi. Definiţia 2. Parametrii care se găsesc în antetul funcţiei se numesc parametri formali. subp:=s. Apelul funcţiei în primul program s-a făcut astfel: “write(subp(n):5:2). În primul caz expresia este trecută ca parametru pentru write. avem o expresie de atribuire.în particular. end. rez:=subp(n). Prin urmare aceştia se numesc parametri efectivi. nu cunoaştem valoarea propriu-zisă a parametrilor. iar în al doilea.Manual de informatică pentru clasa a XI-a 23 var n:integer. i:integer. La apel. begin s:=0.

v[i]:=v[i+1]. end. end. for i:=1 to n-1 do if v[i]>v[i+1] then begin gasit:=true. procedure citesc. Programul conţine trei proceduri: citesc. var gasit:boolean. begin citesc.2...2.  Rezolvare. Subprograme Între parametrii formali si parametrii efectivi trebuie să existe o concordanţă.. număr natural. care va fi studiată la momentul potrivit. var n..2 Un exemplu de utilizare a procedurilor  Problema 2. end. for i:=1 to n do begin write('v['. end.. Se citeşte un vector cu n componente numere reale. . Se cere să se tipărească vectorul sortat.24 Capitolul 2. readln(v[i]).. tiparesc. begin write('n='). begin .. v[i+1]:=man end until not gasit.i. sortez şi tiparesc. Programul este următorul: type vector=array [1. begin repeat gasit:=false. readln(n). procedure sortez. end. man:integer. procedure tiparesc. end.9] of integer. begin for i:=1 to n do writeln(v[i]). Se citeşte n.. man:=v[i]. 2.i:integer. sortez. v:vector. Forma simplificată prin care este descrisă o procedură este: procedure nume.']=').

Structura unui subprogram În esenţă.după cum se întâmplă şi la funcţii.Manual de informatică pentru clasa a XI-a 25  Apelul unei proceduri se face prin utilizarea numelui ei.  Orice variabilă a programului principal este şi variabilă a procedurii (invers nu este adevărat).2. sub forma nume. controlul programului este transferat la prima instrucţiune a procedurii . indiferent dacă este bloc de procedură sau de funcţie. Structura sa este identică cu cea a programului principal. ale subprogramelor şi o instrucţiune compusă. Structura antetului unui subprogram de tip funcţie . se revine în programul principal la prima instrucţiune care urmează celei de apel .2. În acest exemplu.  La apel. apelul unei proceduri este instrucţiune. Structura subprogramelor de tip funcţie În figura următoare este prezentată structura antetului: Figura 2. În realitate. Pentru prezentarea structurii subprogramelor de tip funcţie vom utiliza diagramele de sintaxă. numele subprogramului. procedurile se pot plasa în cadrul programului între declaraţiile de variabile şi instrucţiunea compusă.3. procedurile pot avea parametri întocmai ca şi funcţiile.3.1. Programul principal conţine un antet (program nume) şi un bloc. • Bloc . După executarea procedurii. 2. ale variabilelor.conţine mai multe informaţii importante necesare compilatorului. numită instrucţiunea de apel.cuprinde definiţiile tipurilor de variabile. conţine şi tipul rezultatului (valoarea întoarsă de funcţie). Mai precis. În cazul subprogramelor de tip funcţie.1. un subprogram este alcătuit din: • Antet .  Ca şi funcţiile. 2..în cazul de faţă se întâlneşte o altă instrucţiune de apel. procedurile sunt apelate fără utilizarea parametrilor. lista parametrilor formali.

y:integer):real. . .2.y:real). .26 Capitolul 2.y:real. begin suma:=x+y. x. de orice tip pointer sau de tipul string (acest tip va fi studiat în acest manual).antetul este: “procedure suma(var s:real.y:integer“.antetul este “function suma(x.y:integer):real. end.2. .blocul este (în acest caz lipsesc variabilele locale): begin suma:=x+y.2. Structura antetului unui subprogram de tip procedură Exemplu: procedure suma(var s:real.lista parametrilor formali este: “var s:real.3. de orice tip real. . .blocul este: begin s:=x+y. “ . end.y:real). Lista parametrilor formali este: “x. Subprograme Exemplu: function suma(x. 2.identificatorul (numele) funcţiei este suma. begin s:=x+y.“.identificatorul (numele procedurii) este suma. Structura subprogramelor de tip procedură În figura următoare este prezentat antetul de procedură: Figura 2. x. Funcţia este de tip real (identificatorul de tip). end .“. x. end Important: identificatorul de tip (tipul valorii întoarse de funcţie) poate fi: de orice tip ordinal.

după structura anterior prezentată. are ca subprogram procedura z. end. z. begin x:=3. cele două noţiuni diferă. A defini un subprogram. În cadrul unităţilor de program .Manual de informatică pentru clasa a XI-a 27 2. begin writeln(x). Iniţial programul va atribui variabilei x valoarea 3. Buna înţelegere a lor ne ajută să evităm anumite erori. procedure z. Această formă este proprie limbajului Pascal. begin writeln(x) end. În exemplu. procedure t. Mai mult.vor fi studiate ulterior. Ea tipăreşte conţinutul lui x (3).între declaraţia variabilelor şi instrucţiunea compusă: program exemplu1. În Pascal. apoi apelează procedura z. 2. Definiţia 2. procedura t. înseamnă a-l scrie efectiv. begin writeln('eu sunt z'). . begin x:=3. var x:integer. t end.2.4. end. aceasta afişează un mesaj: 'eu sunt z'.numit şi program principal . În blocul unui alt subprogram. aceste noţiuni sunt utilizate de orice limbaj de programare. procedure t. t end. O problemă importantă este locul unde se defineşte subprogramul. program exemplu2. 3. În blocul programului care-l utilizează . var x:integer. Definirea şi declararea unui subprogram Deşi aparent asemănătoare. subprogramele se definesc în trei locuri: 1.4. La rândul ei. Apoi va apela procedura t. nu numai de Pascal.

.. . În cazul în care subprogramul este definit în blocul programului principal sau în blocul subprogramului.. Un subprogram nedeclarat nu poate fi folosit.. deci poate fi apelată de acesta. Ea poate fi apelată din t.. end. procedure s2. .. subprogramele s1. dar s2 nu poate fi apelată. . Amândouă sunt declarate pentru procedura test. În acest caz.5. sk. s1 este declarată.28 Capitolul 2. se consideră că a fost şi declarat pentru acesta. Procedura s1 poate fi apelată din procedura s2. în ordine. end.  În programul exemplu2.. begin s1. apoi s2 este declarat pentru s3. deci poate fi folosit. Nici una din aceste proceduri nu se consideră declarată pentru programul principal. din s1: procedure s1. înseamnă a-l anunţa. iar sk-1 este declarat pentru sk. s2.  Procedura test conţine definiţia a două proceduri s1 şi s2. 2. Pentru procedura s2. s3. procedura t este definită în blocul programului principal. procedura t este definită în blocul programului principal. În acest caz. Subprograme Definiţia 2. Exemple  În programul exemplu1. A declara un subprogram. begin writeln('s1') end.. În blocul programului principal sau în blocul unui subprogram se definesc. Ea poate fi apelată din programul principal. Exemple  Procedurile s1 şi s2 pot fi apelate din programul principal. begin s1. nu poate fi apelată din programul principal. sk. sk. automat s1 este declarat pentru s2. declaraţia coincide cu definiţia. dar pentru s1 procedura s2 nu este declarată. în absenţa altei clauze. Procedura z este definită în blocul procedurii t. În schimb... 1. writeln ('s2'). s2.

end. forward. begin a. urmat de cuvântul cheie “forward“. procedure a. procedure b. în acest caz. begin s1. În cazul în care subprogramul a fost definit în cadrul unei unităţi de program. 4. s2. declaraţia unităţii. begin s1. 3. end. Declaraţia se face prin antet. procedure b. end. clauza “uses“. begin writeln('Eu sunt b') end. În situaţia prezentată în cazul 2. . Observaţi că. procedure s1. begin test. procedure s2. end. definiţia nu coincide cu declaraţia. are efect de declaraţie pentru toate subprogramele definite în cadrul ei (unităţile de program vor fi studiate în cadrul acestui capitol). ca în exemplul următor.Manual de informatică pentru clasa a XI-a 29 procedure test. begin writeln('s1') end. se poate totuşi ca orice subprogram definit la nivelul unui bloc să fie declarat pentru toate subprogramele definite la nivelul aceluiaşi bloc. end. b. writeln('s2'). begin writeln('Eu sunt a').

begin x:=2. se continuă evaluarea expresiei. 2.y)). înseamnă a-l lansa în executare. function prod(x. Pentru a putea fi apelat din cadrul unui bloc. y:=3.x.1. valoarea găsită este 7: var p.2.5. Apelul funcţiilor În programul de mai jos este apelată o funcţie care calculează produsul a două numere întregi. În exemplu.30 Capitolul 2.y:integer. Programul tipăreşte suma între 1 şi produsul calculat. Rezultatul ar fi fost obţinut mai simplu dacă scriam ”writeln(1+prod(x. Mai jos este prezentat operandul care reprezintă apelul funcţiei.”. un subprogram trebuie declarat la nivelul blocului respectiv. begin prod:=x*y. În exemplu. Observaţii  În cadrul expresiei. Structura operandului care reprezintă apelul funcţiei .6.y)”. Apelul subprogramelor Definiţia 2. p:=1+prod(x.y). writeln(p) end.  După apelul funcţiei. El intră în calcul cu valoarea returnată de funcţie. apelul este un operand. Figura 2. end.  Apelul unei funcţii se realizează din interiorul unei expresii.3. Subprograme 2.y:integer):integer.5. A apela un subprogram. expresia este: ”1+prod(x.2.

 Parametrii formali sunt cei trecuţi în antetul subprogramului. y:=4. writeln(sum).4.2. Putem spune numai că utilizarea parametrilor permite ca subprogramul să fie scris independent de programul principal. Atunci care este motivul pentru care sunt necesari parametrii? Cunoştinţele dobândite până în acest moment nu permit o justificare completă.5.sum). se va executa instrucţiunea care urmează instrucţiunii procedurale. end.sum:integer. writeln(sum). suma(2.b:integer. După apel. 2. numită instrucţiune procedurală. Apelul procedurilor Cele câteva exemple date până acum fac inutilă prezentarea unui alt exemplu.5. Din acest motiv. .3. suma(x.y. Apelul unei proceduri se face printr-o instrucţiune. Aşa cum am văzut.3. iar pentru altul. Să luăm un exemplu: ni se cere să scriem un subprogram care calculează suma a două valori reale. begin x:=3.  Parametrii efectivi sunt cei trecuţi la apelul subprogramului. m cu n. subprogramele pot lucra cu variabilele globale ale blocului care conţine definiţia lor. begin s:=a+b. var s:integer). Sintaxa acestei instrucţiuni este prezentată mai jos: Figura 2. Trebuie să ştim că: 1.2. procedure suma(a. Transmiterea parametrilor la apel În ce priveşte mecanismul de transmitere a parametrilor nu există nici o diferenţă între proceduri şi funcţii. transmiterea parametrilor se va trata unitar. end.Manual de informatică pentru clasa a XI-a 31 2. var x. Ce adunăm? Pentru un program ar trebui să adunăm x cu y.y.2. Structura instrucţiunii procedurale 2.sum).

acest număr este 3.pentru primul apel. Nu este obligatoriu ca numele parametrilor formali să coincidă cu numele parametrilor efectivi. De exemplu. Sintaxa parametrilor trimişi .”.. . dacă parametrii formali a şi b sunt de tipul integer. {incorect} Parametrii formali sunt de două feluri: transmişi prin valoare şi transmişi prin referinţă. prin valoare. v:array[1.  Tipul parametrilor formali trebuie să coincidă cu tipul parametrilor efectivi sau tipul parametrilor efectivi să poată fi convertit implicit către tipul parametrilor formali. sum. sum. v:vector). var s:integer. parametrii efectivi sunt: 2. la fel ca în cazul atribuirii.  Nu este permis să definim tipul parametrilor în cadrul antetului subpro- gramului..b:integer. În exemplu. contrar.9] of integrer). se consideră că sunt transmişi prin referinţă.. parametrii efectivi sunt: x. . În cazul în care aceştia sunt precedaţi de cuvântul cheie var. {corect} procedure test(n:integrer. Iată câteva reguli care trebuie respectate la apel:  Numărul parametrilor formali trebuie să coincidă cu numărul parametrilor efectivi.5. y. Subprograme În programul anterior. Pentru moment.9] of integer. Tipul trebuie să fie predefinit sau să fie declarat în prealabil. . procedura suma calculează suma a două numere întregi. de tipul real). Ea este apelată de două ori din cadrul programului principal.pentru al doilea apel. 3. prezentăm sintaxa parametrilor formali şi a parametrilor efectivi: Figura 2. nu este permis ca parametrii efectivi să fie de alt tip (de exemplu. Tratarea transmiterii prin valoare şi prin referinţă se va face în paragrafele următoare. procedure test(n:integrer.parametrii formali sunt: ”a.32 Capitolul 2. . Exemple type vector=array [1..

Reluăm exemplul anterior:  antetul este: ”procedure suma(a. această zonă se numeşte segment de stivă). dar în prelucrare.Manual de informatică pentru clasa a XI-a 33 2. parametrii sunt: 2.5. parametrii sunt: x. var s:integer).  În cadrul subprogramului.b:integer. Numele lor este cel din lista parametrilor formali.2. conţinutul variabilelor memorate în stivă se va pierde.2. parametrii transmişi şi memoraţi în stivă sunt variabile. Transmiterea parametrilor prin valoare Transmiterea prin valoare se utilizează atunci când suntem interesaţi ca subprogramul să lucreze cu acea valoare. iar pentru cei transmişi prin referinţă.4. sum. sunt prezentate şi valorile memorate: stiva 3 4 adresa variabilei sum a b s La al doilea apel. Cum memorează subprogramele parametrii transmişi ? În acest paragraf vom analiza modul prin care sunt memoraţi parametrii transmişi în momentul lansării în executare a subprogramului.”. dar acestea vor fi tratate la momentul potrivit (vezi recursivitatea!). . În figură.  Memorarea parametrilor transmişi se face în ordinea în care aceştia figurează în antet: de la stânga la dreapta.  Pentru parametrii transmişi prin valoare. 2.5.  la primul apel. sum. 3. se memorează valoarea transmisă.  Memorarea în stivă are şi alte consecinţe. se memorează adresa variabilei transmisă ca parametru. nu ne interesează ca parametrul efectiv (cel din blocul apelant) să reţină valoarea modificată în subprogram.5. unde x reţine ”3” şi y ”4”. subprogramele folosesc o zonă de memorie numită stivă (mai exact. stiva 2 3 adresa variabilei sum a b s Observaţii  La revenirea în blocul apelant. În stivă sunt memorate variabilele subprogramului.  Pentru memorarea parametrilor. y.

begin writeln(n) end.  Afişăm conţinutul variabilei n (cea din stivă). se rezervă spaţiu în stivă. variabila n este incrementată (adică la vechiul conţinut se adaugă 1). Evident. spaţiu care are numele parametrului (deci tot n) şi este iniţializat cu valoarea memorată de variabila n a programului principal. parametrii efectivi trebuie să fie numele variabilelor. Prin urmare. begin n:=1.în acest caz. avem declarată variabila n. procedure test(n:integer). este vorba de variabila memorată în stivă. begin n:=n+1. Exemplu: procedure test(n:integer). writeln(n). se pot transmite prin valoare:  valorile reţinute de variabile .  În programul principal. writeln(n) end. valoarea 2 este pierdută. Exemplu: var n:integer.  În programul principal se tipăreşte conţinutul variabilei n.  În procedură.34 Capitolul 2. deci se tipăreşte 2. care mai întâi se evaluează. end. test(3+4*5). adică 1. În acest moment avem două variabile n şi ambele reţin valoarea 1. La apel. care este iniţializată cu 1. begin test(3).parametrul n este transmis prin valoare. test(n). end.  Apelăm procedura.adică nu mai are spaţiu alocat. variabila n (din stivă) se pierde .  parametrii efectivi sunt valori sau expresii. . . Subprograme Astfel.  La ieşirea din procedură.

readln(y). se pot folosi ca parametri efectivi expresii. var x. function dif (a. transmiterea prin referinţă este mecanismul clasic prin care acestea întorc valori către blocul apelant. este "tradusă" ca adresare indirectă.b:integer):integer. . subprogramul reţine în stivă adresa variabilei. ne putem întreba care este mecanismul prin care.b:integer):integer. writeln(prod(dif(x.b:integer):integer. parametrii efectivi trebuie să fie variabile. în cazul transmiterii prin valoare. end. function suma (a. Aşa cum am văzut.  În cazul transmiterii prin referinţă. begin dif:=a-b.suma(x. care la primul apel reţine valoarea 3 şi la al doilea. Transmiterea parametrilor prin referinţă Parametrii sunt transmişi prin referinţă atunci când ne interesează ca.6.2. în subprogram putem adresa variabila normal (nu indirect)? La compilare. begin prod:=a*b.  În cazul transmiterii prin referinţă. write('y=').  Pentru ca un parametru să fie transmis prin referinţă este necesar ca parametrul formal să fie precedat de cuvântul cheie ”var”.y))). deşi pentru o variabilă transmisă se reţine adresa sa. end.y:integer. valoarea 23. expresiile pot avea ca operanzi funcţii.  În cazul subprogramelor de tip procedură. la revenirea din subprogram. begin write('x='). variabila transmisă să reţină valoarea stabilită în timpul executării subprogramului. readln(x). numită n. Următorul program citeşte două valori x şi y şi calculează expresia (x-y)*(x+y). function prod(a. Exemplu: program test_param. De asemenea. end. end. begin suma:=a+b.5.Manual de informatică pentru clasa a XI-a 35 În procedură se creează o variabilă în segmentul de stivă. orice referinţă la variabila respectivă. În acest caz. 2. La ieşirea din procedură conţinutul variabilei se pierde.y).

A preciza vizibilitatea unei variabile înseamnă a spune care sunt blocurile de unde se poate adresa (ca să-i atribuim o valoare. begin man:=x. de exemplu). Variabilele globale sunt variabilele programului principal. x:=y.  Variabilele globale sunt vizibile la nivelul programului principal şi la nivelul subprogramelor. Subprograme Exemplu. y:=3. var man:integer.y). La ieşirea din subprogram conţinutul variabilelor locale se pierde.0 a limbajului Pascal. procedure b.36 Capitolul 2. begin x:=2. begin writeln(x) end. Programul următor utilizează o procedură care interschimbă valorile reţinute de două variabile. write(x. Ele sunt rezervate într-o zonă specială de date. b.8.7. Programul alăturat tipăreşte de trei ori procedure a. procedure interschimb(var x. end. În versiunea 7. numită segment de date. Este sarcina programatorului să asigure iniţializarea lor cu valorile dorite. Definiţia 2. Acestea sunt transmise prin referinţă. end. interschimb(x.y). end. begin x:=2. Un termen des folosit în practica limbajelor de programare este acela de vizibilitate. după variabilele generate de parametri. y:=man.2. Variabilele locale sunt variabile declarate în interiorul subprogramelor. .' '. writeln(x).y:integer). var x. var x:integer. x:=2. writeln(x) end. variabilele numerice sunt iniţializate cu 0. 2. conţinutul variabilei globale x.6. Aceste variabile sunt memorate în segmentul de stivă. Variabile locale şi globale Definiţia 2. 2.y:integer. begin a.

begin x:=2. Alt termen des utilizat în practica programării este durata de viaţă a unei variabile. a. prin nume.  durata locală . procedure a. . compilatorul va da eroare de sintaxă. Atenţie! Există situaţii în care o variabilă globală nu poate fi adresată din cadrul subprogramului. procedure a. încercat să tipărim x din cadrul programului begin writeln(x) principal.Manual de informatică pentru clasa a XI-a 37  Variabilele locale sunt vizibile doar la nivelul subprogramului în care au fost declarate şi la nivelul subprogramelor definite în acesta. compilare. Variabilele cu această durată de viaţă se numesc dinamice şi nu se tratează în acest manual. conţinutul variabilei locale x. Dacă am fi procedure b. Regula este următoarea: în cazul în care două variabile au acelaşi nume.  durată dinamică . Evident. end. El se referă la perioada de timp în care variabila este alocată în memorie. adică atunci când acesta conţine declaraţia unei alte variabile. begin x:=2. Programul de mai jos tipăreşte 2. avem:  durată statică . dar vizibilităţi diferite.variabila are alocat spaţiu doar în timpul cât se execută un anumit bloc. În acest caz. writeln(x) end. Programul alăturat tipăreşte de două ori 2.variabila are alocat spaţiu pe tot timpul executării programului. begin a. se adresează întotdeauna variabila cu vizibilitatea mai redusă. Astfel. programul ar fi dat eroare de end. se adresează variabila locală. nu 3: var x:integer. writeln(x) end. begin x:=3. var x:integer.alocarea spaţiului de memorie se face în timpul executării prin subprograme speciale sau operatori (în Pascal avem subprograme speciale). în cazul existenţei a două variabile cu aceeaşi vizibilitate şi acelaşi nume. end. cu acelaşi nume. b.

.99] of real. suma(n. . În caz contrar tipul este nestandard (de exemplu.i:integer):integer.. Greşeli care apar la atribuirea valorii de retur a unei funcţii (valoarea pe care funcţia o întoarce) Iată o funcţie.. var s:integer. end. Subprograme Din acest punct de vedere putem spune că variabilele globale au durată de viaţă statică. begin . pe lângă tip.”. în cadrul programului principal. vizibilitate şi durată de viaţă... type vect=array[1.. begin . 2. integer). Se referă la locul unde variabila este memorată. Eroarea constă în faptul că în atribuirea ”suma(n. o variabilă se caracterizează prin: clasă de memorare. Greşeli care apar la transmiterea tipurilor nestandard Reamintim faptul că un tip este standard dacă este cunoscut de limbaj (de exemplu. procedure er (var v:vect). begin calculez s. tipul este descris separat.7.2. Astfel.. Iată o procedură scrisă greşit: procedure er (var v:array[1. prin type. care întoarce o valoare întreagă s (o sumă). tipul care descrie un vector cu componente de tip real).99] of real). var v:vect. iar variabilele locale au durata de viaţă locală. Greşeli frecvente 1... .38 Capitolul 2.. Corect este ”suma:=s.i):=s. Eroarea este dată de faptul că tipul nestandard al variabilei v este descris în lista parametrilor. Programul va da eroare de sintaxă.. . .. . end. Un ultim termen este dat de clasa de memorare a unei variabile... .. function suma( n.i):=s...” au fost trecuţi parametri.. în segmentul de stivă sau în heap. o variabilă poate fi memorată în segmentul de date. end... Pentru a proceda corect. scrisă eronat. 2. Astfel.

care pot fi uşor utilizate în scrierea altor programe... Aici se pot declara tipurile şi variabilele vizibile numai pentru unitatea dată. END. Se observă că o unitate de program are în mod obligatoriu două părţi. variabilelor. plasate şi compilate împreună. programul care o utilizează. În acest sens. O preocupare constantă a informaticienilor este de a pune la dispoziţia programatorilor instrumente eficiente de programare. opţional. Aici se găsesc definiţiile tipurilor de date. limbajul Pascal oferă posibilitatea lucrului cu unităţi de program (unit-uri).]  Prima parte.2. Tot aici se scriu clar toate procedurile şi funcţiile care în parte au fost numai declarate. Prin unitate de program înţelegem un ansamblu de date. Toate procedurile şi funcţiile care aici sunt numai enumerate pot fi folosite de orice program care foloseşte unitatea. în momentul declarării unităţii de program în . Definiţia 2.  Partea opţională a fost prezentată în cadrul formei generale a unităţii.] [tipuri de date şi variabile globale în programul care utilizează unitatea] antete de proceduri şi funcţii IMPLEMENTATION [tipuri de date şi variabile locale (valabile numai pentru unitatea de program)] procedurile şi funcţiile utilizate de unitatea de program [BEGIN . o a treia.  A doua parte.... Pentru orice tip de variabilă declarat aici.. Unităţi de program În activitatea practică se fac aplicaţii cu un număr mare de programe care de multe ori sunt de mari dimensiuni.. Forma generală a unei unităţi de program este următoarea: UNIT <nume unitate>. se pot declara variabile de acest tip atât în cadrul unităţii cât şi în cadrul programului care foloseşte unitatea.8... este precedată de clauza ”INTERFACE”.. obligatorie... o eventuală secvenţă de program care se execută . INTERFACE [USES <numele altor unităţi de program pe care le utilizează>.. şi sunt enumerate procedurile şi funcţiile ce pot fi utilizate din programul principal... . proceduri şi funcţii. se găseşte plasată sub clauza ”IMPLEMENTATION”.. fără a se cunoaşte amănunte necesare realizării lor. precum şi altele ce pot fi folosite numai în cadrul unităţii.9.Manual de informatică pentru clasa a XI-a 39 2.. obligatorie..

begin c:=a-b.b:real). procedure scad(a. Unitatea de program este prezentată în continuare: unit operatii. procedure produs(a. begin c:=a+b. writeln(c:3:2). end.40 Capitolul 2. end.b:real).b:real). end procedure impart(a.b:real. În secvenţa de iniţializare se citesc valorile variabilelor a şi b. vom da un exemplu pur didactic de unitate de program. interface var a. procedure impart(a.b:real).b:real). readln(b). procedure scad(a.b:real). procedure adun(a. procedure adun(a.b:REAL). scădere. Subprograme În continuare.b:real). writeln(c:3:2). înmulţire şi împărţire de variabile reale. writeln('b='). readln(a). writeln(c:3:2). begin if b=0 then writeln('Impartirea nu se poate face ') else begin c:=a/b. end. end. iar variabila c poate fi utilizată numai în cadrul unităţii. implementation var c:real. Aceasta conţine patru proceduri: de adunare. Variabilele a şi b sunt globale pentru programul care utilizează această unitate. end. begin c:=a*b. . procedure produs(a. begin write('a='). writeln(c:3:2). numită OPERATII.

write('d='). scad(c. trebuie să precizăm faptul că limbajul Pascal este livrat cu câteva unităţi de program standard (produse de firma BORLAND).d). • se apelează aceleaşi proceduri utilizând pentru apel două variabile globale ale programului (c şi d).b). • se apelează procedurile de adunare.b). După ce am văzut modul de alcătuire şi construcţie a unei unităţi de program. Pentru utilizarea lor nu este necesară clauza ”USES”. Amintim câteva: SYSTEM.b). scad(a. Procedurile şi funcţiile puse la dispoziţie de limbaj se găsesc în aceste unităţi. impart(a.d). Rularea sa decurge în felul următor: • se cer valorile variabilelor a şi b (declarate în unitate). produs(c. uses operatii. readln(d). impart(c. scădere. readln. înmulţire şi împărţire iar rezultatele se tipăresc. CRT. write('c='). produs(a. Faptul că programul utilizează o unitate se precizează prin clauza ”USES”. begin adun(a. Toate subprogramele folosite de noi până în prezent se află în unitatea de program SYSTEM. . readln(c). var c. end.d).d:real. adun(c.b).d). DOS sau GRAPH.Manual de informatică pentru clasa a XI-a 41 Programul următor utilizează această unitate.

1.i++) s+=1. }  Antetul funcţiei este ”double subp(int n).i<=n. double rez./i.3. Aşa cum vom vedea. +  . } main() { int n.. Exemple de utilizare a funcţiilor  Problema 2. 2 3 n  2 3 n  Rezolvare.. int i. // sau s=s+1. return s. for (i=1. de diferite tipuri. for (i=1..i++) s+=1.”. int i. } Funcţia care calculează E1 este: double subp(int n) { double s=0. cin>>n./i.i++) s+=1. Subprograme în C++ 2.int i./i return s. E 2 = 1 + + + . În continuare prezentăm cele două programe care utilizează funcţia: #include <iostream. cout<<"n=". . cin>>n. există posibilitatea să avem mai mulţi parametri. for (i=1. Rolul său este important deoarece precizează pentru ce valoare trebuie calculată expresia./i. Să se scrie programele care tipăresc valoarea calculată a expresiilor: n 1 1 1  1 1 1 E1 = 1 + + + . Subprograme 2. } main() { int n. } _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ #include <iostream.h> double subp(int n) { double s=0. // sau s=s+1.  Funcţia are un parametru numit n. prod=1. cout<<subp(n).i.42 Capitolul 2.i<=n. cout<<prod.i<=n.3./i return s.i++) prod*=rez. număr natural.3.  Funcţia se numeşte ”subp”.. rez=subp(n). for (i=1.h> double subp(int n) { double s=0. cout<<"n=". + .i<=n. Se citeşte n.

i<n-1. Calculez expresia în mod obişnuit.i. care precizează natura rezultatului. Prin instrucţiunea ”return s. Din acest punct de vedere ei se numesc formali. În terminologia utilizată în teoria subprogramelor . oricare ar fi valoarea lor. număr natural.int n) { int i. de tip double.i++) if (vt[i]>vt[i+1]) { man=vt[i]. parametrul efectiv este n. for(i=0.Manual de informatică pentru clasa a XI-a 43  Funcţia are un anumit tip. aceştia se numesc parametri efectivi. Se citeşte un vector cu n componente numere reale.în exemplu. ”. Definiţia 2. } } void sortez(int vt[10].adică variabile care sunt definite în cadrul ei. Rezultatul este reţinut de variabila locală s. #include <iostream. Atunci când scriem o funcţie nu cunoaştem valoarea propriu-zisă a parametrilor. Aceste variabile se numesc variabile locale.în particular. vt[i]=vt[i+1].  Funcţia are variabile proprii .man. do { gasit=0.int n) { int gasit. for (i=0.i++) { cout<<"v["<<i+1<<"]=". Se citeşte n.”.10. În exemplu. în cazul funcţiilor . } } while (gasit). tipul este double întrucât expresia calculată este de acest tip. Observaţi mecanismul prin care am obţinut aceasta.i<n. Funcţia trebuie să întoarcă rezultatul corect. lucrurile stau altfel: valorile acestora sunt cunoscute.11. Pentru apelul ”rez=subp(n). La apel.  Problema 2.se utilizează termenii parametri formali şi parametri efectivi. Prin urmare. funcţia a primit ca valoare de retur conţinutul variabilei s.  Am văzut că funcţia întoarce un anumit rezultat . Parametrii care se găsesc în antetul funcţiei se numesc parametri formali. } . Definiţia 2.h> void citesc(int vt[10].4. gasit=1. Parametrii care se utilizează la apel se numesc parametri efectivi. În exemplu. vt[i+1]=man. s şi i. cin>>vt[i]. Se cere să se tipărească vectorul sortat.

adică nu au valoare de retur. cin>>n.. Structura unei funcţii În esenţă.n).i<n. parametru2. Subprograme void scriu(int vt[10]. . 2. for(i=0. se revine în programul principal la prima instrucţiune care urmează celei de apel .n. parametrun Există şi posibilitatea ca lista parametrilor formali să fie vidă.i++) cout<<vt[i]<<endl. o funcţie este alcătuită din:  Antet . Ele returnează rezultatul prin intermediul parametrilor. } Programul conţine trei funcţii: citesc.. } main() { int v[10].2.  La apel.int n) { int i. citesc(v. Poate fi tip al unei funcţii orice tip de dată cu excepţia masivelor.  Cele trei funcţii au tipul void. cout<<"n=".acesta conţine mai multe informaţii importante necesare compilatorului: numele funcţiei. Structura antetului este: tip nume(lista parametrilor formali) Lista parametrilor formali este de forma: parametru1. tipul rezultatului.aceasta cuprinde declaraţiile variabilelor locale.în cazul de faţă se întâlneşte o altă instrucţiune de apel.n). sortez şi tiparesc.n). . Fiecare parametru are forma: tip nume.3. scriu(v.44 Capitolul 2. lista parametrilor formali. sortez(v.  O instrucţiune compusă .. controlul programului este transferat la prima instrucţiune a funcţiei. După executarea funcţiei. şi instrucţiunile propriu-zise.

care are forma: return expresie. apelul este un operand.  După apelul funcţiei se continuă evaluarea expresiei. De exemplu.  La apel.funcţia se numeşte t. În absenţa instrucţiunii return.funcţia se numeşte suma. int y) două numere întregi. 7. la evaluarea expresiei este apelată.y=3. de tip float* (un tip care reţine adrese de vectori cu elemente de tipul float). } Apelul funcţiei s-a realizat din interiorul expresiei: ”1+prod(x. { return x*y. int suma(int a.2+3). produsul calculat. #include <iostream. Această ultimă formă de apel nu este valabilă în cazul funcţiilor de tip void. este de tip void (nu returnează rezultat prin nume). float v[20]) . dar poate fi inclusă şi în cadrul expresiilor. În acest caz nu se întoarce nici o valoare.h> În programul alăturat este apelată o funcţie care calculează produsul a int prod (int x. de tip int. .y). returnează un rezultat de tip int şi are doi parametri formali de tip int.Manual de informatică pentru clasa a XI-a 45 Exemple de antete: . } main() Programul tipăreşte suma între 1 şi { int x=2. cout<<1+prod(x. Observaţii  În cadrul expresiei. În exemplu. al doilea numit v. primul numit n.y)”. void t(int n.  O funcţie returnează rezultatul la întâlnirea instrucţiunii return. numiţi a şi b. Trebuie ca tipul expresiei să coincidă cu tipul funcţiei. execuţia funcţiei se încheie după execuţia ultimei instrucţiuni. int b) . nu ştim dacă înainte se efectuează scăderea sau adunarea. El intră în calcul cu valoarea returnată de funcţie. are doi parametri formali. . ordinea de evaluare a parametrilor nu este definită. dacă apelăm funcţia test astfel: test(2-3. execuţia funcţiei se încheie şi se revine la funcţia care a apelat-o. La întâlnirea instrucţiunii return. caz în care. după atribuirea valorii.  O funcţie poate fi apelată de sine stătător (prin nume şi lista parametrilor efectivi).

2. Vizibilitate la nivel de fişier . singurul caz pe care îl tratăm acum. b. • durata de viaţă. deci se pot obţine programe optimizate.inclusiv în cel al funcţiei main(). Astfel avem: a.5. o variabilă se caracterizează prin 4 atribute.precizează locul unde este memorată variabila respectivă. În acest caz timpul de acces la astfel de variabile este foarte mic.46 Capitolul 2. .3.  Fiecărui program i se alocă trei zone distincte în memoria internă în care se găsesc memorate variabilele programului: Segment de date Segment de stivă Heap Figura 2. Declararea variabilelor Până în prezent am declarat variabile doar în corpul funcţiilor . Subprograme 2.3. • tipul variabilei. c. Cele trei zone în memoria internă De asemenea. Vizibilitate la nivel de bloc (instrucţiune compusă). Clasa de memorare .  În general. Acestea sunt: • clasa de memorare. Vizibilitate la nivel de clasă . în heap sau într-un registru al microprocesorului.precizează liniile textului sursă din care variabila respectivă poate fi accesată.este în legătură cu programarea pe obiecte.în cazul în care programul ocupă un singur fişier sursă. în cel de stivă. O variabilă poate fi memorată în segmentul de date. pe care o veţi studia individual. • vizibilitate. există posibilitatea ca variabilele să fie memorate într-un anumit registru al microprocesorului. singurul pe care l-am studiat până în prezent. 1. Variabilele declarate astfel se numesc locale. Vizibilitatea .

t(). astfel de variabile se numesc globale. La declarare. În C++ variabilele pot fi împărţite în trei mari categorii: globale. variabila a poate fi accesată din corpul oricărei funcţii. 2. locale şi dinamice. int t() { a=3. } Variabilele a şi b sunt globale. Dacă anumite funcţii se află plasate înaintea declaraţiilor acestor variabile. Clasa de memorare . A) Variabile globale Acestea se declară în afara corpului oricărei funcţii. Atributele variabilelor globale sunt: 1. } int b. Din acest motiv. Durată locală . În astfel de cazuri.variabila are alocat spaţiu în timpul în care se execută instrucţiunile blocului respectiv. Durată statică .În cazul în care declaraţiile acestora sunt înaintea tuturor funcţiilor. b. . Noi studiem numai primele două categorii. cout<<a. variabilele respective pot fi utilizate de toate funcţiile care urmează în textul sursă declaraţiei variabilei respective. c.segmentul de date. atunci ele sunt vizibile doar pentru funcţiile care sunt plasate după aceste declaraţii.Manual de informatică pentru clasa a XI-a 47 3. Astfel avem: a. variabilele globale şi locale. Vizibilitatea . cout<<a<<endl. dar variabila b poate fi accesată doar din funcţia main(). Durată dinamică . variabilele globale sunt iniţializate cu 0.alocarea şi dezalocarea spaţiului necesar variabilei respective se face de către programator prin operatori sau funcţii speciale. În exemplul anterior. ca mai jos: #include <iostream.variabila are alocat spaţiu în tot timpul execuţiei programului.reprezintă timpul în care variabila respectivă are alocat spaţiu în memoria internă. main() { b=4. acestea sunt vizibile la nivelul întregului program (fişier). Durata de viaţă .h> int a.

Subprograme 3. numită valoare reziduală. } 1. } main() { int b=4. Variabilele declarate în corpul funcţiei main() sunt tot locale. caz în care declaraţia lor trebuie precedată de cuvântul cheie ”register”. Exemplu: void t() { int a=3. Vizibilitatea variabilelor locale este la nivelul blocului în care au fost declarate. variabilele a şi b sunt locale. Există posibilitatea ca acestea să fie alocate în registrele microprocesorului.48 Capitolul 2. cout<<b<<" "<<c. Exemplu: register int b=4. dar variabila c este vizibilă doar la nivelul blocului în care a fost declarată. Durata de viaţă a variabilelor globale este statică. În funcţia următoare am declarat două variabile de tip int. pot fi declarate în orice bloc (instrucţiune compusă) al acestora. Variabila b este vizibilă la nivelul funcţiei. Variabila a este declarată în corpul funcţiei t(). Ele au spaţiu rezervat în tot timpul execuţiei programului. Exemplu: void t() { int b=4. Clasa de memorare a variabilelor locale este implicit segmentul de stivă. Variabilele locale nu sunt iniţializate implicit cu 0. iar variabila b este declarată în corpul funcţiei main(). reţin o valoare oarecare. Mai precis. În ipoteza în care acestea nu sunt iniţializate explicit de programator. 2. { int c=3. B) Variabile locale Acestea sunt declarate în corpul funcţiilor. numite b şi c. În programul următor. } } .

toate numite a. Priviţi programul următor. dar declarate în blocuri diferite: #include <iostream. } cout<<a<<endl. se accesează variabila cu vizibilitatea cea mai mică.4. Variabila i este declarată (şi în consecinţă vizibilă) doar în blocul for: int n=4. cout<<s. într-un anumit bloc sunt vizibile (se pot accesa) mai multe variabile. dacă în programul anterior se tipăreşte variabila a din cadrul subblocului funcţiei. Observaţii  În cazul în care. } În program. t(). } main() { a=5. s=0. cout<<a. dar au domenii de vizibilitate diferite. for (int i=1.Manual de informatică pentru clasa a XI-a 49 În programul următor am declarat trei variabile. În secvenţa următoare se calculează suma primelor 4 numere naturale. iar cei care se găsesc în instrucţiunea de apel se numesc parametri efectivi.i<=n.4.  Există posibilitatea ca. Una este globală. Transmiterea parametrilor Aşa cum am arătat. cout<<a<<endl. De exemplu. .5). se tipăreşte 3.3. parametrii care se găsesc în antetul funcţiei se numesc parametri formali. void t() { int a=4. iar două sunt locale. Acesta conţine o funcţie care calculează suma a două numere naturale. 3. 2. Durata de viaţă a variabilelor locale este atât timp cât durează execuţia blocului respectiv. un ciclu for să conţină declaraţia unei variabile locale. pentru că acesta este conţinutul variabilei cu cea mai mică vizibilitate (cea declarată în subblocul respectiv). toate cu acelaşi nume. se afişează conţinutul tuturor acestor variabile (3.i++) s+=i. { int a=3.h> int a.

 c. Între parametrii formali şi cei efectivi trebuie să existe o anumită concordanţă.  2+7. cout<<suma(2+7. cout<<suma(1. cout<<suma(c. } După fiecare apel al funcţiei se tipăreşte suma obţinută.50 Capitolul 2.3.3)<<endl. . în acest caz. la fel ca în cazul atribuirii. înainte de apel se evaluează expresiile de mai sus. .  Memorarea parametrilor transmişi se face în ordinea în care aceştia figurează în antet: de la stânga la dreapta. suma(1.d)<<endl.parametrii formali sunt expresii de tip întreg (dat de tipul variabilelor).3)<<endl. cout<<suma(2. 3. suma(c. ele sunt convertite către int la fel ca în cazul atribuirii (se trunchiază zecimala).3-1*2) .9.9. Prin urmare. La această regulă există o excepţie care va fi prezentată într-un paragraf separat. int suma(int a .3) . • Tipul parametrilor formali trebuie să coincidă cu tipul parametrilor efectivi sau tipul parametrilor efectivi să poată fi convertit implicit către tipul parametrilor formali. care este descrisă prin regulile următoare: • Numărul parametrilor formali trebuie să coincidă cu numărul parametrilor efectivi. d. suma(2+7.9.  1. 3-1*2. numărul parametrilor formali este 2.d=3. suma(2.  Pentru memorarea parametrilor subprogramele folosesc segmentul de stivă. În acest caz. 3. .parametrii formali sunt expresii constante de tip întreg. suma calculată este 4=1+3.3) . int b) Funcţia este apelată de mai { return a+b. { int c=4. Subprograme #include <iostream.3. Nu este obligatoriu ca numele parametrilor formali să coincidă cu numele parametrilor efectivi. . În exemplul dat.parametrii formali sunt expresii constante de tip real. } pe rând: main()  2.parametrii formali sunt expresii constante de tip întreg. multe ori. 3. întocmai ca pentru variabilele locale. Vom analiza modul în care sunt memoraţi parametrii transmişi în momentul lansării în execuţie a funcţiei.h> Parametrii formali sunt a şi b.d) . iar cel al parametrilor efectivi este tot 2 (pentru un apel). Parametrii efectivi sunt. .3-1*2)<<endl.

după cum ştim. parametrii transmişi şi memoraţi în stivă sunt variabile. cum sunt memoraţi în stivă parametrii în cazul primului apel: stiva 2 3 a b Observaţii  La revenirea în blocul apelant. 2. 1. . dar acestea vor fi tratate la momentul potrivit.  Variabilele obţinute în urma memorării parametrilor transmişi sunt variabile locale. parametrii efectivi trebuie să fie numele variabilelor care se trimit prin valoare.h> void test (int n) { n+=1. Valorile reţinute de variabile.Manual de informatică pentru clasa a XI-a 51  În cadrul subprogramului. } Parametrul n este transmis prin valoare. Se pot transmite prin valoare: 1. Acestea vor fi tratate în continuare. Există două mecanisme de transmitere a parametrilor. cout<<n<<endl. test(n). dar în prelucrare. A. cout<<n<<endl. Transmiterea prin valoare se utilizează atunci când suntem interesaţi ca subprogramul să lucreze cu acea valoare. Expresii (acestea pot conţine şi funcţii). Iată.  Memorarea în stivă are şi alte consecinţe. durata de viaţă a variabilelor locale este locală. În acest caz. conţinutul variabilelor memorate în stivă se pierde . transmiterea prin valoare şi transmiterea prin referinţă. nu ne interesează ca parametrul efectiv (cel din blocul apelant) să reţină valoarea modificată în subprogram. } main() { int n=1. Exemplu: #include <iostream. Valorile reţinute de variabile. Numele lor este cel din lista parametrilor formali. de exemplu.

h> void test (int n) { cout<<n<<endl.h> void vector (int x[10]) { for (int i=0. Evident.  La ieşirea din funcţie.  Tipărim conţinutul variabilei n (cea din stivă). care este iniţializată cu 1. deci se tipăreşte 2. test(3+4*5). } main() { int a[10].52 Capitolul 2. adică 1. se rezervă spaţiu în stivă. Exemplu: #include <iostream.  Apelăm funcţia.i++) cout<<a[i]<<" ". valoarea 2 este pierdută. }  Transmiterea prin valoare a masivelor permite ca funcţiile să întoarcă noile valori ale acestora (care au fost atribuite în funcţii). În acest moment avem două variabile n şi ambele reţin valoarea 1. În programul următor funcţia vector iniţializează vectorul transmis ca parametru. for (int i=0.  În funcţie.i<10. } .  În main() se tipăreşte conţinutul variabilei n. } main() { test(3). Subprograme  În main(). De ce? Numele masivului este un pointer către componentele lui. spaţiu care are numele parametrului (deci tot n) şi este iniţializat cu valoarea memorată de variabila n a programului principal. este vorba de variabila rezervată în cadrul ei. avem declarată variabila n. variabila n (din stivă) se pierde . La apel. Parametrii efectivi sunt expresii care mai întâi se evaluează. Prin valoare se transmite acest nume. Cu ajutorul acestuia accesăm componentele masivului. vector(a). iar în main() se afişează rezultatul: #include <iostream. Prin urmare. variabila n este incrementată (adică la vechiul conţinut se adaugă 1).i<10. 2.adică nu mai are spaţiu alocat.i++) x[i]=i.

Definiţia funcţiei s1 este înaintea definiţiei lui s2. În schimb.13. Definiţiile ambelor funcţii se găsesc înaintea funcţiei main().h> void intersc(int &a. Definirea şi declararea unui subprogram Deşi aparent asemănătoare. Buna înţelegere a lor ne ajută să evităm anumite erori. #include <iostream. cele două noţiuni diferă. cout<<x<<" "<<y. subprogramul reţine în stivă adresa variabilei.y=3. pentru că definiţia lui s2 este după cea a lui s1. înseamnă a-l anunţa. } 2. a=b. deci funcţia s1 poate fi apelată din s2.5. b=man.3. aceste noţiuni sunt utilizate de orice limbaj de programare. Definiţia unui subprogram ţine loc şi de declaraţie! Programul următor conţine două funcţii: s1 şi s2.  În cazul transmiterii prin referinţă. Acestea sunt transmise prin referinţă. după structura anterior prezentată.y).int &b) { int man=a. . Mai mult. Transmiterea prin referinţă. nu numai în C++. } main() { int x=2.12. din s1 nu poate fi apelată funcţia s2. parametrii efectivi trebuie să fie referinţe la variabile. variabila transmisă să reţină valoarea stabilită în timpul execuţiei subprogramului. intersc(x. Definiţia 2.  În cazul transmiterii prin referinţă. Din acest motiv ele pot fi apelate din main(). O problemă importantă este locul unde se defineşte subprogramul. Un subprogram nedeclarat nu poate fi folosit. Parametrii sunt transmişi prin referinţă atunci când ne interesează ca la revenirea din subprogram.Manual de informatică pentru clasa a XI-a 53 B. înseamnă a-l scrie efectiv. Definiţia 2. A defini un subprogram. A declara un subprogram. Programul următor utilizează o funcţie care interschimbă valorile reţinute de două variabile.

h> void s1(). // prototip s1 void s2().54 Capitolul 2.fără main() .“). chiar dacă sunt definite în aceeaşi ordine. orice funcţie .h> void s1() { cout<<"Eu sunt s1"<<endl. cout<<"Eu sunt s1"<<endl. totuşi se poate ca s1 să apeleze pe s2. } Programatorii în C++ obişnuiesc să scrie mai întâi prototipurile tuturor funcţiilor utilizate de program . s2(). În astfel de cazuri se foloseşte prototipul funcţiei (antetul urmat de “. // prototip s2 main() { s1().poate fi apelată din oricare alta: #include <iostream. Subprograme #include <iostream. void s1() { s2(). } void s2() { s1(). } În situaţia prezentată. cout<<"Eu sunt s1"<<endl. El nu conţine definiţia acesteia. Prototipul are rolul de a declara o funcţie. } void s1() { s2().iar după funcţia main() să le definească. } . } main() { s1(). cout<<"Eu sunt s2"<<endl. } main() { s1(). cout<<"Eu sunt s2"<<endl. În acest fel.h> void s2(). #include <iostream.mai puţin main() . } void s2() { cout<<"Eu sunt s2"<<endl. } void s2() { s1().

1.vb:real.cel mai mare divizor comun al lor. if x<-1 then f:=y else if(x>1) return 6/y. readln(b). care întoarce o valoare întreagă . pentru x ∈ [-1. if(x<-1) return y. va:=f(a). va=f(a). cin>>a. . Acesta va fi prezentat în totalitate. void main() begin { float a. float f(float x) var y:real. Aplicaţii care folosesc subprograme  Aplicaţia 2. Prin urmare. Se consideră funcţia:  x +1 1 + x 2 . begin y=x+1.b. then writeln(va) else cout<<vb<<endl. Programul este prezentat în continuare: Varianta Pascal Varianta C++ var a. # include <iostream.  Se citesc două valori reale a şi b.1]. cin>>b. else f:=y/(1+x*x) } end. Să se scrie un program care afişează care dintre valorile f(a) şi f(b) este cea mai mare. Se citesc două numere întregi m şi n.2.  Rezolvare.  Rezolvare.b. pentru x ∈ (-∞.4. scriem o funcţie cmmdc cu doi parametri formali m şi n.  Aplicaţia 2.Manual de informatică pentru clasa a XI-a 55 2.  6  1+ x . cout<<"a=". readln(a). Se cere să se tipărească cel mai mare divizor comun şi cel mai mic multiplu comun al lor. write('b=').va. else if x>1 then f:=6/y else return y/(1+x*x).1. if va>vb if (va>vb) cout<<va<<endl. y:=x+1. else writeln(vb) } end.vb. pentru x ∈ (1. Cunoaşteţi deja modul în care se calculează cel mai mare divizor comun pentru două numere naturale date. vb=f(b). ∞ ).h> function f(x:real):real.-1). Cel mai mic multiplu comun a două numere se poate determina împărţind produsul lor la cel mai mare divizor comun al lor.va. .  f ( x ) =  x + 1. write('a='). { float y. vb:=f(b). Revenim la exemplul de funcţie dat în paragraful 2. cout<<"b=".

cout<<"n=".  Aplicaţia 2.n:integer) int cmmdc(int m.n). { while (m!=n) begin if (m>n) m-=n. } end. se calculează cel mai mare divizor comun între valoarea curentă memorată în c şi cea reţinută de componenta curentă a vectorului.  Rezolvare. } cmmdc:=m. . 2. #include <iostream. write ('m='). else n:=n-m. while m<>n do else n-=m.56 Capitolul 2. void main() { int cm. de data aceasta implementăm o funcţie cu algoritmul lui Euclid pentru calculul celui mai mare divizor comun.b)).c) şi cmmdc(c. b şi c există? În orice expresie se utilizează numai apeluri ale funcţiei cmmdc şi variabilele a. cin>>m.  Exerciţii 1. writeln(cmmdc(m. 3.h> function cmmdc(m. Stabiliţi locul unde trebuie integrat acesta în programul dat şi modificaţi programul principal astfel încât să se utilizeze subprogramul cerut.3. begin cout<<"m=". Cel mai mare divizor comun va fi reţinut din nou de c.cmmdc(a. Pentru varietate. Se citeşte un vector cu n componente numere întregi. end. cmmdc(cmmdc(b. cm=cmmdc(m. Câte expresii diferite care au ca rezultat cel mai mare divizor comun al numerelor naturale nenule a. int n) :integer. readln(m). cin>>n. write ('n=').n:integer. nu prin scăderi repetate. Apoi.b).n)).n)). Dispunem deja de o funcţie care calculează cel mai mare divizor comun a două numere şi am testat-o în programul anterior. writeln(m*n div cmmdc(m. Cu ajutorul ei calculăm cel mai mare divizor comun pentru valorile reţinute de primele două componente şi memorăm această valoare în variabila c. readln(n).n. if m>n then m:=m-n return m. pentru fiecare dintre componentele următoare. Găsiţi două motive pentru care parametrii funcţiei cmmdc trebuie să fie transmişi prin valoare şi nu prin referinţă. Se cere să se tipărească cel mai mare divizor comun al valorilor reţinute de vector.a). algoritm prin împărţiri. cout<<cm<<" "<<m*n/cm. Subprograme Varianta Pascal Varianta C++ var m. Scrieţi un subprogram cmmmc care să returneze cel mai mic multiplu comun a două numere naturale. De exemplu.m. trei expresii sunt: cmmdc(cmmdc(a.c). b şi c.

#include <iostream. until r=0. begin cout<<"n=". mai compact. for(i=1.c:integer. { int r. Se citesc două numere naturale m şi n mai mici decât 10.Manual de informatică pentru clasa a XI-a 57 Varianta Pascal Varianta C++ var v:array[1. end. int n) :integer. write ('n='). }  Exerciţiu În locul secvenţei c  cmmdc(v[1].v[i]).i++) c:=cmmdc(c. readln(n).i. write('Componente vector:').v[2]).h> n. c=cmmdc(v[1]. function cmmdc(m. să afişeze un triunghi. cin>>v[i].n execută c  cmmdc(c. n:=r return m. c:=cmmdc(v[1]. c=cmmdc(c.v[2]) pentru i=3. m=6 n=1 9 1 1 2 2 3 3 4 5 4 5 6 6 7 8 9 7 8 9 1 1 2 3 4 5 2 3 4 5 6 6 7 8 9 1 2 7 8 9 1 2 3 3 4 5 6 7 8 9 Junior Division . void main() { int i. writeln(c) cout<<c.v[2]). do { r=m%n.v[i]) sfârşit pentru Care este valoarea pe care trebuie să o aibă variabila c înaintea acestei secvenţe?  Aplicaţia 2. r:=m mod n. secvenţa: pentru i=1. begin m=n. repeat n=r.i++) for i:=1 to n do read(v[i]).n execută c  cmmdc(c.n:integer) int cmmdc(int m. după regulile pe care le deduceţi din exemplele următoare: 2. } while (r!=0) m:=n.20]of integer.i<=n. m=7 n=9 1.. var r:integer.i<=n.v[i]). cout<<"Componente vector:". Se cere ca programul dvs. int v[21].v[i]) sfârşit pentru dorim să utilizăm.4. cin>>n. } cmmdc:=m end.c. for i:= 3 to n do for(i=3.n. Triunghi special.

 Exerciţii 1. dacă se adaugă o instruc- ţiune care afişează valoarea lui n. tipar(m. void main() { int m.n. 3.n) } end. } writeln } end end. #include <iostream. begin cout<<"m=".int n) procedure tipar(m.i++) begin { for (j=1.n). end. După apelul tipar(m.i<=m. for j:=1 to i do begin n=suc(n). readln(n). write (n). de a relua numărarea de la 1.n:integer. int suc(int k) begin { if (k==9) return 1. void tipar(int m. linia a doua două valori etc. în loc de nsuc(n). 6. for(i=1.j<=i.58 Capitolul 2. Algoritmul este implementat în procedura tipar. else succ:=k+1 } end.j.h> function suc(k:integer):integer.i execută scrie suc(n) sfârşit pentru scrie EOLN sfârşit pentru . if k=9 then succ:=1 else return k+1.n:integer). Cum putem defini subprogramul suc astfel încât secvenţa de afişare din subprogramul tipar să fie: pentru i=1. 7. Vom utiliza o funcţie (succ) care primeşte ca parametru de intrare un număr k şi returnează valoarea următoare. Se observă că dacă n=5 trebuie să obţinem şirul 5. Problema este de a putea număra începând de la n. } n:=suc(n) cout<<endl.j++) for i:=1 to m do begin { cout<<n.j:integer.m execută pentru j=1. ce valoare estimaţi că se va tipări? 2. readln(m).. cin>>n. se utilizează instrucţiunea suc(n)? 3. 9. Cum trebuie să arate definiţia subprogramului suc. iar când s-a depăşit valoarea 9. 8.n) din programul principal. linia 1 având o singură valoare... care are ca parametri formali pe m şi n. { int i. var i. cout<<"n=". 1. transmişi prin valoare. write('n='). Subprograme  Rezolvare. cin>>m. tipar(m. write('m='). dacă în subprogramul tipar. Valorile lor sunt citite în programul principal. 2. Varianta Pascal Varianta C++ var m. Alt aspect al problemei este realizarea tipăririi acestor valori pe m linii. în logica prezentată mai sus.

r=r*10+n%10. while n<>0 do begin while(n!=0) { r:=r*10+n mod 10.  Rezolvare. Este necesar să fie testate toate numerele între m şi n. Care este avantajul scrierii fiecărei funcţii? Acesta este dat de faptul că. n:=n div 10 n/=10. Programul apelant al funcţiei palin este programul principal. Un număr este palindrom dacă. iar funcţia palin apelează la rândul ei funcţia invers. Cu toate că algoritmul ce construieşte inversul lui n distruge de fapt valoarea acestuia. #include <iostream. avem subproblema verificării proprietăţii de număr palindromic. pentru ca apoi să fie tipărite numai cele care îndeplinesc condiţia cerută. begin long invers(long n) r:=0. ne concentrăm exclusiv asupra ei.Manual de informatică pentru clasa a XI-a 59  Aplicaţia 2. Se citesc două numere naturale m<n.  Faptul că numărul al cărui invers se calculează este transmis subprogramului prin valoare este iarăşi un avantaj. end. De exemplu. Vom scrie o funcţie invers. Urmărind apelurile. este posibil ca oricare dintre funcţii să poată fi folosită şi în alte cazuri. Observaţii  Ordinea definirii celor două subprograme devine importantă. în timp ce programul apelant al funcţiei invers este subprogramul palin. Construirea ”inversului“ (nu în sens matematic) unui număr natural este şi ea o subproblemă a problemei verificării proprietăţii de palindrom. end. Pentru fiecare număr. care va returna inversul unui număr natural transmis ca parametru. { long r=0. deci posibilitatea de a greşi este mai mică. deoarece funcţia invers. acest lucru nu este resimţit de programul apelant. Varianta Pascal Varianta C++ function invers(n:longint):longint.5. } invers:=r return r. În plus.h> var r:longint. Pentru aceasta. vom constata că programul principal apelează funcţia palin. de care are nevoie funcţia palin. vom scrie o funcţie numită palin care are rolul de a testa dacă un număr este sau nu palindrom. citit de la stânga la dreapta şi citit de la dreapta către stânga. numărul 121 este palindrom. atunci când scriem funcţia. trebuie declarată anterior acesteia. la rezolvarea altor probleme. algoritmul în continuare devine mult mai simplu. Se cere să se tipărească toate numerele palindrom aflate între m şi n. Să analizăm problema. Dacă dispunem de funcţie. Analiza proprietăţii de număr palindromic o efectuăm construind răsturnatul (inversul) acelui număr şi verificăm dacă cele două valori (numărul şi răsturnatul său) sunt egale. rezultatul este acelaşi. } .

} end. Se cere să se afişeze şirul de valori calculate prin acest procedeu. end. Junior Division  Rezolvare. se afişează seria 25 133 55 250 133. int palin(long n) begin { return invers(n)==n. transformând parametrul n în parametru transmis prin referinţă şi testaţi proprietatea de palindrom folosind compararea n=invers(n) în loc de invers(n)=n. Repetăm procedeul până ce obţinem un număr care este deja prezent în seria generată. Repetăm procedeul: 53+53=250. suma cuburilor numărului reţinut de a doua componentă va fi reţinută de a treia componentă. După ce este memorată o valoare.i:integer. for(i=m. void main() var m. Modificaţi antetul funcţiei invers. pe rând. Numărul n (citit) va fi memorat de prima componentă a vectorului seria.i<=n. 2. se calculează 23+53=133. begin cout<<"m=". }  Exerciţii 1. un program care listează toate numerele cu această proprietate aflate între doi întregi a şi b (a<b<32000). write('m='). Scrieţi. Funcţia este verifică dacă valoarea n există deja în şirul s format din ls componente. Suma cuburilor cifrelor unui număr natural este calculată de funcţia suma.n.a. cu cele obţinute anterior. Cu numărul obţinut procedăm la fel: 13+33+33=55. Explicaţi de ce această modificare nu reprezintă o corecţie a erorii apărute la cerinţa 1? 3. transformând parametrul n în parametru transmis prin referinţă şi urmăriţi efectele modificării efectuate.n. Un număr natural este superpalindrom dacă este palindrom atât el cât şi pătratul său. .i++) for i:=m to n do if (palin(i)) if palin(i) then writeln(i). { int m. suma cuburilor sale. Subprograme function palin(n:longint):boolean. readln(m). Exemplu: pentru n=25.60 Capitolul 2. se tipăreşte numărul de termeni ai seriei şi seria propriu-zisă. write('n='). Suma cuburilor cifrelor.  Aplicaţia 2. Funcţia suma calculează suma cuburilor cifrelor unui număr. Modificaţi antetul funcţiei invers. cout<<"n=". readln(n). Se citeşte un număr natural n≤9999999. ş. palin:=invers(n)=n. pe baza programului dat. Şi iar: 23+53+03=133.6. de componenta a doua.m. Apoi. Exemplu: dacă se citeşte 25. cin>>n. cout<<i<<endl. Se calculează suma cuburilor cifrelor sale. ea este comparată. Dacă este regăsită. cin>>m.i.d.

var s. n/=10.var s:vec.int s[100]. int suma(int n) begin { int s=0. function suma(n:longint):longint. integrându-l într-un program de testare. seria[++ind]=n. ind=0. Exemplu: fişierul f. begin return 0. ind-1)).  Aplicaţia 2. #include <iostream.i<ls.i. n:= suma(seria[ind-1]). } for i:=1 to ind do writeln(seria[i]). { int i. cin>>n. end.h> var n:longint.TXT. Pe prima linie a fişierului sunt două valori: m (numărul de linii ale matricei) şi n (numărul de coloane ale matricei). if(s[i]==n) return 0. int ls) function este(n:longint.100] of longint.c. for(i=0. elementele aflate pe fiecare linie a matricei. este:=true. } for i:=1 to ls do void main() if s[i]=n then este:=false { cout<<"n="..7. Verificaţi subprogramul definit.ind. ind:=1. } n:=n div 10 return s. cout<<seria[i]<<endl. long n. end. ls:integer):boolean.c:integer. ind. for (i=1.i++) var i:integer. until este(n.seria.i. end.i:integer.i<=ind. readln(n). Următoarele m linii ale fişierului conţin. begin s+=c*c*c.i++) seria[ind]:=n. ind:=ind+1. end. în ordine. seria:vec. c:=n mod 10. } suma:=s int este(long n.Manual de informatică pentru clasa a XI-a 61 Varianta Pascal Varianta C++ type vec=array[1. do { seria[1]:=n. write('n=').seria. Să se scrie o procedură care citeşte o matrice cu elemente numere întregi din fişierul text MAT. int seria[100].ind-1). repeat } while(! este(n.in conţine: 3 4 3 1 7 9 1 2 3 4 9 1 3 8 . n=suma(seria[ind-1]). s:=0. s:=s+c*c*c. begin seria[ind]=n. while (n) { while n<>0 do c=n%10.

cout<<endl.j<=n.n:integer).i++) var i. assign(f.txt".n.close(). f>>mat[i][j].t[i.h> of integer.j++) end.' '). } for i:= 1 to linii do begin for j:=1 to coloane do write(m[i.coloane).mat). for (int j=1. .j++) f:text.1.i<=m. void main() for i:=1 to m do { int m. numărul de linii şi numărul de coloane ale matricei.i. var m.m. Subprograme  Rezolvare. for (int i=1. linii. procedure citesc(var t:mat. cout<<mat[i][j]<<" ". void citesc(int mat[100][100]. begin } citesc(m.100.n). for j:=1 to n do citmat(m. read(f.i<=m. f>>m>>n. Pentru a putea fi folosită pentru citirea oricărei matrice de numere întregi.j<=n.'mat.100] #include <fstream. begin f. } reset(f). var m:mat..n. writeln end end. mat[100][100].j:integer.j:integer.txt'). Scrieţi un program care calculează şi afişează valoarea fiecăreia dintre expresiile de mai jos.ios::in). b) H(x) = 10∗{x} (am notat prin {x} partea fracţionară a lui x).62 Capitolul 2. prin listarea matricei: Varianta Pascal Varianta C++ type mat=array[1. pentru x citit de la tastatură: a) G(x) = sin(x)+cos(x)∗cos(2∗x).j]). Probleme propuse 1. int& n) { fstream f("fis. int& m.coloane.j]. procedura va trebui să utilizeze ca parametri de ieşire matricea.. În programul următor se testează procedura. readln(f. for (int i=1.linii.i++) close(f) { for (int j=1.

n<100) şi n numere naturale nenule x1 x2 x3 .. 5. oricare din cele 28 de coifuri diferite îţi poate veni din aparat (sunt oferite aleator). Programul tipăreşte: • 8 pentru că ultima cifră a sumei 11+4+3 este 8... De exemplu. . • se scrie un subprogram care adună două matrice. x2=4. Programul se va realiza astfel: • se scrie un subprogram de citire a unei matrice cu m linii şi n coloane. Utilizaţi subprogramele predefinite Random şi Randomize. 7. Funcţia va returna 1 dacă cele trei segmente pot forma un triunghi şi 0.. Sinaia 9. 4. Programul trebuie să tipărească şi totalul cheltuielilor efectuate pentru obţinerea celor 28 de coifuri.. Scrieţi un program care simulează punerea monezii într-un astfel de aparat până când se obţin toate cele 28 de coifuri. cât şi perimetrul pătratului. xn. Coifurile NFL . • 1 pentru că ultima cifra a lui 1112 este 1. nu ştii ce coif vei obţine. Scrieţi o funcţie care returnează prima cifră a unui număr natural. Unele magazine au aparate care oferă coifuri în miniatură ale echipelor contra 0.. ultima cifră a numărului x1 2 3 n . Există 28 de echipe de fotbal în NFL (Liga Naţională de Fotbal în SUA). 3.. funcţia va returna 1. "Numai ultima cifră". în caz contrar. Scrieţi o funcţie care primeşte 3 parametri de tip real. Să se citească două matrice şi să se efectueze suma lor. Se cere: • ultima cifră a numărului x1 + x 2 +. • se scrie un subprogram de tipărire a unei matrice cu m linii şi n coloane.+ x n .. Junior Division  Indicaţie. Se citesc n (număr natural.. 8. Scrieţi o funcţie care primeşte ca parametru lungimea laturii unui pătrat şi returnează aria sa.25$. numere mai mici ca 30000.Manual de informatică pentru clasa a XI-a 63 2. 6. x x . Scrieţi un subprogram care primeşte ca parametru lungimea laturii unui pătrat şi returnează atât lungimea diagonalei. x • Exemplu: n=3. x3=3. x1=11. Când pui moneda în aparat. cu semnificaţia de lungimi pentru 3 segmente. Scrieţi o funcţie care primeşte ca parametri de intrare lungimile celor două catete ale unui triunghi dreptunghic şi returnează lungimea ipotenuzei. dacă parametrul efectiv este 127. • programul principal rezultă din apelul acestor subprograme. Concursul Naţional al Elevilor.

17. Scrieţi o procedură care interclasează doi vectori sortaţi ştiind că aceştia sunt ordonaţi descrescător. înmulţesc şi împart două numere raţionale. 12.64 Capitolul 2. Design triunghiular. scad. înmulţire. a) Programul utilizează un subprogram care înmulţeşte un polinom oarecare de grad k (coeficienţii se găsesc într-un vector. b) Programul utilizează un subprogram care calculează coeficienţii. Triunghiul de mai jos este generat de un algoritm. Se cere să se calculeze valorile unei funcţii F. Programul va folosi o funcţie care testează dacă un număr este prim sau nu. Un număr raţional este reţinut prin utilizarea unui vector cu două componente. Să se calculeze coeficienţii polinomului P(x)=(x+a)n. Programul va utiliza o funcţie care returnează suma cifrelor unui număr întreg primit ca parametru. n (a şi b reale.b] în n+1 puncte rezultate din împărţirea intervalului [a. Funcţia F va fi dată sub forma unui subprogram de tip funcţie. 14. Să se tipărească toate numerele prime aflate între doi întregi citiţi. Se citesc 3 valori a. 11. adunare.b] în n părţi egale şi să se afişeze cea mai mare dintre aceste valori. Descoperiţi acest algoritm şi folosiţi-l pentru a scrie un program care citeşte numărul n de linii şi generează triunghiul cu n linii corespunzător. n întreg). b. Scrieţi un program care tipăreşte numerele întregi găsite între două valori citite. În ambele cazuri se vor utiliza proceduri de citire. aşa cum rezultă din formula (binomul lui Newton): n ( x + a) n = ∑C k =0 k k n−k na x . 13. Scrieţi subprogramele care adună. Subprograme 10. Subprogramele trebuie să returneze numărul simplificat. Se citesc două polinoame de grade m şi n (coeficienţii fiecărui polinom se citesc într-un vector). 1 232 34543 4567654 567898765 67890109876 7890123210987 Junior Division . 16. transmis prin referinţă) cu polinomul x+a. numere care se divid cu suma cifrelor lor. Să se scrie o procedură care permută două linii date ale unei matrice pătratice. definită pe intervalul [a. 15. Să se afişeze coeficienţii polinomului sumă şi cei ai polinomului produs. în două variante. 18.

a:=a+1. b) 5 6. d) 232.h> function f:integer. { int z=5. writeln(a). end. d) eroare de sintaxă. c) 5. f:=a. end. void t(int a) begin { a++. write(a). test(z). cout<<a. } a) 233. b) 4. #include <iostream.f). void main() begin { t(f(f(f(1)))). cout<<f(). #include <iostream. } begin void main() z:=5. Ce afişează programul următor? Varianta Pascal Varianta C++ function f(x:integer):integer. end. 20. d) 6 5.h> begin int f(int x) f:=x+1 { return x+1. c) 32.Manual de informatică pentru clasa a XI-a 65 19. } procedure t(a:integer). int f() begin { int a=3. } a) 6 6. test(z). void test(int a) begin { a++. } end. void main() begin { a=2. cout<<a. cout<<a. int a. write(z) cout<<z. c) 6 6. end. 21. Ce afişează programul următor? Varianta Pascal Varianta C++ var z:integer. b) 23. write(a). a:=2. return a. #include <iostream. a:=a+1. a:=3. var a:integer. } end.h> procedure test(a:integer). a) 3. writeln(a. t(f(f(f(1)))) } end. Ce afişează programul următor: Varianta Pascal Varianta C++ var a:integer. cout<<a. .

function g(x:real):real. float f(float x) begin { if (x<3) return 3*x-1. int& v[10]) v:array[1.i++) for j:=1 to b do for(j=1.i.j.j<b. t=a. float v) 24.. } a) for i:=1 to a do for(i=1. . Pentru funcţiile definite mai jos.b. increment(t). #include <iostream. . care dintre secvenţe utilizează subprogramul increment pentru a obţine produsul celor două numere? Varianta Pascal Varianta C++ var a..var p(int n. stabiliţi care dintre următoarele antete de subprogram sunt corecte: a) procedure p(n:integer. begin void increment(int& x) x:=x+1 { x++.i<=a. else f:=x-1 } end. end.66 Capitolul 2.. t:=a. var void p(int n.b.i<=b. increment(t). increment(t). float v) d) function p(n):integer. void p(int n..i<=a.h> procedure increment(var x:integer). float g(float x) begin { if (x<10) return 4*x.j++) increment(t).t. care dintre afirmaţii este falsă? Varianta Pascal Varianta C++ function f(x:real):real. if x<10 then g:=4*x else return x-1.i<=b. Pentru varianta de limbaj preferată. writeln(t) cout<<t.i++) for j:=1 to t do for(j=1.j<=b. d) for i:=1 to a do for(i=1. cin>>b.j.10] of integer). 23. int p(int& n.t:integer. Subprograme 22. Ştiind că valorile citite ale variabilelor a şi b sunt numere naturale nenule.j++) increment(t).i++) increment(t).. c) procedure p(n:integer. void main() begin { cin>>a.j<=t.j++) increment(t). int a. c) for i:=1 to b do for(i=1.i. readln(b). readln(a). increment(t). else g:=x-1 } end.i++) for j:=1 to b-1 do for(j=1. } end.v:real). b) function p(n:integer. float& v) v:real). if x<3 then f:=3*x+1 else return x-1. . b) for i:=1 to a do for(i=1.

10) returnează 1210.12) returnează 12. . 25. Fiind dată funcţia următoare. c) Cat(0.12) returnează 12. while n2 <>0 do begin n1*=10. care dintre afirmaţii sunt adevărate? Varianta Pascal Varianta C++ function Cat(n1. n2=n2/10. a) Cat(12. c) Expresia f(g(f(3))) produce valoarea 7.13) returnează -1213. { int inv=0. d) Cat(12. while n2<>0 do begin } inv:=inv*10+ n2 mod 10. end. b) Expresia g(f(3)) produce valoarea 8.Manual de informatică pentru clasa a XI-a 67 a) Expresia f(g(3)) produce valoarea 11. d) Cat(13. a) Cat(-12. Cat:=n1 end. while (inv!=0) n2:=n2 div 10 { n1=n1*10+inv%10.0) returnează 120. care dintre afirmaţii este falsă? Varianta Pascal Varianta C++ function Cat(n1. return n1. Fiind dată funcţia următoare.12) returnează 1312.int n2) :longint.n2:integer) long Cat(int n1. while (n2!=0) begin { inv=inv*10+n2%10. n2:=n2 div 10. inv:=0. { n2/=10. inv=inv/10. } Cat:=n1+n2s end. 26. d) Expresia (f+g)(3) produce valoarea 14. } n1:=n1*10 return n1+n2s.n2:integer): long Cat(int n1. b) Cat(10. { int n2s. n2s=n2. c) Cat(0. var inv:integer. end. begin while (n2!=0) n2s:=n2. while inv<>0 do begin } n1:=n1*10+inv mod 10. b) Cat(12. var n2s:integer.12) returnează 1012.int n2) longint. inv:=inv div 10 } end.0) returnează 12.

m=4. int B[20]. int& p) procedure Elimin(var A:vect. . int m. B=(2. void Elimin (int A[20]. m=5.2. int C[40].int& m) în care: . se obţine C=(2.var C:vect.2. int B[20]. B=(2. n=3. vector obţinut din concatenarea componentelor vectorului A de m elemente cu ale vectorului B cu n elemente.B:vect. p) void Inters(int A[20].3.2. n=3. vector obţinut din reuniunea mulţimilor de numere naturale din vectorii A cu m elemente şi B cu n elemente (presupunând că A conţine valori distincte şi B conţine valori distincte).var C:vect. din A=(1.7).2. int C[40]. prin eliminarea valorilor multiple.7).3).var p:byte).3.m. int procedure Inters(A. Se consideră declarările următoare: Varianta Pascal Varianta C++ type vect=array[1. De exemplu. var m:byte).Elimin are rolul de a returna vectorul care conţine toate elementele distincte ale vectorului iniţial cu m componente. se obţine C=(1. De exemplu.3. int m.3. . De exemplu. din A=(1.3). p=7. pentru A=(1.68 Capitolul 2.var p:byte).. Subprograme 27.B:vect. se returnează A=(1.7).var p:byte). int& p) m. A.Inters are rolul de a returna vectorul C cu p componente. Care dintre secvenţele de mai jos nu calculează reuniunea mulţimilor numerelor naturale reţinute de cei doi vectori? Rezultatul se va găsi în vectorul Z. . void Reun(int A[20]. procedure Concat(A.n:byte.2).3). din A=(1. din A=(1.2.3). Elementele din fiecare vector nu sunt neapărat distincte. vector obţinut din diferenţa mulţimilor de numere naturale din vectorii A cu m elemente şi B cu n elemente (în aceleaşi condiţii ca şi în cazul reuniunii). int n.3). procedure Reun(A. . int C[40].40] of byte. se obţine C=(1. . Se citesc doi vectori X cu a numere naturale şi Y cu b numere naturale.3. De exemplu. p=5. int n.Dif are rolul de a returna vectorul C cu p componente. p=2. m=3.7). void Dif(int A[20]. m=4.5. n=3.n:byte. m. m=4.7).2.var C:vect. int& m. void Concat(int A[20].5. int B[20]. se obţine C=(1.7.2.3. n=3.n:byte.5.5.Reun are rolul de a returna vectorul C cu p componente. int n. int& p) procedure Dif(A.3). B=(2. vector obţinut din intersecţia mulţimilor de numere naturale din vectorii A cu m elemente şi B cu n elemente (în aceleaşi condiţii ca şi în cazul reuniunii).int m. m=4.5). B[20]. int m.2.B:vect.Concat are rolul de a returna vectorul C cu p componente.3.7.B:vect. int n. B=(2.n:byte.var p:byte). int C[40]. De exemplu. var C:vect.3).2.1. p=2.5.

Elimin(Y.p).Z. c) Inters(Y.a.Z. a) Dif(X.b).Y. d) Elimin(X.Z.p).a). Dif(X.Y.a.Z. b) Inters(X.Z.p.Y.p).p). Reun(X.Y. write ('*').Z.b.p).p).j<=(k-2*i+1)/2.b.j. } writeln } end end.Z.b). for j:=1 to 2*i-1 do i++.k:integer.p). C. Dif(X. nici Y-X? Rezultatul se va găsi în vectorul Z.Z. Dif(Y.a.Z. i=1.Z. begin k=2*n-1.b. k:=2*n-1.b.Y.p). Care dintre secvenţele de mai jos nu calculează nici X-Y. d) Dif(X. Se citesc doi vectori X cu a numere naturale şi Y cu b numere naturale. cout<<endl.j<=2*i-1.Z.a).p).Y.Z. while (i<=n) while i<=n do {for(j=1.b.Y. Se citesc doi vectori X cu a numere naturale şi Y cu b numere naturale.Z. B.a.a. b) Elimin(X.b.p). Elementele din fiecare vector sunt distincte. 28. { int i. Dif(Y.b.p).b.p.Z. for j:=1 to (k-2*i+1)div 2 do for(j=1. c) Elimin(Concat(X. b) Dif(X.b.Y.a.p.X. .Y.p). cout<<'*'.b.a.Z. Elementele din fiecare vector sunt distincte. Se consideră subprogramul următor: Varianta Pascal Varianta C++ procedure Afis (n:integer).b. a) Inters(X. Care dintre secvenţele de mai jos calculează intersecţia mulţimilor numerelor naturale reţinute de cei doi vectori? Rezultatul se va găsi în vectorul Z.Z.j++) write (' ').Z.p) d) Intersect(X.k.b.Y.a.Manual de informatică pentru clasa a XI-a 69 a) Concat(X.p).j.p.a. Elimin(Y.b. c) Intersect(X.b.a.p).j++) begin cout<<' '.i:=1.a.Z.Z.a. Concat(X.p). Dif(X.b. void Afis (int n) var i.p).Y.p). Elimin(Z.a.p).Z.a.Z.Z. i:=i+1.a.

B. { A[i][j]=V[i]%2.5.6.10. c) 15.9.11). for j:=2 to 4 do z:=z*2+A[j.4] of byte. . d) 20.9.2.j]:=V[i] mod 2. Care este conţinutul lui V după apel.z:byte.4)? a) V=(1.2.2. Care trebuie să fie iniţial conţinutul lui V.10] of byte.1.i.15.1.j>=0.0). begin V[i]/=2. B. pe penultimul rând? a) 10.5.4).4.3. dacă după apel este V=(1.3. câte caractere ”*” se afişează în total (pe toate rândurile)? a) 8.1. Dacă n=8..6..i++) var A:array[1.2). Dacă n=20.70 Capitolul 2.10).4.10. b) V=(4. c) V=(0. C. b) V=(2. A[i.4.1).13). end.0)? a) V=(2. b) 16. 29.z.i]. c) V=(0.1). end.10. end. C. d) V=(1. d) V=(0. for (i=0. for i:=1 to 4 do } for j:=4 downto 1 do for (i=0. b) 16. b) 20. procedure xx(var V:vect).12). c) 12.j--) i.4.i++) begin { z=A[0][i].3.6. V[i]=z. for (j=3. d) V=(1.i]. A. for i:=1 to 4 do } begin } z:=A[1.10. c) 19. void xx(int V[10]) { int A[4][4].j<4.3.6. z=z*2+A[j][i]. Dacă n=4. conţinutul acestuia la ieşire coincide cu cel de la intrare? a) V=(13.j.9.9). v[i]:=z.j++) V[i]:=V[i] div 2.2.3. câte spaţii se scriu înaintea primului caracter ”*”. Pentru care dintre exemplele următoare de configuraţii ale lui V la intrarea în subprogram. b) V=(1.8). c) V=(0. câte caractere ”*” se afişează pe ultimul rând tipărit? a) 8.. dacă înainte este V=(1. d) 1. for (j=1. Se consideră subprogramul următor: Varianta Pascal Varianta C++ type vect=array[1.6.i<4. Subprograme A.j.i<4. d) 17.

A. a) 2 şi respectiv 3.7. n=3. { int i=0. n=3. A[i]==B[i]) i++. d) m=1. while (i<=m && i<=n && var i:integer.Y. A. Pentru care şir de vectori.2).2.. c) nu există astfel de valori.5.10] of integer.3). n=3. D. Se consideră subprogramul următor: Varianta Pascal Varianta C++ type vect=array[1. int B[10]) function Cmp(m. Pentru m=5. n=1.3). d) 2.2.2.3).2). end. B=(1. int n.X.2). and (A[i]=B[i]) do i:=i+1.2.3). B=(1).7). funcţia Cmp. d) 3 şi respectiv 6.2. B=(1. b) m=2.2). A=(1. then Cmp:=0 else else if i>n then Cmp:=1 return 1. c=(0).2. B=(1). A=(1. B=(1.Y) să returneze aceeaşi valoare ca şi apelul cmp(4.7). int A[10].3).3). A=(1). . int Cmp(int m. C. else if(A[i]<B[i]) if (i>n) and (i>m) return -1. .3). C(1. B=(1.2.3).4. i:=1. else if i>m then Cmp:=-1 } else if A[i]<B[i] then Cmp:=-1 else Cmp:=1. b) 2 şi respectiv 5. . stabiliţi ce valori pot fi scrise în casete astfel încât apelul cmp(4. Y=(1. b) 5.3).X). while (i<=m) and (i<=n) else if(i>m) return -1.2). B.2).3). b) A=(1. else if(i>n) return 1.5. Pentru X=(1.Manual de informatică pentru clasa a XI-a 71 30. a) 3. B=(1. ). A=(1. C=(1. apelată pentru oricare doi vectori consecutivi în şir. begin if(i>n && i>m) return 0. A=(1.2. c) A=(1. d) A=(2). stabiliţi ce valoare poate fi a treia componentă a vectorului B astfel încât funcţia să returneze valoarea 0.2. c) nu există o astfel de valoare.B:vect):byte. returnează aceeaşi valoare? a) A=(1.2. C=(1. n=3. B=(1.4.n:integer. B=(1. Ce valoare returnează funcţia apelată pentru fiecare dintre cazurile următoare? a) m=3. c) m=2.

b) -1. Răspunsuri 19. Subprograme 31. c). c). E. 27. A. d). D. Obţinerea descompunerii în factori primi a unui număr natural n. B. d). d). d) 0. 20. a) 0. A.d2.….tn. B. b). Indicaţie: funcţia realizează compararea lexicografică a doi vectori care reţin numere naturale. 30. 24. b). Ordonarea componentelor pozitive ale unui şir de valori.…. Indicaţie: fiecare număr este scris în binar pe o linie a matricei. A. C. Verificarea respectării limitei de viteză pe un sector de drum pentru un vehicul care îl parcurge şi care este înregistrat la diferite momente de timp de o cameră video. d). Calculul valorilor caracteristice ale unui con (raza bazei. c). d). c). 21. 23. C. C. generatoarea şi înălţimea) pe baza valorii volumului conului şi a ariei sale laterale. c) 1. numărul de camere video n. C.dn şi momentele la care s-a înregistrat trecerea mobilului prin dreptul fiecărei camere t1. A. a).t2. 29. C. B. Se transmit: viteza maximă admisă v. a). c). a). b). distanţele faţă de punctul de început al sectorului de drum unde sunt amplasate camerele: d1. c). B. 26. 25. F. 22. c).72 Capitolul 2. 28. a). valorile negative rămânând pe poziţiile lor iniţiale. b). Determinarea unei subsecvenţe palindromice a unui şir de numere naturale de cel mult două cifre. D. Scrieţi subprograme pentru: A. Se afişează numerele zecimale obţinute din numerele în binar de pe fiecare coloană a matricei. B. c). b). a). Calculul valorilor ce caracterizează o sferă (arie şi volum) pe baza valorii razei sale r. .

Alte operaţii: a) Find . Ne putem imagina un şir de caractere ca un vector în care fiecare componentă reţine un caracter. prin prelucrarea şirurilor de caractere. corezpunzătoare ordonării alfabetice. 73 Capitolul 3 Şiruri de caractere 3. c) Cut este o operaţie prin care. putem modifica o linie. Luaţi. Şirul exemplelor ar putea continua. De aici. de exemplu. În informatică. fiecare caracter este memorat prin utilizarea codului ASCII. dar şi alfabetice. Ele utilizează din plin şirurile de caractere. O astfel de operaţie constă în includerea sau ştergerea unui subşir (caractere consecutive din interiorul unui şir). un program să poată citi sau afişa numele unei persoane sau numele unei localităţi. Prin Paste se inserează un şir de caractere în cadrul altui şir de caractere. afişate şi prelucrate cu ajutorul unor subprograme puse la dispoziţie de limbaj. o astfel de relaţie se numeşte ordine lexicografică. când scriem programele Pascal sau C++ utilizăm un editor de texte. putem crea tabele (Excel. Luaţi codul sursă al unui program (fişiere cu extensii . Observaţi că sursa este un fişier text în care fiecare linie a ei.  Limbajele actuale permit numai introducerea datelor sub formă de şiruri de caractere. Ce operaţii putem face cu editoarele? De exemplu. componentă studiată la ”Tehnologia Informaţiei şi a Comunicaţiilor”. de exemplu. Puteţi găsi altele?  Aţi învăţat. din cadrul unui şir se extrage un subşir al său şi acesta este memorat în Clipboard. după numele persoanelor. În absenţa lor ar fi foarte greu ca. După cum se ştie. Şirurile de caractere au aplicaţii uriaşe în viaţa reală. Generalităţi Programele nu lucrează numai cu numere. o componentă de tip edit. de exemplu. Am văzut că liniile tabelului. b) Replace este o operaţie de înlocuire a unui subşir cu un altul. fiecare linie a fişierului text conţine un şir de caractere. în ordine alfabetică). . de exemplu. care sunt şiruri de caractere. Word sau Access). este o linie a fişierului text.1. Prin urmare.este o operaţie de identificare a unui subşir în cadrul unui şir de caractere. şi vom vedea cum. Să vedem câteva dintre acestea:  Cine n-a folosit un editor de texte? Chiar acum.pas sau . Astfel de operaţii se fac. la ”Tehnologia Informaţiei şi a Comunicaţiilor”. rezultă că între şirurile de caractere există o relaţie de ordine.cpp). Putem sorta liniile unui tabel după o anumită coloană (de exemplu. pot conţine valori numerice. Chiar dacă vreţi. Cum se ordonează alfabetic (lexicografic) mai multe şiruri de caractere? Care sunt principiile care stau la baza acestei ordonări? Despre toate acestea vom învăţa în acest capitol. Şirurile de caractere pot fi citite. cum.

2. în loc de şirul '12'. numit string. apoi este writeln(t). corect este 'Alt ''sir'. Ea este t := 'Iepuras'. nu am memorat cuvinte. Pentru a include apostroful. este necesar să convertim valorile numerice în şiruri şi invers. Atenţie! În memorie. şirul 'Alt 'sir' este scris corect? Răspunsul comportă o anumită nuanţare. veţi introduce de fapt şirul de caractere '12'. end. Exemple: 'Un sir'. Programul va converti şirul de caractere '12' în numărul întreg 12. prezentarea acestora se va face separat. Întrucât există diferenţe semnificative între modul în care sunt reţinute şi prelucrate şirurile de caractere în cele două limbaje (Pascal şi C++). dacă realizăm un astfel de program.74 Capitolul 3 – Şiruri de caractere să introduceţi numărul 12. De ce este nevoie să se procedeze astfel? Motivul este dat de faptul că. nu avem nevoie să-l declarăm cu type. adică este cunoscut. cu alte cuvinte.1. datorită faptului că limbajul este înzestrat cu un tip de date special. . Şiruri de caractere în Pascal 3. În Pascal se poate lucra cu uşurinţă cu ele. 'toamna se numara bobocii'. Cum efectuăm astfel de conversii? Conversiile sunt prezentate în acest capitol. se foloseşte un artificiu: acesta este trecut de două ori. de tip string. Nu întotdeauna o astfel de operaţie reuşeşte.2. trebuie efectuată o validare numerică. pentru a putea lucra cu şirurile de caractere se folosesc variabilele de tip string.1. iniţializată cu şirul 'Iepuras'. deci cu un singur apostrof. dacă am proceda altfel. Noţiuni introductive Poate aţi observat că. pentru că este posibil ca persoana care introduce data respectivă să greşească. O succesiune de caractere cuprinse între două caractere apostrof se numeşte şir de caractere. şirul 'Alt 'sir' este incorect. Tipul string este predefinit. Definiţia 3. care este apostroful final. până în acest moment. Prin urmare. În Pascal. 3. În cazul de faţă. se efectuează şi o anumită validare. Componentele studiate la Tehnologia Informaţiei afişează şiruri de caractere. Prin urmare. afişată. Aceasta înseamnă că în timpul operaţiei de conversie. şirul 'Alt ''sir' este reţinut ca 'Alt 'sir'. compilatorul nu ar putea decide unde se termină şirul. să introducă şirul '1@'. Acestea au fost doar afişate. De exemplu. var t: string. Programul alăturat are declarată o begin variabilă t. O întrebare: între caracterele care alcătuiesc şirul se poate găsi şi apostroful? De exemplu.

nu pe litere. z := t. Observaţi faptul că afişarea s-a realizat. Prin urmare. Întrucât o variabilă de tip string memorează cuvintele sub formă de vector de caractere. Restul octeţilor.Manual de informatică pentru clasa a XI-a 75 Pentru început. prin precizarea numelui. Octeţii de la 1 la 7 memorează caracterele din care este alcătuit şirul. să analizăm modul în care o variabilă de tip string memorează un şir de caractere. Mai mult. pornim de la exemplul dat în programul anterior: 7 I e p u r a s t[0] t[1] t[2] t[3] t[4] t[5] t[6] t[7] t[8] t[255] Ce observăm? Pentru o variabilă de tip string se rezervă automat un vector cu 256 de octeţi. var t: string. De altfel. Programul care urmează afişează acelaşi cuvânt. nici nu ne interesează conţinutul lor. for i := 1 to n do write(t[i]). există posibilitatea să accesăm direct conţinutul unui octet. de la 8 la 255 au un conţinut neprecizat. pe litere: var t: string. aşa cum rezultă din programul următor. i: integer. În loc de 'a'. În cazul variabilelor de tip string există posibilitatea ca atribuirea să se facă direct. ş. end. variabila z reţine cuvântul "iepuras" şi acesta este afişat. aşa cum suntem obişnuiţi. begin t := 'Iepuras'. end. Pentru aceasta.m. putem modifica conţinutul unui singur octet. write(t). programul afişează 'Iepuris'. end. t[2] reţine codul caracterului e. . t[6] reţine 'i'. În exemplul dat. t[1] reţine codul caracterului I. Astfel. aşa cum rezultă din programul următor: var t.d. write(z). în ansamblu. z: string.a. begin t := 'Iepuras'. t[6] := 'i'. numerotaţi de la 0 la 255. şirul 'Iepuras' este alcătuit din şapte caractere. begin t := 'Iepuras'. Primul dintre ei are rolul de a reţine numărul de octeţi ocupaţi de şir. În urma atribuirii.

Din acest motiv există posibilitatea ca o variabilă de tip string să fie declarată în aşa fel încât să ocupe un număr mai mic de octeţi..76 Capitolul 3 – Şiruri de caractere Am văzut că pentru o variabilă de tip string se reţin automat 256 de octeţi. prin utilizarea parantezelor drepte.2. o variabilă de tip string poate reţine mai multe cuvinte. var t: string[4]. prin utilizarea variabilelor de tip string avem avantajul că putem adresa şirul de caractere atât în ansamblu.se consumă memorie inutil. cel de indice 0. are rolul de a reţine lungimea cuvântului memorat. Exemplu: var nume: string. prin codul său. Ce facem în cazul în care cuvântul care va fi memorat. cât şi pe caractere. 3. acest număr este prea mare . Variabila t. variabila poate reţine cuvinte ce au cel mult 4 caractere.. caracterul blank se memorează ca oricare altul. Prin urmare. Primul. atâtea câte pot fi memorate. În astfel de cazuri se reţin numai primele caractere. . din care primul reţine lungimea. Programul afişează 'mama'. Limbajul este înzestrat cu proceduri şi funcţii care uşurează mult lucrul cu şirurile de caractere. În concluzie. t:= 'DANSATOR'. end. separate prin unul sau mai multe blank-uri. Operatorul '+' este binar (adică are doi operanzi) şi poate acţiona asupra datelor de tip string. . ocupă 5 octeţi. begin t := 'mama'. write(t)... are un număr mai mare de litere decât numărul de octeţi ai variabilei care îl memorează? Exemplu: var t: string[4]. prin utilizarea numelui variabilei. Acesta nu este singurul avantaj.2. În multe cazuri. În acest caz. nume:= 'Ion Zaharia'. Concatenarea şirurilor Pentru a concatena două şiruri de caractere se foloseşte operatorul '+'. În exemplu. După cum am învăţat. Exemplu: var t: string[4]. variabila t reţine cuvântul 'DANS'. Ce înţelegem prin concatenare? .

care este precedat de blank. şirurile de caractere pot fi comparate. . 3. De aici tragem concluzia că operaţia de concatenare nu este comutativă (contează ordinea în care sunt trecuţi cei doi operanzi). Observaţii 1. programul ar fi afişat ' exempluacest'.2.şirul a este mai mic decât şirul b.3. două şiruri de caractere (notate a şi b). am obţinut şirul ' exempluacest'. var t. Dacă am fi scris t:=z+t. format astfel: primul şir (cel aflat în stânga operatorului). • prin operaţia (t+z. • şirul obţinut este atribuit variabilei t. z := ' exemplu'. Exemplu: program st6. Observaţi că dacă am făcut concatenarea z+t. Compararea şirurilor Oricât ar părea de curios. end. În programul st6 se concatenează două şiruri de caractere (t:=t+z. writeln(t). • a<b . urmat de al doilea şir (cel aflat în dreapta operatorului). Concatenarea este operaţia prin care din două şiruri de caractere se obţine un al treilea. se pot găsi în una din relaţiile: • a=b .Manual de informatică pentru clasa a XI-a 77 Definiţia 3.) se obţine şirul 'acest exemplu'.adică vor fi memorate numai primele caractere. 2. • a>b .): • variabila t reţine şirul 'acest'. altfel şirul va fi memorat trunchiat . t := t+z. atâtea câte încap. Observăm cât este de important să ţinem cont de poziţia blank-ului (al doilea şir este precedat de blank). Trebuie să avem în vedere că şirul obţinut în urma concatenării să poată fi memorat în întregime . Astfel. begin t := 'acest'. z: string. • variabila z reţine şirul ' exemplu'. 3.şirul a este mai mare decât şirul b.cele două şiruri sunt egale.2.variabila căreia i se atribuie să fie declarată cu un număr de octeţi care să permită memorarea sa.

. Prin urmare. codul lui a[1] este egal cu codul lui b[1]. codul lui a[2] este egal cu codul lui b[2]. • dacă codul primului caracter al şirului b este mai mare decât codul primului caracter al şirului a. atunci cele două şiruri sunt egale (a=b). Avem 3 posibilităţi: • dacă codul primului caracter al şirului a este mai mare decât codul primului caracter al şirului b. Exemplu: codul caracterului d este mai mare cu o unitate decât codul caracterului c. b='bactr'. • în caz de egalitate se compară codurile caracterelor de indice 2. compararea se face pe caractere.78 Capitolul 3 – Şiruri de caractere Dar cum se pot compara două şiruri? Să nu uităm că şi două caractere se pot compara după codul lor. după o anumită logică: dacă un caracter urmează altuia. notate cu a (cu m caractere) şi b (cu n caractere). În continuare prezentăm algoritmul de comparare a două şiruri. Aici m=3. Atunci a<b. şi în urma comparării primelor m caractere a rezultat egalitate. ⇒ Dacă m=n şi în urma comparării tuturor caracterelor a rezultat egalitate. n=4. de indice 1). Exemple: • a='abc'. atunci el are drept cod un număr mai mare decât al caracterului căruia îi urmează. codul lui a[3] este egal cu codul lui b[3] şi n>m (şirul b are mai multe caractere). Am văzut modul în care au fost codificate caracterele. ⇒ Dacă m>n. • a='abc'. Atunci a>b. . atunci a<b (b are mai multe caractere). ⇒ Dacă m<n. atunci a>b (a are mai multe caractere).. b='abca'. codul lui a[1] este egal cu codul lui b[1]. codul lui a[2] este egal cu codul lui b[2]. în ordine alfabetică. • a='abc'. Şirurile sunt succesiuni de caractere. atunci a<b (b>a). Atunci a<b. iar codul lui a[3] este mai mare decât codul lui b[3]. şi în urma comparării primelor n caractere a rezultat egalitate. b='aba'. Fie două şiruri de caractere. atunci a>b. pentru că a[1] este a şi are codul mai mic decât b[1] care este b. ⇒ Se compară codurile primelor două caractere (aflate în stânga şirurilor.. Compararea şirurilor de caractere este extrem de utilă în sortarea alfabetică a cuvintelor (ca în dicţionar). Ordinea astfel impusă se mai numeşte şi ordine lexicografică.

begin write('n= '). repeat inv := false. readln(n). v[i] := v[i+1]. Prin lungimea unui şir de caractere înţelegem numărul de caractere pe care acesta le conţine.Manual de informatică pentru clasa a XI-a 79 În programul următor se sortează alfabetic mai multe şiruri de caractere (cuvinte). • şirul 'mama ' are lungimea 5 (şi blank-ul este caracter). motiv pentru care nu revenim asupra lui.număr întreg.. inv := true. Lungimea şirurilor de caractere Definiţia 3. var v: cuvinte. Pentru aflarea lungimii unui şir avem două posibilităţi: 1. inv: boolean. Prin utilizarea funcţiei length. for i := 1 to n do writeln(v[i]). for i := 1 to n-1 do if v[i] > v[i+1] then begin man := v[i]. end. 3.10] of string. v[i+1] := man. n. care are forma generală: function length(S: String): Integer. man: string. i: byte. . end until not inv.2. Pentru aceasta. Programul este prezentat în continuare: type cuvinte = array[1.4. for i := 1 to n do readln(v[i]). Exemple: • şirul 'mama' are lungimea 4. se declară un vector în care fiecare componentă este de tip string.3. Algoritmul de sortare este unul studiat (care?). şi întoarce lungimea şirului S .

writeln('lungimea sirului a este '. În urma atribuirii. writeln(length(a)) afişează 7. Pentru a-l obţine folosim funcţia ord astfel: ord(a[0]). pentru a obţine codul său. un subşir poate apărea de mai multe ori. Prin urmare. De ce aşa? Atunci când scriem a[0] ne referim la un caracter. 3. După cum am învăţat.2. c: integer. Prin şir vid înţelegem un şir fără nici un caracter. a := 'un test'. c := length(a). Acesta începe în poziţia a treia din şirul iniţial (3 caractere). b) În condiţiile de mai sus. Exemplu: a:=''...4. . prin subşir al său se înţelege un şir de caractere consecutive care se regăsesc în şirul iniţial. chiar dacă primele trei caractere (rni) se regăsesc în şirul iniţial. Un caz aparte de şir este şirul vid. Evident.80 Capitolul 3 – Şiruri de caractere  Exemple de utilizare a) var a: string. În programul următor se afişează lungimea unui şir citit. • Şirul 'mama' are ca subşir şirul 'ma'. . şirul vid are lungimea 0. Fiind dat un şir de caractere. în cadrul unui şir. • Şirul 'harnic' nu are ca subşir şirul 'rnit'. Folosind această expresie obţinem caracterul care are codul dat de număr (în binar). readln(a). Deci. Exemple: • Şirul 'harnic' are ca subşir şirul 'rni'. ord(a[0])). end. Am iniţializat variabila a.începând cu poziţiile 1 şi 3. writeln('lungimea sirului a este '. cu şirul vid. length(a)). begin write('a= '). utilizăm funcţia ord.5. variabila c reţine 7. octetul de indice 0 reţine lungimea şirului. Observăm faptul că subşirul apare de două ori . 2. Tipărirea se face în cele două moduri prezentate: var a: string. de tip string. Subşiruri Definiţia 3.

conţine şirul din care se face extragerea. Acestea sunt prezentate în continuare. • inceput . j: byte.reţine poziţia de început a subşirului care se extrage.  Funcţia copy are rolul de a extrage un subşir din cadrul unui şir dat: function copy(s:string. writeln(b).inceput.lungime:integer):string. Fie şirul 'abcde'. iar funcţia returnează 2 (poziţia de început a subşirului). următorii parametri: • subsir . Exemple: 1. Se caută subşirul 'bcd'. unde: • variabila s (de tip string) . în cazul în care acesta a fost găsit. Fie programul următor: var a.şirul în care se face căutarea. j). i.Manual de informatică pentru clasa a XI-a 81 Funcţii ce acţionează asupra şirurilor de caractere Există mai multe proceduri şi funcţii care acţionează asupra şirurilor de caractere. Se scriu. • sir . Funcţia returnează: • 0. 4 pentru i şi 3 pentru j. readln(i). În cazul în care prin parametrul lungime specificăm mai multe caractere decât are şirul. variabila b va reţine subşirul 'tex'. Dacă se citeşte şirul 'un text'.reţine numărul de caractere care se extrag. Dacă se caută subşirul 'cz'. end. Acesta este găsit. . se extrag caracterele până la sfârşitul şirului. b := copy(a. write('j= '). • poziţia de început a subşirului.sir:string):byte. begin write('a= '). readln(a).  Funcţia pos are rolul de a verifica dacă un şir este subşir pentru altul: function pos(subsir. • lungime . readln(j). în ordine. dacă nu a fost găsit subşirul cerut. b: string. write('i= ').subşirul căutat. funcţia returnează 0 ('cz' nu este subşir al şirului 'abcde'). i.

n). în ordine: procedure delete(var sir: string. unde: • sir_de_ins . Pentru aceasta.numărul de caractere pe care îl are subşirul.poziţia din care se face inserarea. sau. var a. . indice. Pentru aceasta. • Indice . unde: • Sir . Dacă b nu a fost identificat ca subşir al lui a.  Procedura delete are rolul de a şterge un subşir din cadrul unui şir dat. • nr_car . n: integer. b: string. readln(a).şirul care urmează a fi inserat. begin write('a= '). if n = 0 then writeln('b nu este subsir al lui a') else writeln('b este subsir al lui a si incepe in pozitia '. în alt şir. readln(a). poz: integer). end. Programul următor citeşte două şiruri a şi b şi verifică dacă b este subşir al lui a. var şir_unde_ins: string.a). a şi b.indicele primului caracter al subşirului care se şterge. în caz contrar. 3). În ambele cazuri se dau mesaje. În programul anterior am citit două variabile de tip string. write('b= '). begin write('a= '). write('b= '). n := pos(b. insert(b. Exemplu: dacă a reţine şirul 'abcd' şi b reţine şirul '123'.şirul în care se face inserarea. readln(b). ea are ca parametri. end. Şirul reţinut de variabila b este inserat în şirul reţinut de variabila a începând cu poziţia 3. readln(b). • poz . b: string. Exemplu: var a. writeln(a). se afişează poziţia de început a sa. programul afişează 'ab123cd'. ea primeşte ca parametri următoarele: procedure insert (sir_de_ins: string.  Procedura insert are rolul de a insera un şir de caractere începând cu o anumită poziţie. a. nr_car: integer).82 Capitolul 3 – Şiruri de caractere 2.variabila care conţine şirul din care se face ştergerea. • sir_unde_ins .

readln(ch). se afişează 'abe'. ch: string[1]. while poz <> 0 do begin delete(sir. dacă variabila a reţine şirul 'abcde'. readln(a). writeln(pozv).2). sir).3. end end. poz := pos(subsir. readln(sir). . lung: byte. De exemplu. var a:string. lung). pozn+1.1. end. numită a. writeln(sir). end. writeln('introduceti caracterul '). writeln('introduceti subsirul '). pozv: byte. begin writeln('introduceti sirul').Manual de informatică pentru clasa a XI-a 83 Exemplu: Programul următor citeşte un şir de caractere într-o variabilă de tip string. end. writeln(a). lung := length(subsir). 255). sir := copy(sir. readln(subsir). pozv := 0. readln(sir). pozn := pos(ch. sir).  Aplicaţia 3. poz. while pozn <> 0 do begin pozv := pozv+pozn. pozn := pos(ch. poz. pozn. delete(a. subsir: string. sir). Ştergerea tuturor apariţiilor unui subşir din cadrul unui şir: var sir. sir).  Aplicaţia 3.2. begin write('a='). În programul următor se listează indicii tuturor apariţiilor caracterului citit în şir: var sir: string. begin writeln('introduceti sirul'). Şirului citit i se şterge subşirul care începe în poziţia 3 şi are lungimea 2 (două caractere). poz := pos(subsir.

begin writeln('introduceti sirul '). În programul de mai sus se citeşte o valoare întreagă (n). Limbajul dispune de două proceduri care realizează conversia de la valori numerice la şiruri şi invers.variabila (valoarea) numerică . a). sir_sters.lung). end. Dacă . Analizaţi programul următor pentru a descoperi modul în care se realizează aceasta: var sir.3. var S:string). n: integer. sir). poz := pos(sir_sters. readln(sir_sters). Numărul 123.variabila a are acest tip. De exemplu. begin write ('n= '). • S . poz. insert(sir_adaugat. readln(sir_adaugat). Exemplu: var a: string. Rezultatul este afişat. Valoarea este convertită către tipul string . dacă se citeşte 123 se afişează 123. writeln(sir). poz). readln(sir). lung := length(sir_sters). str(n. writeln('introduceti sirul care se adauga). writeln(a) end.2. poz := pos(sir_sters. Conversia de la şiruri la valori numerice şi invers Fie şirul '123'.poz. Înlocuirea tuturor apariţiilor unui subşir cu alt subşir.poate fi întreagă sau reală. sir). Evident. în ordine.variabila de tip string care reţine şirul convertit. dacă este memorat de o variabilă de tip integer. 3. şirul ocupă 4 octeţi (nu uitaţi. while poz <> 0 do begin delete(sir.  Procedura str are rolul de a transforma o valoare numerică în şir. primul octet reţine lungimea). Până aici nimic spectaculos. end. După cum ştim. Pentru aceasta i se transmit. sir_adaugat: string. acesta este diferit de numărul 123. lung: byte. procedure Str(X [: Lg [: Zec ]]. writeln('introduceti sirul care se sterge). doi parametri: • X . ocupă doi octeţi şi este reţinut în cod complementar. sir. readln(n).6.84 Capitolul 3 – Şiruri de caractere  Aplicaţia 3.

pentru că dacă ar fi fost respectată. s-ar fi afişat -67 (deci punctul zecimal nu ar fi afişat). ⇒ Dacă variabila n reţine numărul 123.79'. variabila a va reţine şirul '1234'. am avea acelaşi rezultat. Să analizăm acum modul de efectuare a conversiei. Dacă numărul solicitat de zecimale ar fi fost 0. rezultatul ar fi fost eronat. Observaţi faptul că.  În cazul în care numărul total de cifre al părţii întregi + numărul total de zecimale + 2 (caracterele de semn şi punctul zecimal) ocupă mai mult decât numărul total de octeţi solicitaţi pentru afişare. begin x := -67.a). Ce observăm?  Întotdeauna numărul zecimalelor solicitat de programator este respectat. şirul este completat în stânga cu numărul de blank-uri necesar. deci 4 octeţi pentru memorare.789 către un şir. Aceasta înseamnă că am cerut ca rezultatul (de tip string) să ocupe 5 octeţi . În astfel de cazuri. Atunci la ce folosesc astfel de conversii? Pentru a putea da răspunsul trebuie să mai învăţăm ceva despre modul în care putem apela procedura str. Cerinţa este ca şirul efectiv să ocupe 10 caractere. Acum putem răspunde la întrebarea: la ce folosesc astfel de conversii? Programatorul va avea grijă ca întotdeauna numărul de octeţi ai şirului să fie mai mare sau egal cu numărul de octeţi ai valorii convertite. la afişare. Exemplu: var a: string.789.Manual de informatică pentru clasa a XI-a 85 am fi afişat conţinutul variabilei n. Evident. vom şti care este spaţiul ocupat de şir şi putem să afişăm rezultatele aliniate (tabele). după numele variabilei care se afişează (n) au fost puse caracterul ':' şi numărul 4. writeln(a) end. Cu alte cuvinte nu se respectă cerinţa noastră. În cazul în care numărul efectiv de zecimale este mai mare decât numărul solicitat pentru conversie. x: real. înaintea conversiei numărul este rotunjit. dintre care ultimele două să fie zecimale. ⇒ Dacă variabila n reţine numărul 12345 atunci şirul va fi '12345'. Am fi putut să scriem apelul şi aşa: str(n:4. a). două caractere vor fi semnul '-' şi punctul zecimal.. se obţine şirul ' -67. În acest fel. atunci a va reţine şirul ' 123'. ocupă 4 octeţi. acesta din urmă nu este . În programul de mai sus se converteşte numărul -67. Rezultă că şirul.unde primul reţine lungimea. str(x: 10: 2. ⇒ Dacă variabila n reţine numărul 1234. fără octetul de lungime. În exemplu. ne ocupăm de conversia valorilor reale către şiruri de caractere. În continuare.

variabilă de tip întreg.conţine şirul ce urmează a fi convertit.86 Capitolul 3 – Şiruri de caractere respectat. er). Cu alte cuvinte. readln(a). În cazul în care tentativa a reuşit. De exemplu. x. unde: • s . • variabila_numerica . aceasta va reţine 0 dacă conversia a reuşit sau o valoare diferită de 0. er: integer. Acum studiem conversia inversă de la tipul string către valori numerice (întregi sau reale). conversia nu reuşeşte. var cod_er:integer). var variabila_numerica. x) else begin writeln ('conversia nu a reusit'). x. se trunchiază numai zecimalele nu şi partea întreagă a valorii reale. pentru că şirul conţine caracterul 'a'. if er = 0 then writeln(' conversia a reusit '. De exemplu. • cod_er .  Pentru realizarea conversiei utilizăm procedura val. Programul citeşte un şir de caractere care este reţinut în variabila de tip string a. Să analizăm programul următor: var a: string. semnul '+' nu este trecut. writeln(x) end end. dacă încercăm să convertim şirul '1a2' către o valoare de tip integer. dacă am fi scris x:1:2. După conversie.79'. begin write('Sirul este '). De exemplu. De la început precizăm că nu întotdeauna conversia reuşeşte. val(a. . şirul '123' se poate converti către valoarea de tip integer: 123.  În cazul în care numărul care se converteşte este pozitiv. Se încearcă conversia şirului către o variabilă de tip integer. Ea are trei parametri şi anume: procedure val(s:string. altfel se afişează numai un mesaj prin care se anunţă faptul că tentativa a eşuat. în caz contrar.variabila de tip întreg sau real care va reţine rezultatul conversiei (valoarea numerică). şirul obţinut ar fi fost '-67. se afişează mesajul corespunzător şi conţinutul variabilei.

begin writeln('introduceti sirul '). prin eroare de executare. eroare). dar poate fi convertit către o variabilă de tip real. val(sir. nu este in intervalul dorit ') else writeln('ok!') end. Exemplu: şirul ' 123' se poate converti către valoarea 123.  Dacă în urma conversiei se obţine o valoare numerică care nu poate fi memorată de variabila respectivă. Exemplu: şirul '123 ' nu poate fi convertit către o valoare numerică. iar şirul conţine punctul zecimal.  Dacă şirul conţine un singur caracter literă. şirul '12i' nu poate fi convertit. if eroare<>0 then writeln('val. eroare: integer. el nu poate fi convertit către o valoare numerică.23' nu poate fi convertit către o variabilă de tip întreg.4. . conversia nu reuşeşte. '1. readln(sir).E-3')  Dacă variabila care reţine rezultatul este de tip întreg. De exemplu. Programul următor testează dacă o valoare introdusă este numerică şi dacă este cuprinsă în intervalul [10. Excepţie fac şirurile de caractere care respectă sintaxa unei constante reale în formă ştiinţifică (de exemplu.Manual de informatică pentru clasa a XI-a 87 Observaţii  Dacă şirul de caractere cifre este precedat de un număr de blank-uri.  Aplicaţia 3. Exemplu: şirul '1.  Dacă şirul de caractere cifre este urmat de un număr de blank-uri. conversia reuşeşte. valoare: real.20]: var sir: string. introdusa este eronata') else if (valoare < 10) or (valoare > 20) then writeln('val. valoare. programul se termină anormal. conversia nu reuşeşte.

Pentru datele de acest tip. numele variabilei poate fi urmat de ':' şi de un parametru. şirul va fi scris în întregime. close(f). pointerul se află sau pe caracterul ce urmează după ultimul caracter citit sau pe CR.7. writeln(b). B) Scrierea datelor de tip String În general. a: string[2]. rewrite (f). După citirea unei astfel de variabile. end. var f:text. se iau în considerare lungimea efectivă a şirului şi valoarea parametrului m ce specifică numărul de poziţii pe care se face scrierea. writeln(a). se vor afişa două pe un rând şi unul pe al doilea rând). conţinutul unei date de tip string se scrie în fişier în totalitate.sir:10). reset(f). var f: text. aceasta se ignoră. acesta este scris pe m poziţii aliniat dreapta.txt'). Citirea şi scrierea datelor de tip String din şi în fişiere text A) Citirea datelor de tip String Aceste variabile se citesc începând din poziţia curentă a cursorului până este citit numărul de caractere necesar tipului sau până s-a ajuns la sfârşitul de linie. .2. close(f). end. write(f. În situaţia în care pointerul se află pe CR şi se forţează o nouă citire de tip String. begin assign(f.b).dat'). Dacă valoarea lui m este mai mare decât lungimea efectivă a şirului. sir:='Marian'. iar în faţă se pun blank-uri. Exemplu: se scrie şirul ' Marian'. Opţional.'f1. read(f.a). Programul de mai jos demonstrează acest fapt (dacă linia 1 a fişierului are 3 caractere. se returnează şirul vid.'F1. read(f. b: string. În cazul în care valoarea lui m este mai mică decât lungimea efectivă a şirului. m. begin assign(f. sir:string[6].88 Capitolul 3 – Şiruri de caractere 3.

pentru a reţine şirul "calculator" trebuie să fie rezervate cel puţin 11 elemente de tip char (10 litere plus caracterul nul) .i++) cout<<a[i]. • îl afişăm.1... Dar cum se reţine în memoria internă? Aceasta este reţinută sub forma unui vector de caractere.  char vect[100]="calculator". Primul element. Menţionăm că pentru fiecare caracter este reţinut codul ASCII. cel de indice 0.Manual de informatică pentru clasa a XI-a 89 3. În acest caz. • citim cuvântul. reţine codul ASCII al caracterului 'c'. Citirea şi scrierea şirurilor de caractere Se propune următoarea problemă: să se citească şi să se afişeze cuvântul "calculator". for(i=0. Exemple:  char vect[11]="calculator".. ş..3...adică 11 octeţi. Pentru aceasta. ar trebui să procedăm astfel: • reţinem un vector cu cel puţin 11 componente de tip char .. } .. int i.d. .. Am rezervat mai mulţi octeţi decât era necesar. Generalităţi Am învăţat faptul că o constantă de tip şir de caractere se declară între două caractere "... caracter cu caracter. Convenţia este ca ultimul octet să reţină 0 (codul caracterului nul)..... Prin urmare..i<10... 3.. caracterul nul fiind memorat automat. Şiruri de caractere în C++ 3.m.. for(i=0. a[10] Vectorii de caractere pot fi iniţializaţi la declarare.a.în exemplu 20..... compilatorul face calculul numărului de octeţi necesari...3..2. Programul următor realizează toate acestea: #include <iostream.i<10.3.. al doilea reţine codul ASCII al caracterului 'a'...i++) cin>>a[i].  char vect[]="calculator". de exemplu: "calculator"..h> main() { char a[20].. a[10]=0..... c a l c u 0 a[0] a[1] a[2] ..

dacă la rularea programului anterior tastăm şirul " Un om".d.a. s-au sărit blank-urile. blank). Din acest motiv. De exemplu. prin metoda de mai sus nu poate fi citit un şir care conţine mai multe cuvinte separate prin spaţii.  Procedând ca mai sus.  Un vector poate fi adresat pe componente. un număr mai mare de octeţi.get(vector_de_caractere.m. • a fost întâlnit caracterul transmis ca ultim parametru (implicit. cout<<a. În exemplu. ş. însă şi această modalitate este greoaie. • Se citeşte şirul care începe cu primul caracter care nu este alb şi se sfârşeşte la întâlnirea primului caracter alb (în exemplu. a[1]='a'. se va afişa "Un". Refacem programul anterior: #include <iostream. în limita memoriei pe care o are la dispoziţie programul. } Observaţii  Caracterul nul este adăugat automat. Din păcate. Dar dacă citim un cuvânt cu patru litere? S−ar putea scrie o secvenţă care să citească cuvântul până la întâlnirea caracterului ENTER.  Funcţia: cin. Limbajul C++ permite ca lucrul cu şiruri de caractere să fie cu mult mai simplu. Aceasta înseamnă că citirea se face astfel: • Se sar toate caracterele albe.  Putem să rezervăm. "\n"). pe care o prezentăm în continuare. pentru citirea şirurilor de caractere vom utiliza o funcţie de un tip special. char='\n') citeşte un şir de caractere până când este îndeplinită una dintre condiţiile de mai jos: • au fost citite nr-1 caractere. Exemplu: char a[1000]. putem citi orice cuvânt cu un număr de până la 19 caractere .excluzând caracterul nul. Exemplu: a[0]='c'. cin>>a. int nr.90 Capitolul 3 – Şiruri de caractere Să recunoaştem că o astfel de modalitate de lucru este deosebit de greoaie. .h> main() { char a[20].

La a doua citire. noul şir va începe cu acesta. Ea are rolul de a citi un caracter (fie că este alb. cout<<"sir 1 ". În cazul utilizării repetate a funcţiei cin. Analizaţi programul următor: #include <iostream.  Este inserat caracterul nul.Manual de informatică pentru clasa a XI-a 91 Observaţii  Sunt citite şi caracterele albe. a doua citire nu ar mai fi fost efectuată.get(a.get() fără parametri.get(sir1. există şi funcţia: cin.1000).h> main() { char sir1[1000]. cin. La fel ca mai sus. cin. apare o problemă. dar citirea se întrerupe la întâlnirea caracterului 'g' sau când au fost citite 9 caractere ale şirului. De ce? Sfârşitul primului şir introdus a fost marcat prin tastarea Enter.10. Se citeşte un şir de maximum 2 caractere: char a[10]. dar care diferă prin parametrii primiţi. Aceasta a făcut ca în memorie (buffer) să fie păstrat caracterul '\n'. fie că nu). De exemplu. sir2[25].get(sir2 . Al treilea parametru este trecut în mod facultativ.h> #include <string.3).25).get() fără parametri. cout<<a. se presupune că este '\n'. Observaţie. cout<<"sir 2 ". cin. cin. dacă tastăm 'mama' şi Enter se citeşte şirul "ma". char a[10]. 2. Priviţi următoarele exemple: 1. Prin logica funcţiei . În C++ pot exista mai multe funcţii cu acelaşi nume. Astfel. cin. cout<<a. Dacă nu este trecut. } Dacă după prima citire nu am fi folosit funcţia cin.  Caracterul transmis ca ultim parametru nu este inserat în şir.get() cu trei parametri.get().'g'). care va fi afişat.get(a.

d..în ansamblul lui . Tipul char* Vectorii de caractere au o proprietate deosebită. programul tipăreşte pentru a+1 şirul "asa".a. Priviţi programul următor: #include <iostream. (a+1)[1] reţine caracterul 's'. etc.m. Numărul de ordine al unui octet în memoria internă se numeşte adresa octetului respectiv. Definiţia 3. După cum observaţi. Mai mult.. pentru a+2 şirul "sa". Care este tipul lor? Tipul acestor expresii este char*. .  când scriem a+2. Definiţia 3. Din acest motiv.h> main() { char a[]="masa".6.  În C++.  . cout<<a+1<<" "<<a+2<<" "<<a+3. Utilizatorul nu mai apucă să îşi tasteze textul.5. Prin utilizarea funcţiei fără parametri.  O variabilă de tipul char* poate reţine adresa unui vector de caractere.3. ş. echivalent.. acel caracter se citeşte şi noua citire se poate efectua fără probleme. Astfel. adresăm vectorul începând cu al doilea octet. vectorii astfel adresaţi pot fi accesaţi aşa cum suntem deja obişnuiţi. citirea se face până la întâlnirea lui. numele unui vector de caractere este o adresă constantă de vector şi poate fi atribuit unei variabile de tip char*. } În urma executării.3. . sunt expresii. (a+1)[0] reţine caracterul 'a'. se citeşte şirul vid. a+2. Ce semnificaţie are? Semnificaţia este cea de adresă.. adresăm vectorul . se tipăreşte: "asa sa a".92 Capitolul 3 – Şiruri de caractere cu trei parametri. a+1. În concluzie. Orice expresie are un anumit tip. De ce? Limbajul C++ permite ca un vector de caractere să fie adresat începând de la un anumit octet al său:  când scriem a. 3. puteam scrie a+0. pentru exemplul anterior. Adresa unui vector de caractere este adresa primului său octet.începând cu primul său octet (cel care reţine primul caracter al şirului de caractere).  când scriem a+1. adresăm vectorul începând cu al treilea octet.

putem afişa şirul reţinut prin "cout<<p<<endl.4. De exemplu. am declarat şi o variabilă de tip char* numită p. cout<<p<<endl.  se pot face şi scăderi între adrese. • argumentul este de tip char* (adică o adresă către un şir).h": #include <iostream. } Am declarat un vector de caractere numit a. .  dacă la conţinutul variabilei p se adaugă 1. cout<<p<<endl. pe care l-am iniţializat cu un şir. Forma generală este: size_t strlen(char*).h> main() { char a[]="Exemplu". De asemenea. Exemplu: p[1]. pentru că a este o constantă ce reprezintă o adresă. utilizat în adresarea memoriei.h" (îl putem privi ca pe tipul unsigned int). putem afirma că:  p=a. În aceste condiţii. cout<<p[1]<<endl. Variabila p va reţine adresa vectorului de caractere a. cout<<p<<endl. De exemplu. atribuirea a=p este incorectă.  după atribuire. trebuie să fie inclus fişierul antet "string. dacă tipăresc vectorul se va afişa "xemplu". prin p-a se obţine indicele în v. cout<<p-a.3. Noul vector se poate adresa şi pe octeţi.". În schimb. tot aşa cum includem fişierul "iostream.h". al primului octet al vectorului reţinut de p. p++. *p. adică adresa primului său octet. Lungimea unui şir de caractere Pentru a putea fi folosite funcţiile de prelucrare a şirurilor de caractere. putem utiliza variabila p în aceleaşi condiţii ca variabila v. De exemplu. rezultatul este întreg. p=a. p++. este o atribuire corectă. unde: • size_t este un tip întreg.Manual de informatică pentru clasa a XI-a 93 Considerăm următorul exemplu: #include <iostream.h>  Funcţia strlen are rolul de a returna lungimea efectivă a unui şir (în calculul lungimii nu intră caracterul nul). În acest caz. aceasta va reţine adresa vectorului al cărui prim octet coincide cu al doilea octet al vectorului v. definit în "string. Testaţi programul! 3.

94 Capitolul 3 – Şiruri de caractere

Programul de mai jos citeşte un şir şi afişează numărul de caractere pe care
le are şirul citit (exclusiv caracterul nul):

#include <iostream.h>
#include <string.h>
main()
{ char a[100];
cin.get(a,100);
cout<<"Sirul citit are "<<strlen (a)<<" caractere";
}

Aşa cum ştim din lucrul cu tablouri, atribuirile de forma a=b, unde a şi b sunt
vectori de caractere, nu sunt permise. Tot aşa, o atribuire de forma a="un sir"
nu este permisă. Astfel de operaţii ca şi multe altele se fac cu anumite funcţii, puse
la dispoziţie de limbaj. Pentru ca acestea să poată fi folosite, trebuie să fie inclus
fişierul antet "string.h", tot aşa cum includem fişierul "iostream.h". Ordinea
de includere nu are importanţă. În continuare, vom prezenta cele mai uzuale funcţii
şi modul în care acestea se folosesc.

3.3.5. Copierea şi concatenarea şirurilor de caractere

 Funcţia strcpy are forma generală:
char *strcpy(char* dest, char* sursa);

şi are rolul de a copia şirul de adresă sursa la adresa dest. Copierea se termină
după ce a fost copiat caracterul nul. Se returnează adresa dest. Analizaţi
codul următor:
#include <iostream.h>
#include <string.h>
main()
{ char a[100]="un sir", b[100]="alt sir";
strcpy (a,b);
cout<<a;
}

În programul de mai sus se copiază în vectorul de caractere a şirul de
caractere reţinut de b. Programul va afişa "alt sir". Această copiere simulează
atribuirea: a=b.

 Funcţia standard strcat are forma generală:
char* strcat(char* dest, char* sursa);

şi rolul de a adăuga şirului de adresă dest şirul de adresă sursa. Şirul de adresă
sursa rămâne nemodificat. Această operaţie se numeşte concatenare şi nu este
comutativă. Rezultatul este adresa şirului sursa, iar şirul va avea ca lungime,
suma lungimilor celor două şiruri care au fost concatenate.

Manual de informatică pentru clasa a XI-a 95

Programul următor tipăreşte "mama merge":
#include <iostream.h>
#include <string.h>
main()
{
char a[20]="mama", b[100]=" merge";
strcat (a,b);
cout<<a;
}

 Funcţia strncat are forma generală:

char *strncat(char *dest, const char *sursa, size_t nr);

şi acelaşi rol ca strcat cu deosebirea că adaugă şirului destinaţie primii nr octeţi ai
şirului sursă. Adăugarea caracterelor se face înaintea caracterului nul. Funcţia
returnează adresa de început a şirului destinaţie.

3.3.6. Căutarea unui caracter într-un şir

 Funcţia strchr are forma generală:

char* strchr(char *s, int c);

şi rolul de a căuta caracterul 'c' în şirul s. Căutarea se face de la stânga la
dreapta. În cazul în care caracterul este găsit, funcţia întoarce adresa subşirului
care începe cu prima apariţie a caracterului citit şi se termină cu caracterul nul al
şirului în care se face căutarea. Altfel, întoarce o expresie de tip char* cu valoarea
0 (adică o adresă vidă de şir). Exemplele care urmează ne vor lămuri.

În programul următor se caută în şirul a caracterul 't'. Acesta este găsit, iar
programul tipăreşte şirul "ta este". Evident, acesta este subşirul care începe cu
caracterul 't'.
#include <iostream.h>
#include <string.h>
main()
{
char a[20]="Acesta este";
cout<<strchr (a,'t');
}

 Aplicaţia 3.5. În unele cazuri ne interesează indicele în cadrul vectorului al
caracterului căutat. Acesta se obţine ca diferenţă între două valori de tipul char*.
Descăzutul este adresa returnată de funcţie, iar scăzătorul este adresa vectorului în
care se face căutarea.

96 Capitolul 3 – Şiruri de caractere

Programul de mai jos tipăreşte indicele primei apariţii a caracterului 't', şi anume 4:
#include <iostream.h>
#include <string.h>
main()
{
char a[20]="Acesta este";
cout<<strchr (a,'t')-a;;
}

 Aplicaţia 3.6. În programul următor se citeşte un şir şi un caracter. Dacă
acesta este găsit în şir, se tipăreşte indicele primei apariţii a caracterului în şirul
solicitat, altfel programul semnalează faptul că acest caracter nu există în şir.
#include <string.h>
main()
{
char a[100], *t,c;
cout<< "introduceti sirul "; cin.get(a,100);
cout<< "caracterul cutat "; cin>>c;
t=strchr(a,c);
if (t) cout<<"Indicele este "<<t-a;
else cout<<"sirul nu contine acest caracter ";
}

Observaţii

 Variabila t este de tipul char*.

 Testul de apartenenţă a caracterului la şir s-a făcut prin a vedea dacă
variabila t reţine sau nu 0 (adică o adresă nulă de şir).

 Aplicaţia 3.7. În programul următor se listează indicii tuturor apariţiilor
caracterului citit în şir:
#include <iostream.h>
#include <string.h>
main()
{
char a[100], *t,c;
cout<< "introduceti sirul "; cin.get(a,100);
cout<< "caracterul cutat "; cin>>c;
t=a-1;
do
{
t++;
t=strchr(t,c);
if (t) cout<<"Indicele este "<<t-a<<endl;
} while (t);
}

Fiecare adresă de subşir, care are ca prim caracter cel reţinut de c, intră în
calculul indicelui acelui caracter - din ea se scade adresa de început.

Manual de informatică pentru clasa a XI-a 97

 Funcţia strrchr are forma generală:
char *strrchr(const char *s, int c);

şi acelaşi rol cu strchr, deosebirea fiind că întoarce adresa ultimei apariţii a
caracterului (căutarea se face de la dreapta către stânga). Ea este utilizată în
ipoteza în care se caută ultima apariţie a caracterului în cadrul şirului.

3.3.7. Compararea şirurilor

 Funcţia strcmp are forma generală:
int strcmp(const char *s1, const char*s2);
şi rolul de a compara două şiruri de caractere Valoarea returnată este:

• <0, dacă s1<s2;
• =0, dacă s1=s2;
• >0, dacă s1>s2.

Dar care este mecanismul prin care se compară două şiruri de caractere?
Fie m numărul de caractere al şirului s1 şi n numărul de caractere al şirului s2.
Să presupunem că primele i caractere ale lui s1 coincid cu primele i ale lui s2.

 În cazul în care codul caracterului i+1 al şirului s1 este mai mare decât codul
caracterului corespunzător şirului s2, avem s1>s2.

 În cazul în care codul caracterului i+1 al şirului s1 este mai mic decât codul
caracterului corespunzător şirului s2, avem s1<s2. În cazul în care şi la
această comparaţie avem egalitate, avem patru posibilităţi:

• ambele şiruri au un număr strict mai mare de caractere decât i+1, caz în
care se compară ca înainte caracterele de pe poziţia i+2;

• s1 are i+1 caractere, iar s2 are un număr de caractere mai mare decât
i+1, în acest caz s1<s2;

• s2 are i+1 caractere, iar s1 are un număr de caractere mai mare decât
i+1, în acest caz s1>s2;

• atât s1 cât şi s2 au i+1 caractere, caz în care s1=s2.

Pe scurt, un şir s1 este mai mic ca altul s2, dacă în dicţionar s1 ar figura
înaintea lui s2.

Exemple: "soare">s;
"tata">mama;

98 Capitolul 3 – Şiruri de caractere

Probaţi relaţia de ordine dintre două cuvinte (şiruri care nu conţin
caractere albe) prin utilizarea programului următor:
#include <iostream.h>
#include <string.h>
main()
{
char a[100],b[100];
int semnal;
cout<< "introduceti sirul a "; cin>>a;
cout<< "introduceti sirul b "; cin>>b;
semnal=strcmp(a,b);
if (semnal<0) cout<<"a<b";
else
if (semnal>0) cout<<"a>b";
else cout<<"a=b";
}

Funcţia strcmp face distincţie între literele mari şi mici ale alfabetului.

 Funcţia stricmp are forma generală:
int stricmp(char *s1,char *s2);

şi acelaşi rol ca strcmp. Diferenţa este că nu face distincţie între literele mari şi
mici.
Vectori de cuvinte. Există aplicaţii în care este necesar să se lucreze cu n
cuvinte - înţelegând prin cuvânt o succesiune de caractere care nu sunt albe. În
acest caz avem posibilitatea să declarăm vectori de cuvinte. Acestea sunt, de fapt,
matrice cu elemente de bază de tip char.

Exemplu: char a[10][25];

Fiecare linie din cele 10 ale matricei poate reţine un şir de caractere. Acesta poate
avea cel mult 25 de caractere (inclusiv caracterul nul). Cuvintele pot fi adresate prin
a[0] (primul cuvânt), a[1] cuvântul al doilea, ş.a.m.d.

 Aplicaţia 3.8. În programul următor se citesc n cuvinte. Acestea sunt sortate
alfabetic:
#include <iostream.h>
#include <string.h>
main()
{
char cuvinte[10][25], man[25];
int i,n,gasit;
cout<<"n=";
cin>>n;
for (i=0;i<n;i++)
{ cout<<"cuvant ";
cin>>cuvinte[i];
}

Manual de informatică pentru clasa a XI-a 99

do
{ gasit=0;
for (i=0;i<n-1;i++)
if (strcmp(cuvinte[i],cuvinte[i+1])>0)
{ strcpy(man,cuvinte[i]);
strcpy(cuvinte[i],cuvinte[i+1]);
strcpy(cuvinte[i+1],man);
gasit=1;
}
}
while (gasit);
for (i=0;i<n;i++) cout<<cuvinte[i]<<endl;
}

Dacă pot compara două cuvinte, atunci pot să le sortez. Am folosit sortarea
prin interschimbare. Iată că, algoritmii, cu precădere cei fundamentali, pot fi folosiţi
şi într-un context diferit de cel în care au fost prezentaţi.

3.3.8. Subşiruri

 Funcţia strstr are forma generală:
char *strstr(const char *s1, const char *s2);

şi are rolul de a identifica dacă şirul s2 este subşir (caractere succesive) al şirului
s1. Dacă acesta este identificat, funcţia returnează adresa de început în cadrul
şirului s1, altfel returnează adresa nulă (0). Căutarea se face de la stânga la
dreapta. În cazul în care s2 apare de mai multe ori în cadrul lui s1, se returnează
adresa de început a primei apariţii.
Exemplu: fie char s1[]="xyzt", s2[]="yz", atunci strstr(s1,s2);
returnează s1+1 (adresa caracterului y în s1).

 Aplicaţia 3.9. În programul următor se citesc două şiruri de caractere şi se
testează dacă al doilea este subşir al primului. În caz afirmativ, programul afişează
indicele caracterului de început al subşirului.
#include <iostream.h>
#include <string.h>
main()
{
char sir[1000],subsir[25], *t;
cout<<"introduceti textul ";
cin.get(sir,1000);
cin.get();
cout<<"introduceti subsirul cautat ";
cin.get(subsir,25);
t=strstr(sir,subsir);
if (t) cout<<"este subsir si are indicele "<<t-sir;
else cout<<"nu este subsir";
}

Ştergerea tuturor apariţiilor unui subşir din cadrul unui şir. cout<<"introduceti subsirul ". strncat(man.get(sir.adaug). int lung_subsir. p=strstr(p. lung_adaug=strlen(adaug). p=strstr(p+lung_adaug.subsir).25). cin. Înlocuirea tuturor apariţiilor unui subşir cu alt subşir.man).100). *p. strcat(man. sterg[25].25).get(adaug. adaug[25].get(). Vă las pe dvs. cin.100 Capitolul 3 – Şiruri de caractere  Aplicaţia 3.h> main() { char sir[1000]. lung_sterg=strlen(sterg). cin. cin.//subsir vid. cout<<"cu subsirul ".get(). restul şirului (fără subşir) este copiat pe poziţia de început a subşirului. man[100]. #include <iostream.get(subsir. cout<<"introduceti textul ". cin. strcat(man. cin.subsir[25]. cin. while (p) { man[0]=0.*p. cin.p+lung_sterg).get(). } cout<<sir. while (p) { strcpy(p. lung_adaug.subsir).sterg).get(sterg. } .p+lung_subsir).h> #include <string.h> #include <string.h> main() { char sir[100]. } cout<<sir. }  Aplicaţia 3. Imediat ce am identificat adresa de început a subşirului. int lung_sterg.sterg).get(sir. p=strstr(sir. lung_subsir=strlen(subsir).p-sir). cout<<"inlocuim subsirul ". cout<<"introduceti textul ".25). strcpy(sir.10.11.sir.1000). p=strstr(sir. să descoperiţi modul în care programul următor realizează aceasta: #include <iostream.

9. după prima entitate separatorul este înlocuit automat prin caracterul nul (cod 0). Alte funcţii utile în prelucrarea şirurilor  Funcţia strtok are forma generală: char *strtok(char *s1.separator[]=" . Întrucât caracterele separatoare sunt blank-ul şi virgula. . funcţia întoarce adresa nulă. n entităţi separate prin unul sau mai multe caractere cu rol de separator. În programul următor se citeşte un şir de caractere. cin. separator)..şiruri de caractere care nu sunt albe . fiecare pe un rând.s2). În plus.1000). funcţia întoarce adresa primului caracter al primei entităţi (în exemplu "mama"). p=strtok(sir.". Exemplu: şirul s1 este " mama.3. Entităţile se consideră a fi cuvinte .h> main() { char sir[1000].Manual de informatică pentru clasa a XI-a 101 3. } } Variabila p reţine adresa de început a entităţii.separate prin blank-uri şi/sau virgule.h> #include <string. while (p) { cout<<p<<endl..  Aplicaţia 3.get(sir.12.separator)... p va reţine o valoare diferită de 0.". rezultă că entităţile sunt: "mama"... Şirul s2 este: " . iar şirul s2 ca fiind alcătuit din unul sau mai multe caractere cu rol de separator.s2). p=strtok(NULL. #include <iostream. Principiul de executare este următorul: • şirul s1 este considerat ca fiind alcătuit din 0. 1. caz în care entitatea este afişată şi se va căuta următoarea entitate.aceasta permite ca entitatea să poată fi extrasă cu uşurinţă. . • în momentul în care şirul rămas nu mai conţine entităţi. "tata" "si" "bunicul". după ea. este adăugat caracterul nul . • următoarele apeluri ale funcţiei sunt de forma strtok(NULL. . const char *s2). iar funcţia întoarce de fiecare dată adresa primului caracter al următoarei entităţi şi. Dacă entitatea este găsită. *p. Programul listează entităţile depistate. • la prima apelare care trebuie să fie de forma strtok(s1.. tata si bunicul".

#include <iostream. #include <iostream. Să se verifice dacă şirul este alcătuit exclusiv din caractere numerice. se separă entităţile. *s2="16A32BF".s2) returnează 3 (caracterele 'A'. cin>>cuvant. 2. cout<<"Introduceti cuvantul ". p=strtok(sir. Fie char* s1="AB2def". şi are rolul de a returna numărul de caractere al şirului s1 . p=strtok(NULL.care nu se găsesc în şirul s2. char *s2). Fie char* s1="AB2def". '2' din s1 se găsesc în s2). *p. separator).14.caractere consecutive care încep obligatoriu cu primul caracter .h> #include <string.get(sir..se regăsesc printre caracterele vectorului cifre. cin. Se citeşte un şir de caractere care nu conţine caractere albe. Vom reţine un vector numit cifre. Exemple 1.s2) returnează 0 (primul caracter din s1 nu se găseşte în s2).s2) returnează 0 (pentru că primul caracter 'A' din s1 se găseşte în s2). Componentele lui reţin toate caracterele numerice 0-9. Şirul de caractere citit are toate caracterele numerice dacă toate caracterele sale .  Funcţia standard strspn are forma generală: size_t strspn(char *s1. şi are rolul de a returna numărul de caractere al şirului s1 . while (p) { cout<<p.numărul lor este dat de strlen . Atunci: strcspn(s1..cifre)==strlen(cuvant)) cout<<"numeric". iar strspn(s1. cifre[]="0123456789". const char *s2).caractere consecutive care încep obligatoriu cu primul caracter .102 Capitolul 3 – Şiruri de caractere  Aplicaţia 3. } }  Funcţia strcspn are forma generală: size_t strcspn(const char *s1.". if (strspn(cuvant.separator). Programul următor citeşte un şir de caractere şi tipăreşte şirul obţinut prin eliminarea blank-urilor.  Aplicaţia 3. separator[]=" .h> main() { char cuvant[100]. } .s2) returnează 2 (pentru că primele două caractere 'A' şi 'B' din s1 nu se găsesc în s2).13. care sunt afişate una după alta. *s2="123".h> #include <string.care se găsesc în şirul s2.h> main() { char sir[1000]. else cout<<"nenumeric".1000). 'B'. Atunci: strcspn(s1. În fapt. iar strspn(s1.

Manual de informatică pentru clasa a XI-a 103

 Aplicaţia 3.15. Se citeşte un şir de caractere care nu conţine caractere albe. Să
se verifice dacă şirul e alcătuit exclusiv din caractere nenumerice.
Vom reţine un vector numit cifre. Componentele lui reţin toate caracterele
numerice: 0-9. Cuvântul citit are toate caracterele nenumerice dacă toate
caracterele vectorului cifre - numărul lor este 10 - nu se regăsesc printre
caracterele vectorului cuvant.
#include <iostream.h>
#include <string.h>
main()
{ char cuvant[100], cifre[]="0123456789";
cout<<"Introduceti cuvantul "; cin>>cuvant;
if (strcspn(cifre,cuvant)==10) cout<<"corect ";
else cout<<"incorect";
}

 Funcţia strlwr are forma generală:
char *strlwr(char *s);
Ea converteşte toate literele mari ('A' ... 'Z') în litere mici ('a' ... 'z').
Restul caracterelor rămân neschimbate. Funcţia întoarce adresa s.
 Funcţia strupr are forma generală:
char *strupr(char *s);
şi rolul de a converti toate literele mici ('a' ... 'z') în litere mari ('A ... Z').
Restul caracterelor rămân neschimbate. Funcţia întoarce adresa s. În programul
următor se citeşte un cuvânt. Cuvântul citit se tipăreşte cu litere mari.
#include <iostream.h>
#include <string.h>
main()
{ char a[20];
cout<<" Introduceti cuvantul "; cin>>a;
cout<<strupr(a);
}

 Funcţia strpbrk are forma generală:
char *strpbrk(char *s1, char *s2);
şi acţionează în felul următor:
• Caută primul caracter al şirului s1 în s2. Dacă este găsit, returnează adresa
sa din cadrul şirului s1 şi execuţia se termină, altfel trece la pasul următor.
• Caută al doilea caracter al şirului s1 în s2. Dacă este găsit, returnează adresa
sa din cadrul şirului s1 şi execuţia se termină, altfel trece la pasul următor.
• ...
• dacă nici un caracter al şirului s1 nu aparţine şirului s2, funcţia returnează
adresa nulă.

104 Capitolul 3 – Şiruri de caractere

Exemplu: fie char *s1="abcdefghijklmnopqrstuvwxyz",*s2 = "c12";.
Atunci strpbrk(s1, s2) returnează adresa s1+2 (adresa caracterului c, pentru
că acesta este primul caracter din s1 găsit în s2).

 Aplicaţia 3.16. Se citesc două cuvinte. Se cere să se afişeze toate caracterele
primului cuvânt care se regăsesc în al doilea.
#include <iostream.h>
#include <string.h>
main()
{ char cuvant1[10], cuvant2[10],*p;
cout<<"Introduceti primul cuvant "; cin>>cuvant1;
cout<<"Introduceti al doilea cuvant "; cin>>cuvant2;
p=strpbrk(cuvant1,cuvant2);
while(p)
{ cout<<p[0]<<endl;
p++;
p=strpbrk(p,cuvant2);
}
}

Caracterele primului cuvânt, începând de la primul, sunt căutate în al doilea
cuvânt. Orice caracter găsit se tipăreşte. Aceasta se face pornind de la adresa
şirului, returnată de strpbrk. Apoi, variabila care conţine această adresă se
incrementează. Aceasta pentru ca, la următoarea căutare, să nu fie returnată
aceeaşi adresă de şir.

3.3.10. Conversia şirurilor în valori numerice şi invers

Următoarele funcţii au prototipul în "stdlib.h" şi folosesc pentru
conversia valorilor numerice în şir şi invers.
 Funcţia atof converteşte un şir către tipul double. Dacă conversia eşuează
(se întâlneşte un caracter nenumeric) valoarea întoarsă este 0. Dacă primele
caractere ale şirului sunt albe, acestea sunt ignorate.
double atof(const char *s);

Observaţiile sunt valabile şi pentru următoarele 3 funcţii:
 Funcţia _atold converteşte un şir către tipul long double:
long double _atold(const char *s);

 Funcţia atoi converteşte un şir către tipul int:
int atoi(const char *s);

 Funcţia atol converteşte un şir către tipul long:
long atol(const char *s);

Manual de informatică pentru clasa a XI-a 105

 Aplicaţia 3.17. Se citeşte un text alcătuit din mai multe cuvinte. Se cere să se
calculeze suma valorilor numerice întâlnite în text. Se presupune că valorile
numerice sunt introduse corect.
Şirul se separă în entităţi. Dacă o entitate este alcătuită exclusiv din '+',
'-', '.' şi cifre, atunci ea este convertită în double şi adunată la o variabilă s,
iniţializată cu 0.
#include <iostream.h>
#include <string.h>
#include <stdlib.h>
main()
{ char sir[1000],separator[]=" ",cifre[]="0123456789.+-", *p;
double s=0;
cin.get(sir,1000);
p=strtok(sir,separator);
while (p)
{ if (strspn(p,cifre)==strlen(p)) s+=atof(p);
p=strtok(NULL, separator);
}
cout<<"suma numerelor intalnite in sir este "<<s;
}

 Funcţia ecvt are rolul de a converti o valoare de tip double către un şir.
Caracterul nul este adăugat automat şirului obţinut. Forma generală este:
char* ecvt(double valoare, int poz, int* zec, int* semn);
unde:
• valoare - valoarea de convertit;
• poz - numărul de poziţii pe care trebuie să le ocupe şirul obţinut în urma
conversiei;
• zec - adresa unei variabile, de tip int, care reţine, după apel, numărul
zecimalelor pe care le are numărul;
• semn - adresa unei variabile, de tip int, care are rolul de a memora,
după apel, 1, dacă numărul este negativ, sau 0, în caz contrar.

Până în prezent nu am studiat modul în care putem obţine adresa unei
variabile. Reţineţi că dacă a este o variabilă, &a este adresa ei. Funcţia utilizează
acest mecanism de transmitere a parametrilor pentru ca să poată returna anumite
rezultate pe care le găsim în variabilele respective. În cazul ei, variabila zec va
reţine după apel numărul întregilor, iar variabila semn semnul rezultatului.

Funcţia nu inserează în şir nici punctul zecimal, nici semnul numărului. Şirul
obţinut începe cu cifra cea mai semnificativă a numărului. Pentru ca şirul obţinut să
fie corect, este necesar să fie prelucrat, în continuare, de către programator. Dacă
numărul poziţiilor ocupate de şir (poz) este mai mare decât ocupă data, şirul este
completat în stânga cu un număr de 0 necesar. În cazul în care numărul poziţiilor
este mai mic decât cel ocupat de număr, rezultatul este rotunjit.
Exemple: ecvt(val,3,&zec,&semn);

106 Capitolul 3 – Şiruri de caractere

− dacă val reţine 1234, atunci se returnează şirul "123", zec reţine 4 şi
semn reţine 0.
− dacă val reţine 1239, atunci se returnează şirul "124", zec reţine 4 şi
semn reţine 0.
− dacă val reţine 1, atunci se returnează şirul "100", zec reţine 1 şi semn
reţine 0.
− dacă val reţine -0.001, atunci se returnează şirul "100", zec reţine -3
şi semn reţine 1.

Pentru a vă convinge, rulaţi programul următor:
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
main()
{ double numar;
int zec,semn;
char numar_sir[20]="", numar_prel[20]="";
cout<<"n="; cin>>numar;
//convertesc numarul si tiparesc rezultatul
// asa cum este furnizat de functie
strcpy(numar_sir,ecvt(numar,19,&zec, &semn));
cout<<"Sirul este "<<numar_sir<<" "<<zec<<" "<<semn<<endl;
// prelucrez sirul pentru a tipari rezultatul asa cum
// este asteptat de utilizator.
if (semn) strcat(numar_prel,"-");
strncat(numar_prel,numar_sir,zec);
strcat(numar_prel,".");
strncat(numar_prel,numar_sir+zec,3);
cout<<numar_prel;
}

 Funcţia itoa are rolul de a converti o valoare de tip int într-un şir, a cărui
adresă este memorată în variabila sir. Valoarea baza reţine baza de numeraţie
către care să se facă conversia (de obicei baza este 10). În cazul bazei 10, şirul
obţinut reţine şi, eventual, semnul “-“. Funcţia întoarce adresa şirului obţinut.

char* itoa(int valoare, char *sir, int baza);

 Funcţia ltoa are acelaşi efect ca itoa, deosebirea fiind dată de faptul că se
converteşte către şir o valoare de tip long int.

char* ltoa(long value, char *sir, int baza);

 Funcţia ultoa are acelaşi efect ca itoa, deosebirea fiind dată de faptul că
se converteşte către şir o valoare de tip unsigned long.

char* ultoa(unsigned long value, char *sir, int baza);

Pentru conversii, se mai pot utiliza următoarele funcţii:

Manual de informatică pentru clasa a XI-a 107

 Funcţia
long strtol(const char *s, char **endptr, int baza);

are rolul de a converti un şir către long. În afara şirului care trebuie convertit,
funcţia primeşte ca parametru de intrare adresa unei variabile de tip char*. După
apel, această variabilă va reţine poziţia primului caracter din şir care nu poate fi
convertit. De exemplu, dacă şirul este "12m21", după apel, variabila a cărei adresă
a fost transmisă ca parametru reţine adresa caracterului 'm'. Dar care este rostul
existenţei acestei informaţii? Cu ajutorul ei se pot depista eventualele erori apărute
atunci când se introduc datele. Priviţi şirul dat anterior ca exemplu: e clar că
utilizatorul a dorit să introducă un număr, dar în locul unei cifre a tastat 'm'.

 Aplicaţia 3.18. Programul următor testează dacă o valoare introdusă este
numerică şi dacă este cuprinsă în intervalul [10,20]. Testul propriu-zis se face
atunci când comparăm numărul caracterelor convertite cu lungimea şirului.
Egalitatea are semnificaţia că întreg şirul este numeric. Variabila radix trebuie să
conţină 8, 10 sau 16, adică baza în care este considerat numărul sub formă de şir.
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
main(void)
{ char numar[20], *adresa;
long v;
cin>>numar;
v=strtol(numar,&adresa,10);
if(adresa-numar!=strlen(numar))
cout<<"Data contine caractere nenumerice";
else
if (v<10 || v>20) cout<<"data numerica in afara limitei ";
else cout<<v<<endl;
}

 Funcţia
double strtod(const char *s, char **endptr);
converteşte un şir către double.
 Funcţia
long double _strtold(const char *(s), char **endptr);
converteşte un şir către long double.
 Funcţia
unsigned long strtoul(const char *s,char **endptr,int baza);
converteşte un şir către unsigned long.

În caz de depăşire - adică numărul nu poate fi memorat deoarece este în
afara tipului - ultimele patru funcţii returnează cea mai mare sau cea mai mică
valoare care poate fi memorată de tipul în care se face conversia, după
cum valoarea este pozitivă sau negativă.

108 Capitolul 3 – Şiruri de caractere

3.3.11. Citirea şi scrierea şirurilor de caractere din şi în
fişiere text

3.3.11.1. Operaţia de citire

Pentru a citi o linie a unui fişier text, se poate folosi funcţia următoare:

getline(char* Adresa_sir, int Nr_car, char='\n').

Funcţia citeşte un şir de caractere, până când una dintre condiţiile următoare
este îndeplinită:

a) au fost citite Nr_car-1 caractere;
b) a fost detectat sfârşitul de linie: '\n'.

Evident, în acest fel, la o citire pot fi aduse în memorie cel mult 32766
caractere (vezi limitele tipului int).

Observaţii

 Caracterul care marchează sfârşitul liniei '\n' nu este inserat în şir, în
schimb este inserat caracterul nul (marchează sfârşitul şirului).
 Este suficient să apelăm funcţia cu primii doi parametri, ultimul fiind implicit.
 Dacă se doreşte, ultimul parametru poate fi apelat explicit cu o valoare
convenabilă, caz în care citirea se face până la întâlnirea ei sau până când
au fost citite Nr_car-1 caractere.

Programul următor citeşte linie cu linie un fişier text ale cărui linii nu au
mai mult de 500 de caractere şi afişează liniile pe monitor. Nu se
cunoaşte lungimea fiecărei linii.
#include <fstream.h>
main()
{ fstream f("f.dat",ios::in);
char Linie_citita[501];
while (f.getline(Linie_citita,501))
cout<<Linie_citita<<endl;
}

Observaţie foarte importantă! Prin f>>Linie_citita, citirea se
efectuează astfel:
a) începând cu poziţia curentă a pointerului se sar toate caracterele albe;
b) se citesc toate caracterele până la întâlnirea unui caracter alb.
De exemplu, dacă o linie a fişierului conţine şirul " Afara ploua", se
citeşte şirul "Afara". Testaţi!

strlen(X)).12.4. De această dată se consideră că este şir valid şi cel de lungime 0. numit ins..11.5 câte unul pe linie! #include <iostream. Prin această instrucţiune se scrie întreg şirul. Tot aşa. O funcţie specială (numită constructor) ataşează şirului X. "Citirea" se efectuează cu conversie către tipul respectiv. . strlen(X)). numit istrstream. } Observaţi cât de simplu se detectează sfârşitul şirului. int nr. Programul va afişa numerele 1.h> main() { fstream f("f.h> #include <string. f<<" Afara este"<<endl. începând cu poziţia curentă a pointerului.3. de fiecare dată.2.3. fapt care conduce la o nouă citire şi deci la ciclare.h> #include <strstrea. Şir de caractere Variabilă Un şir X reţine "1 2 3 4 5".dat". } 3. istrstream ins(X. f<<"primavara!". f. permite chiar declararea stream-urilor (fluxurilor) de la şiruri către variabile. Analizaţi exemplul următor: #include <fstream. se pot "citi" mai multe cuvinte dintr-un şir.ios::out). Un tip special. while (ins>>nr) cout<<nr<<endl.3.. Ea are doi parametri: şirul X şi lungimea lui: istrstream ins(X. Operaţia de scriere Scrierea se face prin comanda f<< sir. Programul următor citeşte cuvintele şi le afişează.close(). inclusiv caracterele albe.Manual de informatică pentru clasa a XI-a 109 3. O modalitate de conversie de la şir la alt tip Există şi alte posibilităţi de conversie de şiruri la alte tipuri de date. un stream (flux).h> main() { char X[]="1 2 3 4 5". "Citirea" se efectuează cu ajutorul operatorului “>>” la fel ca din fişier. se testează ca lungimea şirului citit să fie nevidă.2. Pentru aceasta.

Testaţi programul dvs. Cuvintele se consideră separate prin virgulă. Celelalte caractere rămân neschimbate. Se cere să se elimine spaţiile inutile.h> main() { char X[]="1 mama tata 4 bunica". pe fraza: "Batranul Mc Donald's avea 7 vaci care dadeau 120 de litri de lapte pe zi. Programul rearanjează literele în fiecare propoziţie.110 Capitolul 3 – Şiruri de caractere Programul este prezentat în continuare: #include <iostream. Exemple: THE PRICE OF BREAD IS $1. pe o linie. AAA CDEEEEE GHILL NPRS T76-TZ3. Câte cuvinte are textul citit? 2. Ieşirea va fi tot un fişier text. procentul este: 12%. Ele veneau acasa la 4 P. păstrând locul cuvintelor. Se citeşte un fişier text care conţine o singură propoziţie. Scrieţi un program care să mărească toate numerele care apar într-un document (citit dintr-un fişier text) după un procent citit de la tastatură. Creştere automată. spaţiu sau punct. Numai literele de la 'A' la 'Z' sunt afectate. dacă se introduce 12. De exemplu. Se citeşte un text.25 PER POUND. ABC DDEEE EF HIIINO OP $1. Junior Division 5. THE LICENSE PLATE READ G76-ZA3. istrstream ins(X. } Probleme propuse 1. Numele fişierelor este ales de dvs. Toate numerele trebuie afişate cu două zecimale.M" Junior Division . while (ins>>cuvant && strlen(cuvant)) cout<<cuvant<<endl. char cuvant[20]. 4. Se citeşte de la tastatură un text şi o succesiune de caractere.h> #include <strstrea. strlen(X)). Se citeşte de la tastatură un text.h> #include <string.25 PPR RRSTU. Două cuvinte pot fi separate printr-unul sau mai multe spaţii. De câte ori întâlnim această succesiune în cadrul textului? 3. în ordine alfabetică.

De exemplu.txt" şi scrisă pe o singură linie.  subprogram care permite scrierea unui şir într-un fişier text. Şiruri generalizate.numai pentru elevii care studiază limbajul Pascal. în Pascal. "I".  subprogram care converteşte un şir către un întreg. Literele care se elimină se citesc de la tastatură. şirurile cu care se poate lucra pot avea cel mult 255 de caractere. iar ieşirea este în fişierul "output. "MSSSSPP" este versiunea micşorată a cuvântului "MISSISSIPPI". "E".Manual de informatică pentru clasa a XI-a 111 6.txt". prin eliminarea literelor "A". Junior Division 7.  subprogram care şterge un subşir al unui şir dat (se cunoaşte poziţia de început a subşirului).  subprogram care permite concatenarea a două şiruri.  subprogram care converteşte un real către un şir. obţinută prin eliminarea tuturor literelor "I".  subprogram care converteşte un întreg către un şir. Un astfel de şir va fi reţinut ca un vector de 3000 de caractere. Scrieţi subprogramele următoare:  subprogram care determină lungimea unui şir.  subprogram care permite citirea unui şir dintr-un fişier text. .  subprogram care converteşte un şir către un număr real. Presupunem că nu sunteţi mulţumiţi de faptul că. să elimine literele dintr-o frază citită din fişierul text "input.  subprogram care inserează un şir pe o anumită poziţie a altui şir.  subprogram care identifică dacă un şir este sau nu subşir pentru altul. iar sfârşitul unui şir va fi marcat prin memorarea valorii 0 (în binar). Se cere ca programul dvs. dorim să lucrăm cu şiruri care au cel mult 3000 de caractere. Problemă de lucru în colectiv . Eliminare. O frază poate fi micşorată şi ea: "TRAVERSAREA CAII FERATE" are versiunea micşorată "TRVRSR C FRT".

în mai multe subvariabile numite. de un tip real. Făcând abstracţie de faptul că la ”Tehnologia Informaţiei şi a Comunicaţiilor” se folosesc programe gata făcute. O astfel de variabilă este împărţită. Astfel. o alta. Pentru fiecare elev. În ceea ce priveşte răspunsul la a doua întrebare. o posibilitate de a reţine un tabel este dată de crearea unui fişier cu tip.. care au o structură neomogenă. Să analizăm un exemplu. s-o numim Pers. ea conţine numele unei persoane (30 caractere alfabetice). apar situaţii în care toate tipurile de date învăţate până în prezent nu ne sunt de mare folos. dar este recomandabil să le studiaţi în mod individual. va conţine o subvariabilă numită Nume. cum ar fi:  cum se poate reţine în memorie o linie a unui tabel?  cum se poate reţine un tabel pe suport extern? Răspunsul la prima întrebare este dat de existenţa unor variabile speciale. se pot pune mai multe întrebări. De exemplu.112 Capitolul 4 Structuri de date neomogene 4.1. care este un şir de caractere. destinate unor utilizatori cărora nu li se pretinde să fie experţi în informatică. Tipul Record În practică. câmpuri. Noţiuni introductive La ”Tehnologia Informaţiei şi a Comunicaţiilor” am învăţat să lucrăm cu tabele (Excel sau Access). Structuri neomogene în Pascal 4.2. Să considerăm o linie a unui tabel. de un vector care are toate componentele de acelaşi tip. alcătuiesc tabelul. cunoaştem: . observăm că variabila Pers este neomogenă din punct de vedere al structurii. pentru care se alege un tip întreg şi o alta. Presupunem că dorim să prelucrăm anumite date referitoare la mai mulţi elevi. numită Varsta. Liniile. la rândul ei.1. vârsta (un întreg) şi salariul brut (un număr real). 4. Cum se declară şi cum se utilizează o astfel de variabilă vom învăţa în acest capitol. Pentru exemplul considerat. numită Salariu. în totalitatea lor. uneori. spre deosebire. de exemplu. o variabilă. Crearea şi exploatarea fişierelor cu tip nu face parte din programa dvs.2. specializate.

write('nota fizica '). n_mat. n_fiz: real. Exemplu: e. e. e. end. writeln('sexul '. readln(e. write('nota matematica '). write('varsta '). 2.n_mat). end. Vârsta . cum se citeşte şi cum se afişează o variabilă (numită e) de acest tip: type elev = record nume: string[20].Manual de informatică pentru clasa a XI-a 113 1. readln(e. Observaţi faptul că informaţiile referitoare la un elev sunt eterogene (de la numele de tip string.nume). 5. e. separându-le prin virgulă. writeln('nota matematica '. 4.n_fiz). apoi de numele câmpului. writeln('nume '.varsta). readln(e. n_fiz: real. var e: elev. Observaţii  Pentru a adresa un anumit câmp al variabilei de tip RECORD.sex). begin write('nume elev ').'. urmat de '. Nota la matematică – variabilă de tip real.nume reprezintă câmpul nume al variabilei e. e.varsta). observăm cum se declară (în acest caz. se foloseşte numele ei.n_fiz). numit RECORD. sex: char. varsta: byte.variabilă de tip real. În programul următor. În Pascal.se reţine în string[20]. le putem declara deodată. există posibilitatea ca toate aceste informaţii să se regăsească într-un singur tip de înregistrare.o variabilă de tip char reţine 'B' sau 'F'. Nota la fizică .nume). Exemplu: câmpurile: ”Nota la matematică” şi ”Nota la fizică” sunt adiacente şi de tip real. readln(e. elev). până vârsta de tip byte). Numele şi prenumele .variabilă de tip byte. . writeln('nota fizica '. deci declaraţia este: n_mat. writeln('varsta '. e. 3.  În cazul în care avem mai multe câmpuri adiacente (care urmează unul după altul) şi de acelaşi tip. readln(e. Dacă este băiat sau fată .sex). write ('sexul ').n_mat).

Accesul simplificat la câmpuri Aşa cum a fost prezentat. readln(sex).114 Capitolul 4.. end end.sex). begin with e do begin write('nume elev '). writeln('varsta '. var2. tot individual. fiz virsta sex  Câmpurile unei variabile de acest tip pot fi citite sau afişate individual.ocupă 35 de octeţi: 21 octeti 6 octeti 6 octeti 1 octet 1 octet         nume n..nume).2. write('nota matematica '). pot intra în calcule.varsta)... readln(varsta). end.n_fiz). writeln('nota fizica '.n_mat). var e: elev. cu forma generală: with var1. varsta: byte. readln(n_mat).  Dacă a şi b sunt două variabile de acelaşi tip RECORD se pot face fără probleme atribuiri de genul a:=b. în cadrul instrucţiunii subordonate. li se pot atribui valori. . write('nota fizica '). readln(nume). writeln('nota matematica '. writeln('sexul '. Structuri de date neomogene  Iată cum arată în memorie o înregistrare de acest tip . adresarea să se facă simplificat. varn do instrucţiune şi are rolul ca.mat n. readln(n_fiz). write ('sexul '). adică prin utilizarea exclusivă a numelui câmpului. . writeln('nume '.întotdeauna punem numele variabilei în faţă. sex: char. prin utilizarea instrucţiunii WITH. 4. În realitate. n_fiz: real. write('varsta '). n_mat. modul de acces la câmpurile unei variabile de tip RECORD este deosebit de greoi . De asemenea. accesul la câmpurile unei astfel de variabile se poate face mult mai uşor. Reluăm programul anterior.2. dar de această dată am utilizat instrucţiunea with: type elev = record nume: string[20].

deci inclusiv de tip RECORD. Vectori de înregistrări Aşa cum s-a arătat. ş. Adresarea s-ar fi putut face şi aşa: e. Pentru un elev. end. câmpurile care alcătuiesc un tip RECORD pot avea orice tip.zi. o astfel de adresare este greoaie şi nerecomandată… Putem avea oricâte niveluri de imbricare (un tip RECORD include un alt tip RECORD. Înregistrări imbricate În general.data_n.luna. urmat de perechea de paranteze drepte între care este trecut indicele componentei.nume. 4. instrucţiunea WITH. readln(luna). O întrebare: dar RECORD? Răspunsul este afirmativ. atunci numele se selectează prin v[i].2.data_n. Analizaţi programul următor: program r3. with data_n do begin write('ziua nasterii '). data_n: record zi.3.an. end. end. Pentru adresarea simplificată a câmpurilor care o alcătuiesc s-a folosit.d. n_fiz: real. readln(an). write('anul nasterii ').nume. Adresarea câmpurilor se face prin numele vectorului. iar înregistrarea este de tip elev (ca în programul următor). iar ziua naşterii prin v[i].Manual de informatică pentru clasa a XI-a 115 4. e. readln(nume). . se citesc numele şi data naşterii. Aceasta din urmă este de tipul RECORD (include ziua. e.4.). apoi selecţia propriu-zisă se face aşa cum am învăţat. Evident. luna şi anul naşterii). end end. begin with e do begin write('nume elev ').data_n. an: integer. type elev = record nume: string[20].m.a.2. sex: char. readln(zi). n_mat. care include un altul.zi. luna: byte. var e: elev. elementele unui vector pot fi de orice tip. din nou. write('luna nasterii '). Dacă v este vectorul. varsta: byte.

vector_inregistrari = array[1. end. să fi terminat liceul (caz în care dorim să ştim anul terminării şi oraşul) sau să aibă studii superioare (şi atunci ne interesează numele facultăţii şi numărul de ani de studiu în cadrul facultăţii respective). with data_n do begin write('ziua nasterii ').luna:byte. O parte variabilă se dezvoltă în cadrul înregistrării după valorile pe care le ia un câmp situat în cadrul părţii fixe. Structuri de date neomogene Şi de această dată. vârsta şi tipul de studii. var v: vector_inregistrari. i: integer. begin write('n='). readln(luna). an:integer.116 Capitolul 4. Sunt cazuri când un tip înregistrare are o parte fixă urmată de o parte variabilă. Să presupunem că ne interesează o situaţie referitoare la studiile unei persoane. Cum se realizează aceasta? În primul rând. în funcţie de tipul de studii. Limbajul permite ca înregistrările să aibă o structură variabilă. for i := 1 to n do with v[i] do begin write('nume elev '). înregistrarea arată altfel. . poate să fi făcut câteva clase de şcoală generală (ne-ar putea interesa câte clase). caz în care nu mai este necesar să avem alte informaţii. trebuie reţinut că partea variabilă este plasată în cadrul înregistrării după partea fixă. n. write('luna nasterii '). pentru selecţia simplificată. readln(zi).9] of elev. data_n: record zi. write('anul nasterii '). Înregistrare cu variante Nu toate înregistrările au o structură fixă (acelaşi număr de câmpuri) aşa cum au fost cele prezentate. O persoană poate să nu aibă studii. readln(n). end end. 4. unde se citeşte un vector cu n înregistrări de tip elev: type elev = record nume: string[20].5. În concluzie. aşa cum rezultă din programul următor. end. readln(nume). end. readln(an).2. se poate utiliza cu succes instrucţiunea WITH. O astfel de înregistrare are o parte fixă dată de câmpurile care reţin numele..

'g': (nr_cl: integer).fac. oras: string). În funcţie de valorile pe care le poate lua câmpul selector. write('studii '). În esenţă.  Este preferabil ca citirea unei variabile de tip înregistrare cu variante să se facă prin utilizarea instrucţiunii CASE.nume). 'l': (an_t: integer. Observaţii  Pentru fiecare înregistrare de acest tip compilatorul rezervă numărul de octeţi necesari celei mai lungi variante. selecţia părţii variabile făcându-se în funcţie de valorile pe care le ia câmpul studii. case studii: char of 'f': ( ). Aceasta se încadrează între paranteze rotunde şi poate fi chiar vidă.varsta).oras).'.an_t). end end {case} end.studii of 'g': begin write('numar clase ').nume_f). readln(p. readln(p. write('orasul '). end. În dreptul fiecărei valori se trece partea pe care trebuie să o conţină înregistrarea în acest caz. 's': (fac: record nume_f: string[20]. se exemplifică descrierea unui tip de înregistrare variabilă. se foloseşte o clauză specială numită CASE. case p. 'l': begin write('anul terminarii liceului ').an_s). readln(p. readln(p. writeln(' ani de studii facultate '). readln(p. write('varsta '). Pentru selectare. readln(p. Analizaţi programul următor (care citeşte o singură înregistrare): type persoana = record nume: string[30]. Câmpul selector trebuie să fie de tip ordinal cu un număr finit de elemente.studii). 's': begin write('numele facultatii '). begin write('nume '). Câmpul după care se face selecţia apare descris în această clauză.Manual de informatică pentru clasa a XI-a 117 În programul care urmează.nr_cl). readln(p.fac. end. varsta: byte. se scriu pe rând valorile posibile ale câmpului selector. se va dezvolta partea variabilă. readln(p. var p: persoana. an_s: byte end) end. . Câmpurile prezente între paranteze se scriu separate prin '.

.. 3. . Nota informatică .118 Capitolul 4.char[20]. pentru fiecare elev cunoaştem: 1. şi se numeşte elev. float nota_mate.nota_info. doi vectori cu elemente de bază de tip float pentru note şi unul cu elemente de bază de tip int pentru vârstă. Pentru a înţelege aceasta.. În C++ există un tip de date. câte unul pentru fiecare informaţie. Observaţi faptul că informaţiile referitoare la un elev sunt eterogene: şiruri de caractere. Structuri neomogene în C++ 4. Numele . numit struct.int. Tipul struct În practică. nume variabila. . Astfel. O astfel de abordare este greoaie.]>] .3. Astfel am avea doi vectori cu elemente de bază şiruri de caractere pentru nume şi prenume. care ne permite acest lucru. Fiecărui elev i.]>] . îi corespund componentele i ale fiecărui vector. apar situaţii în care toate tipurile de date învăţate până în prezent nu ne sunt de mare folos.3. [<tip> <nume variabila[. nume variabila. Nota matematică . Cum am putea rezolva problema prin utilizarea cunoştinţelor de care dispunem? Ar fi necesari 5 vectori. } [lista de variabile] . Pentru exemplul dat. structura este: struct elev { char nume[20]. }. 4.float.float. Vârsta . vom porni de la un exemplu. Ar fi cu mult mai bine dacă limbajul ar dispune de un mecanism prin care fiecărui elev să-i corespundă o singură înregistrare. 2. Structuri de date neomogene 4.. Forma generală este (ce este trecut între paranteze drepte este considerat facultativ): struct [nume structura] { [<tip> <nume variabila[. Presupunem că dorim să prelucrăm date referitoare la mai mulţi elevi.1..char[20]. 2. nenaturală... int varsta.. . Prenumele . numere reale sau întregi. prenume[20].

În programul următor. • inr. float nota_mate. int varsta. cout<<"Nume ". • inr. care este modalitatea de acces la câmpurile ei? Pentru aceasta.nota_info. Definiţia structurii poate figura atât în cadrul funcţiei main() cât şi în faţa ei.nume. cin>>inr. cin>>inr.varsta.nota_info. main() { elev inr.'. cout<<"Am citit:"<< endl <<inr. Atunci: • inr. cout<<"Nota matematica ". int varsta.nume<<" "<<inr. se citeşte şi se tipăreşte o variabilă de tipul elev: #include <iostream.nota_mate – reprezintă câmpul nota_mate al variabilei inr. Aşa cum rezultă din forma generală. Clasic. În ce ne priveşte.nota_mate<<endl <<inr. Fie inr o variabilă de tipul elev. inr2. }inr1.prenume. cin>>inr.nota_info <<endl <<inr.inr2. inr1 şi inr2 sunt două variabile de tipul elev. Aici.h> struct elev { char nume[20]. 2. scriind la sfârşit numele variabilelor: struct elev { char nume[20]. cin>>inr. cout<<"Nota informatica ". } .reprezintă şirul nume al variabilei inr.Manual de informatică pentru clasa a XI-a 119 Există două posibilităţi de declarare a variabilelor care alcătuiesc structura. declarând variabilele aşa cum suntem obişnuiţi: elev inr1. cin>>inr.nume . prenume[20]. după includerile de fişiere antet.nume[0] – reprezintă primul caracter al şirului nume. se foloseşte operatorul de selecţie directă.varsta. float nota_mate. operator cu prioritate maximă.nota_mate.prenume <<endl <<inr. 1. Se pune următoarea întrebare: fiind dată o variabilă de un tip struct. prenume[20]. }. vom prefera a doua variantă. cout<<"Varsta ". notat cu '. cout<<"Prenume ".nota_info.

se accesează câmpul clasa al substructurii. }. subordonează. . În interiorul său se găseşte o altă structură.2. Tipul structurat elev1. două structuri situatie_1 şi situatie_2. ca mai sus. de această dată fără nume.situatie. struct { int clasa. Structuri de date neomogene Între două variabile de acelaşi tip struct se poate folosi atribuirea. int varsta. }situatie. pe lângă alte tipuri. • inr. o astfel de atribuire se mai numeşte copiere bit cu bit.note[0] . În practică. }. prenume[20]. Înregistrări imbricate Există situaţii când un tip structurat conţine în interiorul său un alt tip structurat. dacă inr1.clasa . variabila inr1 ia aceeaşi valoare ca variabila inr2.3. elementele structurii se accesează prin numele variabilei de tipul structurii respective. în general. prenume[20]. int varsta. ci de un nume prin intermediul căruia poate fi accesat un element al structurii. dar pentru care există declarată "o variabilă" numită situatie. În realitate nu este vorba de o variabilă.se accesează prima notă a vectorului inclus în substructură. 4. float note[20]. float note[20]. Astfel. se foloseşte termenul "imbricate" pentru una sau mai multe structuri incluse una în alta. struct { int clasa. Exemplul următor prezintă o altă posibilitate de declarare a structurilor: struct elev1 { char nume[20].120 Capitolul 4. Structura de bază este elev. situatie_2. În C++. accesarea elementelor situate în interiorul substructurii se face ca mai jos: • inr. }situatie_1. În aceste condiţii.situatie. Priviţi tipul următor: struct elev { char nume[20]. Forma este echivalentă cu cea în care cele două structuri sunt descrise una după alta. prin atribuirea inr1=inr2. Fie inr o înregistrare de tipul elev. inr2 sunt două variabile de tip elev. Am văzut că.

Analizaţi programul următor: #include <iostream. veţi întreba. poate să fi făcut câteva clase de şcoală generală (ne−ar putea interesa câte clase). O astfel de înregistrare are o parte fixă dată de câmpurile care reţin numele. cout<<var..3. Cele trei câmpuri subordonate ocupă respectiv 2 octeţi. }. double c.b. înregistrarea arată altfel. Cum se realizează aceasta? . pentru toate variabilele s-au reţinut 10 octeţi . Ca să fiu clar. } Variabila var este de tipul union.3. numit union.adică octeţii necesari pentru a memora câmpul cel mai lung. Limbajul permite ca înregistrările să aibă o structură variabilă. un vector de caractere şi o variabilă reală. Iată forma generală a unei uniuni: union [<numele uniunii>] { <tip> <nume variabila> .c. În realitate. În exemplu.b. la un moment dat.. . O persoană poate să nu aibă studii. voi da un exemplu. Bine. main() { test var. char b[10]. Aceasta înseamnă că. vom studia un tip aparte de dată structurată. cin>>var. să fi terminat liceul (caz în care dorim să ştim anul terminării şi oraşul) sau să aibă studii superioare (şi atunci ne interesează numele facultăţii şi numărul de ani de studiu în cadrul facultăţii respective). cin>>var.c<<endl. dar la ce folosesc "uniunile"? Sunt situaţii în care înregistrările nu au format fix. Ea conţine un întreg. am utilizat la început variabila întreagă. În concluzie. } [lista de variabile] . ci variabil. apoi şirul de caractere. vârsta şi tipul de studii. caz în care nu mai este necesar să avem alte informaţii.h> union test { int a. 10 octeţi şi 8 octeţi. toate celelalte reguli sunt identice cu cele de la structuri. Să presupunem că ne interesează o situaţie referitoare la studiile unei persoane.Manual de informatică pentru clasa a XI-a 121 4.int i. se poate memora doar un singur câmp dintre cele subordonate. cout<<var. Cu excepţia faptului că numai un câmp poate fi ocupat la un moment dat. Înregistrări cu structură variabilă Pentru început. în funcţie de tipul de studii.

break.m. cin>>pers. reţine 'l'. Uniunea poate figura oriunde în interiorul structurii . studii. cout<<"nr ani de studiu ". cin>>pers. cout<<"Studii f-fara g-generala. ş.a.nume.std. reţine 'g'. dacă persoana respectivă nu are studii.studii) { case 'g': cout<<" numar clase ". dacă are liceul. cin>>pers.oras.nr_ani.liceu. Aceasta conţine o variabilă nr_clase . switch (pers.s-superioare) ". o structură pentru cazul în care studii. struct {int an_t. }facultate. case 'l': cout<<"anul terminarii liceului ".get().nume_f. care reţine tipul studiilor pe care le are persoana respectivă.pentru cazul în care studii reţine 'g'.d. } . Structuri de date neomogene Uniunile pot fi incluse în structuri. cin.std. main() { persoana pers. cin>>pers. int nr_ani. citirea şi afişarea înregistrării se face. cout<<"Nume persoana ".get(pers. înregistrarea are o parte fixă care este alcătuită din numele persoanei respective şi o variabilă de tip char.liceu. cin>>pers. case 's': cout<<"numele facultatii ". char oras[20]. numită studii. structurile pot fi incluse în uniuni.std. sub switch. struct {char nume_f[30].facultate.cin.facultate.an_t.studii.l-liceu. în funcţie de datele citite sau conţinute. Astfel. iar dacă are studii superioare. cout<<"orasul ". înregistrarea are structura variabilă.30).30). 's'. union { int nr_clase. dacă are câteva clase de generală.122 Capitolul 4. break.nr_clase. }std. reţine 'l'. La rândul lor.get(pers. Apoi urmează partea variabilă care este o uniune. Pentru exemplul nostru.nu este obligatoriu ca aceasta să fie scrisă la sfârşitul structurii.std.cin.h> struct persoana { char nume[30]. }liceu. de fiecare dată. dar ocupă un număr fix de octeţi.std. În concluzie. reţine 'f'. Analizaţi programul: #include <iostream. }. În rest.

să se afişeze în ordine descrescătoare a mediilor.studii) { case 'f': cout<<"n-are saracu' studii ". . Aceeaşi problemă ca cea anterioară. În cazul în care pe ultimul loc avem mai mulţi elevi cu aceeaşi medie. toţi aceştia sunt consideraţi admişi. numai că afişarea se va face în ordinea descrescătoare a mediilor.nume_f <<" nr ani de studiu " << pers.an_t <<" in orasul "<<pers. 4. Citiţi o variabilă cu următoarea structură: • nume_elev: 30 caractere.liceu. 3. • nota engleza – real.std.oras. an : intreg. case 'l': cout<<"a terminat liceul in " <<pers. (media se calculează.nr_clase. break.facultate. break.liceu. Testaţi dacă datele au fost introduse corect. 2.Manual de informatică pentru clasa a XI-a 123 //afisez inregistrarea cout<<pers. • nota informatica – real.std.facultate. luna : intreg.nr_ani. • media – real. case 'g': cout<<" numar clase "<<pers. în limita unui număr de locuri sau până când se epuizează toate înregistrările elevilor cu medii mai mari sau egale cu 5. Citiţi n înregistrări de tipul celei de mai sus şi afişaţi-le în ordinea alfabetică a numelui.std. • nota matematica – real. break. Programul va afişa numărul de locuri în plus. Presupunând că înregistrările se referă la un examen de admitere dat. • data_nasterii: zi : intreg. Citirea se va verifica prin afişarea rezultatului. nu se citeşte). switch (pers.std. case 's': cout<<"numele facultatii " <<pers. } } Probleme propuse 1.nume<<endl.std.

prin tip de date se înţelege:  o mulţime de valori. 1   o mulţime de operaţii definite pe mulţimea datelor. este sarcina programatorului ca valoarea reţinută de variabila corespunzătoare lui q să fie diferită de 0. limbajele de programare evoluate (de exemplu. Fie A1 mulţimea valorilor care pot fi memorate prin utilizarea tipului întreg. pentru a lucra cu tipul respectiv. o variabilă de tip rational poate memora valori care aparţin mulţimii A1×A2. 1. Fie A2=A1-{0}. Pentru a veni în sprijinul programatorilor.1. să cunoaştem regula de codificare. În unele lucrări veţi întâlni definiţii ale tipului de date care exclud regula de codificare. datele de intrare. La rândul lor.cele create în timpul prelucrării . { int p. necesităţilor de prelucrare sau a celor de utilizare ulterioară. Exemplu: int. le prelucrează şi obţine date de ieşire. Mai precis. o regulă de codificare a acestora. Evident. Aici 1 se porneşte de la ideea că nu întotdeauna este necesar.124 Capitolul 5 Structuri de date 5. Prin tipul de mai sus se descrie structura unei variabile capabilă să reţină numere raţionale. Atunci. tipurile de date pot fi:  simple . datele intermediare .descriu date care aparţin unor mulţimi rezultate ca produs cartezian al altor mulţimi. .q.q:integer. end }.  structurate . C++ sau Pascal) pun la dispoziţia acestora posibilitatea organizării datelor în anumite "şabloane".şi datele de ieşire sunt structurate (organizate) într-un anumit fel care corespunde intrării. Priviţi declaraţia de mai jos: Varianta Pascal Varianta C++ type rational=record struct rational p. În fiecare caz. Conceptul de structură de date Orice algoritm primeşte date de intrare.descriu date care aparţin unor mulţimi care nu sunt rezultate ca produs cartezian al altor mulţimi. numite tipuri de date.

După caz.. În cele mai multe cazuri. Ele vor fi tratate. Ea este una dintre disciplinele care se includ în cadrul mai larg al informaticii.. algebra se studiază ca disciplină aparte a matematicii. var a:vector.  Structura de date este un concept abstract. conceptul în sine nu precizează locul unde structura respectivă va fi memorată (clasa de memorare) şi nici detaliile de implementare (cele care ţin de limbajul folosit). Definiţia 5. Atunci. tot aşa cum. este prezentat un alt exemplu: Varianta Pascal Varianta C++ type vector=array [1. de exemplu.  În facultăţile de profil (calculatoare. termenul nod poate fi înlocuit cu articol. Vom numi nod. Relaţiile existente între noduri şi operaţiile care pot fi efectuate cu ele vor fi prezentate prin exemple. acest tip este structurat. pe scurt. variabila a poate reţine la un moment dat un element al mulţimii: B ×  B × . Acest lucru este datorat faptului că există mulţi algoritmi care le utilizează. în paragrafele următoare. Prin structură de date vom înţelege un ansamblu de date caracterizat prin relaţiile existente între ele şi prin operaţiile care pot fi efectuate cu datele respective.Manual de informatică pentru clasa a XI-a 125 2. Mai precis. Fie B mulţimea valorilor care pot fi reţinute de tipul real. × de n ori Practica impune utilizarea unor structuri ale datelor de o mare varietate. înregistrare sau entitate.. care nu se suprapun întotdeauna peste tipurile care pot fi descrise prin limbaj. o variabilă de un tip oarecare. De obicei. double a[100].  În practică s-au impus anumite structuri. "ansamblul de date" care formează structura este alcătuit dintr-o mulţime cu un număr variabil de noduri. Mai jos. informatică) se studiază disciplina numită "Structuri de date".  B .1.100] of real. . De reţinut!  Tipul variabilei care alcătuieşte nodul nu caracterizează structura de date.

. Faptul că structura este liniară înseamnă că fiecare nod. X2. Aici.. .. cu excepţia ultimului. Dacă vorbim la modul general de o structură de date. • ştergerea unui nod. . pentru moment. Xn este ultimul nod. caz în care se adaugă noduri. Exemple de structuri care nu sunt liste liniare: • se dau n oraşe şi şoselele care unesc unele dintre aceste oraşe.2. Vom avea deci. X1.126 Capitolul 5.. Xn aflate într-o relaţie de ordine. un nod reţine un număr natural. Astfel. modul în care aceasta va fi implementată (adică unde este memorată şi cum se efectuează operaţiile permise asupra ei). În clasa respectivă pot fi transferaţi elevi din alte clase. are un singur predecesor (care se află imediat înaintea lui în listă). cu excepţia primului nod.1. • schimbarea poziţiei unui nod în cadrul listei. indiferent de poziţia pe care o ocupă în listă. .2. Nodurile vor fi memorate în ordinea alfabetică a numelor elevilor. unde fiecare nod i memorează informaţia infi: inf 1 inf 2 inf n nod1 nod2 nodn Exemple de aplicaţii care utilizează liste liniare: a) Evidenţa situaţiei şcolare a elevilor unei clase. O listă liniară este o colecţie de n≥0 noduri.2. X2 este al doilea nod al listei. Operaţiile permise sunt: • accesul la oricare nod al listei în scopul citirii sau modificării informaţiei conţinute de acesta.. n noduri. b) Se doreşte să se reţină un şir de numere naturale. Din clasă. nu ne interesează. Aici. • adăugarea unui nod. unii elevi pot pleca în alte clase. ne imaginăm lista ca mai jos. are un singur nod succesor (care îi urmează în listă) şi. Structuri de date 5. în ordinea în care au fost citite de la tastatură. • arborele genealogic al unei persoane. un nod reţine numele unui elev şi notele la diversele materii. caz în care se şterg noduri. indiferent de poziţia pe care o ocupă în listă. Pur şi simplu. Prezentarea structurii Definiţia 5. Nu toate aplicaţiile utilizează liste liniare.. Structura de tip listă liniară 5. X1 este primul nod al listei. Fie n numărul elevilor.

a. c) Adăugarea unui nod. O primă formă de alocare este cea secvenţială. de câte ori utilizăm vectorii.d. a) Accesul la oricare nod al listei se poate face cu mare uşurinţă.m. de această dată. primul nod al listei va fi reţinut de primul element al vectorului. ş. În continuare. Altfel spus.d. În multe aplicaţii. atunci scriem V[k] (presupunând că vectorul care reţine nodul se numeşte V). b) Ştergerea unui nod. indiferent de poziţia pe care o ocupă în listă Fie lista alocată secvenţial: 7 3 1 2 8 9 5 8 3 2 6 Eliminăm al doilea nod . nodul 2 (elementul de indice 2 al vectorului) va reţine informaţia celui de-al treilea nod. este obligatoriu ca informaţiile reţinute de nodurile care urmează nodului şters să fie deplasate către stânga: 7 1 2 8 9 5 8 3 2 6 Practic.2. nodurile listei ocupă poziţii succesive în memorie.Manual de informatică pentru clasa a XI-a 127 5. nodul 3. urmărim modul în care putem efectua operaţiile permise cu o listă liniară. Dacă dorim să adresăm nodul k.m. În cazul acestei alocări. al doilea nod al listei de al doilea element al vectorului.conţinut 3 (ştergem conţinutul nodului 2): 7 1 2 8 9 5 8 3 2 6 Pentru a păstra structura şi pentru a nu pierde informaţii. va reţine informaţia celui de-al patrulea nod. ş. această modificare nu prezintă importanţă. Acest tip de alocare se întâlneşte des. Liste alocate secvenţial Din acest moment ne punem problema să vedem modul în care se poate implementa o listă liniară. indiferent de poziţia pe care o ocupă în listă Fie lista alocată secvenţial: 7 3 1 2 8 9 5 8 3 2 6 .2.a.

. Liste alocate înlănţuit Există două feluri de alocare înlănţuită: alocare simplu înlănţuită şi alocare dublu înlănţuită. adr3.3. se utilizează alocarea dublu înlănţuită. ş.  in1. În acest paragraf prezentăm principiile alocării înlănţuite. Alocarea dublu înlănţuită. fiecare nod. deplasăm toate informaţiile asociate nodurilor către dreapta: 7 3 1 2 8 9 5 8 3 2 6 Acum se completează valoarea reţinută de nodul 2: 7 5 3 1 2 8 9 5 8 3 2 6 Observaţii  Şi de această dată. pentru că necesită deplasări ale conţinuturilor nodurilor. adr2.. De exemplu. 5.elementul este ultimul în listă. Aici. dar adăugarea sau ştergerea unui nod se fac cu efort de calcul. Începând cu nodul 2....are semnificaţia "nici o adresă" . adrn reprezintă adresele celor n înregistrări. adăugăm un nod).  nil .2. urmând ca în paragraful următor să arătăm modul în care implementăm listele alocate înlănţuit. . in2. . .. În cazul în care se doreşte ca lista să poată fi parcursă în ambele sensuri. reţine adresa nodului următor. 2.d. accesul la nod este foarte rapid. Alocarea simplu înlănţuită permite parcurgerea listei într-un singur sens (de la stânga la dreapta). nodul 3 va reţine ce anterior reţinea nodul 2. Structuri de date Nodul 2 va conţine 5 (practic.  În concluzie. După cum observăm. O listă liniară simplu înlănţuită este o structură de forma: in1 adr2 in2 adr3 inn nil adr1 adr2 adrn Semnificaţia notaţiilor folosite este următoarea:  adr1.m. inn reprezintă informaţiile conţinute de noduri. nodurile vor reţine informaţii diferite. cu excepţia ultimului.128 Capitolul 5.a. 1. de altă natură decât cele de adresă. la alocarea secvenţială.

 Pentru memorarea listei folosim un vector care are componentele de tip Nod. end. atunci când acesta reţine un număr întreg. nod L[1000]. În acest paragraf vom studia implementarea alocării simplu înlănţuită.. info adresa nodului următor Varianta Pascal Varianta C++ type Adresa=Integer. Priviţi exemplul de mai jos: L 7 3 5 4 1 5 4 0 1 2 3 4 5 6 . typedef int adresa. { int info.Manual de informatică pentru clasa a XI-a 129 fiecare nod reţine adresele predecesorului şi succesorului său. adresa adr_urm. în cazul listelor alocate simplu înlănţuit. aşa cum se vede în figura următoare: nil in1 adr2 adr1 in2 adr3 adrn-1 inn nil adr1 adr2 adrn 5. Implementarea alocării înlănţuite prin utilizarea vectorilor Aşa cum am învăţat. }. var L:lista. Acesta este spaţiul disponibil. descris mai jos: Varianta Pascal Varianta C++ Lista=array[1. nod=record struct nod info:integer.  Iată cum se descrie informaţia dintr-un nod.4. adr_urm:Adresa. În cazul alocării înlănţuite. informaţia memorată de fiecare nod va cuprinde şi un câmp de adresă -în cazul alocării simplu înlănţuită- sau două câmpuri de adresă -în cazul alocării dublu înlănţuită.2. lista liniară este alcătuită din mai multe noduri între care există o relaţie de ordine. Din descriere rezultă că lista poate avea cel mult 1000 de noduri.1000] of Nod.

unde accesul este imediat. ş. lista este: 7 3 5 4 1 5 4 0 1 3 4 5 Ce observăm? a) Fiecare nod trebuie să reţină şi adresa nodului următor. b) Nodurile nu ocupă adrese succesive în memorie. vectorul care reţine nodurile. Structuri de date Dacă facem abstracţie de implementare. al doilea nod al listei este reţinut de componenta de indice 3 a vectorului. va fi dublat de un altul. Prin urmare. la un moment dat. Pentru realizarea practică a implementării apar o serie de probleme. numărul de noduri poate fi depăşit. deşi primul nod al listei este reţinut de prima componentă a vectorului (de indice 1). Gestiunea memoriei. Dacă nu acesta este nodul căutat. Prin urmare.a. Să presupunem că în lista de mai jos dorim să adăugăm. L 3 5 4 1 5 4 1 2 3 4 5 6 ocupat 1 0 1 1 1 0 1 2 3 4 5 6  Problema 2. se poate utiliza o variabilă care memorează numărul de componente ale vectorului care reţine nodurile.130 Capitolul 5. Acestea vor fi rezolvate în cele ce urmează. ale cărui componente reţin 1 sau 0. Adăugarea unui nod.  Problema 1. În exemplul de mai sus. Adresa este.  Problema 3. În practică.m. indicele componentei din vector care reţine informaţia asociată nodului următor. necesarul de memorie este mai mare. un nod cu informaţia 9. iar el este memorat de componenta de indice 3 a vectorului. de fapt. . numărul de noduri este limitat. De exemplu. Accesul la un nod al listei. Aceasta înseamnă că. la alocarea înlănţuită accesul se face începând cu primul nod al listei. În aceste condiţii. Din acest motiv. după cum componenta de acelaşi indice a vectorului L reţine sau nu un nod. Spre deosebire de alocarea secvenţială.d. nodul 2 reţine 5.). după al treilea nod. Eventual. vom face distincţie între numărul unui nod (acesta este în cadrul listei) şi indicele vectorului unde este memorat. Conceptul de listă nu precizează numărul de noduri pe care ea le poate avea. se trece la nodul următor (orice nod conţine adresa nodului următor. programul care lucrează cu o astfel de listă trebuie să gestioneze spaţiul disponibil.

b) Memorăm informaţia: 9. adresa nodului nou introdus. lista va fi: 7 3 5 4 1 5 9 5 4 0 1 3 4 5 5 .Manual de informatică pentru clasa a XI-a 131 L 7 3 5 4 1 5 4 0 1 2 3 4 5 6 ocupat 1 0 1 1 1 0 1 2 3 4 5 6 Prin testarea vectorului ocupat. a) Marcăm nodul ca ocupat (ocupat[2] va reţine 1). adresa următoare reţinută de al treilea nod (pentru că introducerea noului nod se face după acesta): L 7 3 9 5 5 4 1 5 4 0 1 2 3 4 5 6 ocupat 1 1 1 1 1 0 1 2 3 4 5 6 d) Al treilea nod va reţine ca adresă următoare. se observă că primul element liber al vectorului L este cel de indice 2. pentru că acesta s-a introdus după el: L 7 3 9 5 5 4 1 2 4 0 1 2 3 4 5 6 ocupat 1 1 1 1 1 0 1 2 3 4 5 6 După această modificare. L 7 3 9 5 4 1 5 4 0 1 2 3 4 5 6 ocupat 1 1 1 1 1 0 1 2 3 4 5 6 c) Noul nod va reţine ca adresă următoare.

primul nod va reţine ca adresă următoare adresa nodului 3 (pentru că acesta urmează nodului 2). . L 7 4 5 4 1 5 4 0 1 2 3 4 5 6 ocupat 1 0 0 1 1 0 1 2 3 4 5 6 Observaţi faptul că. dar bine înţelese. Prin urmare. Ştergerea unui nod. Structuri de date  Problema 4. ne permit să deducem singuri modul de efectuare a operaţiilor respective pentru liste dublu înlănţuite. 2. acesta devine inaccesibil prin parcurgerea listei începând cu primul nod. Accesul la un nod al listei se face prin parcurgerea nodurilor care îl preced. Informaţiile de adresă. În continuare.132 Capitolul 5. prezente în cadrul fiecărui nod ocupă memorie. Aceasta necesită un efort de calcul. ocupat[3] va reţine 0. L 7 3 5 4 1 5 4 0 1 2 3 4 5 6 ocupat 1 0 1 1 1 0 1 2 3 4 5 6 Al doilea nod se găseşte în elementul de indice 3.  Avantajele alocării înlănţuite sunt date de faptul că operaţiile de adăugare sau eliminare a unui nod se fac rapid. Această adresă se ia din câmpul de adresă următoare a nodului care urmează să fie şters. Exemplele sunt date pentru lista liniară simplu înlănţuită. deşi nu am şters informaţia asociată nodului. Lista va deveni: 7 4 1 5 4 0 1 4 5  Dezavantajele alocării înlănţuite sunt: 1. Să presupunem că în lista de mai jos dorim să ştergem al doilea nod.

k=1. modul de lucru cu stiva: A În stiva iniţial vidă se introduce litera A. ST[n] pot reţine numai litere sau numai cifre. . Stiva este o listă pentru care singurele operaţii permise sunt: • adăugarea unui element în stivă. B Introducem în stivă litera B.. stiva rămâne vidă k=0. iar atunci când scriem ceva în stivă. • eliminarea. Stiva funcţionează pe principiul LIFO (Last In First Out) . Pentru a înţelege modul de lucru cu stiva. O variabilă k indică în permanenţă vârful stivei. Oricât ar părea de simplu principiul stivei. . la scoaterea unei variabile din stivă.3. ST[1]. sau modificarea ultimului element introdus în stivă.Manual de informatică pentru clasa a XI-a 133 5."ultimul intrat primul ieşit".  Stivele se pot aloca secvenţial (ca vectorii). Observaţii  În mod practic. ne imaginăm un număr n de farfurii identice. spunem că aceasta este vidă. consultarea.. vârful stivei va fi la nivelul 1 (k=1).3. o eventuală valoare reziduală se pierde. Adăugarea sau scoaterea unei farfurii se face. valoarea variabilei ce indică vârful stivei scade cu 1. adică ultimul element introdus. Fie ST[i] un vector. A Scoatem din stivă pe A. el are consecinţe uriaşe în programare. deci k va lua valoarea 2. aşezate una peste alta (o "stivă" de farfurii). Structura de tip stivă Definiţia 5. ST[2]. în continuare. A Scoatem din stivă pe B (A nu poate fi scos deocamdată). numai în vârful stivei. Exemplificăm. cu uşurinţă. După ce am scos toate farfuriile din stivă..

Algoritmul se bazează pe următoarele considerente:  la o nouă autoapelare a funcţiei f. în acest caz. Algoritmul va folosi o stivă ST şi o variabilă numită k. ca în cazul mai general al listelor. se urcă în stivă (k se incrementează cu 1) şi se pune noua valoare. Exemple 1. se coboară în stivă.x < 12 Vom începe prin a studia modul de calcul al funcţiei pentru x=15 şi x=8: f(15)=14. Se citeşte x∈Z. în comparaţie cu alocarea dinamică înlănţuită este dat de faptul că numărul de noduri care pot fi memorate la un moment dat este mai mic . nu respectăm principiul stivei. chiar dacă nu este ultimul introdus? Nimeni nu ne opreşte.  În cazul stivei.depinde de gradul de ocupare al segmentului de date. Se cere programul pentru calculul funcţiei: x − 1.  algoritmul se încheie când se ajunge în stivă la nivelul 0. de regulă.134 Capitolul 5. Structuri de date  Pe un anumit nivel se reţine. . însă este posibil să avem mai multe informaţii.  Este posibil să vă întrebaţi: de ce nu putem accesa un element al stivei. ci numai în vârful ei. x ≥ 12 F(x) =  F(F(x + 2)). doar că. pentru că nu se fac operaţii de inserare sau ştergere în interiorul stivei. alocarea secvenţială nu prezintă mari dezavantaje.  în situaţia în care. Funcţia Manna-Pnueli. pentru extragere. pentru valoarea aflată pe nivelul k. se poate calcula funcţia. Singurul dezavantaj. punându-se pe acest nivel noua valoare. o singură informaţie (literă sau cifră). ce indică în permanenţă vârful stivei. f(8)=f(f(10))=f(f(f(12)))=f(f(11))=f(f(f(13)))=f(f(12))=f(11) =f(f(13))=f(12)=11.  În literatura de specialitate veţi întâlni termenul PUSH pentru operaţia de adăugare în stivă a unei înregistrări şi POP.

st[1]-1) cout<<"n="<<st[1]-1. prezentăm schematic funcţionarea sa: 12 13 10 10 11 11 8 8 8 8 8 12 13 8 11 11 12 f=11 Programul este prezentat în continuare: Varianta Pascal Varianta C++ var st:array [1. end. dar exemplul a fost dat în alt scop.Manual de informatică pentru clasa a XI-a 135 Pentru exemplul dat.. k=1. n. main() begin { cout<<"n=". int st[100]. while k>0 do st[1]=n. funcţia ia valoarea 11. definită pe produsul cartezian N×N. Se dă funcţia de mai jos. k:=1. readln(n).1). n) = Ack(m − 1. n + 1. Să se calculeze Ack(m. st[1]:=n. } Se poate demonstra uşor că pentru valori mai mici decât 12. else begin } k:=k-1. if (st[k]<12) st[k]:=st[k-1]+2 { k++. end st[k]=st[k-1]+2.h> integer.k. Observaţia simplifică mult programul. Se citesc m şi n. cin>>n. then st[k]:=st[k+1]-1 if (k>0) st[k]=st[k+1]-1.n. altfel  . else if k>0 { k--. n − 1)).n). n=0 Ack(m − 1. Ack(m.100] of #include <iostream. m=0  Ack(m. if st[k]<12 then begin while (k>0) k:=k+1. 2. end. write('n=').k:integer. Funcţia lui Ackermann. } writeln('f='.

4)=5.1)=ack(1.  în situaţia în care m=0. Structuri de date Pentru a elabora algoritmul. În continuare. Pe nivelul k al stivei se reţin valorile curente m şi n.1))))=ack(0.1)))=ack(0..ack(0.ack(0.0))=ack(1. punând în locul lui m valoarea m-1.ack(0. În funcţie de valorile acestora se procedează astfel:  pentru m şi n diferite de 0.2)) = =ack(0. este necesar un nou calcul de funcţie.ack(1.1): 1 0 0 1 2 0 1 1 1 1 1 1 2 1 2 1 2 1 2 1 2 1 1 0 1 1 1 1 0 2 1 3 1 2 1 2 2 1 1 3 1 2 1 3 1 3 0 1 1 1 0 2 1 2 1 2 0 3 1 3 1 3 1 3 0 4 ack(2.ack(0. valorile m şi n se reţin la nivelul 1.ack(0. Pentru calculul acestei funcţii.ack(1.  pentru cazul n=0.136 Capitolul 5. se rămâne pe acelaşi nivel în stivă.1))=ack(1.ack(0.ack(1. studiem un exemplu numeric: ack(2. funcţia se poate calcula.ack(0.1)=5.ack(1. se coboară în stivă şi se înlocuieşte valoarea lui m cu m-1.ack(0.0))))= =ack(0.ack(0.ack(0. folosim o stivă dublă. valoarea lui n cu valoarea calculată anterior.ack(1.1)))=ack(1.ack(0. Iniţial. caz în care se urcă în stivă şi pe noul nivel se pun argumentele m şi n-1.3)=ack(0. . iar în locul lui n. prezentăm grafic modul de funcţionare a algoritmului pentru exemplul ack(2.0)))= = ack(1.ack(0.ack(2.ack(0.2)))= ack(0.2))=ack(1. valoarea 1. ST.3))=ack(0.

4). De exemplu. end } else begin k:=k-1..Manual de informatică pentru clasa a XI-a 137 Varianta Pascal Varianta C++ type stiva=array [1.10000. { int m. } st[k..'.')='.k:integer. cin>>m. st[k][0]=m.2]=0 t[k][1]=st[k+1][1]+1. cout<<"m=". main() var st:stiva.1]-1. st[k][1]=n. nu veţi reuşi să calculaţi Ack(4.1]:=st[k.2]:=st[k+1. cout<<"ac("<<m<<'. write('n='). st[1. write('m='). st[k. while (k>0) readln(n).. writeln('ac('.2]:=n. of integer. st[k][0]=st[k-1][0]. end if (k>0) else { st[k][0]=st[k][0]-1. { k++. readln(m).'<< st[k.k. cin>>n.1]-1.2] int st[10000][2]. else st[k. if (st[k][0] && st[k][1]) k:=1.1]:=st[k.2]+1) end..1]:=m. cout<<"n=".2]+1 end end.m. then } begin } st[k. .n.2]<>0) if (!st[k][1]) then { st[k][0]=st[k][0]-1.1]<>0) and else (st[k.n. st[k][1]=st[k-1][1]-1.'. if st[k. Încercaţi. begin k=1.1]. Funcţia lui Ackermann ia valori extrem de mari pentru valori mici ale lui m şi n.n. k:=k+1. st[k.2]-1 { k--. begin st[k][1]=1. m.2]:=1 n<<")="<<st[1][1]+1.1]:=st[k-1. if k>0 then begin st[k. while k>0 do } if (st[k.1.2]:=st[k-1. st[k.

4.138 Capitolul 5. Structura de tip coadă Definiţia 5.4. deoarece în această situaţie. O coadă este o listă pentru care toate inserările sunt făcute la unul din capete. Fiecare element al vectorului (nod) reţine un număr natural. Introducem în coadă. Coada funcţionează pe principiul FIFO (First In First Out) - "primul intrat primul ieşit". . Probleme propuse 1. 2. Care dintre structurile de mai jos nu este liniară? a) b) c) d) 2. Să presupunem că simulăm o coadă cu ajutorul unui vector cu zece componente. 4. numerele 1. un număr natural citit de la tastatură. Este cu totul nerecomandabilă alocarea secvenţială a cozii. Structuri de date 5. modificările) la celălalt capăt. pe poziţia p. toate ştergerile (consultările. Se cere să se scrie un subprogram care inserează în listă. care reţin numere întregi. coada va arăta în felul următor: 2 3 4 5 Scoatem din coadă pe 2 şi introducem pe 6: 3 4 5 6 Se observă acest fenomen de "migraţie". 3. pe rând. Un vector reţine pe poziţiile de la 1 la k nodurile unei liste liniare. 1 2 3 4 Dacă scoatem din coadă pe 1 şi introducem în coadă pe 5. are loc un fenomen de migraţie a datelor către ultimele componente ale vectorului (cele de indice mare).

V=(1. Creaţi o listă liniară cu n noduri alocată secvenţial. Scrieţi un set de subprograme care creează şi gestionează o listă liniară simplu înlănţuită. în această ordine. scrieţi subprograme de inserare şi ştergere a unui nod al listei create. Numărul citit este 5. c) Subprogramul va avea parametrul k transmis prin referinţă. 4. nodul 2 numărul 2. 1≤p≤k. Sortaţi n numere naturale utilizând algoritmul de sortare prin inserţie. . Observaţie: aceasta este o modalitate de a genera aleator o permutare a primelor n numere naturale.d.3). primele în listă. mai mare ca 1 şi mai mic decât n. V=(1. Pentru problema anterioară. După rulare trebuie să avem: k=4.a. nu se efectuează deplasări către stânga. Afişaţi lista după p astfel de inversări. câmpul de adresă al ultimului nod reţine adresa primului nod. 5.2. 9. Un vector reţine pe poziţiile de la 1 la k nodurile unei liste liniare. 3.3). 6.Manual de informatică pentru clasa a XI-a 139 Exemplu: k=3. Se cere să se scrie un subprogram care şterge din listă nodul aflat pe poziţia p. V=(1. pentru o listă liniară alocată înlănţuit. în această ordine. Care sunt avantajele utilizării listei alocate înlănţuit în cazul algoritmului de sortare prin inserţie? 8. Fiecare element al vectorului (nod) reţine un număr natural. b) Subprogramul va avea parametrul k transmis prin valoare. urmate de nodurile de la 1 la k-1. Implementarea listelor alocate simplu înlănţuit. Exemplu: k=3. Scrieţi un set de subprograme care creează şi gestionează o listă liniară simplu înlănţuită. După rulare trebuie să avem: k=2. Lucrare în colectiv. Implementarea listelor alocate simplu înlănţuit.5. Nodul 1 va conţine numărul 1.m. ş.2. p=2. 7. De asemenea. Programul va utiliza o listă liniară alocată înlănţuit. alocată secvenţial (prin utilizarea unui vector). alocată înlănţuit. Lucrare în colectiv.3).2. Nodurile de la k la n vor fi. d) Dacă k=p. În cazul în care. p=2. care dintre afirmaţiile de mai jos este falsă? a) Pentru a şterge un nod aflat pe poziţia p sunt necesare k-p deplasări spre stânga ale conţinuturilor celorlalte noduri.3). Se generează aleator un număr natural k. V=(1. se obţine o listă circulară: Creaţi o listă circulară în care fiecare nod reţine un număr natural.

d) stiva are un singur nivel. N. N. Să se scrie un program care:  citeşte M. care se referă la o stivă. L şi-l determină pe K. Coada va fi implementată prin utilizarea alocării înlănţuite. În figura alăturată avem 4 vagoane. Se presupune că pe linia C încap toate cele 4 vagoane şi că un vagon aflat pe linia C poate fi mutat numai pe linia B. Persoana la care s-a oprit numărătoarea este eliminată din cerc. Push 3. Procedeul se repetă până când rămâne un jucător L. Se citesc n valori numere naturale. Push 4. Arbitrul repetă procedeul începând cu persoana următoare celei eliminate. să se afişeze mesajul ‘Imposibil’. Structuri de date 10. 14. prin mutări succesive de vagoane. Răspunsuri 1. Se cere ca. dintre şirurile de vagoane de mai jos nu poate fi obţinut? a) 1 2 3 4. Dacă acest şir nu se poate obţine. c) 3 4 2 1. Pop. unde Push a este operaţia prin care se pune în stivă valoarea a. iar Pop este operaţia prin care se extrage din stivă. d) 4. Urmăriţi secvenţa următoare. Pop Care din afirmaţiile de mai jos nu este adevărată după executarea secvenţei de mai sus? a) Din stivă au fost extrase. d) 3 4 1 2. pentru problema de mai sus. c) stiva conţine valoarea 1. Scrieţi un set de subprograme care gestionează o coadă. b) stiva este vidă. b) 14. valorile: 2. b) 4 3 2 1. b) 11. desigur. să avem pe linia B vagoanele într-o anumită ordine.  citeşte M. 3. Arbitrul. 12.3.140 Capitolul 5. Se cere să se afişeze şirul mutărilor de tip Push nr_vagon şi Pop nr_vagon (aţi recunoscut. în momentul în care se ajunge în situaţia unei mutări imposibile. Care Figura 5. Push 1. Se cere ca. o structură de tip stivă) prin care se poate obţine şirul vagoanelor de pe lina B. numerotate cu 1. Să se scrie un program care. Push 2. d) . vedeţi problema anterioară. prin 4 utilizarea unei stive. valorile citite 2 3 1 să se afişeze în ordine inversă. citeşte ca date de intrare şirul vagoanelor aflate pe linia A şi şirul vagoanelor care trebuie obţinut pe linia B. Pop. în care C primul vagon este cel aflat la ieşirea de pe linia B. 16.2.1. K şi-l determină pe L. Scrieţi subprogramele care implementează o stivă. 4. începând de la un jucător K numără până la M. 11.4. 15. în această ordine. B A 13. În jurul arbitrului sunt aşezaţi N jucători numerotaţi în sens orar.

autoapelul se realizează prin apelul procedurii respective. numerele 7. Modul în care se realizează autoapelul În acest paragraf vom învăţa modul în care subprogramele se autoapelează. Apelul se face la fel ca în cazul în care procedura este apelată din exterior. Recursivitatea a apărut din necesităţi practice date de transcrierea directă a formulelor matematice recursive. pe rânduri separate. Ea constă în posibilitatea ca un subprogram să se autoapeleze. 1: .nu permiteau scrierea programelor recursive. În timp. Oricare ar fi tipul subprogramului. Multe din limbajele de programare evoluate şi mult utilizate .1. 6. 6. Definiţia 6. Mecanismul recursivităţii şi modul cum se gândeşte un algoritm recursiv vor fi prezentate în paragrafele următoare. acest mecanism a fost extins. Procedura prezentată în continuare este recursivă şi afişează. Realizarea autoapelului în limbajul Pascal După cum ştim. Utilizarea frecventă a recursivităţii s-a făcut după anii 80.. fiind utilizat în elaborarea multor algoritmi. Recursivitatea este un mecanism general de elaborare a programelor. . Prezentare generală Recursivitatea este una din noţiunile fundamentale ale informaticii.2. în limbajul Pascal subprogramele sunt de două feluri: proceduri şi funcţii.Fortran. 141 Capitolul 6 Introducere în recursivitate 6..2.  În cazul procedurilor..1. Cobol . 6. însă modul în care se realizează autotransferul diferă. acesta se poate autoapela. din interiorul ei.1.

end end. begin exemplu(7).  În cazul funcţiilor de tip void. Ea afişează.. exemplu (n-1).+7: function suma(n:integer):integer.2.. aceasta se poate autoapela. begin suma:=0. end. begin writeln(suma(7)). pe rânduri separate. .. 6. autoapelul se realizează printr-o operaţie de atribuire. Funcţia următoare calculează suma 1+2+. } } main() { exemplu(7). Introducere în recursivitate procedure exemplu(n:integer). 1: #include <iostream.  În cazul funcţiilor. din interiorul ei. în C++ funcţiile pot fi de tipul void sau de un alt tip.. autoapelul se realizează prin apelul funcţiei respective. exemplu(n-1). end. end.2. însă modul în care se realizează autotransferul. begin if n<>0 then begin writeln(n). numerele 7.. Oricare ar fi tipul funcţiei.142 Capitolul 6. Realizarea autoapelului în limbajul C++ După cum ştim. if n<>0 then suma:=n+suma(n-1). Apelul se face la fel ca în cazul în care funcţia este apelată din exterior. diferă. funcţiile returnează o anumită valoare. operaţie prin care numele funcţiei trebuie să figureze în partea dreaptă a operatorului de atribuire. 6. } . În acest din urmă caz. Funcţia de mai jos este recursivă.h> void exemplu(int n) { if (n!=0) { cout<<n<<endl.

. cout<<"n=". integer.3. pentru a calcula 3!. { int n. cout<<fact(n). Mecanismul recursivităţii  Problemă. Să se calculeze recursiv n!. } 6. A) Pentru a scrie o funcţie recursivă care efectuează acelaşi calcul. procedăm astfel: 3!=fact(3)=3×fact(2)=3×2×fact(1)=3×2×1×fact(0)=3×2×1×1=6. readln(n). Ea este de forma return expresie. autoapelul se realizează prin instrucţiunea return. Funcţia recursivă fact nu face altceva decât să transcrie definiţia recursivă prezentată anterior: Varianta Pascal Varianta C++ var n:integer.h> int fact(int n) function fact(n:integer): { if (!n) return 1. dar în expresia respectivă trebuie să intre şi funcţia care se autoapelează. altfel De exemplu. begin cin>>n. vom porni de la o definiţie recursivă a lui n!. Funcţia următoare calculează suma 1+2+. n=0 n! = fact(n) =  cu n ∈ N n ⋅ fact(n − 1). begin } if n=0 then fact:=1 else fact:=n*fact(n-1) main() end. . #include <iostream. Aceasta este: 1. write('n='). } main() { cout<<suma(7). } writeln(fact(n)) end. else return n*fact(n-1).h> int suma (int n) { if (n!=0) return n+suma(n-1)..Manual de informatică pentru clasa a XI-a 143  În cazul funcţiilor care nu sunt de tipul void.+7: #include <iostream.

subprogramele folosesc o zonă de memorie numită stivă (mai exact. La primul apel al funcţiei fact se creează în n 3 segmentul de stivă o variabilă numită n. În capitolul anterior am studiat proprietăţile structurii numită stivă.valoarea parametrului formal. tot în stivă se rezervă spaţiu pentru variabilele locale (cele declarate în subprogram). n 3 n 1 Funcţia se autoapelează. Parametrul n ia valoarea 1. n 3 . deci în stivă se depune doar parametrul n. De această dată n 2 parametrul n ia valoarea 2. se depun din nou parametrii transmişi şi se rezervă un nou spaţiu pentru variabilele locale. parametrii transmişi şi memoraţi în stivă sunt variabile. De asemenea.  La apelul subprogramului se depun în stivă.  Memorarea parametrilor transmişi se face în ordinea în care aceştia figurează în antet: de la stânga la dreapta. conţine secvenţe prin care se gestionează segmentul de stivă. În stivă se creează un nou nivel. se memorează valoarea transmisă. Această funcţie nu are variabile locale. Funcţia se autoapelează. Numele lor este cel din lista parametrilor formali. În stivă se creează un nou nivel. această zonă se numeşte segment de stivă). prezentăm grafic modul în care funcţionează recursivitatea în cazul funcţiei fact a programului anterior. care reţine 3 .  În cadrul subprogramului. Exact aceleaşi proprietăţi le are şi segmentul de stivă. care reţine n n 2 cu valoarea 1. codul în limbaj maşină. în ordine. Introducere în recursivitate Care este mecanismul prin care subprogramele se pot autoapela? Să ne amintim modul în care subprogramele memorează parametrii transmişi.  În cazul în care subprogramul se autoapelează pe un al doilea nivel.  Pentru parametrii transmişi prin valoare.  Pentru memorarea parametrilor. parametrii transmişi. Singura diferenţă este dată de faptul că gestiunea segmentului de stivă este făcută automat. obţinut în urma compilării. iar pentru cei transmişi prin referinţă se memorează adresa variabilei. Mai exact. În continuare.144 Capitolul 6. care reţine n cu valoarea 2. Acesta este un prim nivel al stivei.

n. n 3 n 1 Pe nivelul 3 se efectuează calculul 1*1=1. } fact(val+1.n. B) Mai puţin eficient. readln(n).prod). end. prod) begin { if (val<=n) if val<=n then { prod*=val. În stivă se creează un nou nivel. n 3 Se revine apoi în main(). n 3 Pe nivelul 1 se efectuează calculul 3*2=6. } writeln(p). Se revine pe nivelul 3. prod:=prod*val.n.int& var prod:integer). #include <iostream. fact=2 Se revine apoi pe nivelul 1. write ('n=').n. begin fact(val+1. p:=1.int n. Parametrul n ia valoarea fact=1 0.prod) } end. cin>>n. fact(1. main() { int val. end. fact(1. n! se poate calcula şi printr-o funcţie ca în exemplul următor: Varianta Pascal Varianta C++ var val. autoapelul n 2 nu mai are loc. cout<<p. begin cout<<"n=".p).n.Manual de informatică pentru clasa a XI-a 145 n 0 Funcţia se autoapelează.h> procedure fact(val. adică s-a calculat 3!. care reţine n n 1 cu valoarea 0. fact=6 Aici se afişează 6. Funcţia ia valoarea 1. 2 n 3 n 2 Pe nivelul 2 se efectuează calculul 2*1=2. void fact(int val.p:integer.p).n. fact=1 n Se revine apoi pe nivelul 2.p=1. .n:integer.

atât din motive didactice cât şi pentru a asigura independenţa subprogramului. Apoi. variabilele locale şi variabilele transmise prin referinţă. pentru n=3.se efectuează prod=prod*val. apoi în programul principal. am preferat varianta de a trece ca parametru variabila transmisă prin referinţă. apoi 1. Întrucât variabila p a fost transmisă prin referinţă. după care funcţia se autoapelează.. subprogramul lucrează cu datele aflate pe un anumit nivel al stivei pentru variabilele transmise prin valoare. se efectuează p=p*val. În exemplu. 3 3 Referinţă către p 2 3 Referinţă către p 2 1 3 Referinţă către p p val n prod Pentru că val este 3 . funcţia se autoapelează: 2 3 Referinţă către p 1 1 3 Referinţă către p p val n prod Pentru că val este 2 . deci p ia valoarea 6.mai mic sau egal cu 3 . Funcţia are trei parametri: doi transmişi prin valoare. Iată conţinutul variabilelor după primul apel: 1 1 3 Referinţă către p p val n prod Pentru că val este mai mic sau egal ca n se efectuează: prod=prod*val.se efectuează prod=prod*val. Introducere în recursivitate Să analizăm şi pentru acest exemplu modul în care se efectuează calculul. Dezavantajul este dat de faptul că stiva va fi încărcată suplimentar cu adresa acestei variabile. programul lucrează cu această variabilă. În concluzie.mai mic ca 3 . . Observaţii  După cum rezultă din aceste exemple. după care se revine pe nivelul 2.146 Capitolul 6. După cum ştim variabilele globale pot fi accesate din orice subprogram.  Există posibilitatea ca subprogramul să lucreze direct cu variabilele globale fără ca acestea să fie transmise prin referinţă.. în loc de prod. unul prin referinţă.

oricare ar fi ele.  Numeroasele exemple care urmează vă vor lămuri asupra celor afirmate. 3.. există posibilitatea ca segmentul de stivă să depăşească spaţiul alocat.. dar cu o modificare esenţială: adăugarea condiţiei de terminare. Observaţii  Un algoritm recursiv se elaborează utilizând acest tip de gândire. Ştiţi povestea cu săracul care a prins un peştişor de aur.a. constatăm că. Constatare. . ş. Wirth. în orice televizor se vede un televizor. A treia dorinţă a omului este să i se îndeplinească 3 dorinţe. caz în care programul se va termina cu eroare. în televizor se va vedea un televizor.  Recursivitatea presupune mai multă memorie în comparaţie cu iterativitatea. care se repetă la infinit. 6.m. întrucât algoritmul trebuie să fie finit.. peştişorul i-o îndeplineşte. Evident. Lăsând gluma la o parte.d. Cum gândim un algoritm recursiv ? Pentru a ne familiariza cu raţionamentul recursiv. ⇒ Starea tratată de subprogram se găseşte pe un anumit nivel al stivei (variabilele rezultate în urma transmiterii parametrilor şi variabilele locale ale subprogramului). Această gândire se aplică în elaborarea algoritmilor recursivi.  Nu întotdeauna alegerea unui algoritm recursiv reprezintă un avantaj. nu o gândire precum cea folosită până acum. 1. nu poate fi vorba de algoritm.  Pentru orice algoritm recursiv există unul iterativ care rezolvă aceeaşi problemă. ⇒ În elaborarea algoritmilor recursivi se aplică raţionamentul: ce se întâmplă la un nivel.. ⇒ Subprogramul care se autoapelează trebuie să conţină instrucţiunile corespunzătoare unui nivel. Anunţ. O cameră de luat vederi are în obiectiv un televizor care transmite imaginile primite de la cameră. o gândire recursivă exprimă concentrat o anumită stare. îi spune prima dorinţă.4. iar în acesta un televizor. Pe scurt. în general. peştişorul i-o îndeplineşte. În anumite restaurante am întâlnit anunţul: “Azi nu se fumează!”. se întâmplă la orice nivel. Omul îi dă drumul. Acesta îi spune omului că dacă îi dă drumul îi îndeplineşte 3 dorinţe. vom porni de la câteva exemple intuitive. îi spune a doua dorinţă.Manual de informatică pentru clasa a XI-a 147  În cazul unui număr mare de autoapelări. când am elaborat algoritmi iterativi. În absenţa acestei condiţii.. 2.

int manna (int x) begin { if (x>=12) return x-1. nu facem altceva decât să transcriem definiţia recursivă a funcţiei. Aplicaţii recursive 6. readln(x). Şi această aplicaţie a fost tratată iterativ. Algoritmul recursiv nu necesită comentarii. definită pe N×N. Ack(m. n) = Ack(m − 1. Această aplicaţie a fost tratată iterativ. În cazul tratării recursive. n=0 Ack(m − 1. În cazul tratării recursive. Se dă funcţia de mai jos. #include <iostream. abordarea recursivă prezintă avantajul scrierii concentrate. prin utilizarea stivei. nu facem altceva decât să transcriem definiţia recursivă a funcţiei.148 Capitolul 6.5. .n). Varianta Pascal Varianta C++ var x:integer. Se citesc numerele m şi n.x < 12  Rezolvare. Introducere în recursivitate 6. write(‘x=‘). cin>>x. m=0  Ack(m. prin utilizarea stivei.1. main() begin { cout<<"x=".  Aplicaţia 6. Se citeşte x∈Z.2.h> int x. În comparaţie cu abordarea iterativă. writeln(manna(x)) } end. Se cere programul pentru calculul funcţiei: x − 1.1. altfel   Rezolvare. if x>=12 else then manna:=x-1 return manna(manna(x+2)). Să se calculeze Ack(m. n + 1. function manna (x:integer):integer. x ≥ 12 F(x) =  F(F(x + 2)). n − 1)). Aplicaţii la care se transcrie o formulă recursivă  Aplicaţia 6.5.1). cout<<manna(x). else manna:=manna(manna(x+2)) } end.

} begin main() write(‘N=‘). #include <iostream. număr natural. end. function ack(m.n-1)). Şirul lui Fibonacci.  Rezolvare.1) else else return ack:=ack(m-1. write(‘n=‘). { cout<<"n=". readln(n). function fib (n:integer):integer. end. } În comparaţie cu abordarea iterativă.Ack(m.  Aplicaţia 6.n).1). abordarea recursivă prezintă avantajul scrierii concentrate şi acela al scutirii programatorului de un efort suplimentar în elaborarea algoritmului. n =1  U n −1 + U n − 2 altfel Se citeşte n.n:integer. writeln(fib(n)) cout<<fib(n). cin>>n.n:integer):integer. int fib (int n) begin { if (!n) return 0.h> int n.Manual de informatică pentru clasa a XI-a 149 Varianta Pascal Varianta C++ var m. cin>>m.ack(m. } . Funcţia fib transcrie definiţia recursivă: Varianta Pascal Varianta C++ var n:integer. { cout<<"m=". cin>>n.n-1)) Ack(m-1. Să se calculeze Un. writeln(ack(m. int Ack(int m. if n=0 then ack:=ack(m-1. end. n=0  U n = 1. #include <iostream. cout<<"n=". } begin main() write(‘m=‘). end. if m=0 else then ack:=n+1 if (!n) else return Ack(m-1. if n=0 then fib:=0 else else if (n==1) return 1. readln(m). Se consideră şirul definit astfel:  0. readln(n).h> int m.3. if n=1 then fib:=1 else else fib:=fib(n-1)+fib(n-2) return fib(n-1)+fib(n-2).n.n)) cout<<Ack(m.int n) begin { if (!m) return n+1.

h> begin write('n='). După ce calculăm u99. Să ne imaginăm cum funcţionează această funcţie. b) = cmmdc(a − b.i<=n. Prezentăm varianta iterativă. f0:=f1.f0. Acest lucru este extrem de ineficient pentru valori mari ale lui n (ex.f2. Se dau două numere naturale a şi b. { int n.150 Capitolul 6. if n=0 then writeln(f0) if (!n) cout<<f0. Introducere în recursivitate În această situaţie. cin>>n. readln(n).i:integer. f2:=f0+f1. f1:=1. Parametrii acestor funcţii sunt depuşi în stivă. reluăm calculele pentru u98. apoi se reia calculul pentru fib(n-2). b − a). n=100). main() f0:=0. } writeln(f2) } end end. #include <iostream.f2. cout<<"n=".f1=1. Rulaţi programul pentru o valoare mai mare a lui n şi . a=b  cmmdc(a.. Procedeul continuă până când este calculat fib(n-1).  Rezolvare.. a < b  .f1.4.. b). } f1:=f2 cout<<f2. Varianta Pascal Varianta C++ var n. else else if n=1 then writeln(f1) if (n==1) cout<<f1. Se cere să se calculeze cel mai mare divizor comun al lor. Utilizăm o definiţie recursivă a celui mai mare divizor comun pentru două numere naturale a şi b: a. begin f0=f1.f0=0. end. care nu necesită comentarii. f1=f2. O astfel de recursivitate se numeşte recursivitate în cascadă.  Aplicaţia 6. Pentru calculul lui fib(n) este necesar să se cunoască fib(n-1) şi fib(n-2). Pentru calculul lui u99 se calculează u98 şi u97. aşteptaţi.a > b cmmdc(a. Cât de simplu se rezolvă problema iterativ şi cât este de rapidă această metodă. este corect să se folosească un program care calculează Un iterativ.. Se calculează u100 ca sumă între u99 şi u98. else else begin { for (int i=2.i++) for i:=2 to n do { f2=f0+f1.

s=0. cin>>b.b-a).b. } Pentru această problemă. #include <iostream.b).b-a) else return cmmdc(a. end. write('a='). Să se scrie o funcţie recursivă pentru a calcula suma cifrelor unui număr natural. cout<<"a=".b)) cout<<"b=". while n<>0 do begin while (n) s:=s+n mod 10. } Rezolvăm aceeaşi problemă iterativ (utilizând definiţia de mai sus): Varianta Pascal Varianta C++ var a. prezentăm varianta iterativă: Varianta Pascal Varianta C++ var n.b.a) cout<<"cmmdc="<<a. readln(b). cin>>a. readln(n). writeln('cmmdc='.h> begin main() write('n='). { int n. cout<<cmmdc(a.b:integer. end.s) } end. . int cmmdc (int a. writeln(cmmdc(a. writeln('s='. #include <iostream. main() write(‘b=‘). este indiferent ce variantă de rezolvare se alege. end. Iniţial. cin>>a. n/=10. readln(b).b:integer.  Rezolvare.b).s:integer.int b) begin { if (a==b) return a. readln(a). while a<>b do while (a!=b) if a>b then a:=a-b if (a>b) a=a-b.5. } begin write(‘a=‘). { cout<<"a=".  Aplicaţia 6. } end. s:=0.h> int a. cin>>b. else b=b-a. else cmmdc:=cmmdc(a. cin>>n.b:integer):integer.Manual de informatică pentru clasa a XI-a 151 Această definiţie este transcrisă în funcţia recursivă cmmdc: Varianta Pascal Varianta C++ var a. cout<<"b=". if a=b then cmmdc:=a else else if a>b if (a>b) then cmmdc:=cmmdc(a-b. readln(a). cout<<"n=". cout<<"s="<<s.b) return cmmdc(a-b. else b:=b-a. n:=n div 10. function cmmdc (a.h> main() begin { int a. { s+=n%10. write('b='). #include <iostream.

writeln(s(n)) cout<<s(n). end. Se consideră şirurile definite recurent astfel: a0=a. cin>>n. } .h> int n. function bn(n:integer):real. #include <math. b n = a n−1b n−1 . } begin main() write('n='). Relaţiile sunt scrise prin utilizarea operatorilor din Pascal (stânga) şi C++ (dreapta): 0. then an:=a else else an:=(an(n-1+bn(n-1))/2 return (an(n-1)+bn(n-1))/2. b0=b. Această idee foloseşte pentru a găsi o relaţie de recurenţă.  Aplicaţia 6. int n. 2 Să se scrie un program care să citească a.6. function an(n:integer):real.h> n:integer. este vorba de recursivitate şi cum acesta nu s-a realizat direct . { cout<<"n=".b. end. forward.152 Capitolul 6. Varianta Pascal Varianta C++ #include <iostream. else s:=n mod 10 + s(n div 10) else return n%10 + s(n/10).h> var a. altfel Programul de mai jos calculează suma utilizând relaţia prezentată: Varianta Pascal Varianta C++ var n:integer. end. n=0 0. n=0 S(n) =  S(n) =  n mod 10+ S(n div 10).o astfel de recursivitate se numeşte indirectă. readln(n). function s(n:integer):integer. altfel n % 10+ S(n / 10). #include <iostream. a.b:real. double a. necesară elaborării variantei recursive. b şi n şi să se calculeze an şi bn.b>0: a n−1 + b n−1 an = . Introducere în recursivitate Reţinem ideea: se izolează ultima cifră.ca în exemplele anterioare . } Există posibilitatea ca un subprogram să se autoapeleze prin intermediul altui subprogram. Din moment ce s-a realizat autoapelul. double bn(int n). iar lui n i se atribuie câtul întreg dintre vechea valoare şi 10. begin int s(int n) if n=0 then s:=0 { if (!n) return 0. begin double an(int n) if n=0 { if (!n) return a.

read(a). readln(b). Raţionamentul este suficient pentru a scrie programul. cout<<endl. { cout<<"a=".h> var a:char. cout<<"n=". if n=0 then bn:=b else return else bn:=sqrt(an(n-1)*bn(n-1) sqrt(an(n-1)*bn(n-1)). if (a!='0') inv(). Sfârşitul şirului este marcat de caracterul “0”. if a<>'0' then inv.’ ‘. cout<<"b=". 6. readln(n). cin>>a. write(‘n=‘).Manual de informatică pentru clasa a XI-a 153 function bn(n:integer):real. Să se scrie o funcţie recursivă care citeşte caractere şi le afişează în ordinea inversă citirii. cin>>a. vom gândi funcţia recursivă astfel:  se citeşte un caracter. Conform principiului. } begin main() write(‘a=‘). Modificaţi programul astfel încât caracterul 0 . #include <iostream. se reapelează funcţia. cin>>b. write(a) } end. readln(a). writeln. Aplicaţii la care nu dispunem de o formulă de recurenţă  Aplicaţia 6. ce se întâmplă la un nivel se întâmplă la orice nivel. bn(n):5:10) } end. . main() begin { inv(). writeln(an(n):5:10.  Exerciţiu. write(‘b=‘). inv. double bn (int n) begin { if (!n) return b.care marchează sfârşitul şirului . void inv() begin { char a.2.5.  dacă este diferit de 0. end. cout<<a.să nu fie tipărit. cin>>n. Varianta Pascal Varianta C++ procedure inv. cout<<an(n)<<' '<<bn(n).  Rezolvare.7.  se tipăreşte caracterul. } end.

an-1.3}.  din fiecare permutare a mulţimii {1. Se dă mulţimea {1. Introducere în recursivitate  Aplicaţia 6.3. se reţine restul. {1. . . Practic. { int rest=n%b.b:integer). write('n='). observăm următoarele:  mulţimea cu un singur element {1} are o singură permutare: {1}..b.3..b:integer..b).. până când câtul este mai mic decât împărţitorul.a2.2}.. Rezultatul se obţine prin scrierea în ordine inversă a resturilor obţinute...an-1. #include <iostream.a3}.an-1. {3... readln(b). din baza 10 în baza k (1<k<10).n.a2}.a1}.1. cout<<rest.2.. Exemplu: pentru n=3 avem: {3.2....b). rest:=n mod b. {2...n} şi se cer toate permutările acesteia...2.2. cout<<"baza=".a2.a2.. Să se scrie o funcţie recursivă pentru a transforma un număr natural n..n-1} ({a1.. cin>>n.154 Capitolul 6. end.1.. { cout<<"n=".an-1. writeln.  Rezolvare... tipărirea restului se face după autoapel (ca şi la problema anterioară).1}. Numărul se împarte la k.a3..n. begin if (n>=b) transform(n/b. readln(n). se reţine restul..9.a3.a3.2}.b).. cin>>b...n}: {n. Să ne amintim algoritmul clasic de trecere din baza 10 în baza k. void transform(int n.int b) var rest:integer.1}.a2...2.  Rezolvare... În vederea rezolvării problemei..3}.8. transform(n.h> int n. {a1.. {a1. } write('baza='). write(rest) main() end. se obţin următoarele permutări ale mulţimii {1. {2.b). Varianta Pascal Varianta C++ var n.an-1}). {a1. begin transform(n.  Aplicaţia 6. {1. câtul se împarte la k. procedure transform(n... if n>b } then transform(n div b.n}.

} begin write('n='). } c:=p[i]. begin p[i]=p[k]. p[i]=p[k]. cout<<endl.n:integer. priviţi reprezentarea din 321 figura alăturată: 21 231 213 1 312 12 132 Figura 6. urmată de o reapelare a funcţiei pentru valoarea k+1. end cin>>n. { for (int i=1.n. for i:=1 to n do write(p[i]).p) end.i++) begin cout<<p[i].int n.n. end. Varianta Pascal Varianta C++ var p: array [1. int p[10]) var p:vector). } permut(k+1. void tipar() var i:integer. writeln } end. c:=p[i]. then tipar for (i=1.i++) else { c=p[i].n. for i:=1 to k do permut(k+1.n. readln(n).n. p[k]:=c main() end { cout<<"n=". if (k==n+1) tipar().i:integer.1.h> n. procedure permut(k.Manual de informatică pentru clasa a XI-a 155 Pentru n=3.. p[i]:=p[k]. var i. begin else if k=n+1 { p[k]=k.8] of integer.c. interschimbăm din nou ai cu aj. Cazul în care n=3 123 Avem în vedere posibilitatea de revenire la situaţia iniţială: după ce am operat o interschimbare a elementelor ai şi aj. begin c=p[i].i<=k.i<=n. } p[i]:=p[k]. permut(1. p[k]=c. p[k]=c. { int i.j. int p[10]. #include <iostream. void permut(int k. p[k]:=k.p).p).p). procedure tipar. p[k]:=c.c:integer. permut(1. .

Aceasta are ca scop evitarea testului de ieşire din matrice. • în caz contrar.1). care se autoape- lează. Iniţial. Algoritmul Fill.1). matricea trebuie să arate astfel: 1 1 1 0   1 1 1 1 A= 1 1 1 1   1 0 0 0   Algoritmul se dovedeşte extrem de util în colorarea unei suprafeţe închise atunci când sunt cunoscute coordonatele unui punct situat în interiorul ei.3) ale unui punct situat în interiorul acestei suprafeţe. A(2. După executarea programului. iar funcţia se autoapelează pentru fiecare dintre elementele învecinate (sus. • în caz afirmativ.y)) are valoarea 0.156 Capitolul 6. semnificând un punct din interiorul acestei suprafeţe. jos. matricea se bordează cu două linii şi două coloane ce conţin elemente care au valoarea 1. stânga).1).2).3). Fie matricea de mai jos: 0 1 1 0   0 0 0 1 A= 0 1 1 1   1 0   0 0 Suprafaţa închisă este dată de elementele A(1. se dau coordonatele x şi y ale unui element al matricei. acesta ia valoarea 1. se iese din funcţie. De asemenea. A(2. dreapta. Introducere în recursivitate  Aplicaţia 6. A(2. Pentru rezolvare se foloseşte funcţia Scriu(). Acest algoritm este cunoscut şi sub denumirea de algoritmul FILL. Funcţia Scriu() funcţionează astfel: • testează dacă elementul matricei la care s-a ajuns (de coordonate (x. Programul este prezentat în continuare: .  Rezolvare.10. A(3. Valorile 1 delimitează o anumită suprafaţă închisă în cadrul matricei (elementele aparţinând acestei suprafeţe sunt marcate cu 0). Se dă o matrice binară. Considerăm coordonatele (2.

.h> var a:matrice: array int a[10][10].a).j]) a[m+1][i]=1. scriu(x.i. } for i:=1 to m do scriu(x. begin scriu(x+1.y.i<=m.y. cin>>y..i. scriu(x+1. for j:=1 to n do } begin for (i=1. for (i=1. { a[0][i]=1.y+1. { cout<<"a["<<i<<'. begin cout<<endl<<endl. cout<<"N=".i<=m. for i:=1 to m do cin>>a[i][j].y]=0 then scriu(x.j.a).a).j.y.0]:=1.i++) a[0. } scriu(x.i<=m.9. { if (!a[x][y]) var a:matrice).y.j]). scriu(x. } for i:=1 to m do cout<<"X=". if a[x. begin cout<<"Y=".i]:=1.a).x. for (i=1.a).i++) write('a['.n. void scriu (int x.j.Manual de informatică pentru clasa a XI-a 157 Varianta Pascal Varianta C++ program umplere.9] of integer.a) main() end { cout<<"M=".' write('N='). i.j++) writeln cout<<a[i][j].m.y-1.'. end.y.j++) write('M=').i++) a[i. readln(m). #include <iostream. a[x.int y.j<=n.y:integer. } for i:=1 to n do begin for (i=1.y]:=1.j<=n. write('Y='). cin>>m. write('X='). end.i<=n. cout<<a[i][j]. readln(a[i. begin scriu(x-1.a). scriu(x.y:integer.y. { a[i][0]=1.n+1]:=1 { for (j=1.i]:=1 a[i][n+1]=1. cout<<endl.m. { a[x][y]=1.i<=m. } scriu(x-1. end. cin>>x. readln(n). readln(x).y+1.a).a).y.'.0. for j:=1 to n do for (i=1. <<j<<"]=". int a[10][10]) procedure scriu (x.y-1. cin>>n.x.j++) end.j<=n. [0..i++) begin for (j=1. end.j]). a[i. { for (j=1.i++) write(a[i. writeln end end. } writeln. a[m+1.a). for i:=1 to m do begin for j:=1 to n do write(a[i. readln(y).n.']='). } writeln. cout<<endl.

a[x. compact(x-1.j.y.y-1. compact(x-1. Problema fotografiei (aplicaţie Fill).y]:=0.y+1. } end } end. x. compact(x. procedure compact void compact(int x.y+1.a).m. se testează dacă mai există elemente cu valoarea ”1” în matrice. compact(x.y]=1 then begin { a[x][y]=0. 1 1 1 1   1 1   1 1 iar în matricea de mai jos este reprezentat un singur obiect: 0 1 1 0   0 0 0 1 A= .y:integer. se poate trage concluzia că în fotografie aveam iniţial mai multe obiecte (altfel. Se apelează apoi funcţia compact() care are rolul de a marca cu 0 toate elementele matricei care aparţin acestui prim obiect identificat.a). int a[10][10]) begin { if (a[x][y]) if a[x.y. compact(x+1. int a[10][10]. Ea înfăţişează unul sau mai multe obiecte.n. compact(x+1.y. La revenire.a).x. var a:matrice). fotografia conţinea un singur obiect). compact(x. compact(x-1. compact(x+1. Introducere în recursivitate  Aplicaţia 6.a). .a).n.m.a).a).gasit. 0 1 1 1   1 0 0 0   Ca şi în problemele anterioare.int y.9] of #include <iostream.h> integer.a). În programul principal se citeşte matricea şi se caută primul element ”1” printre elementele acesteia. În matricea următoare sunt reprezentate două obiecte: 1 1 0 0   0 0 0 1 A= .. compact(x-1. aceasta este bordată cu linii şi coloane având valoarea ”0”.a).a).y-1. compact(x-1.j.y+1.11.y-1.y-1. Varianta Pascal Varianta C++ var a: array [0.a).a).0. compact(x+1. pentru a evita testul ieşirii din matrice.y.y+1. O fotografie alb-negru este reprezentată sub forma unei matrice binare. Porţiunile corespunzătoare obiectului (sau obiectelor) în matrice au valoarea ”1”.y.y:integer.9. compact(x-1. i. compact(x.a).y+1.a). i. dar aici căutarea se face pe 8 direcţii.. (x.y+1.158 Capitolul 6. compact(x+1. Se cere să se determine dacă fotografia reprezintă unul sau mai multe obiecte. gasit:boolean. compact(x+1. În caz afirmativ.a).a).y-1. Algoritmul este tot cel din problema anterioară (Fill).y-1.

j<=n.j++) for j:=1 to n do if (a[i][j]==1) gasit=1.cin>>m.i++) for i:=1 to m do for (j=1. gasit=0. readln(m). c) 1/(2×3)+2/(3*4)+.' write('a['.. compact(x. if gasit else cout<<"un obiect".0]:=0.j]) cin>>a[i][j].n+1]:=0 x=0. gasit:=false.+n/((n+1)(n+2)).y. { cout<<"M=". readln(a[i.i.y. repeat y=0. cout<<"N=".i]:=0. then writeln('mai multe } obiecte') else writeln('un obiect') end. y:=y+1 } until (y=n) or (a[x. end.i<=m. readln(n). a[m+1][i]=0.cin>>n.a).i++) for i:=1 to m do { a[i][0]=0. <<j<<"]=". Calculaţi recursiv suma a n numere naturale citite. a[i.a).i]:=0. for (i=1. } a[i..']='). do x:=0.. begin a[i][n+1]=0. a[0.y]=1) while ((x!=m) && a[x][y]!=1). write('N='). for i:=1 to m do for (i=1..'. 2. for (i=1.+1/n.+n×(n+1).. } repeat while (y!=n && a[x][y]!=1). a[m+1.j]=1 then if (gasit) gasit:=true. } end. compact(x. b) 1+1/2+.i<=m.y]=1). if a[i.i++) begin { a[0][i]=0. } for i:=1 to n do for (i=1. x:=x+1. { y++.Manual de informatică pentru clasa a XI-a 159 begin main() write('M='). do y:=0.'.i<=n. until (x=m) or (a[x. { x++.j.. end.j<=n.j++) begin { cout<<"a["<<i<<'. Probleme propuse 1. Calculaţi recursiv expresiile: a) 1×2+2×3+. cout<<"mai multe obiecte".i<=m.i++) for j:=1 to n do for (j=1. .

12. Atunci când calculatorul propune un număr i.. 8. dacă numărul este prea mare. Este eficient? 4. 9.  k Comparaţi timpul de rezolvare cu cel necesar pentru rezolvarea problemei 4. Se citesc n şi k (numere naturale n>k). Exemplu: Pentru n=4 şi V=(2.6). se va răspunde prin: 1. max(V[1]. se returnează 10. V[n]) altfel. 2. n = 1. Scrieţi un subprogram recursiv prin care calculatorul ghiceşte un număr natural ascuns de dumneavoastră (numărul este cuprins între 1 şi 30000). Scrieţi un subprogram recursiv care calculează câte cuvinte distincte cu 2n caractere se pot forma cu n caractere A şi n caractere B. 6. k = 0. Scrieţi o funcţie recursivă care returnează suma elementelor pare ale unui vector citit. V[2]. Scrieţi o funcţie recursivă prin care se testează dacă un număr natural x se regăseşte între componentele unui vector V cu n numere naturale. 13. V[2].. 10. C =  n − k + 1 k −1 k n Cn altfel.. dacă numărul a fost ghicit. . Calculaţi recursiv Cnk ...j] şi returnează [x] (parte întreagă din x).5. dacă numărul este prea mic. Scrieţi o funcţie recursivă care testează dacă un număr natural n>1 este prim. Scrieţi un program iterativ care rezolvă problema anterioară utilizând aceeaşi formulă. Scrieţi o funcţie recursivă care primeşte ca parametri două numere naturale i<j şi un număr real x∈[i. Calculaţi conform formulei următoare valoarea maximă reţinută de un vector de numere naturale cu n componente: V[1]. Se citeşte un număr natural n. 5. 7. Nu se vor folosi funcţiile specializate ale limbajului.160 Capitolul 6.V[n]) =  max(max(V[1]. prin utilizarea formulei de recurenţă: Cnk = Cnk−−11 + Cnk−1 . 0.. Se cere să se scrie o funcţie recursivă care returnează cea mai mică bază în care se poate considera n.2. Introducere în recursivitate 3. 11.V[n − 1]). Calculaţi recursiv Cnk prin utilizarea formulei: 1.

20. Calculaţi S(n. se returnează 321. k) = 1 k = n. 15. k) 1 < k < n  23. k) =  1.  F(n. 19. Scrieţi o funcţie recursivă care testează dacă un vector cu n numere naturale reţine numai valori distincte. Scrieţi un subprogram recursiv care returnează. Scrieţi o funcţie recursivă care afişează valoarea unui polinom în punctul a. 24. k − 1) − nF(n − 1. k − 1) + kS(n − 1. iar în caz contrar. Scrieţi o funcţie recursivă care returnează numărul cifrelor pe care le are un număr natural primit ca parametru. k) 1 < k < n.2. caz în care funcţia returnează true. cifră cu cifră. n1≤n2 a căror sumă este n. n} S(n − 1.k) nerecursiv. F(n − 1. Exemplu: pentru n=4 şi V=(1011). Scrieţi o funcţie recursivă care verifică dacă un vector cu componente numere naturale este palindrom (afişarea componentelor de la 1 la n coincide cu afişarea lor de la n la 1). Coeficienţii polinomului sunt daţi într-un vector.  Comparaţi eficienţa celor două moduri de calcul.3) avem polinomul P=x2+2x+3. k>n  S(n. Scrieţi un subprogram recursiv care afişează. 22. 17. Exemplu: pentru n=123. oglinditul unui număr natural. 21. se va returna 11. Calculaţi recursiv şi nerecursiv funcţia definită pe N*×N: 0. Fie funcţia definită pe N*×N*. Exemplu: pentru n=123. . Pentru un vector cu n componente 0 sau 1 care are semnificaţia de număr binar. pentru V=(1. returnează false. k = 0 sau k > n. se cere să se scrie o funcţie recursivă care afişează numărul în baza 10.Manual de informatică pentru clasa a XI-a 161 14. 18. 16. oglinditul unui număr natural. Se citesc n şi k. k ∈ {1. Astfel. Scrieţi un subprogram recursiv care descompune în toate modurile posibile un număr natural n în două numere n1 şi n2. se afişează 321. Se cere să se scrie o funcţie recursivă care evaluează funcţia:  0.

a) 12. definit pe N*×N*: P(n+k.2)+.+P(n. } end. b) 21. dacă 1<k<n.k). int t(int n) begin { if (n) if n<>0 then return (n%2==0)*n +t(n-1). n ≠ 0 cmmdc(n.k)=0.k). n=0 Pascal C++ 27. ? Varianta Pascal Varianta C++ function t(n:integer):integer.162 Capitolul 6. m mod n)... n) =  m. utilizând algoritmul lui Euclid: cmmdc(n. Introducere în recursivitate 25. n ≠ 0 cmmdc(m. else t:=0. dacă k=1 sau k=n. int t(int n) begin { if (n) return 10*t(n/10)+ if n<>0 then t:=10*t(n div 10) n%10. Calculaţi iterativ şi recursiv cel mai mare divizor comun pentru două numere naturale m şi n. d) suma numerelor naturale pare mai mici sau egale cu n. Ce se afişează la: writeln(t(12)). n=0 m. b) suma primelor n numere naturale pare.1)+P(n. c) eroare de executare. +(n mod 10) else return 0.k)=P(n. n) =  cmmdc(m. / cout<<t(12). a) suma primelor n numere naturale impare. 26. P(n. Să se calculeze recursiv şi nerecursiv P(n. d) 0. t:=ord(n mod 2=0)*n+t(n-1) else return 0. c) suma numerelor naturale pare strict mai mici decât n. Ce calculează funcţia următoare? Varianta Pascal Varianta C++ function t(n:integer):integer. else t:=0. m % n). 28. . dacă k>n. P(n. } end.k)=1.

30.2. unde V1 şi V2 sunt vectori cu n componente numere naturale: Varianta Pascal Varianta C++ procedure t(n.j). int i. t(n div 16). c) 1000. j:=j+1 end.i. t(n.Manual de informatică pentru clasa a XI-a 163 29. begin t(n.j:integer).1)? a) 1 2 3.1. void t(int n) begin { if (n>15) if n>15 then { t(n/16).3) şi V2=(4. care sunt parametri de intrare pentru funcţia următoare. d) 4 5 1. c) cerinţa nu este corectă. int j) begin { if (i<=n && j<=n) if (i<=n) and (j<=n) then { if (V1[i]<V2[j]) begin cout<<V1[i++]<<" ".i.i. Testele de la 30 la 33 se referă la funcţia de mai jos. ultimele 2 numere afişate vor fi 0? Varianta Pascal Varianta C++ procedure t(n:integer). V1=(1. end. Pentru care dintre numerele de mai jos.j). if V1[i]<V2[j] then else cout<<V2[j++]<<" ". b) 1024. a) 295. begin cout<<n%16<<endl. b) 1 4 2 5 3 1. } writeln(n mod 16) else cout<<n<<endl. end.5. end. d) 10000. .’ ‘). end } else writeln (n). void t(int n. } i:=i+1 } end else // V1 si V2 contin date begin // incepand cu indicele 1 write (V2[j]. Dacă n este egal cu 3. write (V1[i].1) ce se afişează la apelul t(n.’ ‘).

for i:=1 to n do readln(V[i]).i. .1)? a) V1=(1. d) V1=(1. b) V1=(1. if n<>0 then } begin } b(k+1.2. d) întotdeauna se vor afişa cel puţin 6 numere.3) şi V2=(4. 34.164 Capitolul 6.6). Dacă n este 3.4) şi m=2.3) şi V2=(4. c) 4 5 6.6). c) întotdeauna se vor afişa cel mult 3 numere. begin cin>>m. for (i=1.1. b(1. 33. b) 1 2 3 4 5 6.1. ce afişează programul? a) 4.2.i<=n.h> integer.2.1.2. V=(1. V:vector.i++) cin>>V[i].1)? a) întotdeauna se vor afişa cel mult 6 numere. int n) { if (n) procedure b(k.6).6) şi V2=(1.. int m.2. V1=(1. dacă apelul este t(n. end cin>>n.5.i:integer.10] of #include <iostream. d) nici o valoare.3. Ce va afişa funcţia la apelul t(1.5. { b(k+1.2. c) V1=(4.V[20]. end. pentru care din datele de mai jos se afişează un număr maxim de valori distincte. end.n/2). readln (n).n. void b(int k.n. if n mod 2=1 then main() writeln(V[k]).5. Dacă n=4.6)? a) 1 2 3.n div 2).n) dacă n=3.. Introducere în recursivitate 31.m). b) întotdeauna se vor afişa cel puţin 3 numere. var m.n. begin if (n%2) cout<<V[k]<<endl.m). Care dintre afirmaţiile de mai jos sunt corecte dacă n este 3 şi apelul este t(n.5.b(1. { int i. 32.3).8) şi V2=(4. Testele de la 34 la 36 se referă la programul următor: Varianta Pascal Varianta C++ type vector=array[1. b) 3.5) şi V2=(4.5. } readln(m).n:integer). c) 2. d) 1.

b) x:=x(n mod 2). d) 1. end. . c) 2. a) return x(n-2). end } end. pentru care valoare a lui m se afişează toate cele 4 valori ale vectorului? a) 15.Manual de informatică pentru clasa a XI-a 165 35. 39. d) depinde de stivă.. Dacă n=4. c) return x(n-1). c) 0. pentru care dintre valorile de mai jos ale lui m se afişează două valori? a) 4. Dacă n=4. b) de 3 ori. c) de 5 ori.. 36. executarea funcţiei se termină cu eroare? a) 0. 37. } 38. care sunt parametri de intrare pentru funcţia an. b) return x(n%2). . d) x:=x(n div 2).. d) Nu există o astfel de valoare.. d) return x(n/2). astfel încât funcţia să-şi încheie execuţia fără eroare pentru orice valoare admisibilă a argumentului: Varianta Pascal Varianta C++ function x(n:integer):integer. Testele 38 şi 39 se referă la funcţia de mai jos: Varianta Pascal Varianta C++ function an(n:integer):integer. cout<<n. Dacă funcţia este apelată prin an(4).. Pentru care dintre valorile de mai jos. d) nici una dintre valorile de mai sus. b) 16. int an(int n) begin { if n=0 then an:=1 if (n==0) return 1. b) 3. de câte ori se autoapelează? a) de 4 ori.. int x(int n) begin { if n<>0 then if (n) begin { writeln(n). b) -1. În funcţia de mai jos înlocuiţi linia ”. } a) x:=x(n-2). . c) x:=x(n-1). c) 1.” cu una dintre instrucţiunile următoare. else an:=3*an(n-1)+7 else return 3*an(n-1)+7.

begin else if n=1 then Maxim:=V[1] { max=Maxim(n-1). Introducere în recursivitate 40. 0..166 Capitolul 6. Sn=1+2+. Pentru că nu contează dacă s-a inversat B cu B (2n)!. altfel 7. else return max. daca n = 0 Sn =  S n−1 + n. Indicaţii / Rezolvări 1. Varianta Pascal Varianta C++ function int Maxim(int n) Maxim(n:integer):integer. b) de 4 ori. var max:integer. else begin if (max<V[n]) return V[n]. else else if n=1 then an:=1 if (n==1) return 1. main() end. d) de 2 ori. Se obţine: (2n )! = C n n!⋅n! 2n 8. end. { int max. #include <iostream. Există (2n)! permutări ale literelor. se împarte din nou la n!. .n-1+n.h> begin int an(int n) if n=0 then an:=2 { if (n==0) return 2. c) de 9 ori. { cout<<an(4). if max<V[n] then } Maxim:=V[n] } else Maxim:=max. de câte ori se autoapelează funcţia an? Varianta Pascal Varianta C++ function an(n:integer):integer.. max:=Maxim(n-1). end. } begin writeln(an(4)). Întrucât pentru fiecare permutare nu contează dacă s-a inversat A cu A. if (n==1) return V[1]. return an(n-1)-an(n-2)+1. else an:=an(n-1)-an(n-2)+1 else end. (2n)! se împarte la n!.. Sn⇒Sn-1+n. Pentru programul de mai jos. } a) de 8 ori.

i+1). end. begin else return Suma(n-1)+ if n=0 (V[n]%2==0)*V[n]. if n mod i=0 } then Prim:=false else Prim:=Prim(n. 12. 11.x:integer):boolean.x). if i>trunc(sqrt(n))+1 else then Prim:=true if (n%i==0) return 0.i+1). begin else if n=0 if (V[n]==x) return 1. funcţia de mai jos se apelează Prim(n. { if (i>(int)(sqrt(n)+1)) begin return 1.x). Se calculează cifra maximă a numărului şi se adună 1.Manual de informatică pentru clasa a XI-a 167 9. end. De exemplu. then Suma:=0 } else Suma:=Suma(n-1)+ ord(V[n] mod 2=0)*V[n]. .i:integer):boolean. end. Varianta Pascal Varianta C++ function int Suma(int n) Suma(n:integer):integer. { if (n==0) return 0. 10. { if (n==0) return 0. int x) Apartine(n.2): Varianta Pascal Varianta C++ function int Prim(int n. Varianta Pascal Varianta C++ function int Apartine(int n. then else Apartine:=false return Apartine(n-1. else else return Prim(n. else } if V[n]=x then Apartine:=true else Apartine:=Apartine(n-1.int i) Prim(n.

x) else return i.x). Pint:=Pint(i. 15. 14. end.j.x:real):integer. Iniţial. { begin if (i>=j) return 1.Mijloc. Introducere în recursivitate 13. Varianta Pascal Varianta C++ function int PInt(int i.n): Varianta Pascal Varianta C++ function int Palindrom(int i.x) } else Pint:=Pint(Mijloc.j:integer):boolean. Este clasicul algoritm de căutare binară. var Mijloc:integer. if j-i>1 if (x==Mijloc) then return Mijloc. end } else Pint:=i end.j-1). .float x) PInt(i.j:integer. begin else Mijloc:=(i+j) div 2. else else return if V[i]<>V[j] Palindrom(i+1. if (j-i>1) begin { Mijloc=(i+j)/2. if n<10 } then Nrcif:=1 else Nrcif:=NrCif(n div 10)+1.int j.j-1).j. int j) Palindrom(i. if (x<Mijloc) if x=Mijloc return then Pint:=Mijloc PInt(i. { int Mijloc. else else if x<Mijloc then PInt(Mijloc. se apelează cu Palindrom(1. if i>=j else then Palindrom:=true if (V[i]!=V[j]) return 0. Varianta Pascal Varianta C++ function Nrcif (n:longint): int Nrcif(long n) integer.168 Capitolul 6. end.Mijloc. begin else return Nrcif(n/10)+1. { if (n<10) return 1.x). then Palindrom:=false } else Palindrom:= Palindrom(i+1.

trebuie să reţină.Manual de informatică pentru clasa a XI-a 169 16. iniţial. } Oglinda(n div 10). void Oglinda(int n) begin { if (n) if n<>0 then { cout<<n%10. Varianta Pascal Varianta C++ procedure Oglinda(n:integer).n). Variabila ninv. begin } ninv:=ninv*10+n mod 10. if n<>0 then Oglinda (n/10. begin else return gasit:=false. if (i==n) return 1. 17. end. Varianta Pascal Varianta C++ procedure Oglinda(n:integer.n). } end.ninv). ninv). var gasit:boolean.n:integer):boolean. if i=n for (j=i+1. end. write(n mod 10). 0. j:integer. . Distincte(i+1. transmisă prin referinţă. } Oglinda(n div 10. end end. for j:=i+1 to n do } if V[i]=V[j] } then gasit:=true. int& ninv) var ninv:integer). { int gasit.j++) then Distincte:=true if (V[i]==V[j]) gasit=1. void Oglinda(int n. 18.int n) Distincte(i. end.j<=n. Funcţia se apelează cu Distincte(1. else if (gasit) return 0. begin Oglinda (n/10).n): Varianta Pascal Varianta C++ function int Distincte(int i. else begin { gasit=0.j. { if (n) begin { ninv=ninv*10+n%10. if gasit then Distincte:=false else Distincte:= Distincte(i+1.

} n.5). S(3..4)=0.n).2)=1.a)+V[i]. ' '.n. { if (i==1) return V[1]. S(i. Introducere în recursivitate 19..3)=0.k).x).. adică S(i-1. ... Este ineficient să calculăm recursiv.1)=1. pentru calculul elementelor de pe linia i.n)+V[i].int n.a:integer):integer.2)=S(2. . 23. end. begin else if i=1 then Refac:=V[1] return 2*Refac(i-1. Apelaţi cu Refac(n.int a) Valoare(i.1)+2S(2. S(4. S(3.1)=1. } Descompun(i+1. void Descompun(int i.2)=0.int n) begin { if (i<=n/2) if i<= n div 2 then { cout<<i<<" "<<n-i<<endl. S(4.n). Cu excepţia primei linii S(1.1)+2S(3.n. . { if (i==1) return V[i].a): Varianta Pascal Varianta C++ function int Valoare(int i. 20. Mai întâi calculăm: S(1.2)=7... 22.int n) Refac(i.3)=0. Urmează: S(2.1)=1. Apelaţi cu Descompun(1. . S(1..1)=1. if i=1 then Valoare:=V[1] n. writeln (i. S(1.. begin Descompun(i+1. } end end. end.n:integer):integer. S(2.n)+V[i]. else } Refac:=2*Refac(i-1. n-i). 21. begin else return a*Valoare(i-1.8)=0.n): Varianta Pascal Varianta C++ procedure Descompun(i. S(2. . se folosesc rezultatele aflate pe linia i-1.n): Varianta Pascal Varianta C++ function int Refac(int i. S(1.3)=1.x).n:integer).8)=0 S(3.2)=3. Apelaţi cu Valoare(n. else Valoare:=a*Valoare(i-1. S(3.. Să presupunem că avem de calculat S(8.S(2.170 Capitolul 6.a)+V[i].2)=S(3.

d).2. vedeţi “pe viu” motivul pentru care.k).i).s=0. avem: P(n. 39. Exemplu Acum. . Varianta Pascal Varianta C++ function int P(int n. 37. 38.k)=P(n-k+k. 35.s:integer. În cazul în care k<n. c). 32. 33. b).i++) then P:=1 s+=P(n-k. begin } s:=0. if (k>n) return 0. a).i<=n-k. 28. b). 40. end end. a). a).1)+P(n-k..i). a). P:=s. { int i. b).Manual de informatică pentru clasa a XI-a 171 25. var i. intervin mai mulţi operanzi ce se calculează recursiv. a).2)+. begin else if k>n then P:=0 if (k==1 || k==n) return 1. d). } for i:=1 to n-k do s:=s+P(n-k. 36. else else if (k=1) or (k=n) { for (i=1. în expresie. else return s. 27. 34.k:integer):integer.. 29. în care. 31. este de preferat metoda iterativă. în cazul unor astfel de formule de recurenţă.k)=P(n-k. 30. b).+P(n-k. d). a).int k) P(n. a) Autoapelurile se efectuează după schema de mai jos: 4 3 2 2 1 1 0 1 0 Figura 6.

1.2. avem două posibilităţi: 1) am ajuns la o problemă care admite o rezolvare imediată. se poate descompune în alte subprobleme. combinăm rezultatele şi revenim din apel.2.1. DIVIDE ET IMPERA este o tehnică ce admite o implementare recursivă. Valoarea maximă dintr-un vector  Problema 7. care se rezolvă. Se presupune că fiecare dintre problemele în care a fost descompusă problema iniţială. Procedeul se reia până când (în urma descompunerilor repetate) se ajunge la probleme care admit rezolvare imediată.172 Capitolul 7 Metoda DIVIDE ET IMPERA 7. caz în care se rezolvă şi se revine din apel (condiţia de terminare). Am învăţat principiul general prin care se elaborează algoritmii recursivi: ce se întâmplă la un nivel. iar soluţia pentru problema iniţială se obţine combinând soluţiile problemelor în care a fost descompusă. Problema de mai sus este binecunoscută. Se citeşte un vector cu n componente. 7. numere naturale. Aplicaţii 7. caz în care descompunem problema în două sau mai multe subprobleme. se elaborează un algoritm prin DIVIDE ET IMPERA. Tot aşa. 2) nu am ajuns în situaţia de la punctul 1. Fără teama de a greşi. tocmai datorită cerinţei ca problema să admită o descompunere repetată. Cum o rezolvăm utilizând tehnica DIVIDE ET IMPERA? . putem afirma că numărul lor este relativ mic. Evident. se întâmplă la orice nivel (având grijă să asigurăm condiţiile de terminare). pentru fiecare din ele reapelăm funcţia.1. la fel cum a fost descompusă problema iniţială. Prezentare generală DIVIDE ET IMPERA este o tehnică specială şi se bazează pe un principiu extrem de simplu: descompunem problema în două sau mai multe subprobleme (mai uşoare). La un anumit nivel. Se cere să se tipărească valoarea maximă. nu toate problemele pot fi rezolvate prin utilizarea acestei tehnici.

else max:=b.10] of #include <iostream. else begin { a=max(i. then max:=v[i] if (a>b) return a. procedăm astfel: • dacă i=j.n)) end. begin } a:=max(i.i:integer. :integer.i++) end. for i:=1 to n do begin write('v['. int max(int i.n.']='). else else return b.h> integer.j:integer) { int a.j).(i+j) div 2). if i=j b=max((i+j)/2+1. al doilea va conţine componentele de la (i+j) div 2 + 1 la j.j).i<=n.i.int j) function max(i. } readln(n).(i+j)/2). } begin cout<<"max="<<max(1. } b:=max((i+j) div 2+1. { cout<<"v["<<i<<"]=". . if a>b main() then max:=a { cout<<"n=". Programul este următorul: Varianta Pascal Varianta C++ var v:array[1. n. Pentru aceasta. valoarea maximă va fi v[i]. readln(v[i]) end. writeln('max='.b. în practică este preferat algoritmul clasic. end. int v[10]. write('n=')..b:integer. vom împărţi vectorul în doi vectori (primul vector va conţine componentele de la i la (i+j) div 2.n).max(1. Trebuie tipărită valoarea maximă dintre numerele reţinute în vector de la i la j (iniţial i=1 şi j=n). var a. for (int i=1. • contrar. cin>>v[i]. Algoritmul prezentat este exclusiv didactic. if (i==j) return v[i]. rezolvăm subproblemele (aflăm maximul pentru fiecare din ele) iar soluţia problemei va fi dată de valoarea maximă dintre rezultatele celor două subprobleme.Manual de informatică pentru clasa a XI-a 173  Rezolvare. cin>>n.

10] of #include <iostream. end i=p. int a[10]) procedure sort(p. În cele ce urmează. divimp implementează strategia generală a metodei studiate.q:integer. funcţia sort sortează un vector cu maximum două elemente. În aplicaţie. rezultatele se combină (în particular se interclasează). Descompunerea unui vector în alţi doi vectori care urmează a fi sortaţi are loc până când avem de sortat vectori de una sau două componente. var a:vector. { var a:vector).2. begin a[p]=a[q]. vom utiliza algoritmul de interclasare în vederea sortării unui vector prin interclasare. pentru că efectuează cel mult m+n-1 comparări. Sortarea prin interclasare  Problema 7. interc interclasează rezultatele. al doilea cu n elemente. primul cu m elemente.174 Capitolul 7.j. după rezolvarea lor.i:integer. then } begin } m:=a[p]. Se consideră vectorul a cu n componente numere întregi (sau reale).2..  Rezolvare. void interc(int p. int m. { m=a[p].n.int a[10]) a[q]:=m { int b[10]. j=m+1. Să se sorteze crescător. if (a[p]>a[q]) var m:integer.int q. odată sortaţi. Algoritmul de sortare prin interclasare se bazează pe următoarea idee: pentru a sorta un vector cu n elemente îl împărţim în doi vectori care. Interclasarea a doi vectori a fost studiată.i. void sort(int p. Algoritmul de interclasare este performant. k=1. Varianta Pascal Varianta C++ type vector=array [1. n. a[p]:=a[q]. se interclasează.h> integer. utilizând sortarea prin interclasare. int a[10].2.int q. atunci se poate obţine un vector care conţine toate valorile sortate.k. if a[p]>a[q] a[q]=m. end. problema este descompusă în alte două subprobleme de acelaşi tip şi. ambele sortate. Dacă dispunem de două şiruri de valori. Metoda DIVIDE ET IMPERA 7. int m. . Conform strategiei generale DIVIDE ET IMPERA.

k:=k+1 if (i<=m) end for (j=i.Manual de informatică pentru clasa a XI-a 175 procedure interc while (i<=m && j<=q) (p.int q. cin>>a[i].i++) end { cout<<"a["<<i<<"]=".q. main() divimp(p. j:=j+1. k:=k+1 { int m.q.a) else } begin m:=(p+q) div 2.q. k:=k+1 } end. else while (i<=m) and (j<=q) do { b[k]=a[j].j++) else { b[k]=a[j].a). divimp(1. begin k=k+1.j<=q. j:=m+1. cout<<"n=". b[k]:=a[i]. for i:=p to q do void divimp (int p.m. begin } i:=p. write('n=').a). } k:=1.i<=n. begin } if (q-p)<=1 then sort(p.a). for i:=1 to n do writeln(a[i]) end.i++) for i:=j to q do { a[i]=b[k].n. procedure divimp divimp(p.i<=q. } i:=i+1.m. readln(a[i]) end.q. b[k]:=a[j]. else for (i=p.a). begin int a[10]) a[i]:=b[k]. k:=k+1 } end k=1. begin k=k+1. k:=1.a). k:=k+1 } end. { b[k]=a[i].i. divimp(m+1.j++) for j:=i to m do { b[k]=a[i].m.q.n. if a[i]<=a[j] then j=j+1. b[k]:=a[j]. (p. var m:integer. end. begin k=k+1.a) for (i=1.j. begin k=k+1. end if ((q-p)<=1) sort(p. cin>>n. begin } write('a['.q.q.i++) for i:=1 to n do cout<<a[i]<<" ". for (i=1.j<=m. k=k+1. end.a). . i. divimp(m+1.a). interc(p. interc(p. b[k]:=a[i].m.a). var a:vector).m:integer. readln(n).i<=n. } begin divimp(1. else if i<=m then for (i=j. else { m=(p+q)/2.k:integer. i=i+1. if (a[i]<=a[j]) var b:vector.']=').q:integer. { int i.var a:vector).

Rolul acestei funcţii este de a poziţiona prima componentă a[li] pe o poziţie k cuprinsă între li şi ls. • se trece în modul de lucru a). calculăm numărul aproximativ de comparări efectuat de algoritm.2. iar j va lua valoarea ls (elementul care iniţial se află pe poziţia li se va găsi mereu pe o poziţie dată de i sau de j).. − i şi j se modifică corespunzător modului de lucru în care se află programul. = n+ k n + .. n = 1.  Rezolvare. Sortarea rapidă  Problema 7.  T(n) =   n  2T  + n. i va lua valoarea li. În această funcţie există două moduri de lucru: a) i rămâne constant..3. se execută: − dacă a[i] este strict mai mare decât a[j].    2 Avem: T(n) = T(2k ) = 2(T(2k −1 ) + 2k −1 ) = 2T(2 k −1 ) + 2k = 2T(2 k −2 + 2k −1 ) + 2k = 2T(2 k −2 ) + 2k + 2k = . • atât timp cât i<j. Fie acesta T(n). atunci se inversează cele două numere şi se schimbă modul de lucru. j rămâne constant. .. Este necesară o funcţie POZ care tratează o porţiune din vector. care necesită n/2+n/2=n comparaţii: 0. j scade cu 1. fiecare cu n/2 componente. astfel încât toate componentele vectorului cuprinse între li şi k-1 să fie mai mici sau egale decât a[k] şi toate componentele vectorului cuprinse între k+1 şi ls să fie mai mari sau egale decât a[k]. după care urmează interclasarea lor. presupunem n=2k. Metoda DIVIDE ET IMPERA În continuare. altfel. b) i creşte cu 1.   + n = n ⋅ k = n ⋅ log2n  de k ori de k ori 7.. cuprinsă între indicii daţi de li (limita inferioară) şi ls (limita superioară).. Mai simplu.176 Capitolul 7. O problemă se descompune în alte două probleme. − k ia valoarea comună a lui i şi j.2  k + + 2k +2 . Se cere ca vectorul să fie sortat crescător.3. Funcţia este concepută astfel: • iniţial. Fie vectorul a cu n componente numere întregi (sau reale).

totodată toate elementele din dreapta lui fiind mai mari decât el (k=4). int a[100].1.9). În cadrul ei se utilizează metoda DIVIDE ET IMPERA. • i=2.n. j=4. deci se inversează elementele aflate pe poziţiile 1 şi 5.i1. ls=5.9. } .3.1. } i:=li. void poz (int li. deci a=(2.3.i1=0.int ls. • i=3.Manual de informatică pentru clasa a XI-a 177 Pentru a=(6. ceea ce impune o modificare corespunzătoare a indicilor i şi j. j=5.6.j. deci a=(2. j1=-c.j1:integer. • funcţia se încheie. a[i]=c. k.2). { c=a[j]. Funcţia QUICK are parametrii li şi ls (limita inferioară şi limita superioară). begin } i=i+i1.h> integer.6. • a[2]>a[5].100] of #include <iostream.3. După aplicarea funcţiei POZ.k:integer. deci a=(2. • a[2]>a[4]. j:=ls.c.. a[j]=a[i].int a[100]) { int i=li. fapt care reprezintă esenţa algoritmului. k=i. după cum urmează: • se apelează POZ.1. i1:=0.j1=-1.3. var i. se trece la modul de lucru b). c=i1. este evident că elementul care se află iniţial în poziţia li va ajunge pe o poziţie k şi va rămâne pe acea poziţie în cadrul vectorului deja sortat.int& a:vector. while (i<j) var k:integer. procedure poz (li.c. { if (a[i]>a[j]) var a:vector). j1:=-1. toate elementele din stânga lui fiind mai mici decât el.6) şi programul trece la modul de lucru b).k.j=ls. • se apelează QUICK pentru k+1 şi ls. j=4. • a[1]>a[5]. modul de lucru a): • i=1. • se apelează QUICK pentru li şi k-1. li=1.9) şi se revine la modul de lucru a). j=5. i1=-j1.1. • i=2. elementul aflat iniţial pe poziţia 1 se găseşte acum pe poziţia 4. Varianta Pascal Varianta C++ type vector=array [1.9. j=j+j1.ls:integer. Alternanţa modurilor de lucru se explică prin faptul că elementul care trebuie poziţionat se compară cu un element aflat în dreapta sau în stânga lui. var i.n.

i++) i:=i+i1.n). Reţineţi! Sortarea rapidă efectuează în medie n ⋅ log 2 n operaţii.i++) procedure quick(li.ls. end.k. quick(k+1. begin quick(k+1.i<=n. for i:=1 to n do begin write('a['.n). { int i.k.  Demonstraţia necesită cunoştinţe de matematică pe care nu le aveţi la nivelul acestui an de studiu… . for i:=1 to n do writeln(a[i]) end.k-1).ls:integer). for (i=1.ls) end end. c:=i1. end.i<=n. } a[j]:=a[i]. begin write('n='). quick(1.int ls) begin { if (li<ls) if a[i]>a[j] { poz(li. } a[i]:=c. cin>>n. j:=j+j1 cin>>a[i]. cout<<a[i]<<endl. Metoda DIVIDE ET IMPERA while i<j do void quick (int li.i. j1:=-c cout<<"n=". { cout<<"a["<<i<<"]=".']=').a). begin } if li<ls then begin poz(li. readln(n).178 Capitolul 7. c:=a[j]. quick(li. main() i1:=-j1. then quick(li. for (i=1.a). } k:=i quick(1. end.ls. readln(a[i]) end.k-1).ls).

a)=ac. ab. Dacă n=2. − mutarea a n-1 discuri de pe tija c pe tija b.ab. Conform strategiei DIVIDE ET IMPERA.b).b. b.ab.a.c)=H(2.cb. c.b.c.c.Manual de informatică pentru clasa a XI-a 179 7.a.H(1.a).a. Turnurile din Hanoi  Problema 7. se face mutarea ab.b. ab.a.ab.c.c) astfel: ab. H(n − 1. utilizând ca tijă intermediară tija a. cb.  Rezolvare.ab.H(1. c.b. Pe tija a se găsesc discuri de diametre diferite. Parcurgerea celor trei etape permite definirea recursivă a şirului H(n.4. n =1 H(n.b. utilizând ca tijă intermediară. utilizând ca tijă intermediară tija c.b.a.a)=H(1. utilizând ca tijă intermediară tija b.c. 2) pentru n=3.ac.c.b). se fac mutările ac. aşezate în ordine descrescătoare a diametrelor privite de jos în sus. observăm că mutarea celor n discuri de pe tija a pe tija b.2.c. Se cere să se mute discurile de pe tija a pe tija b. avem: H(2.b. ab.ac. Se dau 3 tije simbolizate prin a.ab. b. tija c. adică se mută discul de pe tija a pe tija b.b.cb.b).H(2.a. c. încercăm să descompunem problema în alte două subprobleme de acelaşi tip. În cazul în care n>2. n >1 Priviţi următoarele exemple: 1) pentru n=2. • nu este permis să se aşeze un disc cu diametrul mai mare peste un disc cu diametrul mai mic. Dacă n=1. avem: H(3.a. respectând următoarele reguli: • la fiecare pas se mută un singur disc.c)=H(1.b. urmând apoi combinarea soluţiilor. b. Notăm cu H(n. b). este echivalentă cu: − mutarea a n-1 discuri de pe tija a pe tija c. problema se complică. a. − mutarea discului rămas pe tija b. . c) =  H(n − 1.a.c) şirul mutărilor celor n discuri de pe tija a pe tija b.c).4.bc.a. a).H(1. utilizând ca tijă intermediară tija c.cb.H(1.c)=ab.ca. În acest sens. a.

LF.b. #include <iostream.a) } end } end.c. întrucât fiecare nou dreptunghi are cel mult n-1 găuri. YF.char a. La această problemă compararea soluţiilor constă în a reţine dreptunghiul cu aria maximă dintre cele fără găuri.h> n:integer. write('N='). else else begin { han(n-1. han(n-1. c='c'. cout<<a<<b<<endl. writeln(a.5. precum şi dreptunghiurile care apar în procesul tăierii sunt memorate în program prin coordonatele colţului din stânga-sus (X. Dreptunghiul iniţial. se compară aria ei cu aria unei alte bucăţi fără gaură.2. c:='c'. Menţionăm că dreptunghiul de arie maximă fără găuri este reţinut prin aceiaşi parametri ca şi dreptunghiul cu găuri. HF. având pe suprafaţa ei n găuri de coordonate numere întregi. Metoda DIVIDE ET IMPERA Varianta Pascal Varianta C++ var a. han(n.b.b.5. problema se descompune în alte patru probleme de acelaşi tip.c). a:='a'. în zonele XF.b) { if (n==1) cout<<a<<b<<endl.c:char. main() begin { cout<<"N=".c. char a. procedure han (n:integer.a. În situaţia când acesta prezintă o gaură.180 Capitolul 7.b. 7.  Rezolvare.b. Sunt permise numai tăieturi verticale şi orizontale.c. begin void han (int n.a).c) } end. han(n.H). int n. Dacă bucata nu prezintă găuri.a.b). Coordonatele găurilor sunt reţinute în doi vectori XV şi YV. han(n-1.a.b). Pentru un dreptunghi (iniţial pornim cu toată bucata de tablă).b).c:char).char c) writeln(a. b:='b'. readln(n). .Y). if n=1 then char b.c. verificăm dacă avem sau nu o gaură în el (se caută practic prima din cele n găuri).a. b='b'. dacă dreptunghiul iniţial avea n găuri.c. a. găsită în fazele precedente. problema iniţială se descompune în alte patru probleme de acelaşi tip. cin>>n. a='a'. În concluzie. han(n-1. Problema tăieturilor  Problema 7. Se cere să se decupeze din ea o bucată de arie maximă care nu prezintă găuri. Se dă o bucată dreptunghiulară de tablă cu lungimea l şi înălţimea h.b.b. mai uşoare. prin lungime şi înălţime (L.

Dacă facem o tăietură verticală prin această gaură.int& hf.yv).yv[i]-y.int l. else i++.yv). while (i<=n) and (not gasit) dimp(xv[i].yf.n. lf. gaura trebuie să îndeplinească simultan condiţiile: 1) xv(i)>x.yv).l.hf:integer.yv[10].hf:integer.hf.xf.hf. 2) xv(i)<x+l. { dimp(x. 3) yv(i)>y. y . var l.xv. h+y-yv(i).y. if (xv[i]>x && xv[i]<l && var gasit:boolean. int& lf.h:integer. hf.yv:vect. xv(i)-x.yv(i) x.h+y-yv[i]. var xf. if (xv[i]>x) and (xv[i]<l) dimp(x.yv:vect). yf.yf.i=1.i.lf.lf.xv.xv[10]. (yv[i]<y+h) dimp(x. int h.h> integer.h.y. while (i<=n && !gasit) var xv. int& yf. y.xv[i]-x. begin if (gasit) i:=1.l. l.lf.lf.xf. h. h.yv).xf. gasit:=false. obţinem două dreptunghiuri: 1) x.yf.l+x-xv[i].l.yf.y. do h.xv. 4) yv(i)<y+h.xf.n. În urma unei tăieturi pe orizontală se obţin cele două dreptunghiuri: 1) x.h.9] of #include <iostream.h. then gasit:=true xf.i. else i:=i+1.hf.int y. 2) xv(i).lf. { int gasit=0. yv(i). and (yv[i]>y) and yf.l . i:integer.hf.yf..y.lf.int yv[10]) (x. y. yv[i]>y && yv[i]<y+h) gasit=1.xf. xv. l+x-xv(i).xv.yv[i]. int l.Manual de informatică pentru clasa a XI-a 181 Fie dreptunghiul cu o gaură: h • xv(i). int& xf. void dimp(int x. } .yv(i)-y. procedure dimp int xv[10]. 2) x.y l Pentru a se afla în interiorul dreptunghiului. Programul este următorul: Varianta Pascal Varianta C++ type vect=array [1.

xf.xv. readln(h). end.yv[i]. cin>>h.xv[i]-x.yv) { cout<<"n=".yf.y.xv. dimp(0. write('y['.hf.y. cin>>n.hf.y. xf:=x.i++) else { cout<<"x["<<i<<"]=". begin cin>>yv[i].' y='. readln(xv[i]). hf:=h dimp(0.yf.0. end hf.lf. then cout<<"y["<<i<<"]=". end for (int i=1. for i:=1 to n do begin write('x['. hf=h.yv). lf:=l.lf.l. writeln('x='.xf.xv. cout<<"l=".yf.hf.l+x-xv[i].h+y-yv[i].i.' h='.i<=n.l.']=').yf.xv.yv). lf=l.xf. if (l*h)>(lf*hf) cin>>xv[i].0. } yf:=y. Metoda DIVIDE ET IMPERA if gasit else then if (l*h>lf*hf) begin { xf=x.hf.xv.yf.182 Capitolul 7.lf. hf:=0. readln(n). yf=y.xf. } xf.yv).yf. } dimp(x. h. write('h=').hf.l. lf:=0. h.yv[i]-y. readln(l). cin>>l.xf.xv.lf. dimp(x. cout<<"h=".lf. dimp(x.lf.yf. dimp(xv[i]. lf.yv). cout<<"x="<<xf<<" y="<<yf <<" l="<<lf<<" h="<<hf.h.hf) end.yv).']=').l.' l='. begin } write('n=').h. write('l=').i. readln(yv[i]) end. main() xf. .

calculatorul utilizează placa video. El este cel care a inventat cuvântul “fractal”. Calculatorul permite ca o anumită figură (de exemplu.Manual de informatică pentru clasa a XI-a 183 7. Pentru ca o imagine să poată apărea pe ecran. dar acestea depăşesc cu mult cunoştinţele de matematică din liceu. se încarcă un driver sau altul. Pe parcurs. formată din mai multe figuri iniţiale (de exemplu. care de multe ori contrazic aparenţa. Deoarece performanţele componentelor hardware au depăşit cu mult capacităţile CGA sau EGA. numite Driver-e. de provenienţă latină (“frângere” – a sparge în fragmente neregulate).3. trebuie cunoscute mai întâi noţiunile de bază pentru a lucra în mod grafic.1. biologia sau meteorologia descopereau elemente asemănătoare cu fractalii în viaţa reală. Generalităţi (varianta Pascal) Limbajul Pascal conţine o serie de proceduri şi funcţii care permit realizarea unor aplicaţii grafice. domenii ştiinţifice ca fizica.3. ce poate afişa 640 x 480 puncte în 16 culori. Noţiunea de fractal a apărut ca urmare a studiului vieţii reale. o linie frântă) şi fiecare figură obţinută să se transforme în mod asemănător (aceste transformări necesită foarte multe calcule). 7. Aceste fişiere au extensia “bgi”. ne vom referi în continuare doar la driver-ul VGA (Video Graphics Array). Acestea sunt reunite în unitatea GRAPH. chimia.3. care diferă în funcţie de memoria video şi alţi parametri. un segment) să se transforme într-o alta. Aceştia au proprietăţi matematice extrem de interesante. Elemente de grafică 7. Fractali Fractalii au fost introduşi în anul 1975 prin lucrarea revoluţionară a matematicianului francez Benoit Mandelbrot. însă vom prefera modul standard de înaltă rezoluţie ”VGAHI” (constantă de tip întreg). în care informaţia genetică conţinută în nucleul unei celule se repetă la diferite scări. Aceste forme geometrice au fost considerate în trecut haotice sau “aberaţii geometrice”. Limbajul Pascal deţine o colecţie de astfel de componente software şi în funcţie de placa ce a fost detectată în sistem.1. specifice lor. dezvoltat de firma IBM. trebuie să folosim anumite rutine speciale. . Pentru a accesa o placă video. Driver-ul VGA poate lucra în mai multe moduri. Înainte de a prezenta câteva exemple. “O teorie a seriilor fractale”. ce se găseşte în subcatalogul UNITS. iar multe dintre ele erau atât de complexe încât necesitau calculatoare performante pentru a le vizualiza.1. ce reuneşte totodată diversele teorii dinaintea sa. Acestea vor fi prezentate în continuare.

cum ar fi: lipsa unităţii GRAPH. Vom reţine această procedură pentru că o vom utiliza în exemplele ulterioare.gmode. Tentativa de iniţializare grafică poate eşua din diverse motive.. Forma generală a acestei proceduri este initgraph(gdriver. etc. initgraph(gdriver. care arată calea către unitatea GRAPH. initgraph(gdriver. calea indicată greşit. 2) prin indicarea cu ajutorul primilor doi parametri a unui driver şi a unui mod de lucru solicitate de programator (în acest caz.’c:\tp\bgi’). . Metoda DIVIDE ET IMPERA Selectarea driver-ului şi a modului de lucru se face prin utilizarea procedurii initgraph. if graphresult<>0 then begin writeln(“Tentativa esuata!”).gmode.‘cale’). halt end end. halt end. ca până acum (cu write sau writeln). Aceasta are trei parametri: gdriver (de tip integer) care conţine codul asociat driver-ului.’c:\tp\bgi’). Iniţializarea sistemului grafic se poate face în două feluri: 1) prin a solicita să se identifice automat placa grafică şi corespunzător ei să se încarce un anumit driver şi să se selecteze modul de lucru – cel mai bun din punct de vedere al performanţelor: procedure initg.184 Capitolul 7. begin gdriver := detect. Ieşirea din modul grafic se face prin utilizarea procedurii closegraph. Primii doi parametri sunt transmişi prin referinţă.gmode. nu se mai poate scrie pe monitor. gmode (de tip integer) care reţine modul de lucru şi o variabilă de tip string. Testarea se realizează cu funcţia întreagă graphresult care returnează 0 în caz afirmativ şi o valoare diferită de 0. gmode := VGAHI. nu se poate executa programul pe un calculator ce nu este dotat cu placa grafică specificată): gdriver := VGA. if graphresult<>0 then begin writeln(“Tentativa esuata!”). în caz contrar. Odată intraţi în modul grafic. Constanta detect are valoarea 0 şi se specifică procedurii identificarea automată a driver-ului şi a modului de lucru.

Driver-ul VGA poate lucra în mai multe moduri. se încarcă un driver sau altul. specifice lor.CGI”.2. if (graphresult()) { cout<<"Tentativa nereusita. însă vom prefera modul standard de înaltă rezoluţie ”VGAHI” (constantă de tip întreg). Limbajul C++ deţine o colecţie de astfel de componente software şi în funcţie de placa ce a fost detectată în sistem. Vom reţine funcţia init() pentru că o vom utiliza în exemplele ulterioare."E:\\BORLANDC\\BGI"). } } Constanta DETECT are valoarea 0 şi se specifică funcţiei identificarea automată a driver-ului şi a modului de lucru. numite Driver-e.. Aceste fişiere au extensia “bgi”. Iniţializarea sistemului grafic se poate face în două feluri: 1) prin a solicita să se identifice automat placa grafică şi corespunzător ei să se încarce un anumit driver şi să se selecteze modul de lucru – cel mai bun din punct de vedere al performanţelor: void init() { gdriver = DETECT..". Fişierul ce conţine driver-ul utilizat este “EGAVGA. Acestea sunt reunite în fişierul GRAPHICS. . Deoarece performanţele componentelor hardware au depăşit cu mult capacităţile CGA sau EGA. trebuie să folosim anumite rutine speciale.. ne vom referi în continuare doar la driver-ul VGA (Video Graphics Array). Forma generală a acestei funcţii este initgraph(&gdriver. Selectarea driver-ului şi a modului de lucru se face prin utilizarea funcţiei initgraph. care arată calea către unitatea GRAPH. Pentru a accesa o placă video."cale"). gmode (de tip integer) care reţine modul de lucru şi o variabilă de tip string.&gmode. cout<<"Apasa o tasta pentru a inchide.&gmode.Manual de informatică pentru clasa a XI-a 185 7. Primii doi parametri sunt transmişi prin referinţă.H. Generalităţi (varianta C++) Limbajul C++ (în varianta Borland). conţine o serie de funcţii care permit realizarea unor aplicaţii grafice. Aceasta are trei parametri: gdriver (de tip integer) care conţine codul asociat driver-ului.".3. ce se găseşte în folderul INCLUDE. Pentru ca o imagine să poată apărea pe ecran. getch(). ce poate afişa 640 x 480 puncte în 16 culori. calculatorul utilizează placa video.1. dezvoltat de firma IBM. care diferă în funcţie de memoria video şi alţi parametri. exit(1). initgraph(&gdriver.

Pentru a generaliza însă. trebuie bifată următoarea opţiune. 13 – lightmagenta (violet deschis).1. 3 – cyan (turcoaz). 11 – lightcyan (turcoaz deschis). reprezentate pe 4 biţi. cum ar fi: lipsa unităţii GRAPHICS. nu se poate executa programul pe un calculator ce nu este dotat cu placa grafică specificată): gdriver := VGA. 5 – magenta (violet). se poate schimba setul (paleta) de culori. if (graphresult()) { cout<<"Tentativa nereusita.". initgraph(&gdriver. precum urmează: 0 – black (negru). 1 – blue (albastru). Metoda DIVIDE ET IMPERA 2) prin indicarea cu ajutorul primilor doi parametri a unui driver şi a unui mod de lucru solicitate de programator (în acest caz. nu vom prezenta în detaliu acest aspect. nu se mai poate scrie pe monitor ca până acum (de exemplu. 12 – lightred (roşu deschis). 7.&gmode. Odată intraţi în modul grafic.". 7 – lightgrey (gri deschis). 14 – yellow (galben) şi 15 – white (alb). getch(). Pentru a utiliza mai multe culori (dar nu în acelaşi timp). din meniu: Options / Linker / Libraries / Graphics library. Atenţie! Pentru a putea scrie şi rula programe C++ ce utilizează modul grafic al limbajului. ce se regăseşte în driver-ul limbajului Pascal sau C++. în caz contrar. Testarea se realizează cu funcţia întreagă graphresult() care returnează 0 în caz afirmativ şi o valoare diferită de 0. vom considera modul menţionat anterior. 9 – lightblue (albastru deschis).3. etc. Ieşirea din modul grafic se face prin utilizarea procedurii closegraph(). Aceste culori sunt cele implicite. 4 – red (roşu). 8 – darkgrey (gri închis). 2 – green (verde).. cu cout). 10 – lightgreen (verde deschis).3. Setarea culorilor şi procesul de desenare (Pascal şi C++) Cu siguranţă. calea indicată greşit. placa video utilizată de dvs.."E:\\BORLANDC\\BGI"). . ce poate reda 16 culori.186 Capitolul 7. 6 – brown (maro). cout<<"Apasa o tasta pentru a inchide. are performanţe superioare modului standard VGA. } Tentativa de iniţializare grafică poate eşua din diverse motive. Fiecare culoare de bază are atribuită o constantă de la 0 la 15. exit(1). Întrucât în acest moment nu sunt necesare o multitudine de culori. gmode := VGAHI.

begin main() initg. până la o nouă poziţie.y1).Manual de informatică pentru clasa a XI-a 187  Pentru a seta culoarea de fundal. Pentru a ne muta la poziţia (x..  Selectarea culorii cu care se desenează se face cu ajutorul procedurii (în Pascal) sau funcţiei (în C++) setcolor(culoare).. Acestea întorc valoarea minimă şi respectiv. . setcolor(RED). . un punct se reprezintă printr-un pixel de coordonate x (linia) şi y (coloana).0).  În cazul limbajului C++. lineto(getmaxx(). este prezentat un program ce desenează o linie pe diagonala principală a ecranului (de la colţul din stânga-sus la colţul din dreapta jos): Varianta Pascal Varianta C++ . se utilizează procedura (în Pascal) sau funcţia (în C++) setbkcolor(culoare). moveto(0. Observaţii  Schimbarea culorii nu afectează ce am desenat anterior.0). getch()..y) şi (x1. sau setbkcolor(RED). vom utiliza procedura (în Pascal) sau funcţia (în C++) lineto(x1. Exemplu: setcolor(15).y). ambele valori întregi. Punctul din stânga-sus are coordonatele (0. două funcţii foarte utile sunt getmaxx şi getmaxy (în Pascal) sau getmaxx() şi getmaxy() (în C++).. Astfel.. vom obţine o linie între punctele de coordonate (x. setcolor(red). moveto(0.0).y).y1). Astfel. } De asemenea. { init(). Mai jos. Exemplu.. end. cu ajutorul lor se poate obţine o independenţă relativă a programelor faţă de modul grafic al sistemului. vom folosi procedura (în Pascal) sau funcţia (în C++) moveto(x. Pentru a trasa o linie de la punctul curent. maximă a coordonatelor de pe ecran.. determinat anterior. lineto(getmaxx.getmaxy). Operaţia de desenare Oricare ar fi modul de lucru ales. sau setcolor(WHITE). Exemple: setbkcolor(6). readln.getmaxy()). ci doar ce este scris după apelul acestei rutine. numele simbolic al culorii se scrie obligatoriu cu majuscule..

Rotaţia o efectuează procedura rotplan. notat cu k: x1 − k ⋅ x2 y1 − k ⋅ y 2 xp = . se elimină segmentul din mijloc şi se construieşte deasupra un triunghi echilateral): Figura 7. yp = . iar B are coordonatele (x2. Fiecare latură a sa se transformă aşa cum se vede în figura următoare (se împarte în trei segmente congruente. Să se vizualizeze figura obţinută după ls paşi (număr citit de la tastatură). Pentru a înţelege funcţionarea procedurii. numărul de transformări făcute (n) şi numărul de transformări care trebuie efectuate (ls).1. Fie AB un segment de dreaptă. o procedură numită generator. xp = .3. .y2). Metoda DIVIDE ET IMPERA 7. Exemplu de transformare Fiecare latură a acestui poligon se transformă din nou.y1) şi B(x2. 1− k 1− k Demonstraţi singuri aceste formule! Fie segmentul AB cu A(x1. după aceeaşi regulă. Aflăm coordonata punctului D: DA x1 + 2 ⋅ x 2 y1 + 2 ⋅ y 2 k= = −2.188 Capitolul 7. Considerăm punctele C şi D care împart segmentul în trei segmente congruente. unde A este un punct de coordonate (x1.y2). având ca parametri de intrare coordonatele punctelor care constituie extremităţile segmentului. Acestea se obţin dacă se roteşte punctul C în jurul punctului D cu unghiul π / 3 . Două puncte P1 şi P2 împart segmentul într-un anumit raport. Programul principal va apela.y1).2. pentru fiecare segment care constituie o latură a triunghiului. DB 3 3 Problema constă în stabilirea coordonatelor vârfului noului triunghi echilateral. trebuie să avem un minimum de cunoştinţe specifice geometriei analitice (ce se poate face fără matematică?). Aceasta execută transformarea de ls ori. yp = . Această curbă este cunoscută în literatura de specialitate ca fiind curba lui Koch (Herge von Koch a fost matematician suedez şi a imaginat această curbă în anul 1904). Curba lui Koch pentru un triunghi echilateral Se consideră un triunghi echilateral.

• contrar.y2). se apelează procedura care trasează linia frântă ACVDB. div((y1+2*y2). • în cazul în care segmentul nu a fost transformat de ls ori.int y2.quot. lineto(x2. int x2.y1).h> ls:integer..gmode.Manual de informatică pentru clasa a XI-a 189 Să prezentăm algoritmul care stă la baza procedurii generator: • se porneşte de la segmentul AB. div((y1+2*y2).y2. (y1-yc)*sin(unghi)).y3). (2*y1+y2) div 3).y2).x3.int y3) y2. lineto(x2.quot.h> #include <math. void desenez(int x1. lineto(x3.3).crt.int y1.x2.int x3. int x2.y:integer. begin int x1.3).3). În programul principal au fost alese punctele care determină triunghiul echilateral iniţial – plasat în centrul ecranului – şi pentru fiecare segment ce constituie o latură a acestuia s-a apelat procedura generator. begin lineto(div((2*x1+x2).quot).float unghi) cos(unghi)-(y1-yc)* {x = ceil(xc+(x1-xc)*cos(unghi)- sin(unghi)). • se determină coordonatele punctului care constituie vârful triunghiului echilateral (să-l notăm cu V). { . se colorează interiorul acesteia.y. int ls) end.M_PI/3).quot.y3:integer). se apelează generator pentru segmentele AC.h" var L. procedure rotplan(div((2*x1+x2). .x2.ymax:integer.quot. var x. lineto((2*x1+x2) div 3.int yc.3).y. int y1.y:integer.int y1.int n. x := round(xc+(x1-xc)* int &y. #include "graphics.ls:integer).quot. } (y1+2*y2) div 3).int &x. div((2*y1+y2). var x.gdriver.h> procedure initg. CV.y1. void generator(int x1.int y2.yc.3). VD şi DB. cos(unghi)) } end. Programul este următorul: Varianta Pascal Varianta C++ uses graph. void init() procedure rotplan(xc.h> xmax. { moveto(x1. { int x.y3).. div((x1+2*x2). generator(x1. int gdriver. procedure desenez(x1.quot. lineto(div((x1+2*x2).y1.3). Odată trasă curba. div((2*y1+y2).L. lineto((x1+2*x2) div 3. x.y1).ls.. lineto(x3. .x1. n. void rotplan(int xc.. } y1:integer.quot).3). #include <stdlib.3). #include <iostream.gmode. unghi:real). moveto(x1. y := round(yc+(x1-xc)* y = ceil(yc+(x1-xc)*sin(unghi)+ sin(unghi)+(y1-yc)* (y1-yc)*cos(unghi)). #include <conio.

ls). 3).div((2*y1+y2).quot. ceil(L*(sqrt(3)/2)). } Priviţi mai jos rezultatele obţinute pentru diferite valori ale lui ls: ls = 2 ls = 3 ls = 4 Figura 7. x. generator(160+div(L.quot.2) setfillstyle(1. generator(160+L.n+1. end.div((2*x1+x2).x.2).x.ls).getmaxy-150.ls). 160+L div 2. } x2. if n<ls then generator(div((2*x1+x2).1.quot.quot. end. readln closegraph().y.160. generator(160. 3). getmaxy()-150- 160+L. 3). (2*y1+y2) div 3.y1.x. (y1+2*y2) div 3. floodfill(div(getmaxx().blue). begin 3). setfillstyle(1. } generator((x1+2*x2) div 3. readln(ls).1.1.(x1+2*x2) div 3).quot. write('ls= ').quot.(x1+2*x2) div 3).y2. div 3.y.quot.y. initg.160+div(L.div((x1+2*x2). getmaxy div 2.pi/3).getmaxy-150.getmaxy()-150.ls).x2.1.160.quot.y1. 3). {generator(x1.n+1.ls).quot.n+1.ls).y2.x.quot.190 Capitolul 7. generator(160.(y1+2*y2) div 3. 3. generator(div((x1+2*x2).y. cin>>ls.n+1. red).quot. generator(160+L.1.x2. init(). begin L = getmaxx()-320.y).1.y1. 160+L. generator(x.6). setcolor(red). getmaxy-150.y1.y2.ls). 150-L*round(sqrt(3)/2).ls).getmaxy-150. .ls). floodfill(getmaxx div 2.n+1.getmaxy()- L:=getmaxx-320.getmaxy-150 – getmaxy()-150- L*round(sqrt(3)/2). generator(160+L div 2. getmaxy()-150. generator(x1.(y1+2*y2) div 3.x2.y.4). (2*y1+y2) div 3. getch().quot. 3.ls). 150.n+1. Metoda DIVIDE ET IMPERA begin if (n<ls) rotplan((2*x1+x2) div 3.div((y1+2*y2). else desenez(x1.(2*y1+y2) div 3.2. setcolor(6).n+1.(2*x1+x2) 3).ls).y).ls). { cout<<"ls= ".div((y1+2*y2). generator(x.getmaxy()-150. 2).y2.getmaxy. Exemple de fractali formaţi cu ajutorul curbei lui Koch (triunghi echilateral) .ls). generator((2*x1+x2) div 3. n+1. ceil(L*(sqrt(3)/2)). end main() else desenez(x1.div((2*y1+y2).div(getmaxx().2).ls).

x1. y1:integer. Programul este prezentat în continuare: Varianta Pascal Varianta C++ uses graph. var x.ls:integer.. Exemplu de transformare Fiecare segment al liniei frânte astfel formate se transformă din nou după aceeaşi regulă. Fiecare latură a sa se transformă după cum se vede în figura de mai jos: Figura 7. Curba lui Koch pentru un pătrat Se consideră un pătrat. { . #include <math.. se desenează linia frântă obţinută.h" var gdriver.crt.gmode. Aceasta are ca parametri de intrare coordonatele punctului care determină segmentul.3.gmode.h> #include <stdlib. .ls. Procedura conţine următorul algoritm: • dacă nu a fost efectuat numărul de transformări necesar.. #include <iostream.3.y:integer. se calculează coordonatele punctelor care determină linia frântă obţinută pornind de la segment şi pentru fiecare segment din această linie se reapelează procedura desen..Manual de informatică pentru clasa a XI-a 191 7. numărul de transformări efectuate (n) şi numărul de transformări cerut (ls). • contrar. void init() unghi:real). #include "graphics.L. #include <conio. figura se colorează.h> procedure rotplan(xc.h> procedure initg.. Transformarea şi desenarea unui segment sunt realizate de procedura desen. În final..h> .3. Se cere să se vizualizeze curba după ls transformări (valoare citită de la tastatură). } .yc. int gdriver.

x1.n+1.ls).y5.300. setfillstyle(1.yc.y6. rotplan(xc.ls). -M_PI/2).y7).x2.y3).x3. rotplan(x8.x6. desen(x6.y4. desen(100.y1).n+1.n+1. xc:=(x1+x2) div 2. floodfill(getmaxx div 2.y4. rotplan(xc.ls). y3=div(3*y1+y2.x8.n+1. yc:=(y1+y2) div 2.ls:integer). desen(x5.div(getmaxy().300. end lineto(x8. . x8:=(x1+3*x2) div 4. if (n == ls) lineto(x4. desen(300. desen(x3. y2. -pi/2).ls). float unghi) y3.x1.y2). var x3.x6. rotplan(xc.300. Metoda DIVIDE ET IMPERA procedure desen(x1.int y1.n+1.3).blue).y6.y4. desen(300. desen(100. readln(ls).y8. desen(x1. lineto(x6.n+1.192 Capitolul 7.n+1.x2.quot. desen(x8. rotplan(x3.y1.xc.2).1. readln getch().y7.ls).x8. desen(x3.x7.y3.y8.100.y5. { int x3.y6. } end.ls).100.1.n+1. M_PI/2). x6.quot.y5.. lineto(x3.100.xc. desen(x5. M_PI/2).xc.100. moveto(x1.300.x3.int &x.ls).y8). } } begin write('ls= '). desen(x7.yc.ls).1.ls).ls).y5.y8). xc=div(x1+x2.y5.y6.x7.y2).1.y5).xc.yc.y3.y3.y3.y3.y4.y7. desen(x4.y2.ls).n+1. desen(x1.y1). desen(300.y6).n+1.int begin x2.quot.ls). setcolor(red).y3.y3.100.ls).100.1. lineto(x5. setcolor(6). rotplan(x3.x5. red). y4.2). if n = ls then begin desen(x7.quot. { moveto(x1.y3.yc:integer.1.x6.1.ls).yc.x8.x8. lineto(x8.100.x3.x5.y6. desen(300. main() { cout<<"ls= ".y7.y6.300. closegraph(). pi/2).y8.y3.300.ls).300. desen(xc.int ls) x3:=(3*x1+x2) div 4. rotplan(xc.ls). lineto(x2. x8=div(x1+3*x2. lineto(x4.y8.x7..y3).x5.pi/2).x2.y8.x4.2) getmaxy div 2. initg.y7.2).xc.int &y. begin } if n<=ls then void desen(int x1.yc. int x1. y8:=(y1+3*y2) div 4.quot.y6.ls).x3.x4.300. lineto(x3.y7.x7. lineto(x7.n+1.int y1. } . setfillstyle(1. lineto(x7.yc.x4.yc. desen(x8.y1.int n. end lineto(x2.yc.ls).x4.x3.100.x7.int yc.y5. 4). void rotplan(int xc.yc. floodfill(div(getmaxx(). y3:=(3*y1+y2) div 4. { .x5.100.x5.yc. { x3=div(3*x1+x2.100.y7).y4.ls).y5. xc.quot. cin>>ls.ls). rotplan(x8.y7. desen(100.300.300.x4.y4). desen(x6.y1.300.4).ls).y8.6).n.x6.y6.100. -M_PI/2).n+1.n+1.x4.y3.n+1.y3.ls). desen(x4.1. y8=div(y1+3*y2. if (n<=ls) -pi/2).quot.x7.y4.ls). desen(100. lineto(x5. yc=div(y1+y2.300.y7. end.n+1.y5).100.y7.y8. 4).n+1.y4).ls).y8.quot.y6). lineto(x6.y5.x5.y1.x6. desen(xc.4).y4.int y2.yc.x3.y1.y2. init().

Exemple de fractali formaţi cu ajutorul curbei lui Koch (pătrat) 7.4. pentru diferite valori ale lui ls: ls=1 ls=2 ls=3 Figura 7. aşa cum se vede în figura de mai jos: Figura 7.4. • se roteşte punctul în jurul lui B cu unghiul −π / 4 . Fiecare latură se transformă în mod asemănător. Pentru obţinerea ramurilor se procedează astfel: • se consideră punctul situat pe dreapta determinată de segment şi pentru care avem: CA x1 − 3 ⋅ x 2 3 ⋅ x 2 − x1 y1 − 3 ⋅ y 2 3 ⋅ y 2 − y1 k= = 3.y2) cu un unghi de π / 4 . Exemplu de transformare în cazul unui arbore Lungimea fiecărei ramuri este o treime din lungimea iniţială a segmentului. Cu ajutorul lui se construieşte un arbore.3. . xc = = .Manual de informatică pentru clasa a XI-a 193 Sunt prezentate mai jos imaginile obţinute în urma rulării programului.5. după ls transformări. yp = = . Arborele Se dă un segment AB. Se cere să se vizualizeze figura astfel rezultată. CB 1− 3 2 1− 3 2 • se roteşte acest punct în jurul punctului B(x2.

void rotplan(.h> #include <math. 2. rotplan(x2. se calculează coordonatele punctelor care determină ramurile şi.int y1.n+1. begin if (n<=ls) if n<=ls then { setcolor(1+random(15)). var x.. getmaxy.y2. unghi:real). { int x.-pi/4). cin>>ls.y. { .ls). În cazul în care nu s-au efectuat toate transformările. desenez(x2. void init() procedure rotplan(xc.y. setcolor(6). begin moveto(x1..) .y1. rotplan(x2..x. randomize. readln closegraph().gmode. end. desenez(getmaxx div 2. lineto(x2. div(getmaxx(). moveto(x1.y2..y1)..int n..(3*x2-x1) div . } y1:integer..-M_PI/4).194 Capitolul 7. getch(). getmaxx div 2.div(3*x2- desenez(x2. #include <stdlib.x.y.x.y2. desenez(div(getmaxx(). getmaxy()-250.div(3*y2-y1. #include <conio. int gdriver.quot.ls).n+1.2).quot.y2.int y2. .y.pi/4).x. se reapelează procedura.n+1. Procedura desenez are ca parametri de intrare coordonatele unui segment.getmaxy().ls:integer). } end } end.y.x. n.x2. pentru fiecare segment.y:integer.y. rotplan(x2.L.ls:integer. x1. cout<<"ls= ".(3*x2-x1) div 2).y2. void desenez(int x1. #include "graphics.y2.x1.1. Metoda DIVIDE ET IMPERA În urma acestor rotaţii se obţin coordonatele punctelor care.2).h> xmax.quot.y2. write('ls= ').gmode.h> var gdriver.n+1. desenez(x2. desenez(x2.x. int x2.ls).M_PI/4).int ls) var x.quot.div(3*x2- lineto(x2.y2. determină segmentele ce costituie ramurile arborelui. init().y.2) setbkcolor(white)..ls).y. getmaxy-250.h> procedure initg. 2. readln(ls). x1.1.quot. .quot..ls. Programul este prezentat mai jos: Varianta Pascal Varianta C++ uses graph. initg.2) rotplan(x2.ymax:integer.y2). main() begin { randomize().(3*y2-y1) div 2. { .y.y:integer. împreună cu punctul B.y2.2). numărul de transformări efectuate (n) şi numărul de transformări care trebuie efectuate (ls).(3*y2-y1) div 2.y1). setcolor(1+random(15)).ls).div(3*y2-y1. } ..x. } procedure desenez(x1.h" #include <iostream.crt.ls).y2). se trasează segmentul (cu o culoare oarecare).yc.x.

Exemple: pentru x=1. . Referitor la problema anterioară: care este complexitatea algoritmului folosit? Se va considera ca operaţie de bază adunarea. 2. Se cere să se scrie o funcţie care calculează ln(a) cu 3 zecimale exacte. număr real. timpul de efectuare al calculelor poate fi şi de ordinul zecilor de secunde. 4. Probleme propuse 1. Se ştie că ecuaţia x3+x-1=0 are o singură rădăcină reală în intervalul (0. pentru x=-12. Scrieţi un program.Manual de informatică pentru clasa a XI-a 195 Pentru diverse valori ale parametrului de intrare ls. Să se afişeze partea fracţionară. care o afişează cu 4 zecimale exacte. Nu este permisă utilizarea funcţiei logaritmice a limbajului. vom obţine arborii: ls = 3 ls = 5 ls = 7 Figura 7.23.6.7.7. se va afişa: 0. tehnica aplicată fiind DIVIDE ET IMPERA.  Generarea fractalilor reprezintă o aplicaţie a recursivităţii.1). Se citeşte un număr real x∈(-10000. Pentru valori mari ale lui ls. detaliile sunt greu de observat peste o anumită limită. Exemple de fractali de tip arbore Observaţii  Exemplele grafice prezentate au fost generate pentru valori mici ale lui ls deoarece la tipărire. Nu se vor folosi funcţii specializate ale limbajului. 5. 3. ceea ce poate fi considerat un inconvenient major.23. se va afişa 0. Scrieţi o funcţie care calculează prin metoda DIVIDE ET IMPERA suma numerelor reţinute dintr-un vector. Se citeşte a≥1. 10000).

h> double. end. Condiţia de terminare este ca li − ls < 0. LogN:=LogN(a. altfel rădăcina este în [m.double li.ls).li. rădăcina se găseşte în (li. if abs(li-ls)<0. Problema selecţiei. Se consideră un vector care reţine n numere naturale. din punct de vedere informatic.li. Varianta Pascal Varianta C++ var a:real. Cum f(x) este strict crescătoare (ca diferenţă între funcţia strict crescătoare ex şi o constantă).li. Metoda DIVIDE ET IMPERA 6. } .0001) else return (li+ls)/2. Cu pătratele rămase se procedează la fel. Pătratul din mijloc se elimină.0001 . Victor Mitrana 8.(li+ls)/2.ls) else return LogN(a.h> function LogN(a. m=(a+b)/2. if a=1 then LogN:=0 double ls) else { if (a==1) return 0. Se cere să se determine al t-lea cel mai mic element. rezultă că f(x) are o rădăcină în intervalul (0.a). begin double LogN(double a. double a. Algoritmul pe care îl folosim se numeşte în matematică “metoda înjumătăţirii intervalului”. să se calculeze [ x ] cu patru zecimale exacte! Nu se vor folosi 3 funcţii specializate ale limbajului. Fiind dat x real. atunci trebuie rezolvată ecuaţia f(x)=0. Se consideră un vector cu n componente numere naturale şi 1≤t≤n. (li+ls)/2. De aici. Răspunsuri 1. dar. Imaginaţi o rezolvare care utilizează funcţia Poz de la sortarea rapidă! 7. ln(a)=x ⇔ a=ex ⇔ ex-a=0. Dacă notăm cu f(x)=ex-a. #include <iostream.ls:double): #include <math. Fie li=0 şi ls=a.ls).0001 else then LogN:=(li+ls)/2 if (fabs(li-ls)<0. pentru că trebuie să avem 3 zecimale exacte. if (exp(li)-a)* else (exp((li+ls)/2)-a)<0 if ((exp(li)-a)* then (exp((li+ls)/2)-a)<0) LogN:=LogN(a.196 Capitolul 7. else (li+ls)/2). Vizualizaţi figura după ls astfel de transformări (Covorul lui Sierpinski). Se cere să se determine dacă există un element majoritar (adică un număr care se găseşte în mai mult de [n / 2] + 1 elemente). rădăcina este unică. Avem f(0)=e0-a=1-a<0 şi f(a)=ea-a>0. Dacă f(li)×f(m)<0. corespunde metodei DIVIDE ET IMPERA. 9.m).(li+ls)/2) return LogN(a. Se pleacă de la un pătrat a cărui suprafaţă se divide în 9 părţi egale prin împărţirea fiecărei laturi în 3 părţi egale.

luaţi n=2k: 0 n = 1.LogN(a. la fiecare pas se înjumătăţeşte intervalul în care se caută soluţia şi aceasta corespunde strategiei generale DIVIDE ET IMPERA. readln(a). Programul este prezentat mai jos: Varianta Pascal Varianta C++ type vector=array[1. dacă calculăm x − [x ] .. (li+ls)/2)+ Suma(li.   2  4.i<=n. Partea fracţionară se obţine uşor. end.n)). int ls) n. + Suma((li+ls) div 2+1. A calcula [x] se reduce la DIVIDE ET IMPERA. { cout<<"a=". writeln(' rezultat preluat '. pentru a obţine rezultatul corect. end. var v:vector. dar. Pentru simplitate. se obţine O(n).0.i++) end.ls). int n. } Practic.9] of #include <iostream. <<log(a)<<endl. <<LogN(a. begin } if li=ls then Suma:=v[li] main() else { cout<<"n=".a)<<endl. consideraţi n=2k.a):3:3). În final.  T(n) =   n  2T  + 1 altfel.ls). write('n='). for (i=1. .i:integer. writeln(suma(1.i. writeln(' rezultat cout<<"rezultat calculat " calculat:'. else return function Suma(li. cin>>v[i]. Suma((li+ls)/2+1. Puteţi scrie şi funcţia recursivă care calculează T(n). Fiecare problemă se descompune în alte două şi rezultatul se adună. { if (li==ls) return v[li].(li+ls) div 2) cin>>n. cin>>a. int Suma(int li.0. } readln(n).h> integer.ls:integer):integer. for i:=1 to n do readln(v[i]). 2. Suma:=Suma(li.n). begin cout<<Suma(1.v[10]. cout<<"rezultat preluat " ln(a):3:3).Manual de informatică pentru clasa a XI-a 197 begin main() write ('a='). 3.

if (t<k) ls=k-1. După aplicarea algoritmului de la problema anterioară. Vedeţi problema 1. <<a[t]. elementul căutat are indicele cuprins între li şi k-1 şi reluăm rularea funcţiei Poz între aceste limite. 7. se ajunge în situaţia în care t=k. care se află după rularea funcţiei pe poziţia k. until t=k. după rularea ei.ls. repeat do poz(li. Metoda DIVIDE ET IMPERA 5.ls. 6.k. Altfel spus: elementul A[1]. . Dacă t<k. toate elementele de indice mai mic decât k sunt mai mici sau egale decât A[k] şi toate elementele de indice mai mare decât k sunt mai mari sau egale decât A[k]. { poz(li. în cazul în care k=t.k. elementul din mijloc trebuie să fie majoritar.198 Capitolul 7.a). cout<<"elementul cautat " a[t]). if t>k then li:=k+1. problema este rezolvată. elementul căutat are indicele între k+1 şi ls şi reluăm rularea funcţiei Poz între aceste limite. Funcţia Poz returnează poziţia k pe care se va găsi. Atunci. În plus. if (t>k) li=k+1.a). Secvenţa este: Varianta Pascal Varianta C++ li:=1. writeln('Elementul cautat '. la fiecare pas. ls:=n. if t<k then ls:=k-1. iar dacă t>k. li=1. Datorită faptului că. primul element al vectorului. este al k-lea cel mai mic element din vectorul A. se restrânge numărul valorilor de căutare. ls=n. }while (t!=k).

331. 332. Se citeşte un număr natural n. 133. 231. Cum se poate rezolva această problemă? O primă soluţie ar fi să generăm toate elementele produsului cartezian: {1.2. 211..  nu se dispune de o altă metodă de rezolvare. 12. . În continuare.2.n}. 131. acela al generării permutărilor.2. 223. 122. 199 Capitolul 8 Metoda BACKTRACKING 8. pentru 132 n=3. xn∈An.3}×{1....3}={11.3}×{1. 322.xn.2. 323. x2∈A2.2.2. Fie permutarea 213. 312. 233. 113.2. 321 8. Ea 312 este scrisă sub formă de vector. 132. cu x1∈A1. unde 2∈A1.. mai rapidă. Să se 123 genereze toate permutările mulţimii {1. 112. 313.. 311. 33} ×{1. 222.. 221. De exemplu. 213. 213 231 Pentru această problemă...1. 212... Principiul care stă la baza metodei backtracking Principiul care stă la baza metodei backtracking va fi prezentat printr-un exemplu. 333}.. este prezentat un exemplu de problemă care poate fi rezolvat prin utilizarea tehnicii backtracking. 23.3} sunt prezentate alăturat. . Generarea permutărilor.1. 31.  mulţimile A1.1. permutările mulţimii {1.1. An sunt mulţimi finite. A1=A2=A3={1. 22. ..2.. 21. 32. Când se utilizează metoda backtracking ? Metoda backtracking se foloseşte în rezolvarea problemelor care îndeplinesc simultan următoarele condiţii:  soluţia lor poate fi pusă sub forma unui vector S=x1. 13. iar elementele lor se consideră că se află într-o relaţie de ordine bine stabilită. 123. 1∈A2 şi 3∈A3. A2. 321. 232. Prezentarea metodei 8.3}.3}= {111. 121.x2.

Întrucât există 1 permutări care încep cu 1.xn. vom reţine valoarea următoare.. trecem la elementul 2 . pentru componenta k. oricare ar fi numărul care urmează pe poziţia 3. vom trece la elementul 3 (înainte).xk. din care permutări sunt doar n!.m. dar 123 este permutare. doar 6 sunt permutări. 112… nu sunt permutări. De exemplu. Metoda backtracking Apoi.  Componenta 1 va memora numărul 1.  Nu există permutări care sunt de forma 1. e clar că nu se poate obţine o permutare.2. motiv pentru 1 2 3 care aceeaşi componentă va memora numărul următor. atunci când am generat deja x1x2. motiv pentru care. produsul cartezian are nn elemente. adică 2. deoarece nu este alcătuit din numere distincte. adică să conţină numai numere distincte. ori mergem la componenta k-1 (facem pasul înapoi).1. motiv pentru 1 2 2 care aceeaşi componentă va reţine numărul următor. reluăm căutarea pentru componenta k-1. avem două posibilităţi: ori trecem la componenta k+1 (facem pasul înainte). Produsul cartezian are 27 de elemente şi dintre ele. să ne dăm seama că nu este permutare. Întrucât există permutări care încep cu 1. 1 1  Nu există permutări care încep cu 1. 1 2 pentru aceeaşi componentă.facem pasul înainte.2. Principiul metodei  Metoda backtracking are la bază un principiu simplu: dacă în procesul de generare a unui vector soluţie S=x1x2..a.2..  Componenta 3 va memora numărul 1. . 2. Astfel.. constatăm că valoarea xk nu este bine aleasă (păstrând-o nu se va ajunge la o soluţie).. în cazul în care n=3.d. În general.1.  Componenta 2 va memora numărul 1. 111. iar dacă această valoare nu există.. dacă la un pas al algoritmului am generat 22. 1 2 1  Nu există permutări care sunt de forma 1. urmează să vedem care dintre elementele acestui produs cartezian sunt permutări. Am obţinut deja o primă soluţie şi o afişăm. Trecem la exemplificarea algoritmului pentru generarea permutărilor. adică 3. nu trecem componenta k+1 ci reluăm căutarea pentru altă valoare pentru componenta k. Observaţi faptul că după ce am analizat posibilele valori pe care le poate lua componenta k. Vă daţi seama că un astfel de algoritm de generare a permutărilor este ineficient… Întrebarea este dacă problema nu se poate rezolva eficient? Să observăm că nu are rost să generăm un element al produsului cartezian pentru ca apoi.200 Capitolul 8.2. ş.

1 trecem la valoarea următoare 2.. Din acest motiv.1. vom trece la elementul următor. după înţelegerea metodei backtracking.  Exerciţiu..3. if (valid(k)) back(k+1). vom trece la elementul 2. O modalitate de implementare a metodei backtracking Pentru uşurarea înţelegerii metodei. o astfel de abordare conduce la programe cu multe instrucţiuni. void back(int k) begin { if solutie(k) if (solutie(k)) tipar(). mai întâi vom prezenta un subprogram general. 3 (înainte). Sarcina celui care face programul este să scrie explicit.  Evident. begin while(succesor(k)) init(k). then tipar else else { init(k). Întrucât nu 1 3 2 există permutări de forma 1. Arătaţi cum funcţionează algoritmul până se ajunge la componenta de indice 0.3. 8. Dar.  Prima valoare care poate fi memorată este 1. El va fi apelat prin back(1). . (înapoi). Subprogramul va apela alte subprograme care au întotdeauna acelaşi nume şi parametri şi care.3.Manual de informatică pentru clasa a XI-a 201  Pentru componenta 3. Întrucât există permutări care încep cu 1. Alegem valoarea următoare. while succesor(k) do } if valid(k) then back(k+1) } end end. din punct de vedere al metodei. realizează acelaşi lucru. principiul rămâne nemodificat. vom renunţa la această formă standardizată.2 este soluţie şi o afişăm. Varianta Pascal Varianta C++ procedure back(k:integer).3. Iată subprogramul care implementează metoda. Din acest motiv. subprogramele apelate de acesta. În acel moment. . pentru fiecare problemă în parte.  Algoritmul continuă până când se ajunge la componenta de indice 0. au fost deja afişate toate permutările. aplicabil oricărei probleme. nu există o altă valoare pe care o 1 3 0 putem utiliza. Dar 1. Componenta 2 are deja memorată valoarea 2. 3.

se iniţializează nivelul k. Acest test este realizat de subprogramul valid:  în cazul în care condiţiile sunt îndeplinite. Se va folosi subprogramul init.  În situaţia în care nu a fost obţinută o soluţie. subprogramul se va apela cu back(1). Algoritmul va porni cu componenta de indice 1. Acest parametru are semnificaţia de indice al componentei vectorului pentru care se caută o valoare convenabilă. După cum observaţi. Cum subprogramul este recursiv. Din acest motiv. Pentru permutări.. la componenta k-1. pe rând. Programul de generare a permutărilor este prezentat în continuare. Pentru această operaţie se va utiliza subprogramul tipar. Pentru permutări. toate valorile mulţimii Ak.  Pentru fiecare valoare generată. Metoda backtracking Să-l analizăm! Subprogramul are parametrul k de tip întreg. .. în acest caz este ca valoarea aflată pe nivelul k să fie distinctă în raport cu valorile aflate pe nivelurile inferioare. acest fapt se întâmplă atunci când s-a ajuns pe nivelul n+1.2. Rolul său este de a atribui componentei k valoarea următoare celei deja existente. se apelează subprogramul solutie(k). Pentru aceasta se utilizează subprogramul succesor.202 Capitolul 8. Iniţializarea se face cu valoarea aflată înaintea tuturor valorilor posibile. vom avea o soluţie când s-a generat o secvenţă alcătuită din n numere distincte. urmând ca generarea valorilor pe nivelul k să continue atunci când se revine pe acest nivel.n}.  Dacă s-a obţinut o soluţie. implicit. Condiţia de continuare. iar algoritmul se încheie când k=0..  dacă condiţiile de continuare nu sunt îndeplinite. se trece la componenta k+1. aceasta se afişează.. Pentru permutări. subprogramul este recursiv. pe fiecare nivel. iniţializarea se face cu 0. valorile posibile sunt Ak={1. Pentru aceasta.  Iniţial se testează dacă s-a generat o soluţie.  După iniţializare.  După ce au fost generate toate valorile mulţimii Ak se trece. se generează. se generează următoarea valoare pentru componenta k. se testează dacă aceasta îndeplineşte anumite condiţii de continuare.

Manual de informatică pentru clasa a XI-a 203

Varianta Pascal Varianta C++
var n:integer; #include <iostream.h>
sol:array [1..10] of #include <iostream.h>
integer;
int n, sol[10];
procedure init(k:integer);
begin void init(int k)
sol[k]:=0 { sol[k]=0;
end; }
function succesor int succesor(int k)
(k:integer):boolean; { if (sol[k]<n)
begin { sol[k]++;
if sol[k]<n then return 1; }
begin else return 0;
sol[k]:=sol[k]+1; }
succesor:=true int valid(int k)
end { int i, ev=1;
else succesor:=false for (i=1;i<=k-1;i++)
end; if (sol[k]==sol[i]) ev=0;
function valid return ev;
(k:integer):boolean; }
var i:integer; int solutie(int k)
begin { return k==n+1;
valid:=true; }
for i:=1 to k-1 do
if sol[i]=sol[k] then void tipar()
valid:=false { for (int i=1;i<=n;i++)
end; cout<<sol[i];
cout<<endl;
function solutie }
(k:integer):boolean;
begin void back(int k)
solutie:=(k=n+1) { if (solutie(k)) tipar();
end; else
{ init(k);
procedure tipar; while(succesor(k))
var i:integer; if (valid(k)) back(k+1);
begin }
for i:=1 to n do }
write(sol[i]);
writeln main()
end; { cout<<"n="; cin>>n;
back(1);
procedure back(k:integer); }
begin
if solutie(k)
then tipar
else
begin
init(k);
while succesor(k) do
if valid(k) then
back(k+1)
end
end;
begin
write('n='); readln(n);
back(1)
end.

204 Capitolul 8. Metoda backtracking

8.1.4. Problema celor n dame

 Enunţ. Fiind dată o tablă de şah cu dimensiunea n×n, se cer toate
soluţiile de aranjare a n dame, astfel încât să nu se afle două dame pe
aceeaşi linie, coloană sau diagonală (damele să nu se atace reciproc).

De exemplu, dacă n=4, o soluţie este reprezentată în figura 8.1., a). Modul de
obţinere al soluţiei este prezentat în figurile următoare, de la b) la i):

a) b) c)

d) e) f)

g) h) i)

Figura 8.1. Exemplu pentru n=4

Manual de informatică pentru clasa a XI-a 205

Comentarii referitoare la figurile anterioare
b) Observăm că o damă trebuie să fie plasată singură pe linie. Poziţionăm prima
damă pe linia 1, coloana 1.
c) A doua damă nu poate fi aşezată decât în coloana 3.
d) Observăm că a treia damă nu poate fi plasată în linia 3. Încercăm atunci
plasarea celei de-a doua dame în coloana 4.
e) A treia damă nu poate fi plasată decât în coloana 2.
f) În această situaţie dama a patra nu mai poate fi aşezată. Încercând să
avansăm cu dama a treia, observăm că nu este posibil să o plasăm nici în coloana
a-3-a, nici în coloana a-4-a, deci o vom scoate de pe tablă. Dama a doua nu mai
poate avansa, deci şi ea este scoasă de pe tablă. Avansăm cu prima damă în
coloana a-2-a.
g) A doua damă nu poate fi aşezată decât în coloana a 4-a.
h) Dama a treia se aşează în prima coloană.
i) Acum este posibil să plasăm a patra damă în coloana 3 şi astfel am obţinut o
soluţie a problemei.

Algoritmul continuă în acest mod până când trebuie scoasă de pe tablă
prima damă.

 Pentru căutarea şi reprezentarea unei soluţii folosim un vector cu n componente,
numit sol (având în vedere că pe fiecare linie se găseşte o singură damă). Prin
sol[i] înţelegem coloana în care se găseşte dama de pe linia i.

Alăturat, puteţi observa modul în care este reprezentată
2 4 1 3
soluţia cu ajutorul vectorului sol.

 Două dame se găsesc pe aceeaşi diagonală dacă şi numai dacă este
îndeplinită condiţia:
|sol(i)-sol(j)|=|i-j|
(diferenţa, în modul, dintre linii şi coloane este aceeaşi).

Exemple:
a)
sol(1) = 1 i = 1
sol(3) = 3 j = 3
|sol(1) - sol(3)| = |1 - 3| = 2
|i - j| = |1 - 3| = 2

Figura 8.2.

206 Capitolul 8. Metoda backtracking

b)
sol(1) = 3 i = 1
sol(3) = 1 j = 3
|sol(i) - sol(j)| = |3 - 1| = 2
|i - j| = |1 - 3| = 2

Figura 8.3.

Întrucât două dame nu se pot găsi în aceeaşi coloană, rezultă că o soluţie
este sub formă de permutare. O primă idee ne conduce la generarea tuturor
permutărilor şi la extragerea soluţiilor pentru problemă (ca două dame să nu fie
plasate în aceeaşi diagonală). Dacă procedăm astfel, înseamnă că nu lucrăm
conform strategiei backtracking. Aceasta presupune ca imediat ce am găsit două
dame care se atacă, să reluăm căutarea în alte condiţii. Faţă de programul de
generare a permutărilor, programul de generare a tuturor soluţiilor problemei celor
n dame are o singură condiţie suplimentară, în subprogramul valid. Mai jos,
puteţi observa noua versiune a subprogramului valid. Dacă îl utilizaţi în locul
subprogramului cu acelaşi nume din programul de generare a permutărilor, veţi
obţine programul care rezolvă problema celor n dame.

Varianta Pascal Varianta C++
function valid(k:integer): int valid(int k)
boolean; {
var i:integer; for (int i=1;i<k;i++)
if (sol[k]==sol[i] ||
begin abs(sol[k]-sol[i])==abs(k-i))
valid:=true; return 0;
for i:=1 to k-1 do return 1;
if (sol[k]=sol[i]) or }
(abs(sol[k]-sol[i])=abs(k-i))
then
valid:=false
end;

Problema este un exemplu folosit în mai toate lucrările în care este
prezentată metoda backtracking.

În ceea ce ne priveşte, dincolo de un exemplu de backtracking, am avut
ocazia să vedem cât de mult seamănă rezolvările (prin această metodă) a
două probleme între care, aparent, nu există nici o legătură.

 Exerciţii
1. Desenaţi configuraţia tablei corespunzătoare vectorului sol=(3,1,4,2,5) şi
verificaţi dacă aceasta reprezintă o soluţie a problemei damelor.

Manual de informatică pentru clasa a XI-a 207

2. Explicaţi de ce configuraţia corespunzătoare vecorului sol=(3,1,3,4,5) nu
este o soluţie a problemei damelor. Care este poziţia din sol la care nu s-a făcut
alegerea corectă a unei valori valide?

3. Explicaţi de ce nu orice permutare a mulţimii {1,2, …, n} este o soluţie a
problemei damelor pe o tablă cu n linii şi n coloane.

4. Determinaţi, folosind metoda backtracking, o soluţie care se obţine pe o tablă cu
şase linii şi şase coloane, ştiind că dama plasată pe ultima linie trebuie să se afle
pe a doua coloană. Consideraţi că mai este util să completăm vectorul sol
pornind de la poziţia 1? Dacă pornim de la poziţia 1, ce adaptări trebuie făcute?

8.2. Mai puţine linii în programul sursă

Până în prezent, am rezolvat două probleme prin metoda backtracking:
generarea permutărilor şi problema celor n dame. În ambele cazuri, am utilizat
subprogramul standard.

Este întotdeauna necesar să-l folosim pe acesta sau putem reţine numai
ideea şi, după caz, să scriem mai puţin?

Evident, după ce am înţeles bine metoda, putem renunţa la subprogramul
standard. Pentru aceasta, încorporăm în subprogramul standard unele dintre
subprogramele pe care le-ar fi apelat.

Vom exemplifica această încorporare pentru problema celor n dame, deja
rezolvată standardizat.

 Putem elimina subprogramul init. Este foarte uşor de realizat această
operaţie. În locul apelului vom scrie instrucţiunea prin care componentei k i
se atribuie 0.

 Putem elimina subprogramul solutie. În locul apelului vom testa dacă k
este egal cu n+1.

 Putem elimina subprogramul tipar. În locul apelului vom scrie secvenţa
prin care se afişează st.

 Putem elimina subprogramul succesor. În locul apelului vom testa dacă
st[k]<n şi avem grijă să incrementăm valoarea aflată pe nivelul curent.

Puteţi observa în continuare programul obţinut. Este cu mult mai scurt!
Oricum, ideea de rezolvare rămâne aceeaşi. Subprogramele prezentate au fost
numai încorporate, nu s-a renunţat la ele.

208 Capitolul 8. Metoda backtracking

Varianta Pascal Varianta C++
var sol:array[1..9] of integer; #include <iostream.h>
n:integer; #include <math.h>
int n, sol[10];
function
valid(k:integer):boolean; int valid(int k)
{ for (int i=1;i<k;i++)
var i:integer; if (sol[k]==sol[i] ||
begin abs(sol[k]-sol[i])
valid:=true; ==abs(k-i))
for i:=1 to k-1 do return 0;
if (sol[k]=sol[i]) or return 1;
(abs(sol[k]-sol[i])=abs(k-i)) }
then void back(int k)
valid:=false { if (k==n+1) // solutie
end; //tipar
{ for (int i=1;i<=n;i++)
procedure back(k:integer); cout<<sol[i];
cout<<endl;
var i:integer; }
begin else
if k=n+1 {solutie} { sol[k]=0;
then while(sol[k]<n)
begin // succesor
{tipar} { sol[k]++;
for i:=1 to n do if (valid(k))back(k+1);
write(sol[i]); }
writeln; }
end
main()
else
{ cout<<"n="; cin>>n;
begin
back(1);
sol[k]:=0; {init}
while sol[k]<n do }
{succesor}
begin
sol[k]:=sol[k]+1;
if valid(k)
then
back(k+1)
end
end
end;
begin
write('n=');
readln(n);
back(1)
end.

Uneori veţi întâlni şi o rezolvare precum următoarea, care în subprogramul
back, pentru succesor se foloseşte o instrucţiune repetitivă de tip for:

Testaţi subprogramul anterior pentru problema generării permutărilor. 4 pe un cerc. 3. void back(int k) var i:integer. 4132. datorită distribuţiei pe cerc. Indicaţie: se va considera sol[1]=1 şi se vor permuta doar celelalte elemente. for i:=1 to n do } write(sol[i]).. { int i. 2. A2. Urmăriţi toate modalităţile diferite de a aşeza patru obiecte identificate prin numerele 1.i++) end { sol[k]=i. 3421. 2. 3412 şi 4123 reprezintă una şi aceeaşi configuraţie. begin } sol[k]:=i. Observaţi că ordinea de afişare a soluţiilor depinde de ordinea în care se consideră elementele mulţimilor A1. begin cout<<endl. else if (valid(k)) for i:=1 to n do back(k+1). . Renunţaţi la utilizarea subprogramului valid. else writeln. 4312. 4213. Adaptaţi rezolvarea problemei permutărilor astfel încât să se afişeze numai permutările în care oricare două numere consecutive nu sunt alăturate. la distanţe egale. 3. 4231.. 2341. Vom observa că nu toate permutările de patru obiecte sunt configuraţii distincte.  Exerciţii 1.i<=n. for (i=1. Scrieţi un program care afişează numai permutările distincte conform aşezării pe un cerc. utilizând un vector folosit. … Ce modificări trebuie aduse procedurii recursive back astfel încât permutarile de 4 elemente să fie afişate în ordinea: 4321. 3412 … 1243. în care folosit[i] are valoarea 0 dacă numărul i nu este deja folosit în soluţie şi are valoarea 1 în caz contrar.i<=n. begin if (k==n+1) if k=n+1 { for (i=1. Astfel.i++) then cout<<sol[i]. 5. plasarea valorii i în vectorul soluţie (sol[k]i) trebuie însoţită de memorarea faptului că i este utilizat (folosit[i]1). 4123. la revenirea din recursie (cand se înlătură valoarea de pe poziţia curentă) fiind necesară memorarea faptului că i nu mai este utilizat în soluţie (folosit[i]0). 1234? 4. Condiţia de validare se reduce în acest caz la: Dacă folosit[i]=0 atunci . } if valid(k) then back(k+1) end end. Astfel permutările 1234.Manual de informatică pentru clasa a XI-a 209 Varianta Pascal Varianta C++ procedure back(k:integer).

culoarea 2. O soluţie a acestei probleme este următoarea: 1 • ţara 1 . la obţinerea soluţiei dorite aceasta primind valoarea 1.3. pentru simplitate.  În C++. 4 • ţara 2 . De această dată. Metoda backtracking 8. Pentru exemplificare. cu parametrul EXIT_SUCCESS (o constantă). • ţara 5 . astfel încât acesta să afişeze o singură soluţie. vom considera harta din figura 8.culoarea 4.h“: #include<stdlib. utilizând cel mult 4 culori. Exemplificare: problema colorării hărţilor Sunt probleme care se rezolvă cu metoda backtracking şi în care se cere o singură soluţie.  În Pascal. se cere o soluţie de colorare a hărţii. unde ţările sunt numerotate cu cifre cuprinse între 1 şi 5.h>.4.culoarea 1.  Exerciţiu. Orice succesor va fi condiţionat în plus de valoarea variabilei gata. veţi utiliza procedura halt.culoarea 3. Pentru a o putea utiliza.4. . Cazul în care se cere o singură soluţie. trebuie să includeţi fişierul antet “stdlib. Fiind dată o hartă cu n ţări. vom renunţa la programarea structurată şi vom opri în mod forţat programul. secvenţa care determină oprirea forţată este trecută imediat după ce prima soluţie a fost afişată. veţi folosi funcţia exit. Este demonstrat faptul că sunt suficiente numai 4 culori pentru ca orice hartă să poată fi colorată. Modificaţi programul care rezolvă problema celor n dame.210 Capitolul 8. variabila gata) care să fie iniţial 0.  Problema colorării hărţilor. astfel încât două ţări cu frontieră comună să fie colorate diferit. În ambele cazuri. 3 • ţara 3 .culoarea 1. Implementarea ”ca la carte“ presupune utilizarea unei variabile de semnalizare (de exemplu. 2 • ţara 4 . 5 Figura 8..

h> integer.i++) for j:=1 to n do { sol[k]=i. back(k+1). for i:=1 to n do } for j:=1 to i-1 do back(1). { for (int i=1.j] end.j]). then back(k+1) for (int i=1. a[i][k]==1) return 0. for (int j=1. end } else } for i:=1 to n do begin main() sol[k]:=i. back(1) end.i.i. Evident. #include <stdlib.. unde sol[k] reţine culoarea ataşată ţării k. begin } write('a['. ţara i are frontiera comună cu ţara j A(i. begin valid:=true.i]:=a[i..i<k. for i:=1 to k-1 do } if (sol[k]=sol[i]) void back(int k) and (a[k.j. write(sol[j]). readln(n).Manual de informatică pentru clasa a XI-a 211 Harta este furnizată programului cu ajutorul unei matrice (tablou) An. if valid(k) cin>>n. readln(a[i. cout<<sol[i]. Pentru rezolvarea problemei se utilizează vectorul sol. a[j][i]=a[i][j].n: 1.']='). int valid(int k) { for (int i=1.i++) procedure back(k:integer).j++) end. { cout<<"Numarul de tari=". then valid:=false if (k==n+1) end. .a[10][10]. begin } if k=n+1 then else begin for (i=1.10. orice soluţie are exact n componente. n.j<=i-1. integer. write('Numarul de tari='). if (valid(k)) halt. return 1.10] of int n. exit(EXIT_SUCCESS).i++) function if (sol[k]==sol[i] && valid(k:integer):boolean.i<=4.i++) end.i]=1) { int i. { cout<<"a["<<I begin <<'. j) =  0.'.'<<j<<"]=".1. var i:integer.'. a[j.i<=n.i<=n.h> a:array[1. Varianta Pascal Varianta C++ var sol:array[1.j:integer.i..j. altfel Matricea A este simetrică.9] of #include <iostream. cin>>a[i][j].sol[10].

care este soluţia afişată de programul dat? Este acesta numărul minim de culori necesare? 3. Se dau numele a n persoane. Câte culori sunt suficiente pentru colorarea unei hărţi particulare în care orice ţară se învecinează cu cel Figura 8.. dacă mulţimea este {a. Se dă o mulţime alcătuită din n litere distincte. iar cele de la exterior cu 5 şi 6. De exemplu. bac. Metoda backtracking  Exerciţii 1. astfel încât fiecare cuvânt să conţină n litere distincte. 2. acb.n} în toate modurile posibile? Să observăm că acest algoritm poate fi folosit pentru a aranja oricare n elemente distincte în toate modurile posibile.2. . Dacă ţările din centrul figurii alăturate sunt numerotate cu 1. Daţi exemplu de particularitate pe care poate să o aibă o hartă pentru a fi suficiente două culori pentru colorarea tuturor ţărilor? 8. De exemplu.5.4.b. .2. 3. Aplicaţii ale metodei backtracking în combinatorică 8. Costel Ioana Mihaela. se pune problema să vedem de ce este util acest algoritm. O generalizare utilă Acum. mult două ţări? 4.n}. cab şi cba. 2. Ioana Mihaela Costel. Se cer toate cuvintele care se pot forma cu ele..212 Capitolul 8.1. iar persoanele sunt Ioana. La ce foloseşte faptul că putem aranja numerele {1. Soluţia afişată este şi soluţia care utilizează un număr minim de culori? 2. vom avea cuvintele: abc. că am învăţat să generăm permutările mulţimii {1.c}. 4. Se cere să se afişeze toate modurile posibile în care acestea se pot aşeza pe o bancă.. dacă n=3...4. atunci soluţiile sunt: Ioana Costel Mihaela. Exemple 1. bca. Costel şi Mihaela..

Se dau n mulţimi: A1. De exemplu.3. în al doilea caz.x2.(1.3. Scrieţi programul care rezolvă exemplul 2.2.2). f(3)=3. vom utiliza un vector a. a combinărilor sau a tuturor părţilor unei mulţimi.ki}.. Să observăm că o soluţie este de forma x1. unde a[1]= k1.2.1). de exemplu 213.3} definită astfel: f(1)=2..3. Observăm că valorile care pot fi luate sol[1] sunt între 1 şi k1. …. .2.(2. Pentru exemplul dat.3).  Rezolvare.3).. cu x1∈A1. (2. adică bac.(1...1).(1..2.1.(1..n}.(2. a[n]= kn.(2. xn∈An.3.. a[2]= k2. unde Ai={1.2).(2. nu mai este necesar să utilizăm subprogramul valid.(2. De la început observăm că este necesar să afişăm toate soluţiile.3.2)...Manual de informatică pentru clasa a XI-a 213 În astfel de cazuri.(1. în primul caz sau Costel Ioana Mihaela. dacă n=3.2)... sol[n] va conţine numerele naturale între 1 şi kn.... A2.3... Mulţimea permutărilor mulţimii {1.1.n}→{1.. .3}.2.1. 8. Din acest motiv.2.1.(1.(1. în probleme cum ar fi: generarea tuturor submulţimilor unei mulţimi.  Exerciţiu.. De aici rezultă necesitatea folosirii unui vector sol.2). sol[2] va conţine numerele naturale între 1 şi k2. Important! Să observăm că orice valoare reţinută de sol[i] între 1 şi ki îndeplineşte condiţiile de continuare (este validă).1..2. generarea aranjamentelor.4. valorile care pot fi luate sol[n] sunt între 1 şi kn. unde sol[1] va conţine numerele naturale între 1 şi k1. .3).1). Procedeul de mai sus poate fi folosit pentru oricare altă aplicaţie din combinatorică. x2∈A2.3).1).3)}. cele n elemente distincte se memorează într-un vector V. vectorul a va reţine (2.3}→{1..2.2. pentru k=1.2). (1.(2.3).. f(2)=1.3)....2.2.1).....2. aşa cum vedeţi mai jos: a b c Ioana Costel Mihaela 1 2 3 1 2 3 Atunci când s-a generat o permutare.2. ..2. An. cu n componente. A2={1.n} reprezintă toate funcţiile bijective f:{1.. valorile care pot fi luate sol[2] sunt între 1 şi k2.n.2}..(2.1.1).(2.3. cu n componente.xn. A3={1. Produs cartezian  Enunţ. Se cere produsul cartezian al celor n mulţimi.2. Exemplu: A1={1. A1×A2×A3={(1.... permutarea 213 este funcţia f:{1. Pentru a putea reţine aceste valori.3}. vom afişa V[2]V[1]V[3].2.

begin cin>>n. fără a verifica anumite condiţii. metoda backtracking caută una sau toate soluţiile.. } end } } else begin main() sol[k]:=0. în generarea produsului cartezian. back(1) end. back(1).. 2. readln(a[i]) end. for i:=1 to n do begin write('a['.i:integer. sol[k]:=sol[k]+1. write(sol[i]). end cin>>a[i].×An se mai numeşte spaţiul soluţiilor.i. puteţi observa programul care generează produsul cartezian al mulţimilor date: Varianta Pascal Varianta C++ var n. } begin else for i:=1 to n do { sol[k]=0. produsul cartezian al lor A1×A2×. back(k+1).a:array[1.×kn elemente ale produsului cartezian. end } end. nu este necesar subprogramul valid pentru că se generează toate elementele produsului cartezian. void back(int k) { if (k==n+1) procedure back(k:integer)...a[10]. #include <iostream.. .214 Capitolul 8. Astfel. An. .i++) back(k+1) { cout<<"a["<<i<<"]=". readln(n).i<=n.i<=n. } begin write('Numarul de multimi=').. În acest context.. if k=n+1 then cout<<endl. O altă interpretare pentru metoda backtracking: fiind date n mulţimi: A1. Metoda backtracking Mai jos.i++) begin cout<<sol[i]. se poate justifica faptul că. { for (i=1.10] of int n. sol[10]. for(int i=1. integer.i. while(sol[k]<a[k]) writeln { sol[k]++.h> sol. Observaţii 1. { while sol[k]<a[k] do cout<<"Numarul de multimi=". De aici rezultă că algoritmul este exponenţial. A2. care sunt elemente ale produsului cartezian şi care îndeplinesc anumite condiţii.']='). Avem k1×k2×..

3}. R.2. Aceasta înseamnă că o soluţie este de forma x1.. Generarea tuturor submulţimilor unei mulţimi  Enunţ. T. 8. pentru i ∈ A V[i] =  0. Se cere să se realizeze un tabel. 2. unde: 1. M.bi] şi f=c1x1+c2x2+.0. T}. ea nu trebuie să îndeplinească nici o condiţie de continuare. 3. pentru submulţimea {1. De aici..  Exerciţii 1.Manual de informatică pentru clasa a XI-a 215 Deşi algoritmul este exponenţial. V}.  Rezolvare. cu condiţia să nu existe două consoane alăturate şi nici două vocale alăturate.3. Se dă funcţia f:A1×A2×. Şi în acest caz. Să ne amintim că submulţimile unei mulţimi A se pot reprezenta prin vectorul caracteristic V. Scrieţi programul care generează toate ”cuvintele” cu patru litere care au prima şi ultima literă vocale. unde fiecare mulţime Ai este dată de numerele întregi din intervalul [ai. în care pentru fiecare valoare din domeniul de definiţie să se afişeze valoarea funcţiei corespunzătoare acelei valori.1). litera a doua consoană din mulţimea {P. evident. motiv pentru care subprogramul valid nu este necesar. Tabelarea anumitor funcţii. Scrieţi programul care generează şi numără câte cuvinte de cinci litere ale alfabetului englez se pot forma... puteţi observa programul care generează toate valorile pe care le poate reţine vectorul caracteristic: .4. rezultă că problema se reduce la generarea tuturor valorilor posibile pe care le poate reţine vectorul caracteristic. unde xi∈{0. există aplicaţii utile. Fiind dată mulţimea A={1..3} vom avea V=(1. R. ci∈R. atunci când fiecare mulţime Ai poate lua numai câteva valori şi unde n este suficient de mic.x2... dacă A={1..×An→R...2.+cnxn. iar a treia literă consoană din mulţimea {B.xn. orice valoare ar reţine componenta i.n}. În continuare.1}. se cere să se afişeze toate submulţimile ei.. S.. pentru i ∉ A De exemplu.

Completaţi-l astfel încât programul să afişeze toate submulţimile mulţimii {1. } begin write('n=').i<=n. se afişează combinaţiile: 0010. 1101.i. back(1) end. else writeln { sol[k]=-1. end back(1). Să se afişeze mulţimile formate din n numere prime cu proprietatea că suma elementelor din fiecare mulţime este exact s. Se citesc două numere naturale n şi s (n<10. Problema nu este rezolvată în totalitate. begin back(k+1). integer. Programul afişează numai toate valorile pe care le poate lua vectorul caracteristic. 0111. 1110. sol[10]. s<1000). se obţin valorile: 5 şi 6. #include <iostream. Să se afişeze toate numerele scrise în baza 10 a căror reprezentare în baza 2 are n cifre.2. 5. Se citesc numele a 6 elevi. k<n)..n}! 2.  Exerciţii 1. sol[k]:=-1.i++) if k=n+1 then cout<<sol[i]. back(k+1) main() end { cout<<"n=". De exemplu. end while(sol[k]<1) else { sol[k]++. 0011. end. dintre care exact k sunt egale cu 1. cin>>n. Realizaţi un program care generează combinaţii de n cifre 0 şi 1 cu proprietatea că în orice grup de 3 cifre consecutive există cel puţin o cifră de 1. 1100. 0110. De exemplu. dacă n=4. begin cout<<endl. readln(n). { if (k==n+1) begin { for (i=1. pentru n=3 şi k=2. 1010. 4.10] of int n. 1111. 0100. for i:=1 to n do } write(sol[i]). Afişaţi toate submulţimile mulţimii celor 6 elevi. } while sol[k]<1 do } begin } sol[k]:=sol[k]+1. 1001.h> sol:array[1. 1011. .216 Capitolul 8...i:integer. 3. 0101. Metoda backtracking Varianta Pascal Varianta C++ var n. Valorile n şi k se citesc de la tastatură (n<12. void back(int k) procedure back (k:integer).

. {1. 2...4.. sol[k]>sol[k-1].Manual de informatică pentru clasa a XI-a 217 Observaţii  Fiind dată o mulţime cu n elemente. putem genera elementele ei în ordine strict crescătoare. n≥p. după care se selectează cea (cele) care îndeplineşte condiţiile date.4.. Cum la o mulţime ordinea elementelor nu prezintă importanţă. soluţiile sunt următoarele: {1.. Problema este cunoscută sub numele de “generarea combinărilor de n. (n − p)! p! De exemplu. xp∈A. x2.n}.3}.  Rezolvare. Exemplu.2 =2 . n de n ori De aici rezultă că algoritmul care generează toate submulţimile mulţimii 1. Generarea combinărilor Fiind dată mulţimea A={1.4}. Problema se rezolvă uşor: se consideră ca făcând parte din submulţime numai numerele pozitive. Mulţimea dată şi submulţimea vidă sunt submulţimi ale mulţimii date! De ce? Fiecare componentă a vectorului caracteristic poate reţine două valori. a) Pentru k>1.x2.2. Se ştie că numărul soluţiilor acestei probleme este n! C pn = ... . Se dă o mulţime de numere reale.2. Altfel. . {1. avem 2n submulţimi ale ei. x1.. În anumite situaţii. Se citesc n şi p numere naturale.4} şi {2. n este exponenţial.2. dacă n=4 şi p=3... Această observaţie ne ajută foarte mult în elaborarea algoritmului..n}. 8.. xp trebuie să fie distincte.. veţi rezolva probleme în care se dă o mulţime şi se cere o submulţime a sa care îndeplineşte anumite caracteristici.2.. . În plus. se cer toate submulţimile ei cu p elemente. Se cere să se genereze toate submulţimile cu p elemente ale mulţimii A={1. O soluţie este de forma x1..... problema se poate rezolva prin utilizarea unor algoritmi mai rapizi (polinomiali).. dacă am genera toate submulţimile. unde x1... Prin urmare.3. luate câte p”.4}. numărul de submulţimi este ⋅ 2 2 ⋅ 2.3.  Uneori. x2..xp. Se cere să se determine o submulţime a sa care are suma maximă. Greşeala tipică care se face este că se generează toate submulţimile.  Enunţ....

cin>>p.... else while(sol[k]<n-p+k) begin { sol[k]++. sol[k]≤n-p+k. } else sol[k]:=0.. . readln(n). Varianta Pascal Varianta C++ var sol:array[1.218 Capitolul 8. { if (k>1) sol[k]=sol[k-1]. void back(int k) var i:integer. De aici rezultă că: 1≤sol[1]≤n-p+1. end. { int i. nu mai este necesar să se testeze nici o condiţie de continuare. Metoda backtracking b) Pentru fiecare k∈{1. sol[n-1]<sol[n]≤n-p+p=n. back(k+1). . readln(p)..i++) then cout<<sol[i]. } while sol[k]<n-p+k do } begin main() sol[k]:=sol[k]+1. back(1). int n. else writeln..sol[10]. sol[1]<sol[2]≤n-p+2.p. procedure back(k:integer). write ('p='). Deci: sol[k+1]>n-p+k+1. end } end.. cout<<"p=". begin if (k==p+1) if k=p+1 { for (i=1. begin write('n='). că această ultimă relaţie nu este respectată. end else sol[k]=0.p:integer. end back(1). if k>1 then sol[k]:=sol[k-1] back(k+1).. analiza unei probleme conduce la un algoritm cu mult mai rapid. Examinând raţionamentul propus putem observa că. în anumite cazuri. Relaţiile de mai sus simplifică mult algoritmul. Absurd.2. pentru că ţinând cont de ele. begin cout<<endl. Să presupunem. #include <iostream. for i:=1 to p do } write(sol[i]). cin>>n.i<=p.9] of integer. { cout<<"n=". Aceasta înseamnă că ∃k.h> n. sol[p]>n-p+p=n.. . astfel încât sol[k]>n-p+k. prin absurd.p}.

O soluţie este de forma: x1x2. 21.2. diferenţa fiind dată de faptul că soluţia are p numere. 8. din fiecare astfel de submulţime.... Se ştie că. să obţinem permutările ei.o soluţie are p numere din B. unde x1.. Afişaţi coordonatele vârfurilor tuturor pătratelor care au ca vârfuri puncte din mulţimea considerată. 31. Aceasta înseamnă că nu mai putem pune în soluţie elementele în ordine crescătoare. x2.. Se citesc n şi p. 23... 32. Se dau coordonatele din plan a n puncte. Fiind date p perechi de forma (i. se cer toate grupurile de s<n substanţe astfel încât oricare două substanţe din grup nu intră în reacţie..n}. Pornind de la această observaţie. Spre deosebire de algoritmul de generare a combinărilor. Să se genereze toate aranjamentele de n luate câte p. x2.4.. suntem tentaţi să generăm toate submulţimile cu p elemente ale mulţimii cu n elemente şi. x1. Să observăm că dacă se cunoaşte fiecare submulţime de p elemente a mulţimii de n elemente.. Exerciţiu! Pe de altă parte.p} şi B={1. Se cer toate funcţiile injective definite pe A cu valori în B. la rândul lor. (n − p)!  Enunţ.. .. . 2.. n=3. 21 este funcţia f:A→B dată astfel: f(1)=2. nu n ca în cazul permutărilor. unele substanţe intră în reacţii chimice cu altele... atunci aranjamentele se pot obţine permutând în toate modurile posibile elementele unei astfel de mulţimi. O astfel de problemă este una de generare a aranjamentelor de n luate câte p ( A pn ). xp∈B. . Avem: 12.2.(n − p + 1) .numerele trebuie să fie distincte. Rezultă de aici că algoritmul este acelaşi de la permutări. în anumite condiţii. xp trebuie să fie distincte. aici ne interesează toate permutările unei soluţii (acestea sunt. . alte soluţii). se poate lucra mult mai eficient.xp. Exemplu: p=2.j) cu semnificaţia că substanţa i intră în reacţie cu substanţa j. Se dau n substanţe chimice. În plus. f(2)=1.Manual de informatică pentru clasa a XI-a 219  Exerciţii 1. 13... De exemplu. Generarea aranjamentelor Se dau două mulţimi A={1. Avem relaţiile: n! A pn = = n(n − 1).. Să recapitulăm: .5.

Se citesc n. Metoda backtracking Varianta Pascal Varianta C++ var sol:array[1. cout<<sol[j]. Maria-Doru}.9]of integer.p:integer. { cin>>n. int n. for i:=1 to k-1 do } if sol[k]=sol[i] then valid:=false void back(int k) end.h> n.j. if (sol[k]==sol[i]) begin return 0.  Exerciţii 1. Ana-Cosmin}. Ana. { for (j=1. Doina. #include <iostream. pentru n=5.j++) var i. if valid(k) then cin>>p. valid:=true. { for (int i=1. De exemplu.j<=p. readln(p). {Doina-Doru. Maria.sol[10]. back(1) end. p şi apoi n litere distincte. să se afişeze toate mulţimile de perechi fată-băiat care se pot forma. {Doina-Doru. Maria-Cosmin}. back(k+1) back(1). Cosmin.220 Capitolul 8. Doina-Cosmin}.p.j:integer. begin cout<<endl. Ştiind că toate numele care se termină cu a reprezintă nume de fată. {Maria-Doru. Se citesc n şi apoi numele mici a n persoane.i<k. se afişează mulţimile: {Maria-Doru.i++) write(sol[j]). Două mulţimi sunt distincte dacă cel puţin una dintre perechi diferă. 2. end } end. { sol[k]=i. if (k==p+1) procedure back(k:integer). function int valid(int k) valid(k:integer):boolean. if k=p+1 then } begin else for j:=1 to p do for (i=1. Doina-Cosmin}. {Ana-Doru. .i<=n..i++) var i:integer. Ana-Cosmin}. return 1. writeln if (valid(k)) end back(k+1). Afişaţi toate cuvintele care se pot forma cu p dintre ele. celelalte fiind nume de băieţi. { int i. {Ana-Cosmin. Doru. else } for i:=1 to n do } begin main() sol[k]:=i. begin readln(n).

Există o partiţie care conţine n mulţimi atunci când fiecare element este într-o mulţime şi una care conţine toate mulţimile. 1-3-2. fie prin vecinii pe care îi au aceştia la masă.. Chiar dacă ştim să generăm toate submulţimile unei mulţimi.. b) Ai∩Aj=∅.4.Manual de informatică pentru clasa a XI-a 221 3. 2. n} Definiţia 8.∪Ak=A. Se numeşte partiţie a mulţimii A. Avem partiţiile: {1. sol[i] va lua valori între 1 şi 1+max{sol[1].6.. un set de k≤n mulţimi care îndeplinesc condiţiile de mai jos: a) A1∪A2∪..... atunci sunt posibile 5 configuraţii diferite ale celor 3 acţionari aşezaţi la masa rotundă: 1-2-3..n}... n. adică tocmai mulţimea A. deoarece sunt echivalente. De exemplu. Pentru a putea genera toate partiţiile. Considerăm mulţimea A={1. la masa rotundă.2.... numărul mulţimilor dintr-o partiţie este între 1 şi n. Totuşi. Se cer toate partiţiile mulţimii A={1... elementele mulţimii A trebuie să aparţină de submulţimi consecutive ale partiţiei. atunci elementul i se găseşte în mulţimea k a partiţiei.3}.2. .n}. 2. nu ştim câte mulţimi sunt în partiţia respectivă. sol[2]. 1-4-3. Generarea tuturor partiţiilor mulţimii {1.3} {1. Cei n acţionari ai unei firme trebuie să organizeze un număr maxim de şedinţe tip masă rotundă la care să participe exact p dintre ei. trebuie să găsim o metodă prin care să putem reţine o partiţie.  Din acest motiv.3} {1} {1} {2} {3}  Enunţ. astfel: dacă sol[i]=k. Fie mulţimea A={1. Pentru a avea o ordine în generarea soluţiilor.3} {2} {2. sol. Ştiind că oricare două şedinţe trebuie să difere fie prin acţionarii prezenţi. Cu alte cuvinte. O primă idee ne conduce la folosirea unui vector. Exemplu. .2. cu configuraţia 1-2-3).2. stabiliţi numărul de şedinţe pe care le pot organiza.1.. 8.  Rezolvare. . 2-3-4. 1-3-4. sol[i-1]}. dacă n=4 şi p=3. ∀i≠j∈{1.n}. Se citeşte un număr natural. tot nu ne ajută să generăm toate partiţiile.2} {3} {1...2. 2-4-3 (configuraţiile 2-3-1 şi 3-1-2 nu se consideră. 1.

else begin { maxprec=0.i++) for i:=2 to n do if (maxim<sol[i]) if maxim<sol[i] maxim=sol[i].10]of integer.i<=maxim.1) . j<=n. iar elementul 2 se găseşte în submulţimea 3 a partiţiei. end. } end.3) .1.i++) for i:=1 to maxim do { for (j=1. Să observăm că nici în cazul acestei probleme nu trebuie să verificăm existenţa anumitor condiţii de continuare. max[k]=sol[k]. #include <iostream.2.j++) then tipar if (maxprec<sol[j]) else maxprec=sol[j].1.. Analizaţi programul astfel obţinut! Varianta Pascal Varianta C++ var sol:array[0.i.3}. cout<<"Partitie "<<endl.2.sol=(1. if k=n+1 for (j=1.sol=(1. then write (j. de exemplu.A1={1. sol[10].A1={1} A2={2. procedure back (k:integer).j. for (i=2. max[10]. } writeln. { sol[k]=i.j.sol=(1. În acest caz.i<=n.j. .2.222 Capitolul 8.j<=k-1. . var i.} if maxprec<sol[j] then } maxprec:=sol[j]. for j:=1 to k-1 do back(k+1). begin for (i=1.j++) begin if (sol[j]==i) for j:=1 to n do cout<<j<<" ".1). writeln('Partitie '). Metoda backtracking Prin această condiţie se evită situaţia în care.h> n.sol=(1. int n. lipseşte submulţimea 2 a partiţiei. .3. then maxim:=sol[i].3} A2={2}. if (k==n+1) tipar().2) . Aceasta ar avea semnificaţia că elementele 1 şi 3 se găsesc în submulţimea 1 a partiţiei.j.2) . } . Să exemplificăm funcţionarea algoritmului pentru cazul n=3: .maxprec.i++) maxprec:=0. void tipar() begin { maxim=1. for (i=1.3). .A1={1} A2={2} A3={3}. procedure tipar.2. if sol[j]=i cout<<endl.1) .maxprec:integer.A1={1.maxim:integer. void back(int k) { int i.maxim.' '). maxim:=1.sol=(1.A1={1. vectorul sol reţine (1.2} A2={3}.i.i<=maxprec+1.

2 sau n ale partiţiei. back(k+1) back(1). Modificaţi programul precedent pentru ca acesta să afişeze toate partiţile care conţin exact 3 submulţimi. Observaţi că întotdeauna elementul 1 aparţine primei submulţimi a partiţiei. Pornind de la această bijecţie.  toate soluţiile unei probleme au aceeaşi lungime..5. end.  Exerciţiu. în loc ca algoritmul să genereze partiţiile. practic. Alte tipuri de probleme care se rezolvă prin utilizarea metodei backtracking 8. elementul 2 poate aparţine submulţimilor 1 sau 2 ale partiţiei. sol[k]:=i. Pornind de aici. el va determina conţinuturile vectorului sol. se obţine o partiţie. Puteţi arăta că oricărei partiţii îi aparţine un unic conţinut al vectorului sol. . o funcţie bijectivă de la mulţimea partiţiilor mulţimii A la mulţimea conţinuturilor generate de algoritm ale vectorului sol.5. 8. 1. end. cin>>n. obţinut ca în program? Indicaţie. Generalităţi Toate problemele pe care le-am întâlnit până acum admit soluţii care îndeplinesc următoarele caracteristici:  soluţiile sunt sub formă de vector. } end end. readln(n). unde prin lungime înţelegem numărul de componente ale vectorului soluţie. construiţi vectorul sol! Ţinând cont de faptul că oricărei partiţii îi corespunde un unic conţinut al vectorului sol şi oricărui conţinut al vectorului sol îi corespunde o unică partiţie.  Exerciţiu. am obţinut. begin write('n='). Apoi.Manual de informatică pentru clasa a XI-a 223 for i:=1 to maxprec+1 do main() begin { cout<<"n=". back(1). elementul n poate aparţine submulţimilor.. pentru fiecare conţinut al vectorului sol.. .1.

5. Trecem la stabilirea algoritmului pe care îl vom folosi. 1. se tipăreşte 112 dar şi 211.sol[k]≤n. Se cere să se tipărească toate modurile de descompunere a lui ca sumă de numere naturale. în cazul în care numărul în sine constituie o descompunere a sa şi n. există probleme în care nu se cunoaşte de la început lungimea soluţiei. b) Toate submulţimile cu p elemente ale mulţimii A (generarea combinărilor) au lungimea p. Astfel. Generarea partiţiilor unui număr natural  Enunţ. c) Toate soluţiile sub formă de vector ale problemei generării tuturor partiţiilor mulţimii A au lungimea n. Avem soluţie atunci când sol[1]+sol[2]+.2.224 Capitolul 8. cu ajutorul metodei backtracking se pot rezolva şi probleme care nu îndeplinesc condiţiile de mai sus. 112. pentru n=4. există probleme în care soluţia este sub forma unei matrice cu două sau trei linii etc. Ordinea numerelor din sumă este importantă.n}.. Ea poate fi cuprinsă între 1. 22.sol[k-1].. 8. Fie mulţimea A={1.. De la început.2. Se citeşte un număr natural n.. Atunci: a) Toate permutările mulţimii A au lungimea n. Fiecare componentă a vectorului sol trebuie să reţină o valoare mai mare sau egală cu 1. Rezultă de aici că trebuie să cunoaştem.. avem: 4. în procesul de generare a soluţiilor. 31. .sol[k]=n. 2. 121. 3.. Mai întâi să observăm că. 211. 1111. există probleme care admit mai multe soluţii de lungimi diferite. atunci când numărul este descompus ca sumă a n numere egale cu 1.. Astfel. la fiecare pas k. 13. Metoda backtracking Exemple. suma s= sol[1]+sol[2]+. În realitate..  Rezolvare. De exemplu. Exemplele următoare vă vor convinge. trebuie ca în permanenţă să fie respectată relaţia sol[1]+sol[2]+. 121. observăm că nu se cunoaşte lungimea unei soluţii.

s:integer. back(1) } end. Dar. s:=s-sol[k] } end. #include <iostream. k=3 s=3. while (sol[k]+s<n) while sol[k]+s<n do { sol[k]++. Suma va fi reţinută în permanenţă într-o variabilă globală. s-=sol[k]. de câte ori se face pasul înapoi (se trece la componenta k-1).i<=k-1. } end } end. cin>>n. k=3 s=1.s.Manual de informatică pentru clasa a XI-a 225 O primă posibilitate ar fi ca la fiecare pas să calculăm această sumă. main() begin { cout<<"n=". din s se scade sol[k].i++) for i:=1 to k-1 do cout<<sol[i]. back(k+1). numită s. cout<<endl. int sol[100]. back(1). la s se adună sol[k]. sol[k]:=0. k=1 s=1. readln(n).100] of integer. write(sol[i]). este prezentată funcţionarea algoritmului pentru n=4: soluţie 1 0 0 0 1 1 0 0 1 1 1 0 1 1 1 1 s=0. k=2 s=2. } end else else begin { sol[k]=0. k=4 soluţie soluţie soluţie 1 1 2 1 2 0 0 1 2 1 1 3 s=2.. procedure back (k:integer). n. . se poate lucra eficient. Programul este prezentat în continuare: Varianta Pascal Varianta C++ var sol:array[1. write('n='). De câte ori se trece la componenta următoare (k+1). begin s+=sol[k]. writeln. back(k+1).h> n. s:=s+sol[k]. k=2 s=3. Mai jos. k=3 s=1. sol[k]:=sol[k]+1. k=2 Observaţi modul în care calculăm suma la fiecare pas.i.i. void back(int k) begin { if (s==n) if s=n then begin { for (i=1.

3.226 Capitolul 8.a. Soluţiile se vor genera în ordine crescătoare.. Metoda backtracking  Exerciţii 1. 5) 5 de 1.2 să nu se mai afişeze 2. 1 de 3.6} în felul următor: 10=2+2+2+2+2. v2. 3: 1) 1 de 2. 1 de 3.3. 5.a. Iată soluţiile pentru Suma=5.3. 3) 2 de 1. sol[2] va reţine numărul de monede de tipul 2. sol[1] va reţine numărul de monede de tipul 1. Se dau suma s şi n tipuri de monede având valori de a1. Se presupune că se dispune de un număr nelimitat de exemplare din fiecare tip de monedă.  Rezolvare. 8. ş. 10=2+2+6. a şi b citite de la tastatură). 1 de 2.a2. În aceste condiţii.d. Cum trebuie procedat în cazul în care se cere ca soluţiile să fie afişate o singură dată? Spre exemplu. Plata unei sume cu bancnote de valori date  Enunţ. 10=2+2+3+3. o sol 2 0 1 soluţie pentru exemplul anterior arată ca alăturat.1. 2) 1 de 1. Astfel. Se cer toate modalităţile de plată a sumei s utilizând aceste monede. unde suma 5 a 1 2 3 se formează cu două monede de 1 şi o monedă de 3. Valorile celor n monede sunt reţinute de vectorul a. Rezolvaţi problema scrierii numărului natural n ca sumă de numere naturale alese dintr-o mulţime formată din k valori date {v1. Modificaţi programul în acest sens.1. Astfel. 10 se poate scrie ca sumă de numere alese din mulţimea {2. 4) 3 de 1. Numărul de monede din fiecare tip va fi reţinut de vectorul sol. a[2] valoarea monedei de tipul 2.m. 4. de exemplu la generarea combinărilor.2. n=3 (trei tipuri de monede) cu valorile 1. Adaptaţi metoda de rezolvare astfel încât să se genereze numai partiţiile formate din numere naturale aflate în intervalul [a.b] (n. vk}. 2 de 2.. a[1] va reţine valoarea monedei de tipul 1. ş. dacă s-a afişat descompunerea 1. Ce observăm? . Adaptaţi metoda de rezolvare astfel încât să se genereze numai partiţiile formate din numere naturale distincte.d. 2..an lei.1? Indicaţie: procedeul a mai fost întâlnit. Astfel. 2.1 sau 1.m.5. Adaptaţi metoda de rezolvare astfel încât să se genereze numai partiţiile formate din cel puţin p numere naturale distincte (n şi p citite de la tastatură).. ….

Această situaţie corespunde cazului în care moneda respectivă nu este luată în calcul.a[i]). Orice soluţie are exact n componente (n este numărul de tipuri de monede).' monede cout<<endl. else while(sol[k]*a[k]+s<Suma begin && k<n+1) sol[k]:=-1. void back(int k) begin { if (s==Suma) if s=suma then { cout<<"Solutie "<<endl. cin>>n. procedure back (k:integer). este prezentat programul: Varianta Pascal Varianta C++ var sol.100] #include <iostream. if(sol[i]) for i:=1 to k-1 do cout<<sol[i]<<"monede de " if sol[i]<>0 then <<a[i]<<endl. + a[n]*sol[n] = Suma  Exerciţiu. prin desene. Ca şi la problema anterioară. fiecare componentă a vectorului sol va fi iniţializată cu o valoare aflată înaintea tuturor celor posibile.Suma:integer. vom reţine în permanenţă suma obţinută la un moment dat..i<=k-1. int sol[100].i. Avem soluţie dacă: s = a[1]*sol[1] + a[2]*sol[2] + . .. else end { sol[k]=-1. a[100]. } back(k+1). { while (sol[k]*a[k]+s<Suma) sol[k]++.i.. avem la dispoziţie suma: s = a[1]*sol[1] + a[2]*sol[2] + . begin back(k+1).h> of integer. adică cu -1. main() end { cout<<"suma=". chiar şi cele care nu sunt luate în calcul. sol[k]:=sol[k]+1. la pasul k. cout<<"n="..s. n. } s:=s-sol[k]*a[k] } end. Încercaţi să arătaţi. s:=s+sol[k]*a[k]. 2. end. writeln (sol[i]. Acest număr include toate monedele. 3.. de '.a:array[1. Din acest motiv. and (k<n+1) do s+=sol[k]*a[k].Manual de informatică pentru clasa a XI-a 227 1. + a[k-1]*sol[k-1] 4. Există componente ale vectorului sol care reţin 0. s-=sol[k]*a[k].Suma. begin for(i=1.s.i++) writeln('Solutie'). modul de obţinere a tuturor soluţiilor pentru Suma=5 şi n=3. ca la problema anterioară. cin>>Suma. n. În continuare. } writeln. Astfel.

Din acest motiv. se marchează cu o linie oblică. Acolo 15 11 10 14 unde nu este permisă trecerea dintr-o cameră în alta. readln(n). Fiecare element al matricei reprezintă o cameră a labirintului.']='). pentru o cameră care are ieşire în nord şi vest. write('n='). end. avem 1001(2)=9(10). (1. cunoscând. aşa cum sunt 11 6 15 7 ele cerute de program.3). back(1) end. Într-una din camere. } readln(a[i]).2). Se dă un labirint sub formă de matrice cu m linii şi n coloane. în plus. for i:=1 to n do } begin back(1). Fie l(i.  Exerciţiu.3). Exemplu. est. . pentru fiecare valoare ai numărul limită bi de bancnote cu valoarea respectivă disponibile.6. iar în caz contrar. De asemenea.  Rezolvare 1.j) un element al matricei. Se consideră ieşirile spre nord. Se cere să se găsească toate ieşirile din labirint.228 Capitolul 8. Acest număr este convertit în baza 10 şi reţinut în l(i.4. Acesta poate lua valori între 0 şi 15. se găseşte un om. b=(10.5. varinata s=10x5+1x50 nu corespunde cerinţei deoarece nu avem la dispoziţie 10 monede de 5. Un şir de patru cifre 1 sau 0 formează un număr în baza 2. pentru s=100.50).i. O primă problemă care se pune este precizarea modului de codificare a ieşirilor din fiecare cameră a labirintului. Nu este permis ca un drum să treacă de două ori prin aceeaşi cameră.3). { cout<<"a["<<i<<"]=". O cameră vizitată se reţine prin coordonatele ei: lin (linia) şi col(colana). write ('a['. sud şi vest.5. Adaptaţi rezolvarea problemei plăţii unei sumei cu bancnote date. (2. 8.i<=n. dacă camera iniţială este cea de coordonate (2. ci doar 6. De exemplu. cin>>a[i].i++) write ('suma='). pentru a reţine un traseu vom utiliza o matrice cu două coloane şi mai multe linii: sol. readln(suma). De exemplu. Metoda backtracking begin for (i=1. matricea 11 12 11 12 reţine şi valorile corespunzătoare ieşirilor. Astfel. Problema labirintului  Enunţ. de coordonate lin şi col. a=(2. luate în această ordine. se reţine 0.j). Pentru fiecare direcţie cu ieşire se reţine 1.2) o soluţie este (2. Alăturat este prezentat un labirint.

i++) tipar(k. if (lin==0) for i:=1 to k-1 do cout<<"iesire prin nord" writeln(sol[i.l[10][10]. m.int lin. Nu toate soluţiile au aceeaşi lungime. <<endl. Este o operaţie în care se testează conţinutul unui anumit bit.0. sol[i. else if lin=0 then if (lin==m+1) writeln('iesire prin nord') cout<<"iesire prin sud" else <<endl.j. if lin=m+1 then else writeln('iesire prin sud') if (col==0) else cout<<"iesire prin vest" if col=0 then <<endl.lin. writeln('iesire prin vest') else else cout<<"iesire prin est" writeln('iesire prin est').1]=lin) and } (sol[i.col:integer). writeln('Solutie'). l:array [0.n. int col) function vizitat(k.100. then vizitat:=true. { int v=0.Manual de informatică pentru clasa a XI-a 229 2. Acesta se obţine efectuând un ŞI logic între două valori. atunci efectuăm ŞI logic între 0010(2)=2(10) şi valoarea reţinută în matrice pentru camera curentă. În caz că a fost vizitată. se face pasul înapoi.2] of #include <iostream.col:integer. aceasta se afişează.j.2]=col) void tipar(int k. cout<<sol[i][0]<<" " begin <<sol[i][1]<<endl.i<=k-1. 4.. int vizitat(int k. Evident..1].i. 3.lin. dacă testăm ieşirea spre sud. Spunem că o cameră este accesibilă dacă există intrare din camera curentă către ea. } end.i<=k.h> integer. . if (sol[i. Înainte de a intra într-o cameră accesibilă se testează dacă respectiva cameră a mai fost vizitată sau nu. integer.int lin. for (i=1. Dacă valoarea obţinută este diferită de 0. col:integer):boolean. for i:=1 to k-1 do return v. readln. Pentru aceasta utilizăm funcţia vizitat..10. procedure for (i=1. Atenţie la modul (vedeţi programul) în care testăm dacă o cameră este accesibilă sau nu. întrucât există trasee de lungime diferită. atunci când s-a găsit o situaţie.n.i++) begin if (sol[i][0]==lin && vizitat:=false.lin. int col) end. Se obţine o soluţie atunci când coordonatele camerei unde s-a intrat sunt în afara matricei (nu au linia între 1 şi m şi nu au coloana între 1 şi n). Analizaţi programul: Varianta Pascal Varianta C++ var sol:array [1. atunci avem ieşire din camera curentă către sud.i.' '.2]).lin.10] of m. <<endl.col. int sol[100][2].1. De exemplu. sol[i][1]==col) v=1.. { cout <<" Solutie "<<endl.

1]:=lin. switch(i) for i:=1 to 4 do { case i of case 1: 1:if (l[lin. readln(lin).col).lin+1. 2:if (l[lin.lin+1. write('col=').lin-1.i<=m.i.i<=4.lin-1.col) ! vizitat(k.lin.col) back(1. break. cin>>m.col-1) break. cout<<"Coloana= ". back(k+1." write('l['. end.lin. {case} } end } end.col).230 Capitolul 8.col] case 2: and 4<>0) and not if (l[lin][col] & 4 && vizitat(k.col) { else sol[k][0]=lin.lin.lin. { if (lin==0 || lin==m+1 || begin col==0 || col==n+1) if (lin=0) or (lin=m+1) or tipar(k. readln(n). break.col+1) ! vizitat(k. } . back(k+1.col-1).lin-1. break. 3:if (l[lin. cin>>n.lin.lin. } begin main() write('M=').col). readln(col).'. int lin. readln(l[i. sol[k. end. back(1.lin.i++) sol[k.lin.lin. readln(m).col+1). lin. cout<<"Linie=". back(k+1.col:integer). lin+1. <<j<<"]=". Metoda backtracking procedure void back(int k.col-1) ! vizitat(k.']='). 4:if (l[lin.col] if (l[lin][col] & 8 && and 8<>0) and not vizitat(k. end.lin.2]:=col. for i:=1 to m do for (i=1.col] case 3: and 2<>0) and not if (l[lin][col] & 2 && vizitat(k. back(k+1.col] case 4: and 1<>0) and not if (l[lin][col] & 1 && vizitat(k. cout<<"N=".j]) cin>>l[i][j].j<=n.col). write('N='). (col=0) or (col=n+1) else then tipar(k.col) ! vizitat(k.lin. lin-1.col)) then back(k+1.lin+1. { cout<<"M=". for (int i=1.col).j.col+1)) then back(k+1. cin>>lin.'.col+1). cin>>col. begin sol[k][1]=col.col-1)) then back(k+1.i++) for j:=1 to n do for(j=1. back(k. lin. int col) var i:integer.col)) then back(k+1.j++) begin { cout<<"l["<<i<<".col). } write('lin=').

Ei bine. est. 2. se obţine o rezolvare. deplasarea se poate face dintr-o căsuţă în orice altă căsuţă alăturată. orizontal sau vertical. răspunsul nu este satisfăcător. Se dă un teren sub formă de matrice cu m linii şi n coloane. am rezolvat-o! Este adevărat. . prin care nu se poate trece.4). iar 0 căsuţă liberă. astfel încât aceasta să corespundă unei căsuţe libere. tehnica backtracking necesită un timp exponenţial. Prin urmare. (3.col) se găseşte o bilă. Într-un astfel de subteren. trebuie să studiem teoria grafurilor. (3. Problema seamănă cu cea anterioară. Nu uitaţi. În program. Problema bilei  Enunţ. pentru a o putea depista pe cea de lungime minimă. Dar. deci putem gândi că dacă înlocuim testul de intrare într-o cameră cu cel de altitudine mai mică. deoarece. Fie terenul alăturat. cu condiţia ca ea să existe şi să fie liberă.2). O posibilitate de ieşire din teren este dată 9 7 6 3 de drumul: (2. dar se poate şi mai uşor. problema este mai uşoară decât precedenta. Validaţi poziţia iniţială a omului (lin. 5 8 5 4 altitudinile subterenului vor fi reţinute de matricea t. 8 3 7 1  Analiza problemei şi rezolvarea ei. astfel încât să nu se mai testeze ca celula în care se trece să existe. col). Estimaţi spaţiul de memorie utilizat în această variantă. pentru asta. Fiecare element al matricei reprezintă un subteren cu o anumită altitudine dată de valoarea reţinută de element (număr natural). la fiecare pas.3).3). pe unde se poate trece). sud sau vest. de altitudine strict inferioară porţiunii pe care se găseşte bila. de coordonate (lin.Manual de informatică pentru clasa a XI-a 231 Intrebare. 6 8 9 3 Exemplu.. Să analizăm: mai este necesar să testăm dacă bila nu a ajuns pe un teren pe unde a mai trecut? Nu.5. bila se află în subterenul de coordonate (2. Cum s-ar putea găsi un drum de lungime minimă de la camera iniţială către ieşirea din labirint? Prima idee care ne vine în minte este să generăm toate ieşirile.  Exerciţii 1.. ca în program. Toate la timpul lor. Iniţial. 8.5. Ştiind că bila se poate deplasa în orice porţiune de teren aflată la nord. (2.2). adăugând câte o linie sau coloană suplimentară pe fiecare margine a labirintului. Plasarea într-o celulă de pe margine este echivalentă cu obţinerea unei soluţii. Se cere să se găsească toate posibilităţile ca bila să părăsească terenul. Realizaţi o variantă a rezolvării de la 1. bila se deplasează pe un teren de altitudine strict inferioară. Ca şi în problema prezentată. Problema se poate rezolva cu mult mai eficient. Adaptaţi rezolvarea pentru un labirint în care fiecare căsuţă reţine valoarea 1 sau 0 (1 semnificând căsuţă plină.

cin>>n. Metoda backtracking În rest.lin.1]<4 do switch(sol[k][0]) begin { sol[k.lin-1. t[lin..  Exerciţiu.col).3) va reţine coloana subterenului. int col) procedure { if (lin==0 || lin==m+1 || back(k.100. modul de funcţionare a algoritmului.i++) writeln('Solutie').. break.col:integer. 2 pentru est.lin-1. end. case 1: case sol[k.col]< back(k+1.lin. sol[k. m. } begin main() write('M='). t[lin. cout<<sol[i][1]<<" " for i:=1 to k-1 do <<sol[i][2]<<endl. readln(n). else begin sol[k][2]=col. procedure tipar(k:integer).t[10][10]. then tipar(k) sol[k][1]=lin. prin reprezentare grafică.232 Capitolul 8. iar sol(k.lin. cout<<"n=". while (sol[k][0]<4) sol[k. if(t[lin+1][col]<t[lin][col]) 3:if t[lin+1. of integer.i<=k-1.col+1]< back(k+1. t[lin.col-1). 3 pentru sud şi 4 pentru vest). void back(int k.3] #include <iostream.n.lin+1. sol(k.col:integer). while sol[k.. break. m.j.lin.0.lin. sol(k. break. if (lin=0) or (lin=m+1) or else (col=0) or (col=n+1) { sol[k][0]=0. sol[k][0]++.col-1). void tipar(int k) begin { cout<<"Solutie "<<endl.col+1).lin. if(t[lin][col-1]<t[lin][col]) 4:if t[lin. int lin.col]< back(k+1.1]+1. break. .1]:=0. col==0 || col==n+1) begin tipar(k).col). readln(m).' '.10.1. {case} } end end end. if(t[lin][col+1]<t[lin][col]) 2:if t[lin.1] of if(t[lin-1][col]<t[lin][col]) 1:if t[lin-1.col.3]:=col.col+1).10] int sol[100][3]. Astfel.i.2]:=lin.col] then case 4: back(k+1. Arătaţi.n. { sol[k.col-1]< back(k+1. writeln(sol[i. { cout<<"m=".3]).lin.col] then case 3: back(k+1. cin>>m. t[lin. for(i=1.lin+1..2) va reţine linia subterenului.col] then } back(k+1.h> of integer. write('N=').i.2]. } end.j. pe baza structurii de date propusă. vom propune o rezolvare în care sol este o matrice cu 3 coloane şi un număr mare de linii.col] then case 2: back(k+1.col).1]:=sol[k. } sol[i. Varianta Pascal Varianta C++ var sol:array [1.col).1) va reţine direcţia în care pleacă bila (1 pt nord. t:array [0.

Astfel. write('t['.lin.1000. Fiind dată o poziţie în care se găseşte calul.2.5. dacă lin şi col sunt coordonatele poziţiei unde se găseşte calul.col+y[0]). Varianta Pascal Varianta C++ const x:array[1.-1.1.-2.2. Se consideră o tablă de şah nxn şi un cal plasat  1 16 11 20 3    în colţul din stânga.-2.  25 14 23 8 5    Analiza problemei şi rezolvarea ei.-2. int n. astfel încât să treacă o  15 24 19 4 7    singură dată prin fiecare pătrat al tablei.coloana. Reţineţi acest procedeu! De altfel.'. Matricea t reţine poziţiile pe unde a trecut calul. cout<<"col=".i."<<j<<"]=".col+y[7]). write('lin=').2. end. Pentru a scrie mai puţin cod. observaţi o  22 9 6 13 18  soluţie pentru n=5. col:integer).t[25][25]. acesta poate fi mutat în: (lin+x[0].-2.i++) for j:=1 to n do for (j=1.Manual de informatică pentru clasa a XI-a 233 for i:=1 to m do for (i=1.25.. back(1. st: array[1.-2.8] of #include <iostream. acesta poate sări în alte 8 poziţii.j<=n.1. int col) var i. Săritura calului  Enunţ.1.i<=m. procedure back(k.8] of integer=(2. .-1. În rest.readln(lin). cin>>lin.readln(col).lin..sol[1000][2]...  Exerciţiu. const int x[8]={-1. Se cere să se afişeze o posibilitate  10 21 2 17 12  de mutare a acestei piese de şah.lin. -1. Alăturat. 8.col) end..j]) cout<<"lin=". -2.1.1}. write('col=').h> y:array[1. cele 8 poziţii sunt reţinute de vectorii constanţi x şi y.coloana:integer.h> integer=(-1.col).6.int lin. t: array[-1. integer.1..2.-1.2.'. sus.1. void back(int k.j. } back(1..2.-1.-2). cin>>t[i][j]. { int linie. s-a procedat ca la problema anterioară..2] of const int y[8]={2.1).25] of integer.linie.-2}.-1. cin>>col.j++) begin { cout<<"t["<<i<<". ']=').1.-1. acesta poate fi folosit pentru problemele deja prezentate.i. } readln(t[i. var n:integer. #include <stdlib.(lin+x[7]. Modificaţi programul astfel încât să se afişeze şi direcţia în care bila părăseşte terenul.

(coloana>=1) and } (t[linie. end.coloana]:=1. { cout<<"n=". cin>>n. { if (linie<=n) and t[linie][coloana]=1.i<=k-1. st[i. (linie>=1) and back(k+1.234 Capitolul 8. main() coloana). else halt.' '. } writeln(lin. back(1. back(1.1.coloana).1]:=lin. t[linie][coloana]==0) coloana:=col+y[i]. begin write ('n=').coloana]:=0. end sol[k][1]=col. exit(EXIT_SUCCESS). readln(n). back(k+1. end. { sol[k][0]=lin. st[k.linie. else for (i=0.i++) then cout<<sol[i][0]<<" " begin <<sol[i][1]<<endl.i<=7.1. if (linie<=n && linie>=1 for i:=1 to 8 do && coloana<=n && begin coloana>=1 && linie:=lin+x[i].' '.col). (coloana<=n) and t[linie][coloana]=0.2]:=col. for i:=1 to k-1 do cout<<lin<<" "<<col.1).linie. end } end end. coloana=col+y[i]. . writeln(st[i.coloana]=0) } then } begin } t[linie.i++) begin { linie=lin+x[i]. st[k.2]).1). Metoda backtracking begin if (k==n*n) if k=n*n { for (i=1. t[linie.1].

ştiind că trebuie respectate regulile:  orice drapel are culoarea din mijloc galben sau verde. Dispunem de un algoritm de generare a combinărilor de n elemente luate câte p pentru ele. Se dau k perechi de persoane care aparţin unor firme concurente. 4. 5. dintre care p femei. Să se determine 5 numere de câte n cifre. Se cere să se tipărească toate anagramele cuvântului citit. Anagrame. . 6. Se cer toate soluţiile de aşezare în linie a m câini şi n pisici astfel încât să nu existe o pisică aşezată între doi câini. astfel încât oricare dintre aceste 5 numere să coincidă exact în m poziţii şi să nu existe o poziţie în care să apară aceeaşi cifră în toate cele 5 numere. astfel încât să nu stea alături două persoane de la firme concurente. 2. din care l femei. Se cere o submulţime cu p elemente astfel încât suma elementelor sale să fie maximă în raport cu toate submulţimile cu acelaşi număr de elemente. fiecare cifră putând fi 1 sau 2. 9. Să se precizeze toate delegaţiile care se pot forma. Se citesc n numere naturale distincte.  cele trei culori de pe drapel sunt distincte. existentă în permutarea iniţială. Se citeşte un cuvânt cu n litere. Dintr-un grup de n persoane. verde. Se cer toate permutările care se pot obţine din aceasta astfel încât nici o succesiune de două numere. Fiecare persoană reprezintă o firmă. Cum adaptaţi algoritmul de care dispuneţi pentru a obţine combinările de n persoane luate câte p? 8. Se consideră un vector cu n componente şiruri de caractere. Se poate folosi algoritmul pentru generarea permutărilor? 7. La o masă rotundă se aşează n persoane. 3. Se dă o permutare a primelor n numere naturale. roşu. Se dau primele n numere naturale.Manual de informatică pentru clasa a XI-a 235 Probleme propuse 1. unde. să nu mai existe în noile permutări. Să se precizeze toate drapelele tricolore care se pot proiecta. fiecare şir reprezintă numele unei persoane. albastru şi negru. trebuie formată o delegaţie de k persoane. galben. Se cere să se determine toate modalităţile de aşezare la masă a persoanelor. Avem la dispoziţie 6 culori: alb.

.()(()).()()().  nu conţin nici unul din q obiecte date.  conţin exact un obiect din p obiecte date.  conţin p obiecte date. poziţiile lor fiind cunoscute. Se dă un număr natural par N. se cere să se producă la ieşire toate descompunerile sale ca sumă de numere prime. 14. Se consideră o mulţime de n elemente şi un număr natural k nenul. i+k să fie unul după celălalt şi în această ordine (l=1. astfel încât din fiecare punct. Să se determine o astfel de configuraţie de segmente astfel încât oricare două segmente să nu se intersecteze. Să se genereze toate permutările de N cu proprietatea că oricare ar fi 2≤i≤N.. i+l. dar nu conţin un altul. . precum şi poziţia regelui sunt considerate “nearse“. condiţiile de mai jos şi să se afişeze aceste submulţimi. 15.i+k≤n). Să se calculeze câte submulţimi cu k elemente satisfac. 18.. Se dau n puncte în plan prin coordonatele lor. 12.236 Capitolul 8.2. Metoda backtracking 10. Calul nu poate călca pe câmpuri “arse“. Un cal şi un rege se află pe o tablă de şah. Unele câmpuri sunt “arse“. există 1≤j≤i astfel încât V(i)-V(j)=1. prin care calul să poată ajunge la rege şi să revină la poziţia iniţială.(()()). Să se afle dacă există o succesiune de mutări permise (cu restricţiile de mai sus). 13. Poziţia iniţială a calului. Exemplu: pentru N=6: ((( ))). . dar nu conţin alte q obiecte date. pe rând. “Attila şi regele”. Se cer toate soluţiile de unire a acestor puncte prin exact p drepte. Să se ordoneze în toate modurile posibile elementele mulţimii {1. Găsiţi toate soluţiile naturale ale ecuaţiei 3x+y+4xz=100. de coordonate întregi. pleacă exact un segment.  conţin cel puţin un obiect din p obiecte date.  conţin r obiecte din p obiecte date.. Să se determine toate şirurile de N paranteze care se închid corect. Fiind dat un număr natural pozitiv n. iar orice mişcare a calului face ca respectivul câmp să devină “ars“. Se dau N puncte albe şi N puncte negre în plan. 16. 11.... fie el alb sau negru. Se citesc N perechi de coordonate corespunzând punctelor albe şi N perechi de coordonate corespunzând punctelor negre.n} astfel încât numerele i. 17. Fiecare punct alb se uneşte cu câte un punct negru.(())().  conţin exact un obiect dat. astfel încât mulţimea punctelor de intersecţie ale acestor drepte să fie inclusă în mulţimea celor n puncte.

b) Generarea combinărilor. cât şi pe celălalt avem mai mulţi canibali decât misionari. un cuvânt. permutările cu proprietatea de mai sus sunt: 2134. a) Generarea aranjamentelor. c) Generarea permutărilor. d) Generarea tuturor partiţiilor. cel puţin un actor. Se cere să se scrie un program care să furnizeze toate soluţiile de trecere a apei. Se ştie că. O trupă cu N actori îşi propune să joace o piesă cu A acte astfel încât:  oricare două acte să aibă distribuţie diferită. evident.d. dacă atât pe un mal. 3214. ş. c) Generarea permutărilor. 19.m.  de la un act la altul. 3421. Regula de căutare este următoarea: a) se caută litera de început a cuvântului.a. Se dau coordonatele a n puncte din plan. unde p(k) reprezintă numărul subşirurilor crescătoare de lungime k. . 21. apoi printre cele 4 elemente învecinate cu elementul care conţine noua literă. Se cere să se precizeze 3 puncte care determină un triunghi de arie maximă. 22. Fiind date numele a n soldaţi. Elementele careului sunt litere. Se dă un careu sub formă de matrice cu m linii şi n coloane. se cere:  să se determine toate subşirurile crescătoare de lungime [N/5]. misionarii sunt mâncaţi de canibali. 2314. vine un actor pe scena sau pleacă un actor de pe scenă (distribuţia a două acte consecutive diferă prin exact un actor).. astfel încât să nu fie mâncat nici un misionar. Pe malul unei ape se găsesc c canibali şi m misionari. dacă există vreuna.  în orice act există. 4321. Ce algoritm vom folosi? a) Generarea aranjamentelor. 23. Ei urmează să treacă apa şi au la dispoziţie o barcă cu 2 locuri. ce algoritm vom utiliza pentru a lista toate grupele de câte k soldaţi? Se ştie că într-o grupă. 3241.. Să se furnizeze o soluţie.+p(k). ordinea prezintă importanţă. Se dă. b) litera următoare se caută printre cele 4 elemente învecinate cu elementul care conţine litera de început. 2341.Manual de informatică pentru clasa a XI-a 237 Exemplu: pentru N=4. b) Generarea combinărilor. Fiind dat un număr natural N şi un vector V cu N componente întregi. d) Generarea tuturor submulţimilor. 24. Se cere să se găsească în careu prefixul de lungime maximă al cuvântului respectiv. de asemenea.  să se calculeze p(1)+p(2)+. 20.

Dacă vectorul care reţine numele persoanelor este V.. al patrulea. ce algoritm vom utiliza pentru a determina eficient o submulţime maximală de numere naturale distincte? a) se generează toate submulţimile şi se determină o submulţime maximală care îndeplineşte condiţia cerută. a treia este în O(n2). . a) 25. iar parcurgerea în O(n). Metoda backtracking 25. . Primele două permutări afişate sunt: 321. d) Explicaţie: pe fiecare nivel al stivei se caută succesorii în ordinea n. al treilea. d) Explicaţie: primele două variante prezintă soluţii exponenţiale. b) 123. b) 24. c) 213. trebuie pusă o condiţie suplimentară. Sortarea se poate efectua în O(n×log(n)). 1. d) nici un algoritm dintre cei de mai sus. Prin urmare. Indicaţii 6. Fiind date n numere naturale. 312. 26. se va afişa V[i]. d) 231.. atunci le putem afişa pe cele distincte dintr-o singură parcurgere. 23. complexitatea este O(n×log(n)). Dispunem de un algoritm care generează permutările prin backtracking. n-1. Dar dacă sortăm numerele. 26.. în loc să se afişeze i. De exemplu. al doilea cu al treilea..238 Capitolul 8. b) se generează toate partiţiile şi se caută o submulţime maximală care aparţine unei partiţii oarecare şi care îndeplineşte condiţia cerută. Care este următoarea permutare care va fi afişată? a) 321. c) se compară primul număr cu al doilea. în cuvântul “mama” nu se poate inversa a de pe poziţia 2 cu a de pe poziţia 4. al n-lea. al n-lea. Deşi algoritmul este asemănător. şi atunci când se găseşte egalitate se elimină un număr dintre ele. . 7.. nu este acelaşi.

j) are semnificaţia că persoanele i şi j sunt prietene. Se cunosc relaţiile de prietenie dintre n persoane.  Convenim ca fiecare element să-l numim nod sau vârf. Unele dintre ele sunt unite prin şosele directe (care nu mai trec prin alt oraş). Astfel. s-o notăm cu (i.. în cazul 1. nodul este ţara şi în cazul 4. Se dau n ţări şi se cunoaşte relaţia de vecinătate între ele.1.1 şi convenim ca o astfel de reprezentare s-o numim graf neorientat. 4. Figura 9. 5 3 4 Procedând aşa. 3. 2. iar unele dintre ele sunt asemenea.j) are semnificaţia 2 că ţările i şi j sunt vecine. algoritmii trebuie să prelucreze date referitoare la anumite elemente între care există anumite relaţii. În cazul 1. Convenim ca un astfel de segment să-l numim muchie şi dacă ea uneşte nodurile i şi j. muchia (i.  Relaţia existentă între două noduri o vom reprezenta grafic unindu-le printr-un segment de dreaptă.. nodul este persoana. Exemplu de graf neorientat . Pentru fiecare dintre aceste exemple se poate imagina o reprezentare grafică care să exprime relaţiile existente. Se dau n oraşe. nodul este triunghiul. nodul este oraşul. în cazul 2. În cazul 2.j). Grafuri neorientate 9. muchia (i. 239 Capitolul 9 Introducere în teoria grafurilor 9.1. Introducere Uneori. Să analizăm exemplele următoare: 1. că triunghiurile i şi j sunt asemenea. obţinem o descriere grafică precum cea din figura 9. iar în cazul 4.1. muchia (i. în cazul 3. în 6 cazul 3.1.. Convenim ca un nod (vârf) să-l notăm cu un cerculeţ în care să înscriem numărul lui (de la 1 la n).j) are semnificaţia că între oraşele i şi j există o şosea 1 directă. Se dau n triunghiuri.

Un graf poate fi desenat aşa cum se observă în exemplul următor (vezi figura 9..(2. V = {1. şi vi.2.  E este o mulţime finită de perechi neordonate de forma (vi. Definiţia este restrictivă. .vj∈G sunt adiacente dacă există muchia (vi.  Nu există o muchie care uneşte un nod cu el însuşi (o muchie uneşte două noduri distincte). de 1 exemplu. vj)∈E. această abordare este intuitivă. În graful G=(V. unde:  V = {v1. În exemplul dat anterior. Alt exemplu de graf neorientat Observaţii  Două noduri distincte pot fi unite prin cel mult o muchie. Elementele mulţimii V se numesc noduri (vârfuri). Vom spune că muchia (vi.3.E).240 Capitolul 9.4). vn} este o mulţime finită şi nevidă. În exemplul de mai sus. vom nota cu n numărul nodurilor şi cu m numărul muchiilor. poate exista o muchie de la un nod la el însuşi sau nu se cere ca mulţimea nodurilor să fie finită. Dacă scriem (2. ne referim la aceeaşi muchie (perechea este neordonată). Un graf neorientat este o pereche ordonată G=(V. Definiţia grafului neorientat 1 Definiţia 9. Elementele mulţimii E se numesc muchii.3). în unele lucrări veţi întâlni definiţii mai puţin restrictive. nodurile distincte vi. Definiţia 9. unde i≠j.4.2). v2..5) este incidentă la nodurile 4 şi 5. 2 (4.(1.).1). nodurile 1 şi 5 sunt adiacente. Teoria grafurilor este fundamentată matematic şi în cele ce urmează ne propunem s-o prezentăm sistematic. dar nodurile 2 şi 5 nu sunt adiacente.(3.5.6}.vj)∈E este incidentă la nodurile vi şi vj. Figura 9.2.E). Semnificaţia unei muchii este aceea că uneşte două noduri.5)} 5 3 4 Notaţie: în graful G=(V.E).1.vj∈V.E).3). Muchia (4. 9.. E = {(1.2.1.(1.2) este muchia care uneşte nodul 1 cu nodul 2. Introducere în teoria grafurilor Desigur. .2. unde 1 6 G=(V.2. vj).5). (1.

pentru exemplul 3. se obţine relaţia anterioară. pentru exemplul 2. . d(2)=2. are semnificaţia că triunghiurile i şi j sunt asemenea. înseamnă că ţara i nu se învecinează cu nici o ţară (este situată pe o insulă). Un nod cu gradul 0 se numeşte nod izolat.. .1. O relaţie utilă: fie un graf neorientat cu n noduri şi m muchii. veţi răspunde la întrebările: ce semnificaţie au nodurile sau muchiile şi ce înseamnă gradul unui nod. are semnificaţia că ţara i se învecinează cu k ţări.. Pentru a înţelege bine noţiunile prezentate în acest paragraf. are semnificaţia că persoanele i şi j sunt prietene. înseamnă că persoana i nu are nici un prieten. Dacă notăm cu d1. are semnificaţia că pentru triunghiul i se cunosc k triunghiuri asemenea. Prin urmare. iar pentru exemplul 4. Într-un graf neorientat.1: ... .. iar unul cu gradul 1 se numeşte nod terminal. Astfel. dar modul în care putem afla aceasta va fi tratat separat. dn gradele celor n noduri.  Exerciţiu Daţi un exemplu inspirat din viaţa reală. Pentru exemplul 1.. iar d(6)=0 (6 este nod izolat). d(1)=3. d2. ea are semnificaţia că oraşele i şi j sunt unite printr-o şosea care nu trece prin alte oraşe.fie afirmaţia: gradul nodului i este k.. pentru exemplul 2.. pentru care să găsiţi graful asociat..fie afirmaţia: nodul i este izolat. pentru exemplul 3... .. atunci avem relaţia: d 1 + d 2 + d 3 + . pentru exemplul 2. În exemplul dat.fie afirmaţia: nodurile i şi j sunt adiacente. pentru exemplul 4.. Aici trebuie făcută observaţia că ar putea să existe şi alte triunghiuri asemenea cu el.  Demonstraţie: fiecare muchie face să crească gradele celor două noduri la care este incidentă cu câte o unitate. prin gradul unui nod v se înţelege numărul muchiilor incidente cu nodul v şi se notează cu d(v).Manual de informatică pentru clasa a XI-a 241 Definiţia 9. ne vom referi la exemplele din 9. pentru exemplul 3. Pentru exemplul 1.d n = 2m.. înseamnă că nu există nici o şosea care leagă oraşul i cu alt oraş.. înseamnă că nu există nici un triunghi dintre celelalte n-1 triunghiuri care să fie asemenea cu triunghiul i..3. iar pentru exemplul 4. ea are semnificaţia că din oraşul i pleacă (sosesc) k şosele. Pentru exemplul 1. are semnificaţia că ţările i şi j sunt vecine. are semnificaţia că persoana i are k prieteni..

o matrice pătratică.1. Introducere în teoria grafurilor 9. Pentru fiecare structură de date pe care o vom folosi. câte o muchie (i. în care. 4 Exemplu de graf neorientat Trecem la prezentarea structurilor prin care putem memora datele referitoare la un graf. j) ∈ E a i. Vom fi astfel scutiţi ca.n . ai.pas (pentru Pascal) şi în grafuri. Memorarea grafului prin matricea de adiacenţă An. pentru (i. A. Toate aceste subprograme se găsesc grupate în unitatea de program grafuri. Memorarea grafurilor În acest paragraf. pentru fiecare program pe care îl realizăm.j). să fim nevoiţi să adăugăm liniile de cod necesare citirii şi ne permite să ne concentrăm exclusiv asupra algoritmului. c) dacă graful conţine multe muchii sau nu. j) ∉ E 2 Modul de alegere a structurii îl veţi înţelege pe parcursul studiului acestui capitol. Toate subprogramele pe care le utilizăm citesc datele dintr-un fişier text. b) memoria internă pe care programul o are la dispoziţie. De la început. prezentăm principalele structuri de date prin care grafurile pot fi memorate în vederea prelucrării lor. pentru (i.j au semnificaţia: 1. în care este prezentat un graf şi liniile fişierului text care este citit pentru el: 6 Fişierul text: 1 6 1 2 1 3 1 5 2 2 3 3 4 5 4 5 3 Figura 9. . ca în exemplul de mai jos.3. iar pe următoarele linii. unde elementele ei. j =  0.3.cpp (pentru C++).242 Capitolul 9. precizăm faptul că vom alege o 2 structură sau alta în funcţie de : a) algoritmul care prelucrează datele referitoare la graf. pe prima linie vom scrie numărul de noduri (n). vom avea câte o procedură (funcţie) care citeşte datele respective.

d(1). adică dublul numărului de muchii (2m)..n}. deoarece muchia (i...2... pentru care există muchie de la i la j. de fapt... ∀i ∈ {1..i . De exemplu..2. suma gradelor tuturor nodurilor.. n} . j ∈{1..n}. 6 =  0 0 1 0 1 0 1 0 0 1 0 0   0 0 0 0 0 0 Observaţii 1.3. Tot aşa. i∈{1. Dacă graful citit are un număr mic de muchii...i = 0. Întrucât. d(j). ∀i.2.. atunci matricea de adiacenţă este o formă ineficientă de memorare a lui. rezultă că nu există muchii de la un nod la el însuşi. 5. ! Raţionamentul este asemănător cu cel de la observaţia precedentă. matricea de adiacenţă este prezentată în continuare: 0 1 1 0 1 0 1  0 1 0 0 0 1 1 0 1 0 0 A6. din modul în care a fost definit graful. Suma tuturor elementelor matricei de adiacenţă este.2. adică gradul nodului 1. pentru că astfel se obţine suma nodurilor j. n} . Suma elementelor de pe linia i. are ca rezultat gradul nodului j.n}. d(i). j ∈{1. Evident. 4.. 2. Subprogramele pe care le vom utiliza pentru această modalitate de memorare sunt prezentate în continuare: . adică suma muchiilor incidente la i.Manual de informatică pentru clasa a XI-a 243 Pentru graful din figura 9.j) coincide cu muchia (j. 6. suma elementelor de pe coloana j.i). pentru graful de mai sus.2. j ∈ {1. 3. j = a j . Matricea de adiacenţă este simetrică: ai .. suma elementelor de pe linia 1 este 3. pentru că ea va reţine o mulţime de 0.. rezultă că elementele de pe diagonala principală reţin 0: ai ... are ca rezultat gradul nodului i..

i. n.txt".n.i]:=1. fstreamf (Nume_fis. De exemplu.j). } end. int i. f>>n.close().1. în care pentru fiecare nod se cunoaşte lista nodurilor adiacente cu el. { var A:Mat_ad.Nume_Fis).244 Capitolul 9. Programul următor citeşte datele referitoare la un graf şi afişează matricea sa de adiacenţă: Varianta Pascal Varianta C++ uses grafuri.j. } end. Introducere în teoria grafurilor Varianta Pascal Varianta C++ type mat_ad=array[1.3. Memorarea grafului prin liste de adiacenţă Listele de adiacenţă reprezintă o altă formă de memorare a grafurilor.j++) for j:=1 to n do cout<<A[i][j]<<" ".i<=n. (char Nume_fis[20].4. A[i][j]=A[j][i]=1. int A[50][50].j:byte.A. main() begin { CitireN('Graf. listele de adiacenţă sunt: 1 1 -> 2. end.ios::in). for i:=1 to n do for (int i=1. Reset(f).n).A. writeln.. var n:integer).50.5 6 2 -> 1. cout<<endl. A[i. i. CitireN("Graf.i.cpp" var A:mat_ad.4 3 6 -> 4 Figura 9. B.j<=n. begin while (f>>i>>j) Assign(f..4. write (A[i. int& n) (Nume_Fis:string. end.j]. . #include "grafuri. while(not eof(f)) do } begin readln(f.2.4 2 4 -> 3..i++) begin { for (int j=1. pentru graful din figura 9. var f:text.n).n).txt'. close(f). A[j. Readln(f.5 5 5 -> 1.50] of void CitireN integer.j]:=1.j:integer.3 3 -> 1. f. procedure CitireN int A[50][50].' ').

se utilizează un vector cu n componente. va reţine indicele ultimei coloane din T completată. în care vom pune pe j. Primul nod adiacent cu nodul 3 este 4.k) va reţine Start[i].  T(0. pe care îl vom numi Start şi o matrice T cu 2 linii şi 2m coloane.j) se completează două coloane ale matricei T. pentru că. ca prim element în lista nodurilor adiacente cu i.i) – reprezintă indicele coloanei din T unde se găseşte următorul element din listă. În continuare. 2. Variabila k. Astfel. pentru T(1. de acum. Dacă reţine 0. care are iniţial valoarea 0. pentru că trebuie înregistrat faptul că i este adiacent cu j şi j este adiacent cu i. pentru că succesorul lui i este j. Pentru că trebuie completată o nouă coloană în T. Pentru fiecare muchie (i. Start[i] specifică coloana din T unde începe lista nodurilor adiacente cu i. T(1. 1 2 3 4 5 6 Start 5 7 9 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 T[0] 2 1 3 1 5 1 3 2 4 3 5 4 T[1] 0 0 1 0 3 0 2 4 8 0 10 6  Exemplu de utilizare Dorim să vedem care este lista nodurilor adiacente cu nodul 3.i) – reprezintă un nod al listei nodurilor adiacente. Considerăm primul caz. este j.Manual de informatică pentru clasa a XI-a 245 În acest caz.k) va reţine j.  T(1. 4.4)=0 (T[1][4]). El este ultimul din listă. se incrementează k. vom pune pe j ca prim element în lista nodurilor adiacente lui i şi pe j ca prim element în lista nodurilor adiacente cu i. Start[3]=9. pentru că indicele coloanei în care se găseşte primul nod adiacent (până la această nouă introducere. când devine al doilea din listă) este în Start[i]. atunci acesta este ultimul elerment din lista succesorilor. Start[i] va reţine k. 3. primul nod din lista nodurilor adiacente cu i. T(0. Următorul se găseşte la indicele 8. Următorul nod adiacent cu nodul 3 este nodul 2. adică lista începe în coloana 9 a matricei T. Dacă reţine 0. vom arăta modul în care se construiesc listele de adiacenţă. Semnificaţiile sunt:  Start – pentru fiecare nod i. care se găseşte în coloana de indice k. O astfel de operaţie se realizează în patru paşi: 1. Următorul se găseşte la indicele 4. . Acesta este nodul 1. înseamnă că nodul i nu are noduri adiacente.

var T:Lista.man:integer. } Start[j]:=k.k:integer. f:text. f. } end. Assign(f. while (man<>0)do man=Start[i].i). Introducere în teoria grafurilor Mai jos.T.Nume_Fis). Programul următor citeşte datele despre un graf şi afişează. while(not eof(f)) do Start[i]=k. int& n) procedure Citire_LA_Astatic { (Nume_fis:string..man].50]of integer. T[1.1. cout<<endl. #include "grafuri. int Start[50]. k:=k+1. Readln(f.k]:=i. man:=T[1.Start. end.i.close(). i<<endl.i++) begin { writeln ('Noduri adiac. f>>n.k]:=Start[j]. } close(f).k]:=j.i.j.. cu "<< man:=Start[i].Start[50].j. . pentru fiecare nod. fstream var i.txt".txt'. Start[j]=k.Start. k:=k+1. grafuri. { cout<<T[0][man]<<" ".ios::in). T[1][k]=Start[i].cpp" var Start:pornire.n). begin k++.1. n.j). begin while (f>>i>>j) k:=0.i.k=0. T[0][k]=i. var Start:pornire. Reset(f). cout<<"Noduri adiac.50] of integer. int i. T[1][k]=Start[j]. pornire=array[1. int T[2][50]. readln(f. puteţi observa subprogramele care construiesc listele de adiacenţă (funcţiile au fost adăugate în grafuri. n. f(Nume_fis.cpp): Varianta Pascal Varianta C++ type void Citire_LA_Astatic lista=array[0.T. T[0][k]=j. ("Graf. (char Nume_fis[20]. for i:=1 to n do for (int i=1. begin while (man) write(T[0. T[0.n). lista nodurilor adiacente: Varianta Pascal Varianta C++ uses grafuri.. cu '.. { k++. T[1. T[0. begin main() Citire_LA_Astatic {Citire_LA_Astatic ('Graf. int T[2][50].man].var n:integer).pas şi respectiv. end. man=T[1][man].' ').246 Capitolul 9. writeln } end.i<=n. . } end..man. T:lista.k]:=Start[i].n). Start[i]:=k.

Reţineţi că există algoritmi pentru grafuri care prelucrează datele pornind de la această reprezentare (vedeţi arbori parţiali).y:integer.50] of muchie. end. Memorarea grafului prin lista muchiilor Se utilizează un vector cu m componente. Fiecare componentă va reţine cele două noduri la care muchia respectivă este incidentă şi.1.  Exerciţiu. am obţinut un graf aparte. . Pentru că oricare doi elevi se cunosc. Pentru a transpune în limbaj specific teoriei grafurilor această situaţie. Teoretic.. puteţi observa cum se descrie un vector (V) care reţine muchiile unui graf: Varianta Pascal Varianta C++ type muchie=record struct muchie x. Vom nota un graf complet prin Kn. 9.4. unde n este numărul de noduri ale grafului. Uneori. Definiţia 9. va fi necesar să sortăm muchiile. în anumite cazuri alte informaţii referitoare la muchia respectivă. unde m este numărul muchiilor. fie după altă informaţie asociată lor. }.Manual de informatică pentru clasa a XI-a 247 C. var V:array[1. vom considera că fiecare elev este un nod al grafului. fiecare componentă a grafului reţine numărul de obiective turistice. Pentru exemplul dat. De exemplu. dacă nodurile unui graf reprezintă oraşe. pe care-l vom numi graf complet. pe lângă una dintre metodele de memorare prezentate mai sus. muchie V[50]. În astfel de cazuri. Prin graf complet vom înţelege un graf neorientat în care oricare două noduri sunt adiacente. vom asocia grafului un vector cu n (numărul de noduri ale grafului) componente.y. înseamnă că oricare două noduri sunt unite printr-o muchie. unde fiecare componentă va reţine informaţiile referitoare la nodul respectiv. oricare doi elevi din clasă se cunosc. pentru fiecare astfel de nod se poate memora numărul obiectivelor turistice. nodurilor unui graf li se asociază anumite informaţii. Realizaţi un subprogram care citeşte şi memorează datele referitoare la muchiile unui graf memorat prin lista muchiilor. { int x. Mai jos.4. Graf complet Să considerăm mulţimea elevilor unei clase. Observaţie foarte importantă Uneori. Astfel. fie după nodul de pornire.

gradul oricărui nod este n-1. . avem 2n submulţimi disjuncte ale acesteia (aici este inclusă şi submulţimea vidă şi A).5. unde E1⊆E. fiecărei submulţimi de muchii îi corespunde un graf neorientat. 4 Exemplu de graf complet Relaţii utile: 1.248 Capitolul 9. avem: n(n−1) 2 2 submulţimi ale numărului maxim de muchii.5. unde m este numărul de 2 muchii. iar n. 9. n(n − 1) 2. Acest număr este C2 = n(n − 1) . Prin urmare. Într-un graf complet. Un graf parţial al unui graf neorientat dat G=(V. Graf parţial.  Demonstraţie: am văzut că numărul maxim de muchii pe care le poate avea un graf neorientat este: n(n − 1) 2 şi corespunde unui graf complet. fiind dată o mulţime A cu n elemente. numărul de noduri. aveţi un graf complet cu 4 noduri (K4): 1 2 3 Figura 9. Evident.5. din fiecare nod. Se ştie că.E) este un graf G1=(V. Într-un graf complet. pleacă (sosesc) n-1 muchii. Avem 2 2 grafuri neorientate cu n noduri.E1). Introducere în teoria grafurilor Alăturat.1. avem relaţia: m= . Numărul muchiilor va fi egal cu numărul de submulţimi cu 2 elemente ale mulţimii celor n noduri.  Demonstraţie: fiecare muchie uneşte 2 noduri. Ori. subgraf Definiţia 9. n 2 n(n−1) 3. pentru că nodurile sunt aceleaşi.

În teoria grafurilor. Fiind date n oraşe.6.. Aceasta înseamnă că în G se suprimă anumite muchii şi se obţine un graf parţial G'. unele persoane se ceartă. Intrebare: câte grafuri parţiale are un graf neorientat cu n noduri? Indicaţie: Câte submulţimi are mulţimea muchiilor {1. Obţinerea unui 1 1 graf parţial 2 3 3 rezultă 2 4 4 G=(V. După un timp.E1). Un subgraf al unui graf G este el însuşi sau se obţine din G prin suprimarea anumitor noduri şi a tuturor muchiilor incidente cu acestea. E1⊂E. iar muchiile din E1 sunt toate muchiile din E care sunt incidente numai la noduri din mulţimea V1. Un subgraf al unui graf neorientat G=(V. Priviţi exemplul de mai jos: Figura 9.. Asociem situaţiei date un graf G. se obţine un graf parţial G1. Asociem acestei situaţii un graf G. este el însuşi sau se obţine din G prin suprimarea anumitor muchii.E) G1=(V.. 2. Datorită precipitaţiilor. 1 Obţinerea unui 1 subgraf 2 3 3 rezultă 4 4 G=(V.7.m}? Definiţia 9.. anumite şosele se inundă şi nu mai pot fi utilizate.E1)  Exemple din viaţa reală 1. unde V1⊂V.Manual de informatică pentru clasa a XI-a 249 Un graf parţial al unui graf dat.6. Priviţi exemplul de mai jos: Figura 9.2. între unele dintre ele există o relaţie de prietenie. aceasta înseamnă că în G se suprimă anumite muchii şi astfel. Fiind date n persoane.E) G1=(V1.E) este un graf G1=(V1.E1) . unele dintre ele sunt unite printr-o şosea directă (care nu mai trece prin alte oraşe).

Întrebare: câte subgrafuri are un graf neorientat cu n noduri? Indicaţie: care este numărul de submulţimi ale mulţimii {1.2.1.. Există două modalităţi generale de parcurgere şi anume: parcurgerea în lăţime (BF) şi parcurgerea în adâncime (DF). Aceasta înseamnă că în G vom elimina anumite noduri şi muchiile incidente lor. Mai multe calculatoare (n) sunt legate în reţea cu ajutorul unor cabluri. .250 Capitolul 9. Cum noţiunea nu a fost prezentată până în acest moment. pe care îl considerăm vizitat. 2. G1. 9. jk. apoi cu j2. anumite calculatoare se defectează. în care parcurgerea grafurilor joacă un rol fundamental. Între timp. Astfel. 9. Introducere în teoria grafurilor  Exemple din realitate 1. deoarece o mulţime de algoritmi consacraţi au la bază o astfel de parcurgere. Acestea vor fi tratate separat..  Vizităm toate nodurile adiacente cu j1. Din acest motiv. În paragrafele următoare.1. vizitate în această ordine. V. Parcurgerea în lăţime (BF .. 3 În acest paragraf vom exemplifica parcurgerile doar în cazul grafurilor conexe.6. este nevidă.. Asociem situaţiei date un graf G. în acest paragraf nu vom insista pe aplicaţiile parcurgerilor şi ne vom mărgini numai la prezentarea algoritmilor de parcurgere. Asociem situaţiei date un graf G. se obţine un subgraf al lui G.Breadth First)  Parcurgerea în lăţime se face începând de la un anumit nod i. vom face abstracţie de mulţimea vidă. apoi cu jk. Se dau n firme. ….. veţi găsi multe exemple utile.6.  Parcurgerea continuă în acest mod până când au fost vizitate toate nodurile accesibile. Între unele din acestea se stabilesc relaţii de colaborare.  .. G1. obţinând un subgraf al lui G. Parcurgerea grafurilor neorientate 3 Prin parcurgerea grafurilor înţelegem o modalitate de vizitare a nodurilor acestuia. j2. Între timp.1. Parcurgerea eficientă a grafurilor este esenţială în teoria grafurilor. precizăm doar că vom exemplifica parcurgerea grafurilor în care oricare două noduri sunt "legate" printr-o succesiune de muchii.. .  Vizităm apoi toate nodurile adiacente cu el . anumite firme se desfiinţează.n}? Întrucât mulţimea nodurilor.fie ele j1..

 se afişează. s . nodul i nu a fost vizitat s[i] =  1. ⇒ Parcurgerea BF se efectuează prin utilizarea structurii numită coadă.Manual de informatică pentru clasa a XI-a 251 Exemple de parcurgeri BF ale aceluiaşi graf.indicele primei componente a cozii. pornind de la noduri diferite: 1 Pentru graful alăturat avem: 2 6 3 Nod pornire 1: 1 3 6 2 7 5 4 Nod pornire 3: 3 7 6 1 2 5 4 Nod pornire 6: 6 3 1 7 2 5 4 4 5 7 Figura 9. memorat prin liste de adiacenţă şi matrice de adiacenţă: . Coada va fi alocată prin utilizarea unui vector.indicele ultimei componente a cozii. având grijă ca un nod să fie vizitat o singură dată. . Vom utiliza următoarele notaţii: . coada . care nu au fost vizitate şi se marchează ca vizitate. Atunci când un nod a fost introdus în coadă se marchează ca vizitat.8. Algoritmul este următorul: Nodul de pornire este introdus în coadă şi este marcat ca vizitat. Cât timp coada este nevidă Pentru nodul aflat la începutul cozii:  se trec în coadă toate nodurile adiacente cu el. nodul i a fost vizitat În continuare. vor fi prezentate două exemple de implementări ale parcurgerii în lăţime (BF) a unui graf.vectorul care memorează coada propriu-zisă. . s_c . .vector ce reţine nodurile vizitate: 0.  se extrage din coadă. i_c .

50] of int n. } while p<>0 do p=T[1][p]. 2. begin i_c++. coada[50]. void bf(int nod) procedure bf(nod:integer).p].T[2][50]. end } end.i. T:lista. sf_c=1.i_c. Introducere în teoria grafurilor 1.txt"..i_c=1. sf_c:=sf_c+1.Start.s[50]. begin s[T[0][p]]=1.A[50][50].i:integer.s[50]. while (p) coada[i_c]:=nod. . begin } if s[T[0. coada[50]. Start.T writeln(coada[i_c]).Start[50].sf_c. i_c:=i_c+1.sf_c. Programul următor parcurge BF un graf memorat prin liste de adiacenţă: Varianta Pascal Varianta C++ uses grafuri. integer. s[nod]=1. procedure bf(nod:integer). p:=Start[coada[i_c]]. i_c:=1. s[nod]=1.p:integer. {sf_c++.s:array[1. bf(6). sf_c:=1. coada.p].i_c=1.cpp" var n.i_c. void bf(int nod) Start:pornire. } s[T[0.sf_c:=1. main() p:=T[1. begin Citire_LA_Astatic('Graf.T. i_c:=1.252 Capitolul 9.p]]=0 then cout<<coada[i_c]<<endl. while (i_c<=sf_c) begin { p=Start[coada[i_c]].50] of sf_c=1. bf(3). end. { if (s[T[0][p]]==0) s[nod]:=1. int n.cpp" var n.n). { begin coada[i_c]=nod. #include "grafuri. Programul următor parcurge BF un graf memorat prin matricea de adiacenţă: Varianta Pascal Varianta C++ uses grafuri.p.n). . integer. coada.p]]:=1. } coada[sf_c]:=T[0. { end.txt'. end.. while i_c<=sf_c do coada[sf_c]=T[0][p]. { coada[i_c]=nod. A:mat_ad.s:array[1. Citire_LA_Astatic("Graf. #include "grafuri.

Manual de informatică pentru clasa a XI-a 253 coada[i_c]:=nod. if ((A[coada[i_c]. while (i_c<=sf_c) do while (i<=n) begin {if (A[coada[i_c]][i]==1 i:=1.i]=1) s[i]=1. nevizitate încă. while (i_c<=sf_c) s[nod]:=1. and (s[i]=0)) } then i++. writeln(coada[i_c]). } end end. end. bf(1). s[i]:=1. .A. begin coada[sf_c]=i. begin } sf_c:=sf_c+1.  După vizitarea unui nod. { i=1. && s[i]==0) while i<=n do { sf_c++. Parcurgerea în adâncime (DF . i_c++. până când au fost vizitate toate nodurile adiacente cu el. apoi următorul nod adiacent. begin CitireN('Graf.. Exemple de parcurgeri DF ale aceluiaşi graf. 9. cout<<coada[i_c]<<endl.6. se vizitează primul dintre nodurile adiacente.n). coada[sf_c]:=i.. } i:=i+1.2. main() end.txt'. bf(1)..n). i_c:=i_c+1.9. pe care îl considerăm vizitat. pornind de la noduri diferite: 1 Pentru graful alăturat.Depth First)  Parcurgerea în adâncime se face începând de la un anumit nod i. } end.A.  Parcurgerea continuă în acest mod până când au fost vizitate toate nodurile accesibile.txt". avem: Nod pornire 1: 1 3 7 6 2 5 4 2 6 3 Nod pornire 3: 3 7 6 1 2 5 4 Nod pornire 6: 6 3 7 1 2 5 4 4 5 7 Figura 9.1.  . { CitireN("Graf.

df(1).Start[50]. int s[50]. end. { if (s[T[0][p]]==0) s[nod]:=1.k++) write(nod. begin s[nod]=1.n). s[nod]=1.n.T. #include "grafuri.cpp" var n:integer. s:array[1. T. begin for (k=1. } Start. s:array[1. p:=T[1.A.n). ..k]=1) and (s[k]=0) then df_r(k). var k:integer.p]]=0 } then df(T[0.p]). end.. while p<>0 do p=T[1][p]. p=Start[nod]. #include "grafuri. void df(int nod) Start:pornire. procedure df_r(nod:integer).' ').txt'. Citire_LA_Astatic('Graf. Citire_LA_Astatic("Graf.A.A[50][50]. cout<<nod<<" ". begin df(1).Start.n). for k:=1 to n do } if (A[nod.cpp" var n:integer. { CitireN("Graf. begin } CitireN('Graf.n). void df_r(int nod) A:mat_ad. df_r(k). if(A[nod][k]==1 && s[k]==0) s[nod]:=1. cout<<nod<<" ". writeln(nod. procedure df(nod:integer). main() end. Introducere în teoria grafurilor Exemple de implementări ale parcurgerii DF 1. { int k.p].txt". while (p) p:=Start[nod].50] of integer. { int p.txt". df_r(1). 2.254 Capitolul 9.k<=n. begin } if s[T[0. int s[50]. main() end { end.50]of integer. T:lista. Programul următor parcurge DF un graf memorat prin matricea de adiacenţă: Varianta Pascal Varianta C++ uses grafuri.' ').txt'. df(T[0][p]).n. Programul următor parcurge DF un graf memorat prin liste de adiacenţă: Varianta Pascal Varianta C++ uses grafuri. var p:integer. T[2][50]. df_r(1).

Manual de informatică pentru clasa a XI-a 255

9.1.6.3. Estimarea timpului necesar parcurgerii grafurilor

⇒ Parcurgerea BF (sau DF) a grafurilor, pornind de la matricea de adiacenţă, se
face în O(n2). Practic, pornind de la un nod i, se caută pe linia i a matricei
toate nodurile adiacente cu el şi pentru toate cele găsite se procedează în mod
analog.

⇒ Parcurgerea BF (sau DF) a grafurilor pornind de la listele de adiacenţă se face
în O(m). Pornind de la un nod i, se caută toate nodurile adiacente cu el, dar
acestea se găsesc deja grupate în lista asociată nodului respectiv şi numărul
lor corespunde numărului de muchii incidente acestuia. Algoritmul va selecta,
pe rând, toate muchiile, de unde rezultatul de mai sus.

9.1.7. Lanţuri

Reluăm exemplele de la paragraful 9.1.1.

Pentru exemplul 1. Întrebarea este: fiind date două oraşe a şi b, se poate
ajunge cu maşina din a în b?
Pentru exemplul 2. O persoană, a, află o informaţie importantă. Persoana
transmite informaţia tuturor prietenilor, aceştia, la rândul lor, transmit informaţia tuturor
prietenilor lor, ş.a.m.d. Întrebarea este: informaţia ajunge la persoana b?
Pentru exemplul 4. Fiind date două triunghiuri, a şi b, sunt ele asemenea? Să
observăm că nu este obligatoriu ca să se fi introdus de la început faptul că triunghiul a
este asemenea cu triunghiul b. Această informaţie poate fi dedusă, de exemplu, prin
faptul că a este asemenea cu k, k cu l şi l cu b.

Analizând aceste întrebări pe graful asociat fiecărei situaţii în parte, ajungem la
concluzia că în toate cazurile trebuie să existe o succesiune de noduri de la
nodul a la nodul b cu proprietatea că oricare două noduri sunt adiacente. Dacă
această succesiune nu există, răspunsul este negativ, altfel răspunsul este pozitiv.
Sau, exprimat în teoria grafurilor, aceasta înseamnă că răspunsul depinde de
existenţa unui lanţ de la a la b. De acum, putem prezenta definiţia lanţului.

Definiţia 9.7. Pentru graful neorientat G=(V,E), un lanţ L=[v1,v2...vp]
este o succesiune de noduri cu proprietatea că oricare două noduri vecine
sunt adiacente, adică (v1,v2)∈E, (v2,v3)∈E, ..., (vp-1,vp)∈E. De
altfel, un lanţ poate fi definit prin succesiunea de muchii (v1,v2)∈E, (v2,v3)∈E, ...,
(vp-1,vp)∈E.

 Vârfurile v1 şi vp se numesc extremităţile lanţului.
 Numărul p-1 se numeşte lungimea lanţului. Acesta este dat de numărul de
muchii ce unesc nodurile lanţului.

256 Capitolul 9. Introducere în teoria grafurilor

Definiţia 9.8. Se numeşte lanţ elementar un lanţ care conţine numai noduri
distincte.
1
Exemple: pentru graful din figura alăturată:
1. [1,2,5] este un lanţ elementar cu lungime 2,
între nodurile 1 şi 5. 2 4 3

2. [1,3,4,1,2] este un lanţ (care nu este
elementar) de lungime 4, între nodurile 1 şi 2.
5
Figura 9.10.

 Problema 9.1. Fiind dat un graf şi două noduri ale sale a şi b, să se scrie un
program care decide dacă între ele există un lanţ sau nu, iar în caz că acest lanţ
există, se cere să se afişeze lanţul.

 Rezolvare. Există un lanţ de la a la b, dacă şi numai dacă o parcurgere DF sau
BF, care porneşte de la nodul a, va ajunge să viziteze nodul b. Rămâne de rezolvat
problema modului în care reţinem lanţul de la a la b. Să observăm că fiecare metodă
de parcurgere a grafului, pornind de la un nod j, selectează un altul i, dacă cele
două noduri sunt adiacente şi dacă nodul i este vizitat pentru prima dată. Pentru a
reţine selecţiile astfel efectuate, vom utiliza un vector T, iar elementele acestuia au
semnificaţia următoare:
 j, dacă i este descendent al lui j
T[i] = 
0, dacă i nu a fost selectat
Să mai observăm că un nod este selectat o singură dată, deci, în final, T va
reţine, pentru fiecare nod i, nodul j de la care a fost selectat. Pentru nodul de la care
a pornit parcurgerea (a) vom avea T(a)=0, pentru că acest nod nu a fost selectat de
algoritm. De aici, rezultă că drumul poate fi reconstituit, pornind de la T, astfel: se
afişează b, apoi T[b], apoi T[T[b]] ... până când se obţine valoarea 0. Pentru ca
drumul să nu fie afişat în ordine inversă faţă de modul în care a fost obţinut,
subprogramul refac care îl reconstituie şi îl afişează este recursiv. Programul de mai
jos afişează drumul, pornind de la matricea de adiacenţă a grafului:

Varianta Pascal Varianta C++
uses grafuri; #include "grafuri.cpp"
var n,a1,b:integer; int
s,T:array[1..50] of integer; s[50],A[50][50],n,T[50],a,b;
A:mat_ad;
void refac (int nod)
procedure refac (nod:integer); {
begin if (nod!=0)
if nod<>0 then {
begin refac (T[nod]);
refac (T[nod]); cout<<nod<<" ";
write(nod,' '); }
end }
end;

Manual de informatică pentru clasa a XI-a 257

procedure df_r(nod:integer); void df_r(int nod)
var k:integer; {
begin int k;
s[nod]:=1; s[nod]=1;
for k:=1 to n do for (k=1;k<=n;k++)
if (A[nod,k]=1) and (s[k]=0) if(A[nod][k]==1 && s[k]==0)
then {
begin T[k]=nod;
T[k]:=nod; df_r(k);
df_r(k); }
end }
end;
main()
begin {
CitireN('Graf.txt',A,n); CitireN("Graf.txt",A,n);
write('a='); readln(a1); cout<<"a="; cin>>a;
write('b='); readln(b); cout<<"b="; cin>>b;
df_r(a1); df_r(a);
if (T[b]<>0) then refac(b); if (T[b]!=0) refac(b);
end. }

Observaţii foarte importante

1. Să observăm că vectorul T poate fi folosit pentru a obţine lanţuri de la nodul a la
oricare alt nod al grafului.
2. Dacă refacem rezolvarea prin utilizarea parcurgerii în lăţime, vom observa că
lanţul obţinut are lungimea minimă. Prin natura ei, parcurgerea BF selectează nodurile
în ordinea "depărtării" lor faţă de nodul de la care a început parcurgerea. Astfel, la
început se vizitează primul nod (a), apoi nodurile pentru care lungimea lanţului de la a
la ele este 1, apoi nodurile pentru care lungimea lanţului de la a la ele este 2, ş.a.m.d.

Programul care urmează afişează un lanţ de lungime minimă între nodurile a şi b:

Varianta Pascal Varianta C++
uses grafuri; #include "grafuri.cpp"
var int n,coada[50],s[50],
n,a1,b,i_c,sf_c,i:integer; i_c=1, sf_c=1,
s,T,coada:array[1..50] of A[50][50],i,T[50],a,b;
integer;
void refac (int nod)
A:mat_ad;
{ if (nod!=0)
procedure refac (nod:integer); { refac (T[nod]);
begin cout<<nod<<" ";
if nod<>0 then }
begin }
refac (T[nod]);
write(nod,' ');
end
end;

258 Capitolul 9. Introducere în teoria grafurilor

procedure bf(nod:integer); void bf(int nod)
begin { coada[i_c]=nod;
i_c:=1; s[nod]=1;
sf_c:=1; while (i_c<=sf_c)
coada[i_c]:=nod; { i=1;
s[nod]:=1; while (i<=n)
while i_c<=sf_c do { if (A[coada[i_c]][i]==1
begin && s[i]==0)
i:=1; { sf_c++;
while i<=n do coada[sf_c]=i;
begin s[i]=1;
if (A[coada[i_c],i]=1) and T[i]=coada[i_c];
(s[i]=0) }
then i++;
begin }
sf_c:=sf_c+1; i_c++;
coada[sf_c]:=i; }
s[i]:=1; }
T[i]:=coada[i_c];
main()
end;
{
i:=i+1;
CitireN("Graf.txt",A,n);
end;
cout<<"a=";
i_c:=i_c+1;
cin>>a;
end
end; cout<<"b=";
cin>>b;
begin bf(a);
CitireN('Graf.txt',A,n); if (T[b]!=0) refac(b);
write('a='); }
readln(a1);
write ('b=');
readln(b);
bf(a1);
if T[b]<>0 then refac(b);
end.

Matricea lanţurilor. Întrebări referitoare la situaţiile prezentate la 9.1.1:

a) pentru exemplul 1, întrebarea este: cum putem afla, pentru fiecare oraş în parte,
oraşele în care putem ajunge cu maşina?
b) pentru exemplul 4, întrebarea este: cum putem afla, pentru fiecare triunghi în
parte, care sunt triunghiurile asemenea cu el?

Revenind la graful asociat acestor situaţii, problema constă în a afla pentru
fiecare nod i, nodurile j, pentru care există un lanţ de la i la j. Evident, rezultatele
pot fi reţinute într-o matrice cu n linii şi n coloane (matrice pătratică). Această
matrice se numeşte matricea lanţurilor, iar elementele ei au semnificaţia:

1, dacă există lanţ de la i la j
L(i, j) = 
0, în caz contrar

Manual de informatică pentru clasa a XI-a 259

 Problema 9.2. Fiind dat un graf G, cum putem obţine matricea lanţurilor?
 Răspunsul este uşor de dat. Parcurgem graful începând cu nodul 1. Pentru toate
nodurile j vizitate, vom avea L(1,j)=1, completând astfel prima linie a matricei.
Apoi, vom parcurge din nou, graful, pornind de la nodul 2. Pentru toate nodurile j,
vizitate, vom avea L(2,j)=1, apoi parcurgem graful începând cu nodul 3.... ş.a.m.d.
O anumită îmbunătăţire a algoritmului se obţine dacă ţinem cont de faptul că matricea
lanţurilor este simetrică (de ce?). Lăsăm ca exerciţiu scrierea acestui program.
Întrebare: care este complexitatea acestui algoritm?

9.1.8. Graf conex

Revenind la exemplele de la 9.1.1, putem pune întrebările:
a) pentru exemplul 1: se poate ajunge cu maşina din orice oraş în oricare altul?
b) pentru exemplul 4: toate triunghiurile sunt asemenea între ele?

Dacă la ambele întrebări răspunsul este afirmativ, ce semnificaţie are el pentru
grafurile asociate? Aceasta înseamnă că pentru orice pereche de noduri, există un
lanţ care le are ca extremităţi. Sau, în limbajul specific teoriei grafurilor, cele două
grafuri sunt conexe.

Definiţia 9.9. Un graf neorientat G=(V,E) este conex, dacă pentru orice
pereche de noduri x,y∈V, există un lanţ în care extremitatea iniţială este x
şi extremitatea finală este y.

Exemple Figura 9.11.
1
1. Graful alăturat este conex. De exemplu,
între nodurile 1 şi 5 există lanţul [1,2,3,4,5], dar
şi lanţul [1,3,4,5]. Între nodurile 3 şi 5 există 2 3
lanţul [3,4,5], ş.a.m.d.

Oricum am alege două noduri, există lanţul cerut de
4 5
definiţie.

2. Graful alăturat nu este conex. De exemplu, între Figura 9.12.
nodurile 1 şi 4 nu există nici un lanţ. 4
1

2 3 5

Un graf cu un singur nod este, prin definiţie, conex. Aceasta pentru că nu există
două noduri diferite pentru care să se pună problema existenţei unui lanţ.

260 Capitolul 9. Introducere în teoria grafurilor

 Problema 9.3. Fiind dat un graf G=(V,E), să se scrie un program care să decidă
dacă graful dat este sau nu conex.

 Rezolvare. Ţinând cont de cele învăţate, problema nu este grea. Putem utiliza
una din metodele de parcurgere învăţate, DF sau BF. Ideea este următoarea: dacă,
pornind de la un nod, printr-una din metodele de parcurgere, ajungem să vizităm toate
celelalte noduri, atunci graful dat este conex. Cum putem şti dacă am vizitat toate
nodurile? Simplu, după parcurgere, toate componentele vectorului s reţin 1. Puteţi
scrie acest program?

9.1.9. Componente conexe
Analizăm, din nou, exemplele de la 9.1.1.
a) Pentru exemplul 1: se cere o mulţime de oraşe, astfel încât să se poată circula cu
maşina între oricare două oraşe din mulţime, iar dacă un oraş nu aparţine acestei
mulţimi, atunci nu putem ajunge cu maşina de la el la oricare oraş din mulţime.
b) Pentru exemplul 4: se cere o mulţime de triunghiuri astfel încât oricare două
triunghiuri din această mulţime sunt asemenea, iar dacă un triunghi nu aparţine
acestei mulţimi, el nu este asemenea cu nici unul din mulţime.
Observăm că fiecare astfel de mulţime este maximală în raport cu relaţia de
incluziune. Dacă nu ar fi aşa, ar exista o altă mulţime care ar include-o.
Fie graful asociat unuia dintre cazurile prezentate. În termeni din teoria
grafurilor, problema se reduce la determinarea nodurilor unei componente conexe.

Definiţia 9.10. Fie G=(V,E) un graf neorientat şi G1=(V1,E1) un subgraf
al său. Atunci G1=(V1,E1) este o componentă conexă a grafului
G=(V,E) dacă sunt îndeplinite condiţiile de mai jos:
a) Oricare ar fi x,y∈V1, există un lanţ de la x la y.
b) Nu există un alt subgraf al lui G, G2=(V2,E2) cu V1⊂V2 care
îndeplineşte condiţia a).
Figura 9.13.
Exemple 4
1
1. Graful alăturat este alcătuit din două componente
conexe. Prima este alcătuită din nodurile 1, 2, 3 şi
muchiile care le unesc pe acestea, a doua este 2 3 5
formată din nodurile 4 şi 5 şi muchia care le uneşte.
Figura 9.14.
2. Graful din figura 9.14. conţine 3 componente 4
conexe. Aşa cum un graf, cu un singur nod, este 1
conex, tot aşa un nod izolat alcătuieşte el singur o
componentă conexă.
2 3 5

writeln('Comp conexa'). se dă un graf neorientat.txt". Fiind dat un graf neorientat. Pentru fiecare nod vizitat. . s:array[1. main() { CitireN("Graf.4. pentru un graf conex şi n corespunzător unui graf cu toate nodurile izolate. end.k++) write (nod.50]of integer. Cp. cout<<nod<<" ". void df_r(int nod) A:mat_ad.Manual de informatică pentru clasa a XI-a 261 Observaţii 1.txt'.n).k]=1) and (s[k]=0) } then df_r(k). astfel încât graful să devină conex. Câte componente conexe poate avea un graf neorientat cu n noduri? Numărul lor este cuprins între 1. if((A[nod][k]==1)&& s[nod]:=1. o parcurgere a grafului (DF sau BF) pornind de la un anumit nod. begin df_r(i). după o parcurgere.n).. int s[50]. vizitează toate nodurile componentei conexe care îl conţine. if (s[i]==0) for i:=1 to n do { cout <<"Comp if s[i]=0 then conexa"<<endl. prima uneşte un nod din C1 cu unul din C2.. parcurgerea se reia începând de la primul nod nevizitat. Fie p numărul lor. } end end.  Rezolvare. begin for (k=1. procedure df_r(nod:integer). Evident. { int k. În unele probleme. C2. Varianta Pascal Varianta C++ uses grafuri.A[50][50].. se cere să se afişeze nodurile fiecărei componente conexe. s[nod]=1. Dacă. if (A[nod.i:integer.A. 2. s[i] reţine 1.i. df_r(i).. Vom adăuga p-1 muchii.9.k<=n.n. var k:integer. În astfel de cazuri. (s[k]==0)) for k:=1 to n do df_r(k)..i++) CitireN('Graf. care nu este conex şi se cere să se adauge un număr minim de muchii. După cum uşor vă puteţi da seama. se determină componentele conexe. numărul componentelor conexe este egal cu numărul de parcurgeri necesare pentru a fi vizitate toate nodurile. begin for (i=1. fie ele C1. cout<<endl. } writeln. mai rămân noduri nevizitate. #include "grafuri.' ').cpp" var n.A. ..i<=n. a doua uneşte un nod din C2 cu unul din C3. . ultima uneşte un nod din Cp-1 cu unul din Cp.  Problema (de programare).

deci nu conţine 2 3 5 numai muchii distincte. Mai întâi. De ce s-a ajuns în situaţia să se încerce vizitarea nodului 3 de două ori? Pentru că nodul 2 3 1. să se scrie un program care decide dacă graful conţine cel puţin un ciclu.2) şi (2. [1. figura 9.11. 1 b. putem rezolva problema verificând dacă există un ciclu într-o componentă conexă a sa. Introducere în teoria grafurilor 9. cu excepţia ultimului nod.E). Întrebarea este următoarea: există vreun oraş din care putem face o excursie cu maşina. vedeţi rolul vectorului s). cu graful de mai jos. conţine numai noduri distincte). Pentru simplitate. Figura 9. Un lanţ L care conţine numai muchii distincte şi pentru care nodul iniţial coincide cu nodul final se numeşte ciclu. Şi aici. pentru că nu conţine numai muchii distincte. apoi se încearcă 1 vizitarea nodului adiacent cu 3.2. să vizităm mai multe oraşe şi să ne întoarcem de unde am plecat? Problema se reduce la a afla dacă graful asociat are sau nu cel puţin un ciclu. 4 Astfel.1.5.2. să definim noţiunea.16. Vizităm nodul 1. Fiind dat un graf conex. . [1.2. Începem prin a observa că dacă graful nu este conex. pe care îl parcurgem DF.10.3]. am preferat să considerăm că graful este conex. Cicluri Revenim la exemplul cu oraşele legate prin şosele.1] nu este ciclu. apoi primul nod adiacent al lui 2.2. fie el nodul 2. c.15.3. Vom da un exemplu. lanţul este elementar. şi anume 1. a fost vizitat ca nod de pornire şi pentru că se încearcă vizitarea lui prin lanţul [1. problema se poate rezolva pornind de la o parcurgere DF. Dar acest nod a mai fost vizitat şi tentativa este respinsă. pentru că (1.  Problema 9. algoritmul va ajunge în situaţia de a vizita un nod de două ori (tentativă oricum respinsă.  Rezolvare.1] este ciclu elementar. care coincide cu primul.1] nu este ciclu.1) reprezintă o aceeaşi muchie.3. apoi primul nod adiacent lui.3. [1. s-a obţinut ciclul [1. care coincide cu primul.1]. G=(V. să nu trecem decât o singură dată pe o şosea. avem: 4 a. Exemple: pentru graful neorientat din Figura 9.262 Capitolul 9. Graful conţine cel puţin un ciclu dacă. pentru că algoritmul testează acest lucru. fie el 3.15. cu excepţia ultimului nod. Dacă. atunci ciclul este elementar (adică. în timpul parcurgerii. Definiţia 9.2.2.

Să observăm că. } if gasit then writeln('Da') else writeln('Nu') end.cpp" var n:integer. Observaţie Deşi parcurgerea se face în timp polinomial şi cu toate că programul este simplu..Manual de informatică pentru clasa a XI-a 263 Programul următor testează dacă un graf conţine sau nu cicluri.n. unde m este numărul de muchii. void df(int nod) A:mat_ad. Varianta Pascal Varianta C++ uses grafuri. pentru că altfel ar fi selectată şi această muchie şi s-ar ajunge în situaţia să fie semnalat un ciclu fals. se şterge din matricea de adiacenţă muchia (k. A[k][nod]=0. if A[nod. altfel. #include "grafuri. if (A[nod][k]==1) begin { s[nod]:=1. for(k=1. begin } A[k][nod]:=0. De unde această observaţie? Pentru a o înţelege.k<=n. end. accesat prin intermediul muchiei (nod. odată vizitat un nod. dacă m>n-1 înseamnă că graful conţine cel puţin un ciclu.n). se putea proceda într-un mod cu mult mai inteligent.k]=1 then else gasit=1.A[50][50]. else cout<<"Nu".k). df(1). înseamnă că graful nu conţine cicluri. } if s[k]=0 then df(k) main() else gasit:=true. şi ar contrazice cerinţa. este suficient să verificăm relaţia m=n-1.k++) var k:integer. CitireN('Graf. for k:=1 to n do if (s[k]==0) df(k). aproape că nu este cazul să facem un program. gasit:boolean. Graful fiind conex.A.txt". iar dacă m<n-1 înseamnă că nu este conex.wincrt. iar n este numărul de noduri. trebuie să studiem arborii… .A. df(1).50]of integer. Mai mult. int s[50]. Dacă relaţia este verificată. { int k. s:array[1. { end CitireN("Graf.n). procedure df(nod: integer).txt'. begin if (gasit) cout<<"Da". nod). s[nod]=1.gasit.

în timpul parcurgerii. este.A[50][50].gasit. Prin urmare.50]of integer. cu aceeaşi parcurgere DF(BF). DF (BF) se vizitează toate nodurile. Arbori 9. Dacă ar conţine un ciclu. prin cablu. begin if (s[k]==0) df(k). vom avea un graf conex (fiecare casă trebuie conectată). Cum vor fi conectate casele la cablu? Logic. cele n case ale unui sat. Introducere în teoria grafurilor 9.1. Dacă într-o parcurgere. int k. Se numeşte arbore un graf neorientat care este conex şi nu conţine 1 2 cicluri. atunci.6.i. Dacă un graf are cicluri. din nou.  Rezolvare. program care verifică dacă este arbore. Noţiunea de arbore Să presupunem că o firmă doreşte să conecteze la TV.k++) s[nod]:=1. Se citeşte un graf. atunci graful este conex.i.suma:integer.k<=n. În figura 9. suma. A:mat_ad.11.n. aveţi un exemplu de arbore cu 5 noduri.. #include "grafuri. la o casă va sosi cablul dintr-un singur loc şi.11.nod]:=0. evident. uşor de verificat. if s[k]=0 then df(k) } else gasit:=true. s[nod]=1.17. va trebui ca fiecare casă să fie conectată. void df(int nod) gasit:boolean. iar graful nu va conţine cicluri. . Definiţia 9. else gasit=1. int s[50]. Conexitatea ştim să o verificăm. s:array[1. 3 4 5  Problema 9. } end end. de la ea va porni cablul către altă casă. va exista cel puţin o a doua tentativă de vizitare a unui nod.12.1. obţinem un graf cu proprietăţile de mai sus. Să ne amintim că în cazul în care graful are cicluri.cpp" var n. Să se scrie un Figura 9. Dacă analizăm situaţia prezentată prin prisma teoriei grafurilor. A[k. var k:integer. Varianta Pascal Varianta C++ uses grafuri.264 Capitolul 9. eventual. numit arbore. putem renunţa la cablul care uneşte două case care aparţin ciclului respectiv.1. Astfel. begin for(k=1. Apoi. printr-o simplă parcurgere DF (BF) se poate stabili dacă graful este conex sau nu.k]=1 then A[k][nod]=0.17. if (A[nod][k]==1) for k:=1 to n do { if A[nod. { procedure df(nod:integer).

. Dar G are n noduri. Gp componentele conexe ale grafului. conex şi fără cicluri). else writeln('Arbore'). Fie G1. suma+=s[i]. for i:=1 to n do for (i=1.A. df(1). …. else cout<<"Arbore". df(1). else else if gasit if (gasit) then writeln('Are ciclu') cout<<"Are ciclu".i<=n. există o singură componentă conexă. acesta va avea n-1 muchii. se contrazice definiţia). Vom demonstra prin inducţie. Până la urmă.i++) suma:=suma+s[i]. Dacă nu ar exista un astfel de nod. b) nu conţine cicluri (pentru că G nu conţine cicluri). lanţul va trece de două ori printr-un acelaşi nod.txt".  Demonstraţie ⇒ Fie G un arbore (graf neorientat. Vom presupune proprietatea adevărată pentru arbori cu n noduri (adică au n-1 muchii). Vom demonstra prin reducere la absurd.. Fiecare dintre ele îndeplineşte condiţiile: a) este conexă (aşa a fost aleasă). Fie G un graf neorientat cu n noduri. Rezultă că G este arbore. deci p=1. deci n1+n2+. Rezultă: n=n+p-1. Rămâne de dovedit că G este conex. . Rezultă că fiecare dintre ele este arbore. vom selecta o muchie.A. care nu conţine cicluri.+np=n+p-1.. numărul muchiilor este 0 (se verifică. end.. pentru că mulţimea nodurilor este finită şi pentru că nu există nod terminal. if suma<>n if (suma!=n) then writeln('Nu este conex') cout<<"Nu este conex". are n-1 muchii).. } Teorema 9.1. Rezultă: n1-1+n2-1+. La fiecare pas. Eliminăm nodul terminal şi muchia care îi este incidentă. Fie mi numărul muchiilor şi ni numărul nodurilor fiecărui arbore Gi. Presupunem că G nu este conex.Manual de informatică pentru clasa a XI-a 265 begin main() CitireN('Graf.. suma=0. Înseamnă că arborele cu n+1 noduri va avea n muchii (n-1+1). Avem mi=ni-1. Obţinem un arbore cu n noduri. Dar m1+m2+.+mp=n-1.txt'. să considerăm un lanţ care porneşte dintr-un nod oarecare. ⇐ Fie G un graf cu n-1 muchii. În concluzie. G2. Există cel puţin un nod terminal (nod care are o singură muchie incidentă). { CitireN("Graf. Trebuie să demonstrăm că are n-1 muchii. Fie un arbore cu n+1 noduri. Conform ipotezei făcute.+np-1=n-1. Dacă n=1.n). care nu conţine cicluri. Asta înseamnă că arborele ar conţine cicluri (absurd. suma:=0. G este arbore dacă şi numai dacă are n-1 muchii şi nu conţine cicluri.n).

Pentru aceasta s-a obţinut un credit extern care permite ca toate şoselele să fie recondiţionate. iar muchiile sunt şoselele. în acelaşi timp. mai precis parcurgerea DF.A[50][50]. (dată în paragraful anterior). în urma eliminării anumitor muchii se obţine.E). o metodă de parcurgere a unui graf. 4 5 4 5  Pentru a rezolva problema. dar. puteţi observa un graf conex (stânga) şi un arbore parţial al său.cpp" var n:integer. end. din nou. adică cele care nu determină cicluri. un arbore. df_r(k). Se cere un graf parţial al său.A. Desigur.wincrt.A.n). var k:integer. df_r(1). 1 1 care este arbore. s[nod]=1. Se cere un număr minim de şosele care să nu intre în reparaţii în prima fază.1. Se dă un graf conex. Vom afişa numai muchiile selectate de algoritm.k++) begin if (A[nod][k]==1 && s[k]==0) s[nod]:=1. n-1. df_r(1). begin } CitireN('Graf. se doreşte ca repararea şoselelor să se facă cât mai repede..1. astfel încât condiţia de mai sus să poată fi respectată.7.18. for k:=1 to n do df_r(k). main() end.2. Alăturat. trebuie ca în timpul reparaţiilor să se poată circula. astfel încât să nu rămână localităţi inaccesibile pentru traficul rutier.11.  Problema 9. aşa cum rezultă din teorema 9. procedure df_r(nod:integer). end. { cout<<nod<<" "<<k<<endl. vom folosi.txt". Figura 9. Evident. void df_r(int nod) A:mat_ad. if (A[nod. O astfel de structură se numeşte arbore parţial. Introducere în teoria grafurilor 9.k).k<=n.266 Capitolul 9. Programul următor citeşte datele referitoare la un graf conex şi afişează muchiile unui arbore parţial.' '.k]=1) and (s[k]=0) } then begin } writeln(nod.txt'. #include "grafuri. există posibilitatea ca dintr-un graf conex să se 2 3 2 3 poată obţine mai mulţi arbori parţiali. va trebui să păstrăm un număr minim de muchii. {int k. Care este acel număr minim de muchii care conservă conexitatea? Evident. G=(V. . s:array[1. for (k=1.50]of integer. Cum graful rămâne conex.n). Varianta Pascal Varianta C++ uses grafuri. Dacă considerăm graful în care nodurile sunt localităţile. { CitireN("Graf. int s[50]. Noţiunea de arbore parţial Să presupunem că într-un judeţ se impune repararea tuturor şoselelor care unesc diversele localităţi.n. astfel încât graful să rămână conex.

arcul (i.19. 3 4 Procedând aşa cum am arătat.j) are semnificaţia că i are pe agenda telefonică 1 numărul lui j. vn} este o mulţime finită de elemente numite vârfuri sau noduri.2.j) are semnificaţia că după executarea instrucţiunii i este posibil să 5 se efectueze instrucţiunea j. Noţiunea de graf orientat Nu întotdeauna grafurile neorientate pot exprima "relaţiile" existente între anumite elemente. apeluri de subprograme. Pentru a obţine o lucrare valoroasă. unde:  V={v1. nu şi de la j la i). afişări. nu este obligatoriu ca persoana j să aibă în agendă numărul de telefon al persoanei i. Un program este alcătuit din n instrucţiuni. citiri sau instrucţiuni decizionale.. . Considerăm un grup de n persoane. Arcul (i.  A este o mulţime de arce. Să observăm că dacă persoana i are în agenda telefonică numărul de telefon al persoanei j. Pentru a ne da seama de acest fapt. . 2. unde fiecare persoană deţine un telefon mobil. atribuiri. Aici sunt excluse instrucţiunile repetitive. 3. Elementele între care există anumite relaţii se numesc şi de această dată noduri sau vârfuri. A). 2 Pentru exemplul 3. Un autor de carte doreşte să prezinte anumite noţiuni.j) este reprezentat ca o săgeată de la i la j şi are semnificaţia generală că există o relaţie de la i la j (atenţie. Definiţia 9.1. arcul (i.2.13.. Două vârfuri pot fi unite printr-un arc. 1. arcul (i. vom da câteva exemple. v2. Grafuri orientate 9. În agenda telefonului mobil al fiecărei persoane se găsesc numerele de telefon ale altor k≥0 persoane din grup.Manual de informatică pentru clasa a XI-a 267 9. Vom nota un arc prin perechea ordonată (vi. Pentru a prezenta ordinea în care se execută instrucţiunile.vj) cu i≠j. Pentru exemplul 2.j) are semnificaţia că este necesară cunoaşterea 6 noţiunii i pentru a putea înţelege noţiunea j. Se numeşte graf orientat perechea ordonată G=(V. obţinem un Figura 9. Exemplu de graf orientat graf orientat aşa cum este cel alăturat. acesta doreşte ca orice noţiune pe care o introduce să fie precedată în lucrare de noţiunile pe care le presupune deja prezentate. Acestea rezultă în urma instrucţiunilor decizionale şi a ordinii de executare a instrucţiunilor. unui program i se poate asocia o schemă logică.. Pentru exemplul 1.

vj)∈A şi arcul (vj.15. Definiţia 9. În cazul grafurilor orientate.5).1). Din acest motiv.6} şi A={(1. prin gradul interior al unui nod v vom înţelege numărul arcelor incidente spre interior cu v.6). vârfurile distincte vi. Gradul interior al unui nod va fi notat cu d-(v). mai precis acele grafuri 1 1 orientate în care pentru orice arc (vi. Grafurile orientate se mai numesc şi digrafuri. vj)∈A . puteţi observa un graf 2 2 neorientat (stânga) reprezentat ca un graf orientat (dreapta). avem următoarele cazuri: a) Există numai arcul (vi.(6. vj)∈A este incident spre exterior cu vi şi spre interior cu vj. Din definiţie. Grafurile neorientate sunt cazuri particulare de grafuri orientate.vi).4. în definiţie.(2.14.268 Capitolul 9. Figura 9. avem: i d+(i)=3 şi d-(i)=2.(2. vi)∈A . avem V={1.20. respectă relaţia A⊂V×V.16.3)}. Într-un graf orientat. rezultă că nu există arce de la un nod la el însuşi. Pentru vârful i din figura alăturată. Introducere în teoria grafurilor De exemplu. s-a precizat că un arc este o pereche ordonată de forma (vi.în acest caz spunem că arcul (vi.în acest caz spunem că arcul (vj. 3. Astfel. prin gradul exterior al unui vârf v vom înţelege numărul arcelor incidente spre exterior cu v. Definiţia 9. pentru graful din figura 9.5.vj∈G sunt adiacente dacă există cel puţin un arc care le uneşte. .5). 5.3. Observaţii 1. Să observăm că mulţimea arcelor A.. vi)∈A este incident spre interior cu vi şi spre exterior cu vj. În graful orientat G=(V.vj) cu i≠j. c) Există arcul (vi. un arc a fost definit (vi. 2. Definiţia 9.(4.vi)∈A.21. unde V×V este produsul cartezian al mulţimii V cu ea însăşi. Gradul exterior al unui nod va fi notat cu d+(v). dacă există arcul (vi.19.vi).(6.vj). Astfel. b) Există numai arcul (vj. Alăturat. Figura 9.2.1).vj) există arcul (vj. Într-un graf orientat.vj) nu înseamnă că există automat şi arcul (vj. 4.A).

adică dacă sunt n vârfuri. valoare care rezultă şi din formulă.. iar dacă gradul interior al nodului i este l. este necesar ca alte l noţiuni să fie înţelese. Revenim la exemplele prezentate la începutul acestui paragraf. Un graf orientat este complet dacă oricare două vârfuri i şi j (i≠j) sunt adiacente. Relaţia este adevărată.v1) sau putem avea ambele arce (v1. Trebuie să demonstrăm că dacă sunt n+1 vârfuri.v2) sau arcul (v2. n ( n −1) 2 O relaţie utilă: avem 4 grafuri orientate cu n noduri. Pentru exemplul 2. înseamnă că noţiunea i este necesară pentru înţelegerea a altor k noţiuni. Definiţia 9. iar dacă gradul interior al nodului i este l înseamnă că numărul persoanei i este pe agenda telefonică a l persoane. În acest din urmă caz.. + d − (n) = m . înseamnă că după instrucţiunea i se pot efectua alte k instrucţiuni (i este instrucţiune decizională). Acest vârf poate fi adiacent cu fiecare dintre celelalte n vârfuri în exact 3 moduri (vedeţi adiacenţa) sau poate să nu fie adiacent. înseamnă că persoana i are pe agenda telefonică numerele a k persoane din grup. Atunci.v2) şi (v2. avem n ( n −1) 2 4 grafuri orientate. avem n ( n +1) 2 4 grafuri orientate. Avem relaţia: d + (1) + d + (2) + . înseamnă pentru a înţelege noţiunea i. avem 4 grafuri orientate. iar dacă gradul interior al nodului i este l. pentru că fiecare arc este incident spre exterior cu un vârf şi fiecare arc este incident spre interior cu un vârf.  Demonstraţie. Presupunem formula adevărată. cele două noduri pot să nu fie sau să fie adiacente. putem avea arcul (v1. + d + (n) = d − (1) + d − (2) + . dacă gradul exterior al nodului i este k. Dacă n=1. înseamnă că instrucţiunea i se poate executa după executarea uneia din cele l instrucţiuni. avem 1 graf orientat. Adăugăm vârful n+1. Pentru exemplul 1.. .  Demonstraţia se face prin inducţie. dacă gradul exterior al nodului i este k.v1). dacă înlocuim n cu 1.Manual de informatică pentru clasa a XI-a 269 O relaţie utilă: fie un graf orientat cu n vârfuri şi m arce.17. numărul de grafuri orientate cu n+1 noduri este n ( n −1) n ( n −1) n ( n +1) +n 4 2 ×4 = n 4 2 = 4 2 . Dacă n=2. Pentru exemplul 3. În total.. dacă gradul exterior al nodului i este k.

Pentru fiecare structură de date pe care o vom folosi. Memorarea grafurilor orientate Memorarea grafurilor orientate se face la fel precum memorarea grafurilor neorientate. j) ca în exemplul următor. matricea de adiacenţă este prezentată alăturat. Demonstraţia se face prin inducţie! Exerciţiu! 9.22. Introducere în teoria grafurilor n ( n −1) 2 O relaţie utilă: avem 3 grafuri complete. pentru (i.j au semnificaţia: 1. pentru (i. vom avea câte o procedură (funcţie) care citeşte datele respective.22.. Toate subprogramele pe care le utilizăm citesc datele dintr-un fişier text. Întrucât. în care este prezentat un graf şi liniile fişierului text care este citit pentru el: Fişierul 6 1 text: 1 2 6 1 3 1 5 2 2 3 5 3 4 3 4 5 4 Figura 9.2.o matrice pătratică. j) ∈ A a i. j) ∉ A 0 1 1 0 1 0 0 0 1 0 0 0 Pentru graful din figura 9. rezultă că elementele de 0 0 0 0 0 0 .  0 0 0 1 0 0 Observaţii   0 0 0 0 1 0 0 0 0 0 0 0 1. din modul în care a fost definit graful.pas (pentru Pascal) şi în grafuri. j =  0. Toate aceste subprograme se găsesc grupate în unitatea de program grafuri.270 Capitolul 9. ai. în care pe prima linie vom scrie numărul de noduri (n). Memorarea grafului prin matricea de adiacenţă An. Trecem la prezentarea structurilor prin care putem memora datele referitoare la un graf.n . iar pe următoarele linii câte o muchie (i. unde elementele ei. rezultă că nu   există arce de la un nod la el însuşi.2. A.cpp (pentru C++).

i=0. .n). i ∈{1. 3. oricare ar fi i∈{1. f.j]:=1. Readln(f. Reset(f). diferenţa este dată de faptul că arcul (i. { f:text...k:integer. var Start:pornire. var n:integer).j) şi (j.j:byte. d-(i).2. int Start[50]. atunci matricea de adiacenţă este o formă ineficientă de memorare a lui.j. var n:integer).. B.n} are ca rezultat gradul interior al nodului i.j.k=0..var T:Lista. end.j) este înregistrat o singură dată (nu ca în cazul grafurilor neorientate când reţineam (i. int i. puteţi observa subprogramele care construiesc listele de adiacenţă: Varianta Pascal Varianta C++ procedure Citire_LA_AstaticO void Citire_LA_AstaticO (Nume_fis:string. suma gradelor exterioare (sau interioare) adică suma arcelor. (Nume_Fis:string. 5..j. i. Subprogramele pe care le utilizăm sunt: Varianta Pascal Varianta C++ procedure CitireO void CitireO(char Nume_fis[20]. Suma tuturor elementelor matricei de adiacenţă este.2... Tot aşa. Dacă graful citit are un număr mic de muchii. de fapt..i)).j).. d+(i).n} are ca rezultat gradul exterior al nodului i.. 4. Matricea de adiacenţă nu este în mod obligatoriu simetrică.close(). Memorarea grafului prin liste de adiacenţă Se face la fel ca în cazul grafurilor neorientate. (char Nume_fis[20]. Mai jos. suma elementelor de pe coloana i. m. begin f>>n.Nume_Fis). int i. { var f:text. close(f). 2. 6.Manual de informatică pentru clasa a XI-a 271 pe diagonala principală reţin 0 (ai. int A[50][50]. fstream f(Nume_fis.n}).2. i ∈{1. end. while(not eof(f)) do } begin readln(f. pentru că ea va reţine o mulţime de 0. int T[2][50]. A[i. int& n) var A:Mat_ad. int& n) var i.ios::in).. Assign(f. Suma elementelor de pe linia i. while (f>>i>>j) A[i][j]=1.i..

i. T[1.1.close().  Exerciţiu! Câte grafuri parţiale are un graf cu m arce? Definiţia 9.ios::in). C. k:=0. k:=k+1.19. deci va deveni un graf parţial al grafului iniţial. f>>n. end. Un subgraf al unui graf orientat G=(V.n). .k]:=j. iar arcele din A1 sunt toate arcele din A care sunt incidente numai la vârfuri din mulţimea V1. Aceasta înseamnă că noul graf nu va mai avea anumite arce. while(not eof(f)) do T[0][k]=j.A) este un graf G1=(V. unde V1⊂V.j).23. unde A1⊆A. end.A1) Referitor la exemplul 1 din 9. Memorarea grafului prin lista arcelor se face la fel ca în cazul grafurilor neorientate. unele persoane îşi şterg din agendă numerele altor persoane din grup. Introducere în teoria grafurilor begin fstream f(Nume_fis.k]:=Start[i]. Assign(f. begin T[1][k]=Start[i].A) G1=(V. Un graf parţial al unui graf orientat dat G=(V.Nume_Fis).A1). Un graf parţial al unui graf dat.2. readln(f. 9. sau se obţine din G prin suprimarea anumitor arce. } Start[i]:=k. este el însuşi. } T[0. while (f>>i>>j) Readln(f. Graf parţial. Obţinerea unui graf parţial G=(V. f.3.2. subgraf Definiţia 9.A) este un graf G1=(V1. A1⊂A. Reset(f).18. { k++.272 Capitolul 9. close(f).A1). Start[i]=k. 1 1 2 3 3 2 rezultă 4 4 Figura 9.

24.2. De asemenea.  Pentru graful orientat G=(V. Acesta este dat de numărul arcelor ce unesc vârfurile drumului.  Un circuit într-un graf orientat este un drum în care vârful iniţial coincide cu vârful final şi care conţine numai arce distincte.v3)∈A. nu mai folosesc.. Determinarea existenţei unui drum între două vârfuri şi determinarea unui drum între două vârfuri date. Numărul p-1 se numeşte lungimea drumului.1. dacă un graf conţine sau nu un circuit. Obţinerea unui G1=(V1..v2)∈A. avem: 1. se obţine un subgraf al grafului iniţial. eventual a unui drum de lungime minimă.vp)∈A.A)... 1 1 2 3 3 rezultă 4 4 Figura 9.. Astfel. numerele de telefon ale respectivelor persoane aflate în agenda altora. un drum D=[v1. .4.v2.Manual de informatică pentru clasa a XI-a 273 Un subgraf al unui graf G este graful G sau se obţine din G prin suprimarea anumitor vârfuri şi a tuturor arcelor incidente cu acestea.. Pot exista persoane din grup care îşi pierd telefonul mobil.. Astfel. (v2. Circuite  Parcurgerea grafurilor orientate se face la fel precum parcurgerea grafurilor neorientate. Vârfurile v1 şi vp se numesc extremităţile drumului.. ceilalţi din grup nu mai păstrează numerele de telefon ale acestora. Aceasta înseamnă că parcurgerea se poate face în două feluri. ca şi în cazul grafurilor neorientate... Din nou. (vp-1. Parcurgerea grafurilor.A1) subgraf G=(V.  Exerciţiu! Câte subgrafuri are un graf cu n vârfuri? 9.vp] este o succesiune de vârfuri (v1. În graful iniţial se renunţă la vârfurile respective şi la arcele adiacente lor.v2. 2.A) Referitor la exemplele din paragraful 9. . pentru moment. Subprogramele sunt aceleaşi.vp] este elementar dacă conţine numai vârfuri distincte. Printr-o simplă parcurgere DF putem determina. se obţine un subgraf al grafului iniţial. se face la fel ca în cazul grafurilor neorientate.2.. Drumuri. Autorul renunţă la prezentarea anumitor noţiuni. în adâncime (DF) şi în lăţime (BF).  Un drum D=[v1.

 Exerciţiu! Scrieţi programul care. Pentru exemplul 3: dacă după execuţia 5 instrucţiunii i.  Un lanţ L=[v1. fie arcul (vi+1. înseamnă că există un circuit de la i la j. se ajunge să se execute din nou instrucţiunea i. parcurgem graful şi aflăm toate nodurile pentru care există drum de la i la j.vp] este o succesiune de vârfuri astfel încât între oricare vârfuri distincte din vi. în caz contrar Matricea drumurilor nu este. similar. Un exemplu de limbaj care nu poate fi predat în mod clasic este Java. la rândul lor. atunci programul conţine structuri repetitive. vom avea L(i. Pentru a o determina.v2. j) =  0.A). Se mai întâmplă şi aşa. Pentru toate nodurile atinse (mai puţin i). Dacă există posibilitatea ca ea să sune o persoană al cărei număr îl are în agendă. se completează linia i. Introducere în teoria grafurilor Anumite noţiuni prezentate în cazul grafurilor neorientate se regăsesc şi în cazul grafurilor orientate. simetrică..1. pentru cel puţin un set de date de intrare.  Pentru graful orientat G=(V.v2.25. Pentru exemplul 2: dacă graful are un circuit înseamnă că există cel puţin o noţiune care nu poate fi explicată decât prin intermediul altora care.. Aşa cum am definit pentru grafuri neorientate matricea lanţurilor.m. cel mai simplu 2 program utilizează din plin programarea orientată pe obiecte. dacă după ce se execută instrucţiunea i. grafuri orientate matricea drumurilor: 1.d până este sunată persoana j. Pentru exemplul 1: persoana i are un mesaj de transmis persoanei j. De asemenea.vp] este elementar dacă conţine numai vârfuri distincte. De 6 asemenea. Reluăm exemplele din paragraful 9. .a. aceasta o altă persoană ş.2.. dacă există drum de la i la j D(i. afişează matricea drumurilor. pentru fiecare nod i. dacă există posibilitatea ca persoana i să ajungă să primească mesajul pe care l-a transmis. iar 7 graful asociat conţine circuite. se poate forma pentru Figura 9. pornind de la un graf.274 Capitolul 9. Astfel. ar trebui 1 explicate exact prin noţiunea care nu poate fi explicată fără ele. se ajunge să se execute instrucţiunea j.. care se studiază după ce am învăţat să 3 4 scriem programe simple...vi+1).j)=1. De exemplu.vi+1 există fie arcul (vi.. un lanţ D=[v1.. înseamnă că există un lanţ de la i la j. în cazul general.vi). înseamnă că există un drum de la i la j.

∃ drum de la x la y şi drum de la y la x. Componente tare conexe Reluăm exemplul 1 din paragraful 9. 2. y ∈V1. memorat prin intermediul matricei de adiacenţă.2. subgraful care conţine vârfurile: 5 7 7 2 3 . De exemplu. Succesorii nodului i vor fi reţinuţi în vectorul suc. pentru graful dat. toate nodurile j. Pentru a determina toţi succesorii vârfului i. Definiţia 9. Se cere să se determine vârfurile fiecărei componente tare conexă.A) este o componentă tare conexă dacă: 1. Subgraful G1=(V1. ∃ drum de la x la y şi drum de la y la x.A). subgraful care conţine vârful 6 Figura 9. Nu există un alt subgraf al lui G. există un drum de la i la j şi există şi un drum de j la i. astfel încât toţi membrii grupului să afle de acest loc? Judecând după graful orientat asociat. succesorii vârfului 1 sunt vârfurile 1.A1) al grafului G=(V. Graful orientat G=(V. ∀ x.d. pentru care există drum de la i la j. Întrebarea este: există posibilitatea ca oricare membru al grupului.. aceştia să-i sune pe alţii ş.A2) cu V1⊂V2 care îndeplineşte condiţia 1. să propună un loc de întânire şi să-şi anunţe telefonic prietenii din agendă.  Problema 9.21. Definiţia 9. . subgraful care conţine vârfurile: 1 2 3 . ar trebui ca de la oricare membru al grupului să existe un drum către oricare alt membru al grupului.1.2. la care se adaugă i. 2.y∈V. Graful alăturat are patru componente 5 tare conexe: 1 4 .Manual de informatică pentru clasa a XI-a 275 9.  Rezolvare a) Vom numi succesori ai vârfului i. Aceasta înseamnă că oricare ar fi nodurile i şi j. din păcate.A) este tare conex dacă ∀x.a. Un graf cu această proprietate se numeşte graf tare conex. subgraful care conţine vârful 4 6 .20. s-au rătăcit.8.5. Să presupunem că grupul celor n persoane efectuează o excursie la munte şi. vom efectua o parcurgere DF a grafului pornind de la acest vârf.26. G2=(V2. 3 şi 4..m. Fie un graf orientat G=(V. Graf tare conex.

atunci ar trebui să existe drum de la i la el. se afişează vârfurile fiecărei componente conexe. predecesorii vârfului 1 sunt: 1. Mai jos. la care se adaugă i. se incrementează nrc. Vom numi predecesori ai vârfului i. toţi predecesorii lui i. Variabila nrc. şi de la el la i. 2 şi 3. ar mai exista un vârf cu această proprietate. iniţial. pentru fiecare vârf i dacă suc[i]=0 toţi succesorii lui i. Introducere în teoria grafurilor b) Fie i un vârf al grafului. Pentru graful dat. inclusiv i. există atât drum de la k la l (de la k la i şi de la i la l) cât şi drum de la l la k (de la l la i şi de la i la k). Mulţimea nodurilor cu această proprietate va fi o componentă tare conexă a grafului. d) De acum. inclusiv i. va reţine numărul curent al componentei tare conexe care urmează să fie identificată. pentru care suc[i]≠pred[i] vor reţine 0. puteţi observa evoluţia vectorilor suc şi pred: 1 2 3 4 5 6 7 1 2 3 4 5 6 7 suc 1 1 1 1 0 0 0 suc 1 1 1 0 0 0 0 pred 1 1 1 0 0 0 0 pred 1 1 1 0 0 0 0 1 2 3 4 5 6 7 1 2 3 4 5 6 7 suc 1 1 1 2 0 0 0 suc 1 1 1 2 0 0 0 pred 1 1 1 2 2 2 0 pred 1 1 1 2 0 0 0 1 2 3 4 5 6 7 1 2 3 4 5 6 7 suc 1 1 1 2 3 0 3 suc 1 1 1 2 3 4 3 pred 1 1 1 2 3 0 3 pred 1 1 1 2 3 4 3 . cu valoarea iniţială 1. De ce? Pentru că între două vârfuri k şi l. Fiecare componentă a vectorilor suc şi pred reţine. valoarea 0.276 Capitolul 9. atunci el va face parte din componenta tare conexă a vârfului i. caz în care acesta ar fi fost găsit prin procedeul dat. vor reţine nrc. Mulţimea nodurilor cu această proprietate este maximală în raport cu relaţia de incluziune. Dacă. putem redacta algoritmul. prin absurd. pentru care există drum de la j la i. toate vârfurile j. toate componentele i. care nu aparţine acestei mulţimi. c) Dacă un vârf este simultan succesor şi predecesor al lui i. vor reţine nrc.

k<=n. if suc[j]<>pred[j] then nrc++.i.i<nrc.' '). #include "grafuri. for (j=1.i++) pred[j]:=0.k<=n. nrc:=1. A:mat_ad. for (k=1. { int k.j<=n. procedure df_r1(nod:byte). df_r1(i).Manual de informatică pentru clasa a XI-a 277 Programul este prezentat în continuare: Varianta Pascal Varianta C++ uses grafuri.txt".j++) end.n). begin } suc[j]:=0. if (A[k][nod]==1 && for k:=1 to n do pred[k]==0) if (A[k.j++) df_r2(i).50] of int suc[50].k++) begin if (A[nod][k]==1 && suc[nod]:=nrc. begin cout<<endl. cout<<"Componenta"<<i<<endl.i++) if suc[i]=0 if (suc[i]==0) then { suc[i]=nrc. suc[i]:=nrc.A. writeln ('Componenta '. { end. if (suc[j]==i) for i:=1 to nrc-1 do cout<<j<<" ".j.cpp" var suc.nod]=1) and (pred[k]=0) df_r2(k). nrc=1.pred:array[1. void df_r2(int nod) procedure df_r2(nod:byte). A[50][50]. . byte.j:integer. suc[nod]=nrc.. writeln. suc[k]==0) for k:=1 to n do df_r1(k). end. pred[50]. n.n). } for j:=1 to n do } if suc[j]=i then write (j. var k:byte.j<=n. begin for (k=1. n.i<=n. end. begin df_r1(i). CitireO("Graf.nrc. end.i).k]=1) and (suc[k]=0) } then df_r1(k). df_r2(i). for (i=1. then df_r2(k). if (A[nod. if(suc[j]!=pred[j]) for j:=1 to n do suc[j]=pred[j]=0.i. nrc:=nrc+1 for (j=1.nrc. for i:=1 to n do for (i=1.txt'. void df_r1(int nod) { int k. var k:byte.k++) pred[nod]:=nrc. pred[nod]=nrc.A. main() begin { CitireO('Graf. } end.

4. Există persoane care nu au decât c.dreapta i este paralelă cu dreapta j.A) este conex dacă ∀x. ∃ lanţ de la x la y. Subgraful G1=(V1. Există k persoane din grup astfel izolate. Definiţia 9. ∃ lanţ de la x la y. În grupul celor n persoane. G2=(V2. Introducere în teoria grafurilor Şi în cazul grafurilor orientate se păstrează noţiunea de graf conex şi noţiunea de componentă conexă. există o dreaptă j care este paralelă cu ea. ∀ x. 2.A2) cu V1⊂V2 care îndepli- neşte condiţia 1. d.23. Între n firme există relaţii de colaborare. A zis adevărul? 2. complet cu k noduri. O relaţie de colaborare este dată ca o pereche de forma i⇔j şi are semnificaţia că firma i colaborează cu firma j. Stabiliţi corespondenţa între afirmaţiile din stânga şi cele din dreapta. fiecare a. un singur prieten. Definiţia 9.A) este o componentă conexă dacă: 1. Fiind date n persoane şi m relaţii de prietenie între ele de forma: persoana i este prietenă cu persoana j. 1.A1) al grafului G=(V. Graful asociat nu are noduri 4. Nu există un alt subgraf al lui G.22. Se dau n drepte şi m relaţii de forma: di||dj . y ∈V1. Se ştie că graful asociat este conex. c) Mulţimea punctelor de intersecţie ale acestor drepte este nevidă. Care dintre afirmaţiile de mai jos este falsă? a) Pentru orice dreaptă i. Demonstraţi că într-un graf neorientat numărul nodurilor de grad impar este par.y∈V. Graful orientat G=(V.278 Capitolul 9. Probleme propuse 1. . O cunoştinţă mi-a zis: la mine în birou suntem 5 persoane. b) Toate dreptele sunt paralele între ele. Graful asociat este complet. Graful asociat conţine un subgraf oricare alta din grup. terminale. 5. se cere să se stabilească corespondenţele între afirmaţiile din stânga şi cele din dreapta. 3. Fiecare persoană este prietenă cu b. Graful asociat are noduri persoană are cel puţin un prieten. Fiecare dintre noi colaborează cu exact 3 persoane. încât oricare dintre ele este prietenă cu toate celelalte k-1. 3. 2.

1. Unele dintre ele sunt unite printr-o ţeavă prin care poate circula apa. Exerciţiile 8 şi 9 se referă la graful din 3 5 figura 9. Astfel. Anumite firme dau faliment şi nu într-un graf parţial al său. 4 6 8.2.Manual de informatică pentru clasa a XI-a 279 1. Ştiind că fiecare bazin poate fi dotat cu un robinet. b) Unele persoane din grup simpatizează alte persoane din grup. Fiind dat un grup de n persoane. Există un lanţ de la i la j. Anumite firme îşi întrerup relaţia grad n-1. 6. 7. O firmă colaborează cu toate e. c. d. de colaborare. bazinul 1 este unit cu bazinul 2. Cu ajutorul unor firme interme- diare dintre cel n firme. b. Care este matricea de adiacenţă a 7 grafului? Figura 9. se cere numărul minim de robinete care asigură umplerea tuturor bazinelor. mai funcţionează. c) În cazul în care toate persoanele lucrează într-o firmă. Daţi exemple de bazine unite care asigură cerinţa problemei. Care este numărul minim de ţevi prin care se pot uni două bazine. Orice firmă colaborează cu toate a.27. Graful asociat se transformă celelalte. unele persoane din grup sunt şefi pentru alte persoane din grup. 6. firma i poate face afaceri cu firma j. în care dintre situaţiile de mai jos se poate folosi pentru modelare un graf neorientat? a) Unele persoane din grup cunosc alte persoane din grup. într-un subgraf al său.27. celelalte. Graful asociat se transformă 3. bazinul 4 cu bazinul 5 şi bazinul 2 cu bazinul 3. astfel încât să se poată umple toate bazinele cu un singur robinet. 4. Graful asociat este complet. 1 1 1 1 0 1 0 0 1 1 1 0 1 0 0 1 1 1 0 1 0 0 1 1 1 0 1 0         1 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 1 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 1 0 0 0         1 0 1 1 0 0 1 1 0 1 0 0 0 1 1 0 1 0 0 0 1 1 0 1 0 0 0 1 0  0 0 0 1 0 0  0  0 0 0 0 0 0  0  0 0 0 0 1 0  1  1 1 1 1 1 1  1 0 0 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1         0 0 0 1 0 1 1 0 0 0 1 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 1 0 a) b) c) d) . 5. Graful asociat are un nod de 2. 6. d) Unele persoane din grup sunt prietene cu 1 2 alte persoane din grup. La un ştrand există 6 bazine.

10. 1 2 3 4 2 1 9. Care dintre matricele de mai jos ar putea fi matricea de adiacenţă a grafului alăturat? 0 1 0 1 0 0 1 0 1 0 0 1 0 1 0 0 1 0 0 0         1 0 1 0 0 1 0 1 0 0 1 0 1 0 0 1 0 1 0 1 0 1 0 1 1 0 1 0 1 1 0 1 0 1 0 0 1 0 1 1         1 0 1 0 1 1 0 1 0 0 1 0 1 1 1 0 0 1 0 1 0 0 1 1 0  0 0 1 0 0  0 0 0 1 0  0 1 1 1 0      Figura 9. reprezentat cu 4 1 3 ajutorul listelor de adiacenţă.28.5. 9.7. Nodul 2 este izolat.4] este un lanţ. 9. [1. Introducere în teoria grafurilor 9.4.4.7. subgraf al grafului din problemă. este Figura 9.30.28. adevărat. Care dintre matricele de mai jos poate fi matricea de adiacenţă a unui graf neorientat? 0 1 0 1 0 0 1 0 0 0 1 1 1 1 1 0 1 0 1 0         1 0 1 0 0 1 0 1 0 0 1 1 1 1 1 1 0 1 0 0 0 1 0 1 1 0 1 0 1 1 1 1 1 1 1 0 1 0 1 1         1 0 1 1 1 1 0 1 0 1 1 1 1 1 1 1 0 1 0 1 0  0 1 1 0  0  0 1 1 0  1  1 1 1 1 0  0 1 1 0  a) b) c) d) . 3 9.8. Graful este alcătuit din 2 componente conexe. Care este valoarea de adevăr a afirmaţiilor de mai jos (A.280 Capitolul 9. Graful din problemă are ca subgraf graful din 7 figura 9. Nodul 4 are gradul 3. Figura 9. Graful din problemă conţine un ciclu de lungime 3.6.1. 1 9.9. Graful alăturat.1] este un ciclu.1. Graful din problemă are ca graf parţial graful 4 din figura 9.3.3. 2 9. 9. fals): 9. iar F. [2.28.2. Nodul 1 are gradul 2.29. a) b) c) d) 11. 3 1 4 9. 10. 6 9.

6. Precizaţi care dintre afirmaţiile urmă- toare sunt adevărate şi care sunt false.Manual de informatică pentru clasa a XI-a 281 12.31? 0 0 0 0 0 1 0 0 0 1 1 0 0 1 1 1         0 0 0 0 1 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1         0  0 0 0  0  0 0 1  0  0 0 0  1  1 1 0  Figura 9. 1 3 Toate afirmaţiile se referă la graful din 2 figura 9. Câte muchii are graful neorientat reprezentat de matricea de 0 1 0 1 1   adiacenţă alăturată? 1 0 0 1 1 0 0 0 1 1 13. Un arbore parţial al grafului dat are 7 muchii. 19.32. Care este numărul de grafuri neorientate cu 5 noduri? 19. 19. "3 2 5 1 4 6 7" reprezintă o 7 parcurgere în lăţime a grafului. "6 1 2 3 5 4 7" reprezintă o 5 parcurgere în adâncime a grafului. Care este numărul minim şi numărul maxim de noduri izolate pe care îl poate avea un graf neorientat cu 10 noduri şi 10 muchii? 18. a) b) c) d) 15. Numărul minim de muchii care trebuie eliminate pentru a obţine un arbore parţial este 2. Care este numărul de cifre 0 pe care îl reţine matricea de adiacenţă a unui graf neorientat cu n noduri şi m muchii? 17.1.32. 4 6 19. "6 1 2 3 5 4 7" este un lanţ în graful dat. Numărul maxim de muchii care pot fi eliminate astfel încât graful să rămână conex este 3.8. Care dintre matricele de mai jos este matricea de adiacenţă a unui subgraf al grafului din figura 9. 19. 19.31.3.5. Figura 9. Care este numărul maxim de componente conexe pe care le   1 1 1 0 1 poate avea un graf neorientat cu 5 noduri şi 4 muchii? 1  1 1 1 0  14. Care este numărul minim şi care este numărul maxim de componente conexe pe care le poate avea un graf neorientat cu 8 noduri şi 6 muchii? 16. "3 2 5 1 4 6 7" este un lanţ în graful dat. Există două noduri din graf pentru care nu există un lanţ care le uneşte.7. 19. 19.9. .4.2. 19. 19. Numărul minim de muchii care pot fi eliminate pentru ca graful să nu conţină cicluri este 3.

6. Se dă un graf neorientat şi o succesiune de noduri ale lui.12. Introducere în teoria grafurilor 20. Cu ajutorul parcurgerii în lăţime se poate determina. un lanţ între două noduri ale grafului. 20. Cu ajutorul parcurgerii în adâncime se poate determina dacă un graf neorientat are cel puţin un ciclu. Un arbore conţine exact o componentă conexă. Există graf complet cu n>2 noduri care nu conţine cicluri. Fiecare dintre ele alcătuieşte un graf parţial al grafului dat. Se dă un graf neorientat memorat sub forma matricei de adiacenţă. prin eliminarea anumitor muchii se poate obţine un arbore. Orice graf neorientat cu 10 noduri şi 9 muchii este un arbore. 20. Cu ajutorul parcurgerii în lăţime se poate determina dacă un graf este conex. 20.9. Să se afişeze toate nodurile care au gradul maxim. 20. Algoritmul va utiliza coada creată ca listă liniară simplu înlănţuită implementată static.7. Să se scrie un subprogram care transformă matricea de adiacenţă a unui graf în liste de adiacenţe. 24. Precizaţi dacă afirmaţiile de mai jos sunt adevărate sau false. Orice graf neorientat are un graf parţial care este arbore. Orice graf complet este alcătuit dintr-o singură componentă conexă. 20. 21. 20.282 Capitolul 9. Pentru ca graful să devină conex. dacă există. Se dă un graf neorientat memorat prin liste de adiacenţă.5. un lanţ între două noduri ale grafului. Se dă un graf memorat prin matricea de adiacenţă şi un nod al său. 20.4. Se cere să se scrie un subprogram care decide dacă succesiunea dată este sau nu un lanţ. Să se scrie un subprogram care decide dacă graful dat conţine sau nu cicluri.11.1. 20. 23. Fiecare dintre ele alcătuieşte un subgraf al grafului dat. Un graf este alcătuit din două componente conexe. Un graf este alcătuit din două componente conexe. 25. Un graf este alcătuit din două componente conexe. Se cere să se parcurgă graful în lăţime. Din orice graf complet. . Să se scrie scrie un subprogram care transformă listele de adiacenţă în matrice de adiacenţă. Orice graf complet are un subgraf care este arbore. este suficient să eliminăm o anumită muchie.14. 26. 20. 20. 20.8. dacă există.13.2. pornind de la nodul v. 20. 20. Cu ajutorul parcurgerii în adâncime se poate determina. v. 22.3.10. 20.

Se dă matricea de adiacenţă a unui graf neorientat. bani alte persoane din grup. Se obţine partiţia: {1. Se cere să se afişeze toate ciclurile de lungime 4.yi≤200. 1. • oricare ar fi x. Se cere să se afişeze matricea drumurilor. Printr-o astfel de pereche se înţelege că x este echivalent cu y.. Partiţia determinată de o relaţie de echivalenţă Se consideră o mulţime A.y. 2.Manual de informatică pentru clasa a XI-a 283 27. 28. din x R y.. Într-un grup de n persoane.yi) cu 0≤xi. Considerăm că fiecare punct este unit cu cel mai apropiat punct diferit de el (dacă există mai multe puncte la distanţă minimă. . Algoritmul va utiliza parcurgerea în adâncime. x R x (x este echivalent cu x). Persoana i nu a împrumutat cu a) gradul interior al nodului i este 0. Fiind dată matricea drumurilor unui graf. Se cere să se determine partiţia generată de relaţia de echivalenţă considerată pe mulţime. 30. (2 3). 31.y) de numere de acest tip. n. Se dă matricea de adiacenţă a unui graf neorientat. pentru orice i=1. . (4 5). din x R y şi y R z. Se dă un graf memorat sub forma matricei de adiacenţă. Observaţie: dacă persoana i împrumută cu bani persoana j.. rezultă x R z. Să se determine numărul de regiuni şi să se vizualizeze regiunile (punctele şi legăturile dintre ele). Un ciclu se va afişa o singură dată.. 32..z∈ mulţimii A. proprietate numită tranzitivitate. 2. împrumută altor persoane diverse sume de bani! Modelând problema cu ajutorul grafurilor orientate. Se cere să se afişeze toate ciclurile de lungime k. se cere să se scrie programul care afişează componentele conexe. anumite persoane.. este o relaţie de echivalenţă dacă respectă următoarele trei condiţii: • oricare ar fi x∈A. atunci există un arc de la i la j.7}. rezultă y R x.y∈A. Numim regiune o mulţime maximală de puncte cu proprietatea că oricare dintre ele sunt unite printr-un lanţ. O relaţie oarecare R între elementele acestei mulţimi. Se cere un algoritm eficient. (6 7).2.6.3. se cere să stabiliţi corespondenţa dintre afirmaţiile din stânga şi cele din dreapta. 33. proprietate numită reflexivitate. Persoana i nu a împrumutat bani de la alte persoane din grup. (7 1). Exemplu: citim (1 2). se uneşte cu fiecare dintre acestea). 29. Se citeşte o mulţime de numere între 0 şi 255 prin citirea a n perechi (x.2. • oricare ar fi x.5} a mulţimii {1. b) gradul exterior al nodului i este 0.7} {4. Se dau n puncte distincte în plan: Pi(xi. proprietate numită simetrie..

d) 4. Introducere în teoria grafurilor 34. Se dau n mulţimi de numere naturale: A1. b) 3.. c) 2. 39. b) 1 2.35. c) Graful este tare conex.. 2 Figura 9. 2. A1⊂A2⊂. a) De la A1 la An există un lanţ de lungime n-1. An-1=An.An. Ai⊂Aj. d) 2 5. Ai⊂Aj⊂Ak.. b) De la Ak la Ai există un lanţ de 3.33. A2. d) 0 0. Câte componente tare conexe conţine? 4 3 a) 4. . Figura 9.. d) 1 2.34. 4 3 2 Figura 9. 35. 40.33? 3 a) 1 1. Refaceţi problema anterioară în cazul în care se consideră n numere naturale şi relaţia de divizibilitate. c) 2 3. d) 1. b) 1 3. lungime 2. Care este nodul cu grad interior maxim şi care 2 este nodul cu grad exterior minim? a) 1 1. c) 2 2. Acestor mulţimi li se asociază un graf orientat astfel: dacă mulţimea Ai este inclusă în mulţimea Aj. 36. încercaţi 1 să adăugaţi noi situaţii în care se cere corespondenţa. în graful asociat vom avea arcul (Ai. care este lungimea celui mai lung lanţ elementar şi care este lungimea celui mai lung 1 drum elementar? a) 3 2. A1=A2=. An-1⊂An.. d) De la Ai la Ak există un drum de lungime 2.Aj). 37. Câte componente conexe şi câte componente tare conexe conţine graful din figura 9. 1. Ak⊂Aj. b) 2. b) 2 2. Problemele de la 38 la 41 se referă la graful alăturat: 5 38. Stabiliţi corespondenţa dintre operaţiile din stânga şi cele din dreapta. 4. Câte circuite conţine? 1 a) 3. c) 1. De asemenea..284 Capitolul 9.34. c) 1 0. Nu vom considera cazul de incluziune a unei mulţimi în ea însăşi. În graful din figura 9.

Se cere drumul care trece printr-un număr minim de camere între o cameră iniţială şi una finală. apoi perechile de coordonate ale obstacolelor. 47. 2. 2.Y2) de pe tablă şi. n sortate. Se cere să se listeze toate ciclurile de lungime 3. Se dă matricea de adiacenţă a unui graf orientat. Sortare în limita posibilităţilor. Se citesc: N. să se tipărească numărul minim de mutări necesare. Problema se va rezolva în cazul în care graful este dat prin matricea de adiacenţă şi în cazul în care el este dat prin liste de adiacenţă. * 51 . X1. se . Se dă matricea de adiacenţă a unui graf orientat. La fel ca mai sus. 46. d) 4. gradul interior şi gradul exterior. nebunul nu poate trece peste aceste obstacole. …. Se consideră că într-un vector V cu n componente se pot inversa numai conţinuturile anumitor componente dintre cele n.Manual de informatică pentru clasa a XI-a 285 41. O pereche de componente de indice i şi j ale căror conţinuturi se pot inversa este dată de perechea i şi j. Se dă un graf orientat. 45. Să se indice dacă există vreun drum între două puncte A(X1. Fiind date m astfel de perechi şi ştiind că vectorul conţine numerele 1. L(i. Programul va afişa matricea de adiacenţă. La fel ca la problema anterioară. pe tablă se pot afla obstacole la diferite coordonate.j) =-1 dacă prin camera respectivă nu se poate trece şi 0 în caz contrar. pentru fiecare vârf în parte. 42. Programul va afişa listele de adiacenţe ale acestuia.Y1) şi B(X2. Y1. Y2. …. Se cere să se afişeze. n într-o ordine oarecare. Problema se va rezolva în cazul în care graful este dat prin matricea de adiacenţă şi în cazul în care el este dat prin liste de adiacenţă. Se dă un graf prin lista muchiilor. În plus. Programul va decide dacă graful este neorientat. Care este numărul minim de arce care trebuie adăugate pentru ca graful să devină tare conex? a) 1. 43. 48. dar se cere să se determine dacă succesiunea respectivă este sau nu lanţ (lanţ elementar). X2. * 50 . c) 3. * 53 . iar în caz afirmativ se va preciza dacă este sau nu un drum elementar. Se cere să se listeze toate circuitele de lungime 3. 52 . Fiind date un graf orientat şi o succesiune de vârfuri să se decidă dacă succesiunea este drum. Să se afişeze distanţele minime de la camera de coordonate (l. se cere ca vectorul să conţină numerele 1. b) 2. Pe o tablă de şah de dimensiuni nxn se poate deplasa un nebun conform * regulilor obişnuite ale şahului. 44. Se dă matricea de adiacenţă a unui graf orientat. L. în caz afirmativ. Pentru sortare se inversează numai conţinuturile componentelor care se pot inversa (care sunt perechi dintre cele m). 49. Algoritmul lui Lee. Se dă un labirint sub forma unei matrice pătratice.c) la toate camerele accesibile din camera iniţială. Dacă sortarea este posibilă. Se dau listele de adiacenţe ale unui graf orientat.

4-b. iar dacă sortarea nu este posibilă. c) Faptul că graful este conex. rezultă că suma tuturor gradelor este un număr impar. Linia 3 m următoarele m linii conţin fiecare câte o pereche de indici i. 5-a. Absurd. 3-c. 4-b. Dacă ar fi impar. 1-d.286 Capitolul 9. pentru că ea este egală cu dublul numărului de muchii. De asemenea. 2-c.. se ştie traseul a k linii de autobuz (staţiile prin care acestea trec). Se cere ca aplicaţia să furnizeze modul în care o persoană se poate deplasa cu autobuzul între două staţii date. j. Datele de intrare se găsesc în fişierul text date. 4. . 6. Răspunsuri 1. în ipotezele: a) În număr minim de staţii. Introducere în teoria grafurilor vor afişa indicii componentelor care se inversează. Dar din di||dj şi dj||dk ⇒ di||dk rezultă că toate dreptele sunt paralele între ele. Ori.1.. 2-e. Ar rezulta un graf cu 5 noduri. Graful conţine 3 componente conexe. b) Prin utilizarea unui număr minim de linii de autobuz.in astfel: Linia 1 n Linia 2 1. 1-d. Lucrare în echipă. se afişează Nu. deci m nu ar fi număr întreg. 2. Nu. Cum fiecare persoană colaborează cu exact 3 persoane. acesta trebuie să fie un număr par. 5.2. Se doreşte scrierea unei aplicaţii de informare a călătorilor privind transportul în comun într-un oraş. De aici. Exemplu: 3 Programul va afişa: 3 1 2 1 2 2 2 3 2 3 1 2 * 54 . Aceasta înseamnă că 2m=15. ! Este sarcina dvs. este necesar ca graful să fie conex. înseamnă că între oricare două noduri există un lanţ care le are ca extremităţi. sunt suficiente 2 ţevi. Pentru a folosi un singur robinet. Se cunosc cele n staţii de autobuz din oraşul respectiv. rezultă că suma gradelor este 15. Cum are 3 componente conexe. 3. 6. 3-a. 6. Cum suma gradelor pare este un număr par.. . Pentru fiecare componentă conexă este necesar un robinet. să organizaţi intrările şi ieşirile de date. înseamnă că fiecare nod are gradul 3. suma gradelor impare ar fi un număr impar. n într-o ordine oarecare.

5.7. 9. A. răspunsul este d) pentru că relaţia de prietenie este reciprocă. 20. A. Dacă eliminăm nodul din centru. se obţin 4 noduri izolate. 0 şi 5. F.8.5. F. F. 13. 20. 17. 20. 20. 9. j nu poate fi şeful lui i. 19.6. rămân 1 nod cu gradul 3 şi 3 noduri cu gradul 1. F. 20.l)=1 A(k. nu este nevoie să desenaţi graful pentru ca. 19. 8. a) Dacă matricea are 4 linii şi 4 coloane. 9.4. 15. 30.11. Prin urmare. A.j)=1 A(i.13. 19. 29. puteţi desena graful. F.3. 19. 19.9.6. 2. 4 noduri care formează un ciclu. A (lăsăm un singur nod). Oricare alt nod am elimina. 19. nu este obligatoriu ca j să cunoască pe i. Matricea de adiacenţă are n2 elemente.7. A.8. A. este suficient să însumaţi elementele reţinute de matrice si să împărţiţi rezultatul la 2. 20. sau acelea în care matricea nu este simetrică. 14. Descompunerea unui graf în componente conexe. F. graful reprezentat de matricea de adiacenţă trebuie să aibă vârfurile cu aceleaşi grade cu vârfurile grafului reprezentat în desen. b) Dacă matricea este de adiacenţă.5.14. Backtracking. b). 2 componente conexe şi 5 componente conexe. A.10. 9. A. 20. Evident. 9.9. 10. 9.j) presupune că i este în relaţie cu j şi j este în relaţie cu i. 9.10. F. Se ştie că suma gradelor tuturor nodurilor este egală cu dublul numărului de muchii. 20.7. 8. 9.1. A. atunci vă puteţi orienta după gradele vârfurilor. 19. dar. 16. Avem: A(i. A. 19. A.9. 18. F. A. nu este obligatoriu ca j să simpatizeze pe i. A. Dacă matricea este dată corect. 20.. F. 31. A. dacă i este şeful lui j. F. F. 11. 32. A.j)=1 A (k. Pentru a putea modela anumite relaţii cu ajutorul unui graf neorientat trebuie ca relaţia existentă între i şi j să fie reciprocă. A. F. dacă i simpatizează pe j.2. 20. În concluzie. eliminaţi variantele în care aveţi 1 pe doagonala principală. 19. 20.2. 20. 9. 20. pentru că muchia (i. 210. Fie i<j<k<l.3. F. 9.2. să-i număraţi muchiile. 20. este clar că subgraful ar rezulta prin eliminarea unui singur nod şi a muchiilor incidente lui.l)=1 . A.1.1.6. Am văzut faptul că suma tuturor cifrelor de 1 (adică a gradelor vârfurilor) este 2m (unde m este numărul de muchii). A. 20. d) Desigur. mai uşor.8.4. Dacă i cunoaşte pe j. apoi. 9. F. n2-2m. 12. 19.Manual de informatică pentru clasa a XI-a 287 7. O soluţie are lungimea k.3.12. A.4.

Printre camerele vecine cu ea se caută una care este marcată cu k-2. 1-b.j)=2. nodurile corespunzătoare sunt unite printr-o muchie. Vom introduce în coadă coordonatele camerei iniţiale. 53. Drumul se afişează în ordinea inversă găsirii lui. 39. Se procedează ca la problema anterioară. . de la ele la nodul iniţial. 41. Apoi. 37. 2-a. Vom încărca în coadă coordonatele tuturor camerelor vecine pentru care L(i. astfel. O parcurgere în lăţime determină distanţa minimă între două noduri. Deducem.j)=3. i2. iniţial. (i2. 33. pentru toate camerele accesibile vecine cu ea vom avea L(i.. ş..... 52. vom avea L(i.j)=2. Nodurile sunt indicii elementelor vectorului..j)=0. ik sunt unite printr-un drum: atunci interschimbările (i1. (ik-1.. a). că marcarea este corectă. Dacă nodurile i1. . c). 2-b.. ik-1 nemodificate. Algoritmul lui Lee. .288 Capitolul 9. i1) inversează conţinuturile elementelor de indice i1 şi ik. se pleacă de la camera finală.. 36. 38. b). 34. ik-2). marcată cu k. Algoritmul se termină când coada este vidă. Se trece apoi la următorul element din coadă cu care se procedează asemănător. 3-a. Printre vecinele acestei camere se caută una care este marcată cu k-1. Se poate lucra direct pe matricea L.j)=1. L(i. Când conţinuturile a două elemente se pot inversa. se afişează matricea L. în matricea de adiacenţă se formează un dreptunghi.m. de la 1 la n. a). Astfel. 1-d. apoi pentru toate camerele accesibile cu ele vom avea L(i.. i3). 50.. ik). Se procedează în mod asemănător până se ajunge la camera iniţială marcată cu 1. se vizitează nodurile în ordinea lungimii drumului. Putem evita memorarea acestuia. . (ik-1. Idee: pentru camera iniţială vom avea L(i. i2).d.j)=1. În final. b).. 40. pentru care. c). lăsând conţinuturile celorlalte elemente de indici i2. Trebuie identificate toate dreptunghiurile astfel formate. Pentru fiecare astfel de cameră.. Se ştie că. Imediat ce a fost vizitată camera finală. de la camera finală la cea iniţială. . Introducere în teoria grafurilor Astfel. 51.. Asociem problemei un graf neorientat. se reface drumul de la camera iniţială către ea. 4-c. (i2. prin parcurgerea în lăţime. Pentru a obţine această marcare vom parcurge în lăţime graful asociat.a.

5×10-45] ∪ single 32 simplă precizie [1.1×104932. -3. 1.4×1038] double virgulă mobilă. -1. [-1. 1.9×10-29. 289 Anexa 1 Memento A.5×10-45.4×10-4932. [-3.7×10308.7×1038. Tipuri standard A) Tipuri întregi Ocupă Nume tip Semnificaţie Valori admise (biţi) shortint întreg scurt 8 de la -128 la 127 integer întreg 16 de la -32768 la 32767 longint 32 de la -2147483648 la întreg lung 2147483647 byte număr natural scurt 8 de la 0 la 255 word cuvânt 16 de la 0 la 65535 B) Tipul caracter . Exemple: 'a'.2×1018] D) Tipul logic – boolean – poate reţine doar două valori true şi false.'1' (caracterul '1' nu trebuie confundat cu numărul 1).2×1018. [-1. 9.4×1038. [-1.1.4×10-4932] extended 80 format lung ∪ [3.'A'.1.7×1038] virgulă mobilă. C) Tipuri reale Ocupă Nume tip Semnificaţie Valori admise (în modul) (biţi) virgulă mobilă.7×10308] virgulă mobilă. Limbajul Pascal A. 1.1.un caracter se notează între apostrofuri. .1×104932] comp virgulă mobilă 64 [-9. 3. -2.9×10-29] ∪ real 48 simplă precizie [2. -5×10-324] ∪ 64 dublă precizie [5×10-324.

1.(operator unar). Se pot reprezenta numere întregi cuprinse în intervalul: [−2. D) Constantele simbolice.1. Dim = NrMaxValori*2-1. .147.1. Ele reprezintă numerele 512∗10 şi −45. Caracterele din şir pot fi specificate enumerându-le între apostrofuri. Prioritatea operatorilor Iată cele 4 grupe de prioritate. Ultimele două numere folosesc o scriere neîntâlnită în matematică. Memento A. Grupa 1 (prioritate maximă): NOT. Mesaj = 'Nu exista solutie'#10#13.2. identificator = expresie. MOD. PI = 3. −45. A. . 2. . a numerelor raţionale) care pot fi reprezentate în calculator.. Exemplu: 'abc'. Operatori A. 23 -3 C) Constante şir de caractere.290 Anexa 1. 512E+23.14.1∗10 ]. Modulul numerelor reale se găseşte în intervalul [3. −45.1.483.1E−3. B) Constante reale.1.26.647]. + (operator unar). După cum reiese şi din denumire.3. În locul virgulei se -4352 4932 foloseşte punctul.648.3. Constante A) Constante întregi. Exemplu: const NrMaxValori = 30. Exemple: 2.483.34.4∗10 . Definirea constantelor simbolice precede definirea tipurilor utilizator şi se realizează astfel: const identificator = expresie. /. Acestea sunt constante care au în program un anumit nume. Sunt alcătuite dintr−o submulţime a numerelor întregi care pot fi reprezentate în memoria calculatorului. Expresiile care intervin în definirea constantelor trebuie să poată fi evaluate la momentul compilării programului. Grupa 2 (operatorii din grupa doi se mai numesc şi operatori multiplicativi): AND.1∗10 .147. cu ajutorul lor se reprezintă şiruri de caractere. Sunt alcătuite dintr−o submulţime a numerelor reale (mai precis. *. DIV..

Operanzii sunt în mod obligatoriu de tip întreg. altfel rezultatul este de tip întreg. XOR.rezultat + şi . rezultatul este de tip real. /.1.  Operatorul *.  Operatorul DIV. +. <=. altfel este de tip întreg. rezultatul este de tip real. Operanzii sunt de tip întreg sau real. *. MOD). Are semnificaţia de împărţire întreagă. Operatorul DIV furnizează rezultat corect numai dacă ambele valori sunt numere întregi pozitive. Dacă cel puţin un operand este real. Este obligatoriu ca fiecare operand să fie separat cu cel puţin un spaţiu de operator. • semnul câtului se stabileşte după regula semnelor (+ cu + rezultat +. Operanzii sunt de tip întreg sau real.cu . -. altfel este de tip întreg. apare ca sumă de şiruri (caz pe care nu-l discutăm acum). Rezultatul este de tip întreg. Are semnificaţia de înmulţire. . Operatori aritmetici Operatorii aritmetici sunt de două feluri: • operatori unari (+ şi -). Se poate ca unul să fie de tip întreg şi celălalt de tip real.  Operatorul -.cu + rezultat -).cuprinde şi operatorii relaţionali): <. Dacă cel puţin unul din operanzi este. întotdeauna rezultatul este de tip real. >=.  Operatorul /. Are semnificaţia de adunare. =. De asemenea. Operanzii săi sunt de tip întreg sau real. Dacă cel puţin unul din operanzi este de tip real.rezultat -. A. 13 div 4=3). Rezultatul pentru operatorul DIV se obţine astfel: • se face împărţirea întreagă a celor două numere care sunt considerate pozitive (de exemplu. -. Grupa 4 (operatori cu cea mai mică prioritate .Manual de informatică pentru clasa a XI-a 291 Grupa 3 (operatorii din această grupă se numesc şi operatori aditivi): OR.<>. • operatori binari ( +. Operatorul + apare şi ca operator unar. DIV. Are semnificaţia de împărţire. + cu . >.2.  Operatorul +. . real rezultatul este de tip real. Are semnificaţia de scădere. Operanzii pot fi de tip întreg sau real dar.3.

Dacă relaţia este adevărată rezultatul va fi TRUE. Fie a MOD b. rezultatul este corect numai dacă ambii operanzi sunt pozitivi. Dacă relaţia este adevărată rezultatul va fi TRUE.1. Ca şi în cazul operatorului DIV.4. altfel rezultatul va fi FALSE. Şi aici. Operanzii trebuie separaţi de operator prin cel puţin un spaţiu.3. Fiind daţi doi operanzi a şi b.  Operatorul XOR (sau exclusiv).  Operatorul <= (mai mic sau egal). rezultatul este TRUE. Fiind daţi doi operanzi a şi b. altfel rezultatul va fi FALSE. operatorul <= arată dacă este adevărată sau nu relaţia a<=b.1. altfel rezultatul va fi FALSE. Are semnificaţia de rest al împărţirii pentru numere întregi. regula este foarte simplă: dacă argumentele sunt diferite rezultatul este TRUE. A. Dacă relaţia este adevărată rezultatul va fi TRUE. operatorul >= arată dacă este adevărată sau nu relaţia a>=b. Operatori relaţionali  Operatorul < (mai mic).  Operatorul > (mai mare). Modul de acţiune se poate observa mai jos: NOT (TRUE)=FALSE. Regula este simplă: dacă unul dintre operanzi este TRUE. Fiind daţi doi operanzi a şi b.  Operatorul AND (şi).a DIV b (R=D-Î×C). Rezultatul se obţine astfel: a . Fiind daţi doi operanzi a şi b.  Operatorul >= (mai mare sau egal). Operanzii sunt în mod obligatoriu de tip întreg. altfel rezultatul este FALSE. Memento  Operatorul MOD. Dacă relaţia este adevărată rezultatul va fi TRUE. NOT (FALSE)=TRUE. operatorul = arată dacă este adevărată sau nu relaţia a=b. iar rezultatul va fi întotdeauna de tip întreg. Fiind daţi doi operanzi a şi b. operatorul < arată dacă este adevărată sau nu relaţia a<b.  Operatorul = (egal).3. altfel rezultatul va fi FALSE.  Operatorul OR (sau).292 Anexa 1. A. . Operatori logici  Operatorul NOT (negare). altfel rezultatul va fi FALSE. contrar el este FALSE. Regula de obţinere a rezultatului este foarte simplă: rezultatul este TRUE numai dacă ambii operanzi au valoarea TRUE (altfel rezultatul este FALSE).3. operatorul > arată dacă este adevărată sau nu relaţia a>b. Dacă relaţia este adevărată rezultatul va fi TRUE.

O succesiune de separatori '. Instrucţiuni 1.  Operatorul OR (SAU) este binar. Instrucţiunea de atribuire este de forma: v:=expresie.1. A.  Operatorul NOT (negare) este unar. unde v este numele unei variabile. . • variabila v ia această valoare. Se face ŞI logic pentru toate perechile de biţi aflaţi pe aceeaşi poziţie a celor doi operatori. rezultatul este 0. Regula fundamentală este următoarea: tipul expresiei trebuie să coincidă cu tipul variabilei v. Dacă biţii sunt diferiţi rezultatul este 1.  Operatorul AND (ŞI) este binar. Există două forme ale acestei instrucţiuni: Forma 1. IF expresie logică THEN instrucţiune1 ELSE instrucţiune2 Principiul de executare este următorul: • se evaluează expresia logică. Principiul de executare este următorul: • se evaluează expresia. altfel. 2. se pleacă de la reprezentarea binară a numerelor. se execută instrucţiunea plasată după THEN. Dacă cel puţin un bit este 1 rezultatul este 1. iar în orice alt caz. Se face SAU logic pentru toate perechile de biţi aflaţi pe aceeaşi poziţie a celor doi operatori. rezultatul este 1.Manual de informatică pentru clasa a XI-a 293 Dacă operanzii sunt de tip întreg. rezultatul este 0.' indică prezenţa mai multor instrucţiuni vide. Nu se trece nimic. Transformă toţi biţii 1 în 0 şi invers. dar totuşi există. în caz contrar se execută instrucţiunea plasată după ELSE. Se face XOR pentru toate perechile de biţi aflaţi pe aceeaşi poziţie a celor doi operatori. rezultatul este 0. • dacă aceasta ia valoarea TRUE. Dacă ambii biţi sunt 1.4. Instrucţiunea IF. 3. Instrucţiunea vidă. altfel.  Operatorul XOR (SAU EXCLUSIV) este binar.

se obţine FALSE. • în situaţia în care nici una din instrucţiunile 1.. Reproduce structura Cât timp. Instrucţiunea WHILE.... . • dacă şi clauza ELSE este absentă. procedeul continuă până când... execută. 6. Corespunde structurii alternative multiple. Instrucţiunea compusă.cn1. 4. c1. z1. Forma generală este: WHILE expresie logică DO instrucţiune Principiul de executare este următorul: • se evaluează expresia logică şi în caz că aceasta are valoarea TRUE se execută instrucţiunea.[c2.294 Anexa 1. se execută instrucţiunea aflată după THEN.znp]: instrucţiunep [ELSE instrucţiune] END Aici.cn1]: instrucţiune1... Instrucţiunile se scriu între begin şi end. Instrucţiunea CASE. se trece la instrucţiunea următoare. Principiul de executare este următorul: • se evaluează expresia ordinală..... se execută instrucţiunea plasată după ELSE. 5.. Principiul de executare este următorul: • se evaluează expresia logică..p nu este precedată de acea constantă. p1. • se execută acea instrucţiune care are în faţă constanta obţinută în evaluarea expresiei.. la evaluarea expresiei... se evaluează din nou expresia. IF expresie logică THEN instrucţiune.. Forma generală a instrucţiunii CASE este: CASE expresie ordinală OF c1.pn2]: instrucţiune2....[p2.. în caz contrar se trece la instrucţiunea următoare. Se utilizează pentru a putea scrie mai multe instrucţiuni care vor fi interpretate de compilator ca una singură. • în situaţia în care aceasta are valoarea TRUE..[z2. Memento Forma 2.. dacă aceasta are valoarea TRUE se execută din nou instrucţiunea.znp reprezintă constante de acelaşi tip ca şi expresia ordinală.

. se atribuie variabilei de ciclare valoarea obţinută în urma evaluării expresiei 1. .1. Această instrucţiune reproduce structura REPEAT UNTIL şi are forma generală: REPEAT i1. Forma 1.. • Dacă valoarea obţinută în urma evaluării expresiei 1 este egală cu valoarea obţinută în urma evaluării expresiei 2.2. Instrucţiunea REPEAT. se execută instrucţiunea subordonată şi executarea FOR este încheiată.. 8. Instrucţiunea FOR. i1.. de tip integer. Atunci când cunoaştem de câte ori se execută o secvenţă este bine să se utilizeze instrucţiunea FOR.. . Principiul de executare este următorul: • se execută secvenţa de instrucţiuni. executarea FOR este încheiată. Pasul 2. i2. Se evaluează cele două expresii. char sau boolean. i2. • Dacă valoarea obţinută în urma evaluării expresiei 1 este strict mai mică decât valoarea obţinută în urma evaluării expresiei 2. Principiul de executare este următorul: Pasul 1. contrar se trece mai departe. FOR variabilă := expresie1 TO expresie2 DO instrucţiune unde: − variabila poate fi de orice tip ordinal (de exemplu. se atribuie variabilei de ciclare valoarea obţinută în urma evaluării expresiei 1 şi se trece la 2. Pasul 2.Manual de informatică pentru clasa a XI-a 295 7. • se evaluează expresia logică. • dacă aceasta ia valoarea FALSE se execută din nou secvenţa de instrucţiuni. Ea are două forme. in reprezintă instrucţiuni. − expresie1. prezentate în continuare. expresie2 sunt expresii de acelaşi tip cu variabila. • Dacă valoarea obţinută în urma evaluării expresiei 1 este strict mai mare decât valoarea obţinută în urma evaluării expresiei 2. in UNTIL expresie logică Aici. dar în nici un caz de tipul real). .

are forma generală: function Sin(X: Real): Real. executarea instrucţiunii FOR se încheie. . Câteva funcţii utile  sinus:ℜ→[-1. FOR variabilă := expresie1 DOWNTO expresie2 DO instrucţiune În acest caz.1]. {arctangent (x)} Atenţie: argumentul trebuie exprimat în radiani. variabila de ciclare scade la fiecare pas. • Dacă valoarea reţinută de variabila de ciclare este egală cu valoarea obţinută în urma evaluării expresiei 2. are forma generală: function Cos(X: Real): Real. A. Se execută instrucţiunea subordonată.296 Anexa 1.2. are forma generală: function ArcTan(X: Real): Real.3. Funcţia logaritmică are următoarele proprietăţi: pentru A. B > 0. {cosinus (x)}  arctangent:ℜ→(-π/2.71) are forma generală: function Exp(X: Real): Real.1]. unde f(x)=ex (e este un număr iraţional. ln(AB ) = B ⋅ ln(A). e≈2. Memento Pasul 2. Pasul 2.  Funcţia logaritmică f:ℜ+→ℜ.5. Forma 2. unde f(x)=ln(x) este funcţia inversă funcţiei exponenţiale şi are forma generală: function Ln(X: Real): Real.  cosinus:ℜ→[-1.2.  Funcţia exponenţială f:ℜ→ℜ+. • Dacă valoarea reţinută de variabila de ciclare este strict mai mică decât valoarea obţinută în urma evaluării expresiei 2 (evaluare efectuată la început) se adună 1 variabilei de ciclare şi se trece la 2.π/2). Celelalte funcţii uzuale se obţin prin aplicarea formulelor trigonometrice.1. avem : ln(A ⋅ B) = ln(A) + ln(B).

unde: f(x) = x . rezultatul este eronat.75) returnează -2. . are forma generală: function Sqrt(X: Real): Real. Atenţie: dacă X este negativ.  Funcţia „parte întreagă” function Int(X: Real): Real. unde f(x)=x2: function Sqr(X: Real): Real. deducem pentru x>0. returnează partea întreagă a lui x.  Funcţia „parte fracţionară” function Frac(X: Real): Real. y Aceasta înseamnă că putem calcula xy . returnează valoarea rotunjită a lui x.75) returnează 2. Exemple: int(2. y oarecare: x y = eln(x ) = e y⋅ln(x) .  Funcţia de trunchiere function Trunc(X: Real): Longint. returnează X-Int(X).pentru că funcţia putere nu există în Pascal: exp(y*ln(x)).  Funcţia „valoare absolută” function Abs(X). returnează valoarea trunchiată a argumentului. unde x este o valoare întreagă sau reală.0.  Funcţia pătratică f:ℜ→ℜ+.0.  Funcţia de rotunjire function Round(X: Real): Longint.  Funcţia radical f:ℜ+→ℜ+. int(-2.Manual de informatică pentru clasa a XI-a 297 Din faptul că funcţia logaritmică este inversa funcţiei exponenţiale şi din a doua relaţie de mai sus. returnează x .

O constantă în baza 8 se declară precedată de un 0 nesemnificativ. Acestea sunt precedate de 0X sau 0x.4 ×10-38.294. admite tipurile de mai jos: A) Tipuri întregi Ocupă Nume tip Semnificaţie Valori admise (biţi) unsigned char caracter fără semn 8 de la 0 la 255 char caracter 8 de la -128 la 127 unsigned int întreg fără semn 16 de la 0 la 65535 int întreg 16 de la -32768 la 32767 unsigned întreg lung fără 32 de la 0 la 4.2. dublă precizie [3. Se reţine numărul întreg 123(8).2.295 long semn 32 de la -2.483.298 Anexa 1.648 la long întreg lung cu semn 2.2. • hexazecimale (în baza 16).3.483. Exemplu: pentru 0X1A2 adică 1A2(16) sau 0x1a2. Constante 1.2.1 ×10+4932] 2 double 80 format lung A. Acestea se clasifică astfel: • zecimale (în baza 10). Limbajul C++ A. în varianta Borland C++. Constante întregi.967. 56. float 32 [3.1.7×10-308 . Exemplu: 0123.1. 1239. Memento A. • octale (în baza 8). 1. adică 1A2(16).4×10-493 .147.147.647 B) Tipuri reale Ocupă Nume tip Semnificaţie Valori admise (în modul) (biţi) virgulă mobilă. double 64 [1.4×1038] simplă precizie virgulă mobilă. . Tipuri standard Limbajul C++.7×10+308] dublă precizie long virgulă mobilă. Exemple: 23.

Codul său este 97(10)=141(8)=61(16). pentru anumite caractere. 6.3. Constante caracter. • bel: '\a'. acesta este precedat de caracterul 'x'.'\x5c'. De exemplu.reprezintă valoarea constantei. Caractere albe (whitespaces).'\7'.'\x27'. • cr ('\r').Manual de informatică pentru clasa a XI-a 299 2.5E-12.'\xd'.. În cazul când se foloseşte codul scris în baza 16. • valoare . Să considerăm o constantă caracter 'a'.66. unde: • tip . Uneori. • nume . Secvenţe escape. • apostrof: '\''. . constanta se introduce prin codul său într-una din bazele 8 sau 16. constanta 'a' poate fi scrisă (echivalent) astfel: '\141' sau '\x61'. • tab vertical ('\v'). 3. O secvenţă escape începe prin caracterul '\' (backslash). -2.5×10-12. Constante reale Exemple: -45. • tab orizontal ('\t'). • backslash: '\\'. 'a'. Pentru a da un nume constantelor folosim const. 2.'\xa'.'\x7'. Constante şir de caractere Exemplu: "'acesta este un text". 4.reprezintă numele constantei. Forma generală a unei astfel de declaraţii este (construcţia dintre paranteze drepte este opţională): const [tip] nume=valoare. Printr-o secvenţă escape. Au un rol special în cadrul operaţiilor de citire/scriere.'\12'. Acestea se trec între două caractere apostrof (').'\134'.2. aşa cum rezultă din exemplele următoare. Acestea sunt: • blank (' '). '1'. tipul este int). adică.'\15'.'\47'. . • newline ('\n'). se pot utiliza şi semne speciale. • cr: '\r'. 0. • newline: '\n'. Exemple: 'A'.reprezintă tipul constantei (dacă este absent. 1. 5.

adică acţionează asupra unui singur operand). Prioritatea operatorilor (în ordine descrescătoare) Priviţi următorul tabel: prioritate operator asociativitate 1 ()[]− >::.(binar). • / (binar). *− > * s →d 4 */ % s →d 5 +− s →d 6 <<>> s →d 7 <<=>>= s →d 8 ==! = s →d 9 & s →d 10 ^ s →d 11 | s →d 12 && s →d 13 || s →d 14 ?: d →s 15 =* =/ =+ =− =& =^ =|=<<=>>= d →s 16 . are semnificaţia de înmulţire.2.2. pentru adunare. s →d 2 !~ +−+ +− −*(typecast )sizeof newdelete d →s 3 . . • . • + plus (unar).300 Anexa 1. • % (binar).2.3. Memento A.minus (unar.3. s →d A.1.3. Operatori aritmetici În C++ există următorii operatori aritmetici: • . pentru scădere. • * (binar). pentru împărţire. • + (binar). Operatori A. restul împărţirii întregi.2.

altfel returnează 0.3. Operatori relaţionali În C++ există următorii operatori relaţionali: • < (mai mic). • <= (mai mic sau egal). în caz contrar. Operatori de egalitate Aceştia sunt: • == pentru egalitate. Rezultatul obţinut este corect din punct de vedere matematic numai dacă ambii operanzi sunt numere naturale. Operatorul ”/” (împărţire) acţionează în mod diferit în funcţie de operanzi: a) dacă ambii sunt de tip întreg. + cu -.2. în cazul în care inegalitatea este respectată şi 0. A. etc. • > (mai mare).Manual de informatică pentru clasa a XI-a 301 Observaţii 1. În cazul în care relaţia indicată de operator este respectată. Cu toate acestea.3. În cazul în care se împart două valori întregi. b) semnul câtului se stabileşte după regula semnelor (+ cu + rezultat +. b) dacă cel puţin un operand este de unul din tipurile reale. 3. • != pentru inegalitate. Rezultatul unei operaţii logice este 1. 2. rezultatul este întreg şi are semnificaţia de împărţire întreagă. rezultatul este corect (din punct de vedere matematic) numai dacă valorile care se împart sunt pozitive.3. expresia returnează 1. se procedează astfel: a) se face împărţirea întreagă a celor două valori care sunt considerate în modul. rezultat -). Operatorul ”%” acţionează numai asupra operanzilor de tip întreg.4. • >= (mai mare sau egal). rezultatul este real (se efectuează împărţirea obişnuită). .2. A.

Aceşti operatori acţionează numai asupra operanzilor de tip întreg.  Dacă operatorul este prefixat. • ~ negare pe biţi (operator unar). • ^ sau exclusiv pe biţi.pentru decrementare. • -. Operatori de incrementare şi decrementare Aceşti operatori sunt unari şi au rolul de a incrementa (adună 1) sau decrementa (scad 1) conţinutul unei variabile. A.  Dacă operatorul este postfixat.2. Operatorii pot fi prefixaţi (aplicaţi în faţa operandului) sau postfixaţi (aplicaţi după operand).3. altfel el este 0. variabila este incrementată (decrementată) după ce valoarea reţinută de ea intră în calcul. Memento A. Operatori logici pe biţi Limbajul C++ este dotat cu un set de operatori care permit accesul la bit. Aceştia sunt: • <<. Operatorul şi logic (binar) acţionează astfel: dacă ambii operanzi sunt diferiţi de 0. • & şi pe biţi. rezultatul este 1.şi logic. A. Operatori logici Există trei operatori logici: • ! .3.2. variabila este incrementată (decrementată) înainte ca valoarea reţinută de ea să intre în calcul. altfel rezultatul este 0.7.negare logică. rezultatul este 0. • || . >> operatori de deplasare.5. . altfel rezultatul este 1. Operatorul negare logică acţionează astfel: dacă operandul este o valoare diferită de 0.sau logic. Operatorul sau logic (binar) acţionează astfel: dacă cel puţin unul din operanzi este o valoare diferită de 0. Operatorii sunt: • ++ pentru incrementare.302 Anexa 1.3. • | sau pe biţi. • && .6. rezultatul este 1.2.

…. Operatorul ”>> ” este binar. A. v1. În plus. OP1 OP2 OP1&OP2 OP1^OP2 OP1|OP2 0 0 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 1 0 1 Operatorul ”~” (negare pe biţi) are rolul de a inversa conţinutul biţilor (dacă un bit conţine 0. . cu un număr de poziţii egal cu valoarea reţinută de al doilea operand. ”|” .. Poziţiile rămase libere (în dreapta) vor reţine valoarea 0. se aplică regulile de conversie pentru expresii aritmetice. Dacă operandul din stânga este de un tip întreg fără semn. Dacă al doilea operand reţine valoarea m. Operatorul ”=” se foloseşte într-o expresie de forma: v=expresie Aici. Principiul de executare este următorul: • se evaluează expresia. va conţine 1 şi invers). Operatori de atribuire În C++ atribuirea este operator..3. • variabilei v i se atribuie valoarea obţinută (dacă este cazul. În cazul operatorilor binari ”& ”.2. fiecare poziţie din stânga rămasă liberă se completează cu valoarea reţinută de bitul de semn. v este o variabilă. în C++ avem mai mulţi operatori de atribuire.Manual de informatică pentru clasa a XI-a 303 Operatorul ”<<” este binar. ”^”.8.=vn=expresie unde v. Atunci când cei doi operanzi nu au aceeaşi lungime (dar numai atunci . El are rolul de a deplasa către stânga conţinutul tuturor biţilor operandului din stânga sa. dacă ambii operanzi sunt de tip char şi rezultatul este de tip char). poziţiile rămase libere (în stânga) vor reţine valoarea 0.de exemplu. El are rolul de a deplasa către dreapta conţinutul tuturor biţilor operandului din stânga cu un număr de poziţii egal cu valoarea reţinută de al doilea operand. rezultatul se obţine aplicând pentru fiecare pereche de biţi aflaţi pe aceeaşi poziţie regulile din tabelul următor. Se pot efectua şi atribuiri multiple de forma: v=v1=v2=. se efectuează conversia respectivă). vn sunt variabile. o astfel de deplasare este echivalentă cu împărţirea întreagă cu 2m. În cazul în care primul operand este un întreg cu semn.

• dacă aceasta produce o valoare diferită de 0.. Întrucât. principiul de executare este următorul: • se evaluează expresia. se generează un cod maşină eficient).. S-a convenit ca întreaga expresie (care cuprinde cele n expresii separate prin virgulă) să producă ca rezultat valoarea obţinută în urma evaluării ultimei expresii (evident.dacă este cazul). ”>>=”.9. ”-=”. expresiile se evaluează în ordinea exp1. .10. • altfel. Memento În acest caz. se efectuează conversia necesară). în primul caz. exp2. ”+=”. A. • conţinutul variabilei v1 este atribuit variabilei v (eventual. se evaluează exp2 şi exp3 este ignorată (nu se evaluează).. În ansamblu.3. ca mai jos: exp1. Operatorul condiţional Se foloseşte în expresii de genul: exp1?exp2:exp3 Principiul de executare este următorul: • se evaluează exp1.. • . . expn. • conţinutul variabilei vn este atribuit variabilei vn-1 (eventual. A.. • valoarea obţinută este atribuită variabilei vn (eventual convertită .. ”<<=”.3. O atribuire de forma: v op expresie. ”/=”.. expn.2.exp2. se evaluează exp3 şi exp2 este ignorată.304 Anexa 1. are acelaşi rezultat ca v=v op expresie (diferenţa este că. Pentru atribuiri se mai pot utiliza şi operatorii: ”*=”. se efectuează conversia necesară). operatorul virgulă se asociază de la stânga la dreapta. ”&>”. expresia este de tipul lui exp2 sau exp3 şi produce valoarea exp2 sau exp3 (în funcţie de cea care se evaluează).' (virgulă) C++ permite programatorilor să scrie mai multe expresii separate prin virgulă. după cum rezultă din tabel.2. ”^=” sau ”|=”. tipul acestei valori este şi tipul expresiei).. Operatorul '.. ”%=”.

Manual de informatică pentru clasa a XI-a 305 A. dorim ca unul sau mai mulţi operanzi să intre în calcul convertiţi aşa cum dorim (nu implicit). se execută instrucţiune1. if (expresie) instrucţiune1 else instrucţiune2 Principiul de executare este următorul: • se evaluează expresia. înaintea operandului se trece între paranteze tipul său. Exemplu: fie declaraţia: float x= -1.. se execută instrucţiunea subordonată. În limbajul C++ şi o atribuire este o expresie. if (expresie) instrucţiune Principiul de executare este următorul: • se evaluează expresia. A. Atunci: (int)x=-1 (se face conversia din float în int prin trunchiere).2. se evaluează expresia. 2. Pentru aceasta. Operatorul sizeof Are rolul de a returna numărul de octeţi utilizaţi pentru memorarea unei valori.11. Operatorul sizeof poate fi utilizat într-una din cele două forme prezentate în continuare: sizeof (expresie) sizeof (tip) A. Instrucţiunea ”if” se poate utiliza în următoarele două forme: Forma 1.3. La întâlnirea unei astfel de instrucţiuni. • dacă valoarea produsă este 0 se execută instrucţiune2.9.12. Forma 2. Instrucţiuni 1.3. Instrucţiunea expresie este de forma: expresie. • dacă valoarea produsă de aceasta este diferită de 0. • dacă valoarea produsă de aceasta este diferită de 0.2. Operatorul de conversie explicită De multe ori..2.4. .

. . : : in. break. .... ... În absenţă.. i2.. [default: secvenţă instrucţiunin+1]. altfel se execută numai secvenţa instrucţiunin+1.. se execută instrucţiunea subordonată. Memento 3..... } 4.306 Anexa 1.. • pasul 2: dacă valoarea produsă de aceasta este diferită de 0... Principiul de executare: • se evaluează expresia.. i2.. 5.. case expn: secvenţă instrucţiunin.. Se utilizează în cazul în care se doreşte ca mai multe instrucţiuni să fie tratate de compilator ca o singură instrucţiune. unde i1..... case exp2: secvenţă instrucţiuni2.. execută. instrucţiunii şi se trece la instrucţiunea următoare.. break. • dacă aceasta produce o valoare egală cu cea produsă de expi.. break. Forma generală este: while (expresie) instrucţiune Principiul de executare este următorul: • pasul 1: se evaluează expresia. Instrucţiunea compusă. se execută. Alternativa default este facultativă. in sunt instrucţiuni: { i1..... − instrucţiunii reprezintă o secvenţă oarecare de instrucţiuni. în cazul în care nu există coincidenţă de valori.. Instrucţiunea ”while” Această instrucţiune reproduce structura de tip Cât timp . apoi se revine la pasul 1.. Este de forma de mai jos. se trece la instrucţiunea următoare. în ordine. Instrucţiunea ”switch” are forma generală: switch (expresie) { case exp1: secvenţă instrucţiuni1... } unde: − expresie are semnificaţia: expresie de tip întreg. − expi sunt expresii constante de tip întreg. altfel se trece la instrucţiunea următoare...

Forma generală a acestei instrucţiuni este următoarea: do instrucţiune while(expresie). execuţia instrucţiunii do se termină. A. Rolul ei este de a întoarce x (modulul lui x). • pasul 2: se evaluează expresiatest.dacă expresia produce la evaluare o valoare diferită de 0.. includeţi fişierul math. apoi se trece la pasul 3. altfel se trece la pasul 1. între paranteze se găsesc trei expresii: − expresieinitializare se foloseşte de regulă.5. Este de remarcat faptul că în cadrul acestei expresii (cu rol special) este posibil chiar să declarăm variabila de ciclare (cu valoare iniţială). pentru iniţializarea variabilei de ciclare. altfel se trece la instrucţiunea următoare (se termină execuţia instrucţiunii for). expresieincrementare) instrucţiune După cum se observă.Manual de informatică pentru clasa a XI-a 307 6.h>.2. − expresietest se foloseşte pentru a testa dacă se execută instrucţiunea subordonată .h: #include <math.. Instrucţiunea ”for” are forma generală: for (expresieiniţializare.  Funcţia abs are forma generală: int abs(int x). • pasul 2: se evaluează expresia. se execută instrucţiunea subordonată for. . Principiul de executare: • pasul 1: se evaluează expresieiniţializare (un caz special este acela în care aceasta conţine şi declaraţia variabilei de ciclare). • pasul 3: se evaluează expresia de incrementare şi se revine la pasul 2. instrucţiunea subordonată for se execută.  Funcţia fabs are forma generală double fabs(double x). Traduce în limbaj structura Execută. are acelaşi rol cu abs.cât timp. În cazul în care valoarea produsă la evaluare este 0. − expresieincrementare se foloseşte pentru incrementarea variabilei de ciclare. expresietest. numai că întoarce valoarea unui număr real (chiar double). 7. Principiul de executare este următorul: • pasul 1: se execută instrucţiunea subordonată. Instrucţiunea ”do while”. Câteva funcţii utile Pentru a le utiliza. În cazul în care aceasta produce o valoare diferită de 0.

unde lg( x) = log 10 ( x). şi calculează funcţia e x : ℜ → ℜ *+ .  Funcţia ceil are forma generală double ceil(double x). şi calculează funcţia ln( x) : ℜ *+ → ℜ. şi calculează valoarea funcţiei sin( x) : ℜ → [−1. floor (-23.  Funcţia pow are forma generală double pow(double x. .308 Anexa 1. şi calculează valoarea funcţiei cos( x) : ℜ → [−1. Rezultatul este în intervalul (−π . π ). 2 2  Funcţia atan are forma generală: double atan(double x).  Funcţia sin are forma generală double sin(double x).  2   Funcţia exp are forma generală double exp(double x). ].  Funcţia cos are forma generală double cos(double x).78)=123.34)=-24. double y). Motivul? x  Funcţia floor are forma generală double floor(double x).  Funcţia acos are forma generală: double acos(double x).  Funcţia tan are forma generală double tan(double x). şi calculează valoarea rotunjită a lui x (rotunjirea se face în minus).  Funcţia asin are forma generală: double asin(double x). ). Exemple: floor (123.double x) y şi calculează arctg ( ).34)=-23. π ].1]. ceil(-23. şi calculează  π  valoarea funcţiei tg ( x) : ℜ − k ⋅ π + k ∈ Ζ → ℜ. şi π π calculează valoarea funcţiei arctg ( x) : ℜ → (− . unde ln( x) = log e ( x) .  Funcţia log10 are forma generală double log10(double x). şi π π calculează valoarea funcţiei arcsin( x) : [−1.1] → [0. Exemple: ceil(123. şi calculează funcţia lg( x) : ℜ *+ → ℜ.78)=124. Memento  Funcţia labs are forma generală long int labs(long int x). şi calculează valoarea rotunjită a lui x (rotunjirea se face în plus). şi calculează valoarea funcţiei arccos( x) : [−1.  Funcţia log are forma generală double log(double x). numai că întoarce valoarea unui întreg lung. şi acelaşi rol cu abs.1].1] → [− . şi calculează x y . 2 2  Funcţia atan2 are forma generală: double atan2(double y.

legată la Internet: Internet Router Switch 1 Switch 2 Subreţeaua 1 Subreţeaua 2 Figura A. În continuare. atunci vom avea un graf extrem de complex şi foarte diversificat din punct de vedere al structurii. în afarǎ de Informaticǎ. vom prezenta o schemă de principiu care descrie o reţea de calculatoare. unii dintre voi v -aţi pus o serie de întreb ǎri referitoare la aplicabilitatea teoriei grafurilor în problemele reale: • unde pot utiliza grafurile şi de ce? • existǎ aplicaţii din alte domenii.1. se poate obţine o bunǎ optimizare a resurselor (umane sau materiale) sau a timpului. grafurile sunt foarte utile într -o multitudine de aplicaţii din diverse domenii. A. Spre exemplu. dupǎ cum veţi vedea în continuare. ce pot fi rezolvate cu ajutorul teoriei grafurilor? În fapt.1. Exemplu de reţea de calculatoare legată la Internet . iar prin utilizarea lor. Reţele de comunicaţie Comunicaţia între diversele dispozitive electronice din zilele noastre reprezintă poate cea mai răspândită aplicaţie practică a teoriei grafurilor. dacă ne referim la reţelele de calculatoare sau la Internet şi dacă considerăm fiecare calculator ca fiind un nod. 309 Anexa 2 Aplicaţii practice ale grafurilor Cu siguranţǎ.

etc.  Există o întreagă teorie legată de reţelele de calculatoare. către un altul din Subreţeaua 2. Fiecare router reţine toate informaţiile despre celelalte router-e existente în reţea şi despre trafic. dar ceea ce este însă de reţinut este faptul că din punct de vedere topologic. protocoalele de comunicaţie efectuează operaţii specifice pentru asigurarea transmisiei bidirecţionale între fiecare dispozitiv terminal (calculator). Router-ele utilizează protocoalele de comunicaţie care au la bază algoritmi de optimizare ce trebuie să determine cea mai bună cale. trimiţându-se un mesaj de tip echo (“ecou”) către router-ele determinate anterior. Pe fiecare nivel însă.Aplicaţii practice ale grafurilor Observaţii  Structura anterioară este de tip arbore. NAT (Network Address Translation). Când se porneşte un astfel de router.310 Anexa 2 . Router-ul are o legătură cu un ISP (Internet Service Provider). permiţându-le acestora să ia decizii la nivel local. Se face apoi un test prin care se analizează timpul de răspuns. fără a cunoaşte în prealabil destinatarii (mesaj de tip broadcast). Când ne referim la drumul cel mai bun.  Router-ul este un dispozitiv electronic care decide calea (drumul optim) pe care vor fi trimise informaţiile de la un calculator din Subreţeaua 1. cum ar fi: IP (Internet Protocol). cărui destinatar îi este dedicat blocul de date. Fiecare Router deţine o serie de liste. Există două tipuri de algoritmi de routare mai importante. Aceste liste trebuie reactualizate frecvent pentru a preveni anumite modificări topologice ale reţelei. avem în vedere numărul de “hopuri” (din engleză. Comunicaţia optimă (calea cea mai scurtă între două noduri) este realizată cu ajutorul protocoalelor specializate de routare. Fiecare router îi va răspunde cu un mesaj în care va ataşa adresa IP a sa. numite tabele de routare. ”hops”) pe care trebuie să le parcurgă datele până la destinaţie sau un alt punct intermediar sau durata/viteza de trimitere a informaţiilor. în care sunt memorate adresele (fizice şi logice) tuturor nodurilor care au legătură fizică directă cu el şi drumurile optime deja cunoscute şi parcurse. utilizate în funcţie de modalitatea router-ului de a reţine şi de a analiza informaţiile structurale ale reţelei: • Algoritmi de routare globali. el trimite un mesaj către toate celelalte router-e din reţea. identificându-se astfel. Protocoale de routare Un protocol de routare are rolul de a obţine şi de a trimite informaţiile topologice ale reţelei către Router-e. în funcţie de adresa MAC (Media Access Control. Răspunsul primit de la . o reţea de calculatoare se poate reprezenta sub forma unui graf. RIP (Routing Information Protocol). Switch-ul decide la rândul său. identificator unic pe glob) a fiecărei plăci de reţea. Pentru a se conecta la reţeaua Internet. La nivel local.

Astfel. Când pachetele de date ajung la Router 2. cu atât numărul de calcule efectuate la nivel de router este mai mare. se poate utiliza Dijkstra) este apoi aplicat. De exemplu. • Algoritmi de routare descentralizaţi. dacă avem trei router-e legate în serie: L1 L2 Router 1 Router 2 Router 3 Figura A. media traficului sau. Astfel. Algoritmul de determinare a drumului minim între oricare două noduri ale reţelei (de exemplu. ci doar calea către acea regiune. mai simplu. dispozitivul obţine o “hartă” a reţelei pe care o reţine apoi în tabelul său de routare. Managementul informatic al proiectelor permite gestiunea. Legătura cu celelalte regiuni se face prin anumite router-e. Problemele reale pe care le întâmpină reţelele de calculatoare se datorează numărului mare de dispozitive (noduri) din reţea. Fiecare router deţine informaţii doar despre toate router-ele din regiunea sa. Instrumente de management economic Proiectele şi situaţiile economice determinate de punerea în practică a acestora.2. el verifică lista sa de routare şi decide cum să trimită pachetele de date spre destinaţie. se poate implementa virtual o ierarhizare a reţelei. Astfel. planificarea şi controlul resurselor astfel încât obiectivele propuse să se atingă în mod optim şi la timp. Exemplu de reţea în cazul în care Router 1 trebuie să trimită date către Router 3. face schimb de tabele cu celelalte router-e. scăzând astfel eficienţa reţelei. . reactualizându-şi astfel informaţiile.Manual de informatică pentru clasa a XI-a 311 fiecare este reţinut pentru a fi utilizat în continuare. În cazul unei reţele de dimensiuni foarte mari. Router-ele ce au implementate un astfel de algoritm reţin informaţiile doar despre nodurile legate în mod direct (adiacente). Cu cât această valoare este mai mare. Astfel. împărţindu-se pe regiuni. presupun efectuarea unor activităţi interco- nectate. un algoritm de acest tip funcţionează corect. ca un fel de “porţi” de ieşire spre exterior. care pot fi modelate prin intermediul grafurilor. A. un router dintr-o regiune nu reţine nici o informaţie despre un altul dintr-o altă regiune. numărul de noduri intermediare. informaţiile vor trece automat prin Router 2. dar poate încetini traficul. considerându-se pentru fiecare legătură un cost ce depinde de timpul de răspuns. router-ul memorează costul fiecărei legături directe şi la o anumită perioadă de timp. coordonarea.2.

Exemplu de graf de activităţi . activităţi). Teoria grafurilor vine în ajutorul oricărui analist de proiect prin modelarea acestor activităţi.312 Anexa 2 .Aplicaţii practice ale grafurilor O aplicaţie foarte răspândită a grafurilor orientate o constituie simularea proiectelor complexe ce presupun o multitudine de activităţi distincte. . prin structurarea grafică a dependenţelor dintre ele şi prin determinarea timpului necesar de realizare a proiectului. În cadrul unui proiect. activităţile se pot efectua: . Proiectul este format dintr-o serie de activităţi (şi evenimente). iar lungimea asociată unui arc semnifică timpul de desfăşurare al activităţii. Drumul critic reuneşte activităţi a căror întârziere duce la întârzierea realizării întregului proiect. Exemple: terminarea etapei de analiză. de aceea trebuie supravegheate cu mare atenţie. efectuate într-o anumită perioadă de timp (cu un început şi un sfârşit definit). Figura A. Exemple: proiectarea unei componente. implementarea unui algoritm. etc. La final. rezultatul este scopul pentru care a fost dezvoltat acel proiect. terminarea unor teste. Un astfel de graf presupune două tipuri de componente:  arcele – reprezintă activităţile sau etapele elementare ale lucrării. Acesta reprezintă drumul cel mai lung de la faza iniţială la faza finală a proiectului şi este numit drum critic. ele sunt un punct de verificare al evoluţiei lucrării. Numim drum critic al unui graf de activităţi un drum de lungime maximă care leagă nodul iniţial de cel final.în serie: o activitate nu poate începe până când alta nu a fost terminată.  nodurile – reprezintă evenimente care pot fi interpretate ca indicând realizarea unor obiective parţiale ale lucrării. efectuate în serie sau în paralel. În evaluarea oricărui proiect este necesară cunoaşterea timpului maxim de execuţie a întregii lucrări. etc. Un graf de activităţi este un graf asociat unei lucrări complexe a cărei realizare presupune desfăşurarea mai multor acţiuni (procese.3. sosirea materialelor de construcţie.în paralel: mai multe activităţi desfăşurate în acelaşi timp. Activităţile şi evenimentele ce formează drumul critic poartă şi ele denumirea de critice.

j] – marginea totală a unei activităţi. au fost introduse câteva noţiuni teoretice. MT[i. ce semnifică durata cu care se poate întârzia începerea activităţii A[i. atingerea sa nu poate să dureze cu mai mult de 6 (16-10) unităţi faţă de data sa aşteptată de terminare. Pentru evenimentul 4. se pot analiza în detaliu anumite aspecte particulare ale fiecărui eveniment sau activitate.3.j] – marginea liberă a unei activităţi. 2. Să revenim la exemplul din figura A. se poate observa că pentru toate evenimentele ce aparţin drumului critic. însă cu suma ponderilor arcelor egală. În Capitolul 9 aţi studiat grafurile şi algoritmul lui Roy-Floyd. ca fiind tj-ti-d(A[i. Având cunoscut drumul critic pentru un graf asociat unui proiect. ce specifică perioada de timp în care poate avea loc evenimentul Vi. iar data limită. fără a modifica data de aşteptare a evenimentului Vj. ti*]. pentru care notăm cu Vi (vârfurile) evenimentele şi cu A[i. În urma unor calcule uşoare.j] (arcul de la Vi la Vj) activităţile. nu există în mod obligatoriu un singur drum critic.j]). ti* . ce vor fi prezentate în continuare. în funcţie de durata drumului critic. Cele două valori asociate evenimentului Vi determină un interval de fluctuaţie. 7.data aşteptată a unui eveniment Vi ca fiind drumul cel mai lung de la V1 la Vi (cea mai mare distanţă). egală cu 16 (21-5) unităţi.data limită a unui eveniment Vi ca fiind diferenţa între tn (data aşteptată a lui Vn) şi drumul maxim de la Vi la Vn. Considerând cunoscute toate datele aşteptate şi cele limită pentru graf.j]. ti = ti*. metodă ce permite determinarea drumului maxim într-un graf. fără a modifica data limită a evenimentului Vj. . notat cu [ti. 5 şi 6.3. drumul critic este format din nodurile: 1. iar în cazul unei întârzieri. Arcele ce formează drumul critic au aceste două valori nule (nu le este permisă nici o întârziere). Sunt cazuri în care graful conţine mai multe drumuri critice. Această tehnică se poate implementa cu succes pentru a detecta drumul critic într-un graf de activităţi.j]). Putem astfel considera că evenimentul 4 trebuie să fie atins după 10 unităţi temporale. definim în continuare două noţiuni privitoare la arce: ML[i. Astfel.Manual de informatică pentru clasa a XI-a 313 În figura A. ce semnifică durata cu care se poate întârzia începerea activităţii A[i. Timpul de terminare al proiectului este de 21 de unităţi (măsura de unitate a costului). fără a schimba timpul total asociat proiectului (drumul critic). care nu sunt critice. ca fiind tj*-ti-d(A[i. vom avea data aşteptată egală cu 10 (5+2+3) unităţi. Se consideră un graf de activităţi.j]. Vom defini: ti . Dorim să cunoaştem cum se pot derula celelalte activităţi. De altfel.

înainte de a o alege pe cea considerată optimă. Chimie molecularǎ Ştiinţa care se ocup ǎ cu studiul moleculelor se numeşte chimie molecularǎ. utilizează cu succes metode de optimizare ca cea a “Drumului Critic“. se pot asocia urmǎtoarele trei matrice: . Mai jos. Exemplu de graf molecular asociat Pentru graful neorientat prezentat anterior. ǎ se numesc În chimie.Aplicaţii practice ale grafurilor Intervalul de fluctuaţie permite managerului de proiect să utilizeze resursele. Grafurile de activităţi sunt extrem de utile în evaluarea lucrărilor complexe. O moleculǎ este formatǎ din cel puţin doi atomi şi este neutrǎ din punct de vedere electric. o modalitate prin care poate testa o multitudine de variante. echipamentele şi utilajele rămase libere pentru a ajuta alte activităţi şi implicit pentru a micşora durata de efectuare a întregului proiect (în cazul în care se poate realiza acest lucru). iar arcele semnificǎ legǎturile dintre atomi. soft-urile specializate ce oferă metode complexe de analiză. A. Dupǎ cum era de aşteptat. De asemenea.4. iar reprezentarea lor permite analistului de proiect o viziune de ansamblu şi totodată. nodurile rep rezintǎ atomii.314 Anexa 2 . este prezentat un exemplu de graf molecular neorientat pentru o hidrocarburǎ (lipC4): 6 5 7 2 1 3 4 Figura A. Formula chimicǎ şi structura unei molecule reprezintǎ cei mai importanţi factori care-i determinǎ proprietǎţile.3. considerǎm cǎ o moleculǎ reprezintǎ cea mai micǎ particulǎ a unei substanţe chimice ce reţine toate proprietǎţile sale chimice şi de compoziţie. grafurile ce descriu topologia molecular grafuri moleculare. Presupunându-se cunoscute elementele teoretice de baz ǎ.

. lucru obişnuit în structurile moleculare. pentru o formulǎ chimicǎ complexǎ. extrem de utile în analiza moleculară. Existǎ mai mult de 400 de astfel de indici topologici şi sunt folosiţi în determinarea similaritǎţ ilor structurale între molecule. cu precizarea că în programele specializate se evită ciclarea algoritmului prin utilizarea unei condiţii de stop. Prezentǎm doar douǎ dintre ele: . .indicele Weiner – introdus în anul 1947 de chimistul Harry Weiner pentru a studia structura molecularǎ: n n ∑∑ (d ) 1 W (G ) = ⋅ ij . Exemplul prezentat anterior este foarte simplu. iar simularea experimentelor cu aj utorul unui PC diminueazǎ considerabil timpul necesar de lucru. 2 i =1 j =1 unde (d ) ij reprezintǎ un element al matricei drumurilor minime. mai multe caracteristici topologice importante pot fi obţinute direct. simulând totul direct pe calculator… Teoria grafurilor este folositǎ cu succes în chimie şi genetic ǎ. 2 i =1 j =1 unde (∆) ij este un element al matricei Detour.indicele de drum (Detour) – se obţine din matricea drumurilor maxime: n n ∑∑ (∆) 1 ω= ⋅ ij . dar imaginaţi-vǎ cât de uşor poate fi pentru un chimistǎ sanalizeze aceste date. Drumurile maxime sunt totuşi determinate.Manual de informatică pentru clasa a XI-a 315 0 1 0 1 1 0 0 0 1 2 1 1 2 2 0 3 2 3 1 2 2       1 0 1 0 0 0 0 1 0 1 2 2 3 3 3 0 3 2 4 5 5 0 1 0 1 0 0 0 2 1 0 1 3 4 4 2 3 0 3 3 4 4       1 0 1 0 0 0 0 1 2 1 0 2 3 3 3 2 3 0 4 5 5 1  0 0 0 0 1 1  1  2 3 2 0 1 1  1  4 3 4 0 1 1  0 0 0 0 1 0 0 2 3 4 3 1 0 2 2 5 4 5 1 0 2       0 0 0 0 1 0 0 2 3 4 3 1 2 0 2 5 4 5 1 2 0 matricea de adiacenţǎ matricea drumurilor matricea drumurilor minime (distanţa) maxime (Detour) Observăm faptul că acest graf asociat conţine cicluri. Dupǎ ce au fost determinate aceste matrice.

068 D 090 Z 112 p 003 ♥ (etx) 025 ↓ (em) 047 / 069 E 091 [ 113 q 004 ♦ (eot) 026 → (eof) 048 0 070 F 092 \ 114 r 005 ♣ (enq) 027 ← (esc) 049 1 071 G 093 ] 115 s 006 ♠ (ack) 028 ⌐ (fs) 050 2 072 H 094 ^ 116 t 007 • (bel) 029 ↔ (gs) 051 3 073 I 095 _ 117 u 008 _ (bs) 030 ▲ (rs) 052 4 074 J 096 ` 118 v 009 □ (tab) 031 ▼ (us) 053 5 075 K 097 a 119 w 010 ◙ (lf) 032 (spaţiu) 054 6 076 L 098 b 120 x 011 ♂ (vt) 033 ! 055 7 077 M 099 c 121 y 012 ♀ (np) 034 " 056 8 078 N 100 d 122 z 013 ♪ (cr) 035 # 057 9 079 O 101 e 123 { 014 ♫ (so) 036 $ 058 : 080 P 102 f 124 | 015 ☼ (si) 037 % 059 .316 Anexa 3 Tabela codurilor ASCII Cod Caracter Cod Caracter Cod Caracter Cod Caracter Cod Caracter Cod Caracter 000 (nul) 022 ▀ (syn) 044 . 081 Q 103 g 125 } 016 ► (dle) 038 & 060 < 082 R 104 h 126 ~ 017 ◄ (dc1) 039 ' 061 = 083 S 105 i 127 018 ↨ (dc2) 040 ( 062 > 084 T 106 j 019 ‼ (dc3) 041 ) 063 ? 085 U 107 k 020 ¶ (dc4) 042 * 064 @ 086 V 108 l 021 § (nak) 043 + 065 A 087 W 109 m Codul ASCII extins Cod Caracter Cod Caracter Cod Caracter Cod Caracter Cod Caracter Cod Caracter 128 Ç 149 ò 170 ¬ 191 ┐ 212 ╘ 233 Θ 129 ü 150 û 171 ½ 197 └ 213 ╒ 234 Ω 130 é 151 ù 172 ¼ 193 ┴ 214 ╓ 235 δ 131 â 152 _ 173 ¡ 194 ┬ 215 ╫ 236 ∞ 132 ä 153 Ö 174 « 195 ├ 216 ╪ 237 ∅ 133 à 154 Ü 175 » 196 ─ 217 ┘ 238 ∈ 134 å 155 ¢ 176 ░ 197 ┼ 218 ┌ 239 ∩ 135 ç 156 £ 177 ▒ 198 ╞ 219 █ 240 ≡ 136 ê 157 ¥ 178 ▓ 199 ╟ 220 ▄ 241 ± 137 ë 158 _ 179 │ 200 ╚ 221 ▌ 242 ≥ 138 è 159  180 ┤ 201 ╔ 222 ▐ 243 ≤ 139 ï 160 á 181 ╡ 202 ╩ 223 ▀ 244 ⌠ 140 î 161 í 182 ╢ 203 ╦ 224 α 245 ⌡ 141 ì 162 ó 183 ╖ 204 ╠ 225 ß 246 ÷ 142 Ä 163 ú 184 ╕ 205 ═ 226 Γ 247 ≈ 143 Å 164 ñ 185 ╣ 206 ╬ 227 π 248 ° 144 É 165 Ñ 186 ║ 207 ╧ 228 Σ 249 • 145 æ 166 ª 187 ╗ 208 ╨ 229 σ 250 − 146 Æ 167 º 188 ╝ 209 ╤ 230 µ 251 √ 147 ô 168 ¿ 189 ╜ 210 ╥ 231 τ 252 ⁿ 148 ö 169 _ 190 ╛ 211 ╙ 232 φ 253 ² 254  255 . 066 B 088 X 110 n 001 ☺ (soh) 023 ¥ (etb) 045 . 067 C 089 Y 111 o 002 ☻ (stx) 024 ↑ (can) 046 .