Sunteți pe pagina 1din 316

Vlad Huanu Tudor Sorin

INFORMATIC

(filiera teoretic, profilul real, specializarea


matematic-informatic) i
(filiera vocaional, profil militar MApN, specializarea
matematic-informatic)

Ciclul superior al liceului,


clasa a XI-a

Editura L&S Soft


Bucureti
Copyright 2006-2016 L&S SOFT
Toate drepturile asupra acestei lucrri aparin editurii L&S SOFT.
Reproducerea integral sau parial a textului din aceast carte este posibil
doar cu acordul n scris al editurii L&S SOFT.

Manualul a fost aprobat prin Ordinul ministrului Educaiei i Cercetrii


nr. 4446 din 19.06.2006 n urma evalurii calitative organizate de ctre
Consiliul Naional pentru Evaluarea i Difuzarea Manualelor i este
realizat n conformitate cu programa analitic aprobat prin Ordin al
ministrului Educaiei i Cercetrii nr. 3252 din 13.02.2006.

Refereni tiinifici:

Prof. Dr. Victor Mitrana, Facultatea de Matematic, Universitatea Bucureti


Prof. grad I Valiana Petrior, Colegiul Naional Bilingv George Cobuc

Tiparul executat la S.C. LUMINATIPO s.r.l.


Str. Luigi Galvani nr. 20 bis, sector 2, Bucureti

Descrierea CIP a Bibliotecii Naionale a Romniei


HUANU, VLAD
Informatic : manual pentru ciclul superior al liceului : clasa a XI-
a - (filiera teoretic, profilul real, specializarea matematic-informatic)
i (filiera vocaional, profil militar MApN, specializarea matematic-
informatic) / Vlad Huanu, Tudor Sorin. - Bucureti : 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. Stnjeneilor nr. 6, Sector 4, Bucureti;
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. Noiunea de tablou .. 7
1.2. Cum citim i cum afim un tablou bidimensional?... 8
1.3. Aplicaii cu tablouri bidimensionale..10
Probleme propuse.. 16
Rspunsurile la testele gril..19

Capitolul 2. Subprograme 20
2.1. Noiunea de subprogram . 20
2.2. Subprograme n Pascal................................................................................... 22
2.2.1. Un exemplu de utilizare a funciilor..................................................... 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 funcie.............................. 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 funciilor.................................................................... 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 trimii?.......... 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. Greeli frecvente................................................................................. 38
2.2.8. Uniti de program............................................................................... 39
2.3. Subprograme n C++....................................................................................... 42
2.3.1. Exemple de utilizare a funciilor.......................................................... 42
2.3.2. Structura unei funcii........................................................................... 44
2.3.3. Declararea variabilelor........................................................................ 46
2.3.4. Transmiterea parametrilor.................................................................. 49
2.3.5. Definirea i declararea unui subprogram............................................ 53
2.4. Aplicaii care folosesc subprograme................................................................ 55
Probleme propuse.. 62
Rspunsuri...... 72

Capitolul 3. iruri de caractere 73


3.1. Generaliti . 73
3.2. iruri de caractere n Pascal.74
3.2.1. Noiuni 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. Subiruri.............................................................................................. 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 fiiere text................. 88
3.3. iruri de caractere n C++. 89
3.3.1. Generaliti............................................................................... 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. Cutarea unui caracter ntr-un ir....................................................... 95
3.3.7. Compararea irurilor........................................................................... 97
3.3.8. Subiruri.............................................................................................. 99
3.3.9. Alte funcii 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 fiiere text............ 108
3.3.11.1. Operaia de citire............................................................. 108
3.3.11.2. Operaia 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. Noiuni introductive.. 112
4.2. Structuri neomogene n Pascal.. 112
4.2.1. Tipul Record.................................................................................. 112
4.2.2. Accesul simplificat la cmpuri........................................................... 114
4.2.3. nregistrri imbricate.......................................................................... 115
4.2.4. Vectori de nregistrri........................................................................ 115
4.2.5. nregistrare cu variante...................................................................... 116
4.3. Structuri neomogene n C++.. 118
4.3.1. Tipul struct..................................................................................... 118
4.3.2. nregistrri imbricate.......................................................................... 120
4.3.3. nregistrri 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 secvenial..................................................................... 127
5.2.3. Liste alocate nlnuit......................................................................... 128
5.2.4. Implementarea alocrii nlnuite prin utilizarea vectorilor.................129
5.3. Structura de tip stiv.... 133
5.4. Structura de tip coad..... 138
Probleme propuse 138
Rspunsuri.... 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 recursivitii. 143
6.4. Cum gndim un algoritm recursiv?....147
6.5. Aplicaii recursive..... 148
6.5.1. Aplicaii la care se transcrie o formul recursiv............................... 148
6.5.2. Aplicaii la care nu dispunem de o formul de recuren.................. 153
Probleme propuse 159
Indicaii / Rezolvri.. 166

Capitolul 7. Metoda Divide et Impera 172


7.1. Prezentare general 172
7.2. Aplicaii ..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 tieturilor.......................................................................... 180
7.3. Fractali .. 183
7.3.1. Elemente de grafic.......................................................................... 183
7.3.1.1. Generaliti (varianta Pascal)............................................ 183
7.3.1.2. Generaliti (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 ptrat........................................................ 191
7.3.4. Arborele.............................................................................................193
Probleme propuse 195
Rspunsuri... 196

Capitolul 8. Metoda Backtracking 199


8.1. Prezentarea metodei .. 199
8.1.1. Cnd 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 puine linii n programul surs. 207
8.3. Cazul n care se cere o singur soluie. Ex.: problema colorrii hrilor. 210
8.4. Aplicaii ale metodei backtracking n combinatoric... 212
8.4.1. O generalizare util........................................................................... 212
8.4.2. Produs cartezian............................................................................... 213
8.4.3. Generarea tuturor submulimilor unei mulimi................................... 215
8.4.4. Generarea combinrilor.................................................................... 217
6 Cuprins

8.4.5. Generarea aranjamentelor................................................................ 219


8.4.6. Generarea tuturor partiiilor mulimii {1,2, ..., n}................................ 221
8.5. Alte tipuri de probleme care se rezolv prin utilizarea metodei backtracking. 223
8.5.1. Generaliti....................................................................................... 223
8.5.2. Generarea partiiilor unui numr 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. Sritura calului.................................................................................. 233
Probleme propuse 235
Indicaii.. 238

Capitolul 9. Grafuri 239


9.1. Grafuri neorientate... 239
9.1.1. Introducere........................................................................................ 239
9.1.2. Definiia grafului neorientat............................................................... 240
9.1.3. Memorarea grafurilor......................................................................... 242
9.1.4. Graf complet...................................................................................... 247
9.1.5. Graf parial, subgraf.......................................................................... 248
9.1.6. Parcurgerea grafurilor neorientate.................................................... 250
9.1.6.1. Parcurgerea n lime (BF bredth first)........................... 250
9.1.6.2. Parcurgerea n adncime (DF depth first)...................... 253
9.1.6.3. Estimarea timpului necesar parcurgerii grafurilor.............. 255
9.1.7. Lanuri............................................................................................... 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. Noiunea de arbore......................................................... 264
9.1.11.2. Noiunea de arbore parial............................................... 266
9.2. Grafuri orientate... 267
9.2.1. Noiunea de graf orientat.................................................................. 267
9.2.2. Memorarea grafurilor orientate......................................................... 270
9.2.3. Graf parial, subgraf.......................................................................... 272
9.2.4. Parcurgerea grafurilor. Drumuri. Circuite.......................................... 273
9.2.5. Graf tare conex. Componente tare conexe....................................... 275
Probleme propuse 278
Rspunsuri... 286

Anexa 1. Memento 289

Anexa 2. Aplicaii practice ale grafurilor 309

Anexa 3. Codul ASCII 316


7

Capitolul 1

Tablouri

1.1. Noiunea de tablou

Anul trecut am studiat tablourile unidimensionale numite uneori, prin analogie


cu matematica, vectori. n acest an studiem tablourile bidimensionale numite, tot
prin analogie cu matematica, matrice.

Definiia 1.1. Un tablou este o structur omogen (format din elemente


de acelai fel) cu un numr bine determinat de componente. Tabloul se
identific printr-un singur nume, iar componentele sale se identific prin
intermediul unui sistem de indici.
Alturat avem reprezentat un tablou. Un a1,1 a1, 2 . . a1,n
element al acestuia, ai,j, se gsete pe linia i i
a2,1 a2 , 2 . . .
coloana j. Este esenial de reinut faptul c toate a a3, 2 . .a3,n
elementele tabloului au acelai tip. 3,1
A= . . . . .
.
. . . .
am 1,1 am 1, 2 . . am 1,n

am ,1 am , 2 . . am ,n

1. Un magazin ofer spre vnzare n produse. Se dorete s se rein


vnzrile lunare, valorice, din fiecare tip de produs. Putem organiza
datele sub form de tablou astfel:
vom numerota cele n produse cu 1, 2, ..., n;
lunile le vom numerota cu 1, 2, ..., 12;
elementul ai,j semnific valoarea ncasat din vnzarea produslui i n luna
j a anului - prin urmare, elementele tabloului sunt de tip real.

Intrebri posibile

a) Dac magazinul vinde 5 produse, cte elemente are tabloul?


b) Care sunt indicii de adresare n tablou pentru a afla vnzrile din produsul
5 n luna septembrie?
c) Cum se poate calcula suma ncasat n luna mai?
8 Capitolul 1. Tablouri

d) tiind c, din punct de vedere economic, anul este mprit n patru


trimestre i c fiecare trimestru are trei luni, cum se poate calcula suma
ncasrilor din trimestrul patru al anului?
e) Cum putem determina produsul din vnzarea cruia s-a ncasat anual
suma maxim?
f) Care este luna cu cele mai mari ncasri?

2. Tot aa, se poate memora, sub form de tablou, situaia la nvtur a celor m
elevi ai unei clase. Dac numerotm elevii cu 1, 2, ..., m i materiile pe care acetia
le studiaz cu 1, 2, ..., n, atunci ai,j reprezint media pe care o are elevul i la
materia j.

ntrebri posibile
a) Dac n clas sunt 30 de elevi i acetia studiaz 8 materii, cte 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 situaii n care
se utilizeaz tablouri bidimensionale (matrice).

1.2. Cum citim i cum afim un tablou bidimensional ?

n clasa a X-a am nvat s lucrm cu masive unidimensionale. Pentru a


adresa un element al unui vector se utilizeaz un singur indice. n cazul matricelor,
vom utiliza doi indici. Mai jos, putei observa cum se declar o matrice cu 10 linii i
9 coloane, cu elemente de tip ntreg:

Varianta Pascal Varianta C++


type tablou = array[1..10,1..9] of real; int a[10][9];
...
var a:tablou;
sau direct
var a:array [1..10,1..9] of real;

n Pascal, matricea are liniile 1,2,...,10 i coloanele 1,2, ,9 i, de exemplu,


elementul de pe linia a treia i coloana a patra se adreseaz prin a[3,4].
n C++, matricea are liniile 0,1,...,9 i coloanele 0,1,,8 i, de exemplu,
elementul de pe linia a treia i coloana a patra se adreseaz prin a[2][3].
Uneori, pentru simplitate, vom folosi liniile i coloanele matricei ncepnd de
la 1. n aceste condiii se pierde o linie i o coloan, fiecare de indice 0.
Considerm acest fapt neesenial.
Manual de informatic pentru clasa a XI-a 9

De multe ori nu tim cte linii i cte coloane va


trebui s aib tabloul. n acest caz, tabloul se declar
cu un numr maxim de linii i un numr maxim de
coloane, n aa fel nct acesta s corespund oricrui
set de date de intrare. Evident, ntr-un astfel de caz
exist o risip de memorie intern, dar... programul nu
se va termina cu eroare. n figura alturat, avei
reprezentat grafic un tablou cu 8 linii i 8 coloane, din
care, ntr-un anumit caz, utilizm numai primele 4 linii i
primele 5 coloane.

Figura 1.1. Exemplu de tablou

Programul care-l utilizeaz va funciona corect dac avem cel mult 8 linii i
cel mult 8 coloane.

n programul urmtor se citete i se afieaz un tablou. Iniial se citesc


numrul de linii i de coloane ale tabloului (m i n). Observai modul n
care am afiat tabloul - de aa natur nct i pe ecran s arate ca o
matrice, adic fiecare linie s fie scris pe un rnd.

Varianta Pascal Varianta C++


var m,n,i,j:integer; #include <iostream.h>
a:array[1..10,1..9] of integer; main()
begin { int m,n,i,j,a[10][9];
write ('m='); cout<<"m="; cin>>m;
readln(m); cout<<"n="; cin>>n;
write ('n='); for (i=0;i<m;i++)
readln(n); for(j=0;j<n;j++)
for i:=1 to m do { cout<<"a["<<i+1<<','
for j:=1 to n do <<j+1<<"]=";
begin cin>>a[i][j];
write ('A[',i,',',j,']='); }
readln(a[i,j]); for (i=0;i<m;i++)
end; { for (j=0;j<n;j++)
cout<<a[i][j]<<' ';
for i:=1 to m do
cout<<endl;
begin
}
for j:=1 to n do
}
write (a[i,j],' ');
writeln;
end
end.

n memorie, tablourile sunt reinute pe linii. Aceasta nseamn c la nceput


este memorat prima linie, apoi a doua, .a.m.d.
10 Capitolul 1. Tablouri

1.3. Aplicaii cu tablouri bidimensionale

Aplicaia 1.1. Interschimbare de linii. Se citete un tablou cu m linii i n


coloane. Se citesc, de asemenea, i dou numere naturale distincte x i y,
cuprinse ntre 1 i m. Se cere s se interschimbe linia x cu linia y. La nceput vom
afia tabloul iniial, apoi pe cel obinut prin interschimbarea liniilor x i y.
Exemplu: Considerm tabloul de mai jos, cu m=3, n=3 i liniile x=2, y=3:
1 2 3

4 5 6
7 8 9

n urma interschimbrii liniilor 2 i 3 se obine:
1 2 3

7 8 9
4 5 6

Rezolvare. Dup cum am nvat, pentru a interschimba coninutul a dou


variabile se utilizeaz o a treia, de manevr. De aceast dat, se interschimb dou
linii. Am fi tentai ca manevra s fie un vector cu n componente. Cu puin atenie ne
dm seama c putem folosi ca manevr o singur variabil de acelai tip cu cel al
componentelor de baz ale tabloului. n rest, analizai programul de mai jos:

Varianta Pascal Varianta C++


type matrice = array[1..10, #include <iostream.h>
1..10] of byte; main()
var mat: matrice; { int mat[10][10],m,n,i,
m, n, i, j, x, j,x,y,man;
y, man: integer; cout<<"m="; cin>>m;
begin cout<<"n="; cin>>n;
{-citirea datelor-} for(i=0;i<m;i++)
write ('m= '); readln(m); for(j=0;j<n;j++)
write ('n= '); readln(n); {
for i := 1 to m do cout<<"mat["<<i+1<<','
for j := 1 to n do <<j+1<<"]=";
begin cin>>mat[i][j];
write('mat[',i,',',j,']='); };
readln(mat[i,j]); cout<<"x=";
end; cin>>x;
write('x= '); readln(x); cout<<"y=";
write('y= '); readln(y); cin>>y;
{- tiparesc tabloul initial-} cout<<endl;
for i := 1 to m do for (i=0;i<m;i++)
begin {
for j := 1 to n do for (j=0;j<n;j++)
write (mat[i,j],' '); cout<<mat[i][j]<<' ';
writeln; cout<<endl;
end; }
Manual de informatic pentru clasa a XI-a 11

{-interschimbare linii-} for(j=0;j<n;j++)


for j:=1 to n do { man=mat[x-1][j];
begin mat[x-1][j]=mat[y-1][j];
man := mat[x,j]; mat[y-1][j]=man;
mat[x,j] := mat[y,j]; }
mat[y,j] := man; cout<<endl;
end; for (i=0;i<m;i++)
writeln; { for (j=0;j<n;j++)
cout<<mat[i][j]<<' ';
{-tiparesc tabloul inversat-} cout<<endl;
for i := 1 to m do }
begin }
for j := 1 to n do
write(mat[i,j],' ');
writeln;
end;
end.

Aplicaia 1.2. Spirala. Se citete un tablou cu n linii i n coloane. Se cere s se


afieze elementele tabloului n ordinea rezultat prin parcurgerea acestuia n
spiral, ncepnd cu primul element din linia 1, n sensul acelor de ceas.

Exemplu. Fie tabloul:

1 2 3

4 5 6
7 8 9

Elementele vor fi afiate n ordinea:
1 2 3 6 9 8 7 4 5.

Rezolvare. Pentru a putea rezolva problema, privim tabloul ca pe un ansamblu


alctuit din mai multe dreptunghiuri "concentrice":

* * * * *

* * * * *
* * * * *

* * * * *

* * * * *

Tabloul de mai sus este un ansamblu format din trei dreptunghiuri


"concentrice" - ultimul are un singur element i nu a putut fi reprezentat. Dup ce
stabilim numrul de ptrate (cum?), afim elementele aflate pe fiecare latur a
fiecrui ptrat n ordinea cerut, avnd grij ca elementele aflate n coluri s nu fie
afiate de dou ori.
12 Capitolul 1. Tablouri

Varianta Pascal Varianta C++


type matrice = array[1..10, #include <iostream.h>
1..10] of integer; main()
var mat: matrice; { int mat[10][10],n,i,j,k;
n, i, j, k: integer; cout<<"n="; cin>>n;
begin for(i=1;i<=n;i++)
{-citirea datelor-} for (j=1;j<=n;j++)
write ('n= '); readln(n); { cout<<"mat["<<i<<','
for i := 1 to n do <<j<<"]=";
for j := 1 to n do cin>>mat[i][j];
begin }
write('mat[',i,',',j,']='); for(k=1;k<=n/2+1;k++)
readln(mat[i,j]); { for(i=k;i<=n-k+1;i++)
end; cout<<mat[k][i]<<endl;
{-listare-} for(i=k+1;i<=n-k+1;i++)
for k := 1 to n div 2 + 1 do cout<<mat[i][n-k+1]<<endl;
begin for(i=n-k;i>=k;i--)
for i := k to n-k+1 do cout<<mat[n-k+1][i]<<endl;
writeln(mat[k,i]); for(i=n-k;i>=k+1;i--)
for i := k+1 to n-k+1 do cout<<mat[i][k]<<endl;
writeln(mat[i,n-k+1]); }
for i := n-k downto k do }
writeln(mat[n-k+1,i]);
for i := n-k downto k+1 do
writeln(mat[i,k])
end;
end.

Aplicaia 1.3. Pe o tabl cu n linii i m coloane (n, m numere naturale,


1n,m30), sunt plasate pe unele poziii jetoane cu litere, conform unui joc corect
de SCRABBLE. tiind c vocalele au cte un punct, iar consoanele cte dou,
stabilii valoarea total a cuvintelor de pe tabl. Valorile n i m i configuraia tablei
se citesc din fiierul tabla.txt, conform exemplului (locurile goale de pe tabl
sunt memorate sub forma unor caractere punct).

Exemplu. Pentru datele de intrare:


3 5
D.SAU
ALTI.
..A..
se afieaz 20
(DA=3, STA=5, ALTI=6, SAU=4, AI=2).

Rezolvare. Cutarea cuvintelor pe tabl i cumularea punctelor lor este, de


obicei, prima soluie la care ne gndim. Algoritmul corespunztor acestei soluii pare
ns destul de complicat. Simpla parcurgere a matricei de caractere i adunarea
valorilor corespunztoare literelor nu este o strategie bun, deoarece se pierde
Manual de informatic pentru clasa a XI-a 13

punctajul literelor care apar ntr-un cuvnt pe orizontal i unul pe vertical i care ar
trebui luate de dou ori n calcul. De aceea, vom analiza n plus, pentru fiecare liter,
dac ea face parte dintr-un singur cuvnt (are litere vecine doar pe orizontal sau
doar pe vertical) i se puncteaz obinuit, sau face parte din dou cuvinte (are litere
vecine i pe orizontal i pe vertical) i atunci se puncteaz dublu.

Varianta Pascal Varianta C++


#include <fstream.h>
var a:array[1..30,1..30]of char; char a[31][31];
n,m,i,j,dir:byte; int n,m,i,j,dir,p,pt;
p,pt:integer; ifstream f("tabla.txt");
f:text;
begin void main()
assign(f,'tabla.txt'); {
reset(f); f>>n>>m;
readln(f,n,m); for(i=1;i<=n;i++)
for i:=1 to n do for(j=1;j<=m;j++)
begin f>>a[i][j];
for j:=1 to m do pt=0;
read(f,a[i,j]); for(i=1;i<=n;i++)
readln(f) for(j=1;j<=m;j++)
end; if (a[i][j]!='.')
pt:=0; {
for i:=1 to n do if(a[i][j]=='A'||
for j:=1 to m do a[i][j]=='E'||
if a[i,j]<>'.' then a[i][j]=='I'||
begin a[i][j]=='O'||
if (a[i,j]='A') or a[i][j]=='U')
(a[i,j]='E') or p=1;
(a[i,j]='I') or else p=2;
(a[i,j]='O') or dir=0;
(a[i,j]='U') then p:=1 if (i>1 && a[i-1][j]!='.'
else p:=2; || i<n &&
dir:=0; a[i+1][j]!='.')
if (i>1) and (a[i-1,j]<>'.') dir++;
or if (j>1 && a[i][j-1]!='.'
(i<n) and (a[i+1,j]<>'.') || j<m &&
then dir:=dir+1; a[i][j+1]!='.')
if (j>1) and (a[i,j-1]<>'.') dir++;
or pt+=p*dir;
(j<m) and (a[i,j+1]<>'.') }
then dir:=dir+1; cout<<pt<<endl;
pt:=pt+p*dir }
end;
writeln(pt)
end.

Dac utilizm artificiul de a borda matricea cu caractere punct:


pentru i=0,n+1 execut
a0,i'.'; an+1,i'.'
ai,0'.'; ai,n+1'.'

14 Capitolul 1. Tablouri

atunci testarea vecinilor elementelor de pe margine se realizeaz ntocmai ca i n


cazul celor din interiorul matricei:

dac ai-1,j'.' sau ai+1,j'.'


dirdir+1 (cel puin un vecin pe direcia vertical)

dac ai,j-1'.' sau ai,j+1'.'


dirdir+1 (cel puin un vecin pe direcia orizontal)

Se mai poate analiza i cazul n care o liter nu are nici un vecin pe niciuna
dintre direciile orizontal sau vertical (caz n care litera nu s-ar puncta deloc), ns
problema precizeaz c literele sunt aezate corect conform jocului de scrabble,
deci nu pot fi amplasate litere izolate.

Aplicaia 1.4. Sortarea fr comparaii este o metod de sortare care permite


sortarea a n numere naturale, fr a face nici mcar o comparaie ntre ele.

Vom prezenta algoritmul pe un exemplu n care se sorteaz cresctor 6


numere naturale: 6, 36, 41, 25, 40 i 30.

Iniial, irul celor n numere se mparte n 10 clase: prima clas conine


numerele care se termin cu 0, a 2-a clas, cele care se termin cu 1, ... a 10
clas, cele care se termin cu 9.

Pentru memorarea numerelor care aparin fiecrei clase vom utiliza o matrice,
denumit Mat, cu 10 coloane, n care prima linie are indicele 0. Elementele din linia 0
rein numrul de elemente din ir care se gsesc 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

Obinem din nou irul de numere, aezndu-le n ordinea coloanelor i n


ordinea n care le-am pus n fiecare coloan: 40, 30, 41, 25, 6, 36. mprim din
nou irul n 10 clase, 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
Manual de informatic pentru clasa a XI-a 15

Obinem din nou irul de numere, aezndu-le n ordinea coloanelor i n


ordinea n care le-am pus n fiecare coloan: 6, 25, 30, 36, 40, 41. De
aceast dat, irul este sortat.

Observaii

Dac k este numrul maxim de cifre a numerelor din ir, operaia de


mprire a numerelor n 10 clase se va relua de k ori.

Algoritmul este "mare consumator" de memorie!

Varianta Pascal Varianta C++


var A:array [1..100] of #include <iostream.h>
integer; int A[100],Mat[100][100],n,
Mat:array[0..100,0..100] NrCif,i,j,k,Cif,Zece;
of integer;
n,NrCif,i,j,k,Cif,Zece: main()
integer; { cout<<"n="; cin>>n;
begin for (i=1;i<=n;i++)
write('n= '); readln(n); { cout<<"A["<<i<<"]=";
for i := 1 to n do cin>>A[i];
begin }
write('A[',i,']= '); Zece=1;
readln(A[i]) for(NrCif=1;NrCif<=4;NrCif++)
end; { if (NrCif>1) Zece*=10;
Zece:=1; for (i=1;i<=n;i++)
for NrCif:=1 to 4 do {Cif= (A[i] / Zece) % 10;
begin Mat[0][Cif]++;
if NrCif>1 then Mat[Mat[0][Cif]][Cif]=A[i];
Zece:=Zece*10; }
for i:=1 to n do //Extrag din mat. in vector
begin k=0;
Cif:=(A[i] div Zece) for (i=0;i<=9;i++)
mod 10; if (Mat[0][i])
Mat[0,Cif]:=Mat[0,Cif]+1; for (j=1;j<=Mat[0][i];j++)
Mat[Mat[0,Cif],Cif]:=A[i] { k++;
end; A[k]=Mat[j][i];
{Extrag din mat. in vector} }
k:=0; for (i=0;i<=9;i++)
for i:=0 to 9 do Mat[0][i]=0;
if Mat[0,i]<> 0 then }
for j:=1 to Mat[0,i] do for (i=1;i<=n;i++)
begin cout<<A[i]<<endl;
k:=k+1; }
A[k]:=Mat[j,i]
end;
for i:=0 to 9 do
Mat[0,i]:=0;
end;
for i:=1 to n do
writeln(A[i]);
end.
16 Capitolul 1. Tablouri

Probleme propuse
1. Se citete un tablou cu n linii i n coloane, numere ntregi. Un astfel de tablou, n
care numrul liniilor coincide cu numrul coloanelor, se numete tablou ptratic.
a) Pentru un tablou ptratic A, numim diagonal principal, elementele aflate pe
"linia" care unete A[1,1] cu A[n,n].

Exemplu. Pentru tabloul de mai jos

1 2 3
,
4 5 6

7 8 9
elementele sunt: 1, 5 i 9.

Se cere:

a1) suma elementelor aflate pe diagonala principal;


a2) suma elementelor aflate deasupra diagonalei principale;
a3) suma elementelor aflate sub diagonala principal.

b) Pentru un tablou ptratic A, numim diagonal secundar, elementele aflate pe


"linia" care unete A[n,1] cu A[1,n].

Exemplu. Pentru tabloul

1 2 3

4 5 6 ,

7 8 9
elementele sunt: 7, 5 i 3.

Se cere:
b1) suma elementelor aflate pe diagonala secundar;
b2) suma elementelor aflate deasupra diagonalei secundare;
b3) suma elementelor aflate sub diagonala secundar.

Cerine suplimentare:

pentru fiecare cerin se va face un program separat;


n nici un program nu se va folosi instruciunea if.

2. Interschimbai coloanele unei matrice cu m linii i n coloane astfel nct n linia k,


elementele s fie n ordine cresctoare.
Manual de informatic pentru clasa a XI-a 17

3. Un teren este dat sub forma unui tablou A cu n linii i m coloane. Elementul
A[i,j] reine altitudinea ptrelului de coordonate i i j. S se afieze
coordonatele "ptrelelor vrf" (un ptrel este vrf dac toi vecinii si au o
altitudine strict mai mic).

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

Indicaie. Atenie! Maximele sau minimele pe linii sau coloane pot s nu fie
unice. Dac vom considera numai o valoare dintre acestea, s-ar putea s pierdem
soluii.

5. Pentru o matrice a, cu n linii i m coloane citit din fiierul mat1.txt, se cere


s se afieze cel mai mare element din matrice i indicii acestuia. Scriei programul
pseudocod corespunztor.

6. Pentru o matrice de numere reale a, cu 5 linii i 4 coloane, citit de la tastatur,


se cere s se afieze suma elementelor strict pozitive din matrice.

7. Pentru o matrice a, cu n linii i n coloane citit din fiierul mat3.txt, se cere


s se decid pe care dintre cele dou diagonale, suma elementelor constitutive
este mai mare. Se va afia un mesaj corespunztor.

8. Pentru 5 cifre n,a,b,c,d citite de la tastatur, se cere s se creeze fiierul


mat5.txt care s conin o matrice cu 2n linii i 2n coloane, format din 4
submatrice disjuncte, fiecare cu n linii i n coloane, una dintre ele fiind format
numai cu componente egale cu a, una numai cu componente egale cu b, una cu c
i una cu d. Este necesar construirea n prealabil a matricei n memorie?

9. Fereastra. Fiind dat o matrice cu m linii i n coloane. Se cere s se afieze


toate submatricele cu 3 linii i 3 coloane ale matricei iniiale. Un astfel de procedeu
este utilizat atunci cnd, de exemplu, o imagine este mult prea mare i ea este
afiat cu ajutorul unei ferestre.

Exemplu: m=4, n=4. Matricea iniial i submatricele sunt prezentate mai jos:

10. Fiind dat o matrice cu m linii i n coloane, se cere s se afieze elementele n


ordinea sugerat n figura de mai jos:

Figura 1.2. Exemplu


18 Capitolul 1. Tablouri

11. Fiind dat o matrice cu m linii i n coloane se cere s se


afieze elementele n ordinea sugerat n imaginea alturat.

Figura 1.3. Exemplu

12. Fiind dat o matrice cu n linii i n coloane (ptratic) cu numere naturale i fiind
date dou elemente ale matricei de coordonate (x1,y1) i (x2,y2), care dintre
relaiile de mai jos testeaz dac elementele se gsesc 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";
else writeln('Nu'); else cout<<"Nu";
b) if x1-y1=x2-y2 b) if (x1-y1==x2-y2)
then writeln('Da') cout<<"Da"
else writeln('Nu'); else cout<<"Nu";
c) if abs(x1-y1)=abs(x2-y2) c) if (abs(x1-y1)==abs(x2-y2))
then writeln('Da') cout<<"Da";
else writeln('Nu'); else cout<<"Nu";
d) if abs(x1-x2)=abs(y1-y2) d) if (abs(x1-x2)==abs(y1-y2))
then writeln('Da') cout<<"Da";
else writeln('Nu'); else cout<<"Nu";

13. Se d un vector V cu m*n componente numere ntregi. Care dintre secvenele


de mai jos copiaz vectorul V n matricea Mat cu m linii i n coloane? Copierea se
realizeaz completnd mai nti elementele de pe linia 1, apoi elementele de pe
linia 2, ..., iar la sfrit, elementele de pe linia m.

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;k<=m*n;k++)
div n,1+k mod m]:=V[k]; Mat[1+(k-1)/n][1+(k-1)% m]=V[k];
b) for k:=1 to m*n do b) for (k=1;k<=m*n;k++)
Mat[1+(k-1) div m,1+(k-1) mod Mat[1+(k-1)/m][1+(k-1)% n]=V[k];
n]:=V[k]; c) for (k=1;k<=m*n;k++)
c) for k:=1 to m*n do Mat[1+(k-1)/n][1+(k-1)% n]=V[k];
Mat[1+(k-1) div n,1+(k-1) mod d) for (k=1;k<=m*n;k++)
n]:=V[k]; Mat[1+k/n][1+k%n]=V[k];
d) for k:=1 to m*n do Mat[1+k
div n,1+k mod n]:=V[k];
Manual de informatic pentru clasa a XI-a 19

14. Se d un vector V cu m*n componente numere ntregi i o matrice Mat cu m


linii i n coloane. Care dintre secvenele de mai jos copiaz matricea Mat n
vectorul V? Copierea se realizeaz astfel: mai nti se copiaz linia 1, apoi linia 2,
..., iar la sfrit, linia m.

Varianta Pascal Varianta C++


a) for i:=1 to n do a) for (i=1;i<=n;i++)
for j:=1 to m do for (j=1;j<=m;j++)
V[j+(i-1)*m]:=Mat[i,j]; V[j+(i-1)*m]=Mat[i][j];
b) for i:=1 to m do b) for (i=1;i<=m;i++)
for j:=1 to n do for (j=1;j<=n;j++)
V[j+(i-1)*n]:=Mat[i,j]; V[j+(i-1)*n]=Mat[i][j];
c) for i:=1 to m do c) for (i=1;i<=m;i++)
for j:=1 to n do for (j=1;j<=n;j++)
V[i+(j-1)*n]:=Mat[i,j]; V[i+(j-1)*n]=Mat[i][j];
d) for i:=1 to m do d) for (i=1;i<=m;i++)
for j:=1 to n do for (j=1;j<=n;j++)
V[j+(i-1)*n]:=Mat[j,i]; V[j+(i-1)*n]=Mat[i,j];

Rspunsurile la testele gril

12. d); 13. c); 14. b).


20

Capitolul 2

Subprograme

2.1. Noiunea de subprogram

Definiia 2.1. Prin subprogram vom nelege un ansamblu alctuit din


declarri i instruciuni scrise n vederea unei anumite prelucrri,
ansamblu implementat separat i identificat printr-un nume.

Pn n prezent am fost doar utilizatori de subprograme. Exemple de astfel


de subprograme folosite sunt:

matematice: sin, cos, abs, exp, trunc (Pascal) / floor (C++);


de manipulare a fiierelor: close (Pascal) / .close() (C++).

n acest capitol nvm s lucrm cu subprograme. Pentru a nelege


noiunea de subprogram, vom porni de la dou exemple.

1. Se consider funcia:

x +1
1 + x 2 , pentru x [-1,1];

f ( x ) = x + 1, pentru x (-,-1); .
6
1+ x , pentru x (1, ).

Se citesc dou valori reale a i b. S se scrie un program care afieaz care dintre
valorile f(a) i f(b) este cea mai mare.

Ce observm? C att pentru calculul valorii funciei n punctul a, ct i


pentru calculul valorii funciei n punctul b, aplicm acelai tip de raionament:
ncadrm valoarea respectiv (a sau b) ntr-unul dintre cele trei intervale i aplicm
formula de calcul corespunztoare.

Cum procedm? Prin utilizarea cunotinelor dobndite pn n prezent,


scriem secvena de program care calculeaz valoarea funciei calculat pentru x=a
i se mai scrie o dat (sau se copiaz i se adapteaz) secvena de program care
calculeaz valoarea funciei calculat pentru x=b.
Manual de informatic pentru clasa a XI-a 21

Oare nu se poate lucra i altfel? Rspunsul este afirmativ. Se scrie un


subprogram care calculeaz valoarea funciei ntr-un punct x oarecare i se
apeleaz subprogramul: o dat pentru x=a i nc o dat pentru x=b. Valorile
calculate la cele dou apeluri, se compar ntre ele i se afieaz rezultatul cerut.

2. Se citete n, un numr natural. Se citesc apoi n numere reale. Se


cere s se afieze cele n numere n ordinea cresctoare a valorilor lor.

Desigur, tim s rezolvm aceast problem n mai multe feluri, pentru c


tim s memorm un ir de valori (folosind un vector) i am studiat mai multe
metode prin care se poate obine ordonarea cresctoare a unui ir de valori
(folosind algoritmi de sortare). De aceast dat, vom implementa o metod
cunoscut, cea de sortare, dar vom utiliza subprogramele.

Vom scrie un subprogram care citete un vector, unul care tiprete un


vector i un al treilea care sorteaz vectorul dup una din metodele cunoscute.

n acest caz, programul ar arta astfel:

Pasul 1 - se apeleaz subprogramul care citete vectorul.


Pasul 2 - se apeleaz subprogramul care sorteaz vectorul.
Pasul 3 - se apeleaz subprogramul care tiprete vectorul.

Fa de scrierea clasic, aici problema a fost descompus n trei


probleme mai simple (citire, sortare i tiprire). n general, o problem complex
se rezolv mai uor dac o descompunem n alte subprobleme mai mici. Apoi,
ansele de a grei la scrierea unui subprogram sunt cu mult mai mici dect acelea
de a grei la scrierea unui program mare.

n plus, dac ntr-un alt program este necesar sortarea altui vector de
numere reale, metoda clasic ne permite s alegem din secvena de instruciuni ce
formeaz programul pe cele ce realizeaz sortarea, s le copiem n noul program
i s facem eventualele adaptri (numrul de componente i numele vectorului pot
fi altele). Aceste operaii sunt destul de greoaie i necesit mult atenie. Prin
implementarea modular, cu ajutorul subprogramelor, preluarea se realizeaz
mult mai uor.

Putem acum enumera unele dintre avantajele utilizrii subprogramelor:

reutilizarea codului - odat scris, un subprogram poate fi utilizat de ctre


mai multe programe;
elaborarea algoritmilor prin descompunerea problemei n altele mai
simple - n acest fel, rezolvm cu mult mai uor problema;
reducerea numrului de erori care pot aprea la scrierea programelor;
depistarea cu uurin a erorilor - verificm la nceput subprogramele,
apoi modul n care le-am asamblat (le-am apelat din cadrul programului);
realizarea unor programe uor de urmrit (lizibile).
22 Capitolul 2. Subprograme

2.2. Subprograme n Pascal

2.2.1. Un exemplu de utilizare a funciilor

Problema 2.1. Se citete n, numr natural. S se scrie programele care


afieaz valoarea calculat a expresiilor:

n
1 1 1 1 1 1
E1 = 1 + + + + ; E 2 = 1 + + + + .
2 3 n 2 3 n

Rezolvare. Funcia care calculeaz E1 este:


function subp(n:integer):real;
var s:real;
i:integer;
begin
s:=0;
for i:=1 to n do s:=s+1/i;
subp:=s;
end;

Analiza programului anterior

Antetul funciei este function subp(n:integer):real;.

Funcia se numete subp.

Ea este recunoscut de compilator prin faptul c antetul este precedat de


cuvntul cheie function.

Funcia are un parametru numit n. Rolul su este important i anume


precizeaz pentru ce valoare trebuie calculat expresia. Aa cum vom
vedea, exist posibilitatea s avem mai muli parametri, de diferite tipuri.
Funcia are un anumit tip, care precizeaz natura rezultatului. n exemplu,
tipul este real ntruct expresia calculat este de acest tip.

Funcia are variabile proprii - adic variabile care sunt definite n cadrul ei. n
exemplu, ele sunt s i i. Aceste variabile se numesc variabile locale.

Am vzut c funcia ntoarce un anumit rezultat - n exemplu, de tip real.


Observai mecanismul prin care am obinut aceasta. Calculez expresia n
mod obinuit. Rezultatul este reinut de variabila local s. Prin atribuirea
subp=s;, funcia a primit ca valoare de retur coninutul variabilei s.

n continuare, prezentm cele dou programe care utilizeaz funcia:


Manual de informatic pentru clasa a XI-a 23

var n:integer;
function subp(n:integer):real;
var s:real;
i:integer;
begin
s:=0;
for i:=1 to n do s:=s+1/i;
subp:=s;
end;
begin
write ('n='); readln(n);
write(subp(n):5:2);
end.
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

var n,i:integer;
rez,prod:real;
function subp(n:integer):real;
var s:real;
i:integer;
begin
s:=0;
for i:=1 to n do s:=s+1/i;
subp:=s;
end;
begin
write ('n='); readln(n);
rez:=subp(n);
prod:=1;
for i:=1 to n do prod:=prod*rez;
write(prod:5:2);
end.

Funcia este apelat ntotdeauna din cadrul unei expresii.


Apelul funciei n primul program s-a fcut astfel: write(subp(n):5:2);,
iar n al doilea, rez:=subp(n). n primul caz expresia este trecut ca parametru
pentru write, iar n al doilea, avem o expresie de atribuire.

n terminologia utilizat n teoria subprogramelor - n particular, n cazul


funciilor - se utilizeaz termenii parametri formali i parametri efectivi.

Definiia 2.2. Parametrii care se gsesc n antetul funciei se numesc


parametri formali.
Atunci cnd scriem o funcie, nu cunoatem valoarea propriu-zis a
parametrilor. Funcia trebuie s ntoarc rezultatul corect, oricare ar fi valoarea lor.
Din acest punct de vedere ei se numesc formali.

Definiia 2.3. Parametrii care se utilizeaz la apel se numesc parametri


efectivi.
La apel, lucrurile stau altfel: valorile acestora sunt cunoscute. Prin urmare
acetia se numesc parametri efectivi.
24 Capitolul 2. Subprograme

ntre parametrii formali si parametrii efectivi trebuie s existe o concordan,


care va fi studiat la momentul potrivit.

2.2.2 Un exemplu de utilizare a procedurilor

Problema 2.2. Se citete n, numr natural. Se citete un vector cu n


componente numere reale. Se cere s se tipreasc vectorul sortat.

Rezolvare. Programul este urmtorul:


type vector=array [1..9] of integer;
var n,i:integer;
v:vector;
procedure citesc;
begin
write('n='); readln(n);
for i:=1 to n do
begin
write('v[',i,']='); readln(v[i]);
end;
end;
procedure tiparesc;
begin
for i:=1 to n do writeln(v[i]);
end;
procedure sortez;
var gasit:boolean;
man:integer;
begin
repeat
gasit:=false;
for i:=1 to n-1 do
if v[i]>v[i+1]
then
begin
gasit:=true;
man:=v[i]; v[i]:=v[i+1]; v[i+1]:=man
end
until not gasit;
end;
begin
citesc;
sortez;
tiparesc;
end.

Programul conine trei proceduri: citesc, sortez i tiparesc. Forma


simplificat prin care este descris o procedur este:
procedure nume;
begin
........
end;
Manual de informatic pentru clasa a XI-a 25

Apelul unei proceduri se face prin utilizarea numelui ei, sub forma nume;.
Mai precis, apelul unei proceduri este instruciune, numit instruciunea
de apel.
La apel, controlul programului este transferat la prima instruciune a
procedurii - dup cum se ntmpl i la funcii. Dup executarea procedurii,
se revine n programul principal la prima instruciune care urmeaz celei de
apel - n cazul de fa se ntlnete o alt instruciune de apel.
Ca i funciile, procedurile se pot plasa n cadrul programului ntre
declaraiile de variabile i instruciunea compus.
Orice variabil a programului principal este i variabil a procedurii (invers nu
este adevrat).

n acest exemplu, procedurile sunt apelate fr utilizarea parametrilor. n


realitate, procedurile pot avea parametri ntocmai ca i funciile.

2.2.3. Structura unui subprogram

n esen, un subprogram este alctuit din:

Antet - conine mai multe informaii importante necesare compilatorului,


numele subprogramului, lista parametrilor formali. n cazul subprogramelor de
tip funcie, conine i tipul rezultatului (valoarea ntoars de funcie).

Bloc - cuprinde definiiile tipurilor de variabile, ale variabilelor, ale


subprogramelor i o instruciune compus. Structura sa este identic cu cea a
programului principal, indiferent dac este bloc de procedur sau de funcie.

Programul principal conine un antet (program nume) i un bloc.

Pentru prezentarea structurii subprogramelor de tip funcie vom utiliza


diagramele de sintax.

2.2.3.1. Structura subprogramelor de tip funcie

n figura urmtoare este prezentat structura antetului:

Figura 2.1. Structura antetului unui subprogram de tip funcie


26 Capitolul 2. Subprograme

Exemplu:
function suma(x,y:integer):real;
begin
suma:=x+y;
end

- antetul este function suma(x,y:integer):real;;

- identificatorul (numele) funciei este suma. Lista parametrilor formali este:


x,y:integer. Funcia este de tip real (identificatorul de tip);

- blocul este (n acest caz lipsesc variabilele locale):

begin
suma:=x+y;
end

Important: identificatorul de tip (tipul valorii ntoarse de funcie) poate fi: de


orice tip ordinal, de orice tip real, de orice tip pointer sau de tipul string
(acest tip va fi studiat n acest manual).

2.2.3.2. Structura subprogramelor de tip procedur

n figura urmtoare este prezentat antetul de procedur:

Figura 2.2. Structura antetului unui subprogram de tip procedur

Exemplu:
procedure suma(var s:real; x,y:real);
begin
s:=x+y;
end;

- antetul este: procedure suma(var s:real; x,y:real);

- identificatorul (numele procedurii) este suma;

- lista parametrilor formali este: var s:real; x,y:real;;

- blocul este:
begin
s:=x+y;
end;
Manual de informatic pentru clasa a XI-a 27

2.2.4. Definirea i declararea unui subprogram

Dei aparent asemntoare, cele dou noiuni difer. Buna nelegere a lor
ne ajut s evitm anumite erori. Mai mult, aceste noiuni sunt utilizate de orice
limbaj de programare, nu numai de Pascal.

Definiia 2.4. A defini un subprogram, nseamn a-l scrie efectiv,


dup structura anterior prezentat. O problem important este locul
unde se definete subprogramul.

n Pascal, subprogramele se definesc n trei locuri:

1. n blocul programului care-l utilizeaz - numit i program principal - ntre


declaraia variabilelor i instruciunea compus:
program exemplu1;
var x:integer;
procedure t;
begin
writeln(x)
end;
begin
x:=3;
t
end.

2. n cadrul unitilor de program - vor fi studiate ulterior.

3. n blocul unui alt subprogram. Aceast form este proprie limbajului


Pascal. n exemplu, procedura t, are ca subprogram procedura z. Iniial
programul va atribui variabilei x valoarea 3. Apoi va apela procedura t. Ea
tiprete coninutul lui x (3), apoi apeleaz procedura z. La rndul ei,
aceasta afieaz un mesaj: 'eu sunt z'.

program exemplu2;
var x:integer;
procedure t;
procedure z;
begin
writeln('eu sunt z');
end;
begin
writeln(x); z;
end;

begin
x:=3;
t
end.
28 Capitolul 2. Subprograme

Definiia 2.5. A declara un subprogram, nseamn a-l anuna. Un


subprogram nedeclarat nu poate fi folosit.

1. n cazul n care subprogramul este definit n blocul programului principal


sau n blocul subprogramului, se consider c a fost i declarat pentru
acesta, deci poate fi folosit. n acest caz, declaraia coincide cu definiia.

Exemple

n programul exemplu1, procedura t este definit n blocul programului


principal. Ea poate fi apelat din programul principal.

n programul exemplu2, procedura t este definit n blocul programului


principal, deci poate fi apelat de acesta. Procedura z este definit n blocul
procedurii t. Ea poate fi apelat din t. n schimb, nu poate fi apelat din
programul principal.

2. n blocul programului principal sau n blocul unui subprogram se definesc, n


ordine, subprogramele s1, s2, ..., sk. n acest caz, automat s1 este
declarat pentru s2, s3, ..., sk, apoi s2 este declarat pentru s3, ..., sk, iar sk-1
este declarat pentru sk.

Exemple

Procedurile s1 i s2 pot fi apelate din programul principal. Procedura s1


poate fi apelat din procedura s2, dar s2 nu poate fi apelat, n absena altei
clauze, din s1:

procedure s1;
begin
writeln('s1')
end;
procedure s2;
begin
s1;
writeln ('s2');
end;
begin
s1;
s2;
end.

Procedura test conine definiia a dou proceduri s1 i s2. Nici una din
aceste proceduri nu se consider declarat pentru programul principal.
Amndou sunt declarate pentru procedura test. Pentru procedura s2, s1
este declarat, dar pentru s1 procedura s2 nu este declarat.
Manual de informatic pentru clasa a XI-a 29

procedure test;
procedure s1;
begin
writeln('s1')
end;
procedure s2;
begin
s1;
writeln('s2');
end;
begin
s1;
s2;
end;

begin
test;
end.

3. n situaia prezentat n cazul 2, se poate totui ca orice subprogram definit


la nivelul unui bloc s fie declarat pentru toate subprogramele definite la
nivelul aceluiai bloc, ca n exemplul urmtor.

Declaraia se face prin antet, urmat de cuvntul cheie forward.

Observai c, n acest caz, definiia nu coincide cu declaraia.

procedure b; forward;

procedure a;
begin
writeln('Eu sunt a');
b;
end;

procedure b;
begin
writeln('Eu sunt b')
end;

begin
a;
end.

4. n cazul n care subprogramul a fost definit n cadrul unei uniti de program,


declaraia unitii, clauza uses, are efect de declaraie pentru toate
subprogramele definite n cadrul ei (unitile de program vor fi studiate n
cadrul acestui capitol).
30 Capitolul 2. Subprograme

2.2.5. Apelul subprogramelor

Definiia 2.6. A apela un subprogram, nseamn a-l lansa n


executare. Pentru a putea fi apelat din cadrul unui bloc, un subprogram
trebuie declarat la nivelul blocului respectiv.

2.2.5.1. Apelul funciilor

n programul de mai jos este apelat o funcie care calculeaz produsul a


dou numere ntregi. Programul tiprete suma ntre 1 i produsul calculat. n
exemplu, valoarea gsit este 7:

var p,x,y:integer;

function prod(x,y:integer):integer;
begin
prod:=x*y;
end;

begin
x:=2;
y:=3;
p:=1+prod(x,y);
writeln(p)
end.

Apelul unei funcii se realizeaz din interiorul unei expresii. n exemplu,


expresia este: 1+prod(x,y). Rezultatul ar fi fost obinut mai simplu dac
scriam writeln(1+prod(x,y));.

Observaii

n cadrul expresiei, apelul este un operand. El intr n calcul cu valoarea


returnat de funcie.
Dup apelul funciei, se continu evaluarea expresiei.

Mai jos este prezentat operandul care reprezint apelul funciei.

Figura 2.3. Structura operandului care reprezint apelul funciei


Manual de informatic pentru clasa a XI-a 31

2.2.5.2. Apelul procedurilor

Cele cteva exemple date pn acum fac inutil prezentarea unui alt
exemplu. Trebuie s tim c:

1. Apelul unei proceduri se face printr-o instruciune, numit instruciune


procedural. Sintaxa acestei instruciuni este prezentat mai jos:

Figura 2.4. Structura instruciunii procedurale

2. Dup apel, se va executa instruciunea care urmeaz instruciunii procedurale.

2.2.5.3. Transmiterea parametrilor la apel

n ce privete mecanismul de transmitere a parametrilor nu exist nici o


diferen ntre proceduri i funcii. Din acest motiv, transmiterea parametrilor se va
trata unitar.

Parametrii formali sunt cei trecui n antetul subprogramului.


Parametrii efectivi sunt cei trecui la apelul subprogramului.

Aa cum am vzut, subprogramele pot lucra cu variabilele globale ale


blocului care conine definiia lor. Atunci care este motivul pentru care sunt
necesari parametrii?

Cunotinele dobndite pn n acest moment nu permit o justificare


complet. Putem spune numai c utilizarea parametrilor permite ca subprogramul
s fie scris independent de programul principal. S lum un exemplu: ni se cere s
scriem un subprogram care calculeaz suma a dou valori reale. Ce adunm?
Pentru un program ar trebui s adunm x cu y, iar pentru altul, m cu n.

var x,y,sum:integer;
procedure suma(a,b:integer; var s:integer);
begin
s:=a+b;
end;
begin
x:=3; y:=4;
suma(x,y,sum); writeln(sum);
suma(2,3,sum); writeln(sum);
end.
32 Capitolul 2. Subprograme

n programul anterior, procedura suma calculeaz suma a dou numere


ntregi. Ea este apelat de dou ori din cadrul programului principal.

- parametrii formali sunt: a,b:integer; var s:integer;;


- pentru primul apel, parametrii efectivi sunt: x, y, sum;
- pentru al doilea apel, parametrii efectivi sunt: 2, 3, sum.

Iat cteva reguli care trebuie respectate la apel:

Numrul parametrilor formali trebuie s coincid cu numrul


parametrilor efectivi. n exemplu, acest numr este 3.

Tipul parametrilor formali trebuie s coincid cu tipul parametrilor


efectivi sau tipul parametrilor efectivi s poat fi convertit implicit ctre
tipul parametrilor formali, la fel ca n cazul atribuirii.

De exemplu, dac parametrii formali a i b sunt de tipul integer, nu este


permis ca parametrii efectivi s fie de alt tip (de exemplu, de tipul real).

Nu este obligatoriu ca numele parametrilor formali s coincid cu numele


parametrilor efectivi.

Nu este permis s definim tipul parametrilor n cadrul antetului subpro-


gramului. Tipul trebuie s fie predefinit sau s fie declarat n prealabil.

Exemple
type vector=array [1..9] of integer;
...
procedure test(n:integrer, v:vector); {corect}
procedure test(n:integrer, v:array[1..9] of integrer); {incorect}

Parametrii formali sunt de dou feluri: transmii prin valoare i transmii


prin referin. n cazul n care acetia sunt precedai de cuvntul cheie var, se
consider c sunt transmii prin referin, contrar, prin valoare.

Tratarea transmiterii prin valoare i prin referin se va face n paragrafele


urmtoare. Pentru moment, prezentm sintaxa parametrilor formali i a
parametrilor efectivi:

Figura 2.5.
Sintaxa parametrilor trimii
Manual de informatic pentru clasa a XI-a 33

2.2.5.4. Cum memoreaz subprogramele parametrii transmii ?

n acest paragraf vom analiza modul prin care sunt memorai parametrii
transmii n momentul lansrii n executare a subprogramului.

Pentru memorarea parametrilor, subprogramele folosesc o zon de memorie


numit stiv (mai exact, aceast zon se numete segment de stiv).
Memorarea parametrilor transmii se face n ordinea n care acetia
figureaz n antet: de la stnga la dreapta.
Pentru parametrii transmii prin valoare, se memoreaz valoarea transmis,
iar pentru cei transmii prin referin, se memoreaz adresa variabilei
transmis ca parametru.
n cadrul subprogramului, parametrii transmii i memorai n stiv sunt
variabile. Numele lor este cel din lista parametrilor formali.

Relum exemplul anterior:

antetul este: procedure suma(a,b:integer; var s:integer);;


la primul apel, parametrii sunt: x, y, sum, unde x reine 3 i y 4.

n stiv sunt memorate variabilele subprogramului. n figur, sunt prezentate


i valorile memorate:

stiva 3 4 adresa variabilei sum

a b s
La al doilea apel, parametrii sunt: 2, 3, sum.

stiva 2 3 adresa variabilei sum

a b s
Observaii

La revenirea n blocul apelant, coninutul variabilelor memorate n stiv se va


pierde.
Memorarea n stiv are i alte consecine, dar acestea vor fi tratate la
momentul potrivit (vezi recursivitatea!).

2.2.5.5. Transmiterea parametrilor prin valoare

Transmiterea prin valoare se utilizeaz atunci cnd suntem interesai ca


subprogramul s lucreze cu acea valoare, dar n prelucrare, nu ne intereseaz ca
parametrul efectiv (cel din blocul apelant) s rein valoarea modificat n subprogram.
34 Capitolul 2. Subprograme

Astfel, se pot transmite prin valoare:

valorile reinute de variabile - n acest caz, parametrii efectivi trebuie s fie


numele variabilelor.

Exemplu:
var n:integer;
procedure test(n:integer);
begin
n:=n+1;
writeln(n)
end;
begin
n:=1;
test(n);
writeln(n);
end.

- parametrul n este transmis prin valoare.

n programul principal, avem declarat variabila n, care este iniializat cu 1.

Apelm procedura. La apel, se rezerv spaiu n stiv, spaiu care are


numele parametrului (deci tot n) i este iniializat cu valoarea memorat de
variabila n a programului principal. n acest moment avem dou variabile n i
ambele rein valoarea 1.

n procedur, variabila n este incrementat (adic la vechiul coninut se


adaug 1). Evident, este vorba de variabila memorat n stiv.

Afim coninutul variabilei n (cea din stiv), deci se tiprete 2.

La ieirea din procedur, variabila n (din stiv) se pierde - adic nu mai are
spaiu alocat. Prin urmare, valoarea 2 este pierdut.

n programul principal se tiprete coninutul variabilei n, adic 1.

parametrii efectivi sunt valori sau expresii, care mai nti se evalueaz.

Exemplu:
procedure test(n:integer);
begin
writeln(n)
end;

begin
test(3);
test(3+4*5);
end.
Manual de informatic pentru clasa a XI-a 35

n procedur se creeaz o variabil n segmentul de stiv, numit n, care la


primul apel reine valoarea 3 i la al doilea, valoarea 23. La ieirea din procedur
coninutul variabilei se pierde.

Aa cum am vzut, n cazul transmiterii prin valoare, se pot folosi ca


parametri efectivi expresii. De asemenea, expresiile pot avea ca operanzi funcii.
Urmtorul program citete dou valori x i y i calculeaz expresia (x-y)*(x+y).

Exemplu:
program test_param;
var x,y:integer;
function suma (a,b:integer):integer;
begin
suma:=a+b;
end;
function dif (a,b:integer):integer;
begin
dif:=a-b;
end;
function prod(a,b:integer):integer;
begin
prod:=a*b;
end;
begin
write('x='); readln(x);
write('y='); readln(y);
writeln(prod(dif(x,y),suma(x,y)));
end.

2.2.5.6. Transmiterea parametrilor prin referin

Parametrii sunt transmii prin referin atunci cnd ne intereseaz ca, la


revenirea din subprogram, variabila transmis s rein valoarea stabilit n timpul
executrii subprogramului.

n cazul transmiterii prin referin, parametrii efectivi trebuie s fie variabile.


n cazul subprogramelor de tip procedur, transmiterea prin referin este
mecanismul clasic prin care acestea ntorc valori ctre blocul apelant.
Pentru ca un parametru s fie transmis prin referin este necesar ca
parametrul formal s fie precedat de cuvntul cheie var.

n cazul transmiterii prin referin, subprogramul reine n stiv adresa variabilei.

n acest caz, ne putem ntreba care este mecanismul prin care, dei pentru o
variabil transmis se reine adresa sa, n subprogram putem adresa
variabila normal (nu indirect)? La compilare, orice referin la variabila
respectiv, este "tradus" ca adresare indirect.
36 Capitolul 2. Subprograme

Exemplu. Programul urmtor utilizeaz o procedur care interschimb valorile


reinute de dou variabile. Acestea sunt transmise prin referin.
var x,y:integer;
procedure interschimb(var x,y:integer);
var man:integer;
begin
man:=x; x:=y; y:=man;
end;
begin
x:=2; y:=3;
interschimb(x,y);
write(x,' ',y);
end.

2.2.6. Variabile locale i globale

Definiia 2.7. Variabilele globale sunt variabilele programului principal.


Ele sunt rezervate ntr-o zon special de date, numit segment de date.
n versiunea 7.0 a limbajului Pascal, variabilele numerice sunt
iniializate cu 0.

Definiia 2.8. Variabilele locale sunt variabile declarate n interiorul


subprogramelor. Aceste variabile sunt memorate n segmentul de stiv,
dup variabilele generate de parametri. Este sarcina programatorului s
asigure iniializarea lor cu valorile dorite. La ieirea din subprogram
coninutul variabilelor locale se pierde.

Un termen des folosit n practica limbajelor de programare este acela de


vizibilitate. A preciza vizibilitatea unei variabile nseamn a spune care sunt
blocurile de unde se poate adresa (ca s-i atribuim o valoare, de exemplu).

Variabilele globale sunt vizibile la nivelul programului principal i la nivelul


subprogramelor.
var x:integer; Programul alturat tiprete de trei ori
procedure a; 2, coninutul variabilei globale x.
procedure b;
begin
writeln(x)
end;
begin
x:=2;
b;
writeln(x)
end;
begin
a;
x:=2;
writeln(x);
end.
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.
procedure a; Programul alturat tiprete de dou ori 2,
var x:integer; coninutul variabilei locale x. Dac am fi
procedure b;
ncercat s tiprim x din cadrul programului
begin
writeln(x) principal, programul ar fi dat eroare de
end; compilare.
begin
x:=2;
b;
writeln(x)
end;
begin
a;
end.

Atenie! Exist situaii n care o variabil global nu poate fi adresat din


cadrul subprogramului, adic atunci cnd acesta conine declaraia unei alte
variabile, cu acelai nume. n acest caz, prin nume, se adreseaz variabila
local. Regula este urmtoarea: n cazul n care dou variabile au acelai
nume, dar vizibiliti diferite, se adreseaz ntotdeauna variabila cu
vizibilitatea mai redus. Evident, n cazul existenei a dou variabile cu
aceeai vizibilitate i acelai nume, compilatorul va da eroare de sintax.

Programul de mai jos tiprete 2, nu 3:


var x:integer;
procedure a;
begin
x:=2;
writeln(x)
end;
begin
x:=3;
a;
end.

Alt termen des utilizat n practica programrii este durata de via a unei
variabile. El se refer la perioada de timp n care variabila este alocat n memorie.
Astfel, avem:

durat static - variabila are alocat spaiu pe tot timpul executrii programului.
durata local - variabila are alocat spaiu doar n timpul ct se execut un
anumit bloc.
durat dinamic - alocarea spaiului de memorie se face n timpul executrii prin
subprograme speciale sau operatori (n Pascal avem subprograme speciale).
Variabilele cu aceast durat de via se numesc dinamice i nu se trateaz n
acest manual.
38 Capitolul 2. Subprograme

Din acest punct de vedere putem spune c variabilele globale au durat de


via static, iar variabilele locale au durata de via local.

Un ultim termen este dat de clasa de memorare a unei variabile. Se refer


la locul unde variabila este memorat. Astfel, o variabil poate fi memorat n
segmentul de date, n segmentul de stiv sau n heap.

Astfel, pe lng tip, o variabil se caracterizeaz prin: clas de memorare,


vizibilitate i durat de via.

2.2.7. Greeli frecvente

1. Greeli care apar la transmiterea tipurilor nestandard

Reamintim faptul c un tip este standard dac este cunoscut de limbaj (de
exemplu, integer). n caz contrar tipul este nestandard (de exemplu, tipul care
descrie un vector cu componente de tip real). Iat o procedur scris greit:
procedure er (var v:array[1..99] of real);
...
begin
...
end;

Eroarea este dat de faptul c tipul nestandard al variabilei v este descris n


lista parametrilor. Programul va da eroare de sintax.
Pentru a proceda corect, tipul este descris separat, n cadrul programului
principal, prin type.
type vect=array[1..99] of real;
...
var v:vect;
...
procedure er (var v:vect);
...
begin
...
end;

2. Greeli care apar la atribuirea valorii de retur a unei funcii (valoarea pe


care funcia o ntoarce)

Iat o funcie, scris eronat, care ntoarce o valoare ntreag s (o sum).


function suma( n,i:integer):integer;
var s:integer;
begin
calculez s;
.......
suma(n,i):=s;
end;
Eroarea const n faptul c n atribuirea suma(n,i):=s; au fost trecui
parametri. Corect este suma:=s;.
Manual de informatic pentru clasa a XI-a 39

2.2.8. Uniti de program

n activitatea practic se fac aplicaii cu un numr mare de programe care de


multe ori sunt de mari dimensiuni. O preocupare constant a informaticienilor este
de a pune la dispoziia programatorilor instrumente eficiente de programare. n
acest sens, limbajul Pascal ofer posibilitatea lucrului cu uniti de program
(unit-uri).

Definiia 2.9. Prin unitate de program nelegem un ansamblu de date,


proceduri i funcii, plasate i compilate mpreun, care pot fi uor utilizate
n scrierea altor programe, fr a se cunoate amnunte necesare
realizrii lor.

Forma general a unei uniti de program este urmtoarea:

UNIT <nume unitate>;


INTERFACE
[USES <numele altor uniti de program pe care le utilizeaz>;]
[tipuri de date i variabile globale n programul care utilizeaz
unitatea]
antete de proceduri i funcii

IMPLEMENTATION
[tipuri de date i variabile locale (valabile numai pentru unitatea de
program)]
procedurile i funciile utilizate de unitatea de program
[BEGIN
....... o eventual secven de program care se execut
....... n momentul declarrii unitii de program n
....... programul care o utilizeaz.
END.]

Prima parte, obligatorie, este precedat de clauza INTERFACE. Aici se


gsesc definiiile tipurilor de date, variabilelor, i sunt enumerate procedurile
i funciile ce pot fi utilizate din programul principal. Pentru orice tip de
variabil declarat aici, se pot declara variabile de acest tip att n cadrul
unitii ct i n cadrul programului care folosete unitatea. Se observ c o
unitate de program are n mod obligatoriu dou pri, opional, o a treia.
Toate procedurile i funciile care aici sunt numai enumerate pot fi folosite de
orice program care folosete unitatea.

A doua parte, obligatorie, se gsete plasat sub clauza IMPLEMENTATION.


Aici se pot declara tipurile i variabilele vizibile numai pentru unitatea dat.
Tot aici se scriu clar toate procedurile i funciile care n parte au fost numai
declarate, precum i altele ce pot fi folosite numai n cadrul unitii.

Partea opional a fost prezentat n cadrul formei generale a unitii.


40 Capitolul 2. Subprograme

n continuare, vom da un exemplu pur didactic de unitate de program,


numit OPERATII. Aceasta conine patru proceduri: de adunare, scdere,
nmulire i mprire de variabile reale. Variabilele a i b sunt globale
pentru programul care utilizeaz aceast unitate, iar variabila c poate fi utilizat
numai n cadrul unitii. n secvena de iniializare se citesc valorile variabilelor a i
b.

Unitatea de program este prezentat n continuare:


unit operatii;

interface
var a,b:real;
procedure adun(a,b:real);
procedure scad(a,b:real);
procedure produs(a,b:real);
procedure impart(a,b:real);

implementation
var c:real;

procedure adun(a,b:real);
begin
c:=a+b;
writeln(c:3:2);
end;

procedure scad(a,b:real);
begin
c:=a-b;
writeln(c:3:2);
end;

procedure produs(a,b:real);
begin
c:=a*b;
writeln(c:3:2);
end

procedure impart(a,b:REAL);
begin
if b=0 then writeln('Impartirea nu se poate face ')
else
begin
c:=a/b;
writeln(c:3:2);
end;
end;

begin
write('a=');
readln(a);
writeln('b=');
readln(b);
end.
Manual de informatic pentru clasa a XI-a 41

Programul urmtor utilizeaz aceast unitate. Faptul c programul utilizeaz


o unitate se precizeaz prin clauza USES. Rularea sa decurge n felul urmtor:

se cer valorile variabilelor a i b (declarate n unitate);

se apeleaz procedurile de adunare, scdere, nmulire i mprire iar


rezultatele se tipresc;

se apeleaz aceleai proceduri utiliznd pentru apel dou variabile


globale ale programului (c i d).

uses operatii;
var c,d:real;
begin
adun(a,b);
scad(a,b);
produs(a,b);
impart(a,b);
write('c=');
readln(c);
write('d=');
readln(d);
adun(c,d);
scad(c,d);
produs(c,d);
impart(c,d);
readln;
end.

Dup ce am vzut modul de alctuire i construcie a unei uniti de


program, trebuie s precizm faptul c limbajul Pascal este livrat cu cteva uniti
de program standard (produse de firma BORLAND). Amintim cteva:

SYSTEM, CRT, DOS sau GRAPH.

Procedurile i funciile puse la dispoziie de limbaj se gsesc n aceste uniti.


Toate subprogramele folosite de noi pn n prezent se afl n unitatea de program
SYSTEM. Pentru utilizarea lor nu este necesar clauza USES.
42 Capitolul 2. Subprograme

2.3. Subprograme n C++


2.3.1. Exemple de utilizare a funciilor
Problema 2.3. Se citete n, numr natural. S se scrie programele care
tipresc valoarea calculat a expresiilor:
n
1 1 1 1 1 1
E1 = 1 + + + ... + ; E 2 = 1 + + + ... + .
2 3 n 2 3 n

Rezolvare. n continuare prezentm cele dou programe care utilizeaz funcia:


#include <iostream.h>
double subp(int n)
{ double s=0; int i;
for (i=1;i<=n;i++) s+=1./i;
return s;
}
main()
{ int n;
cout<<"n="; cin>>n;
cout<<subp(n);
}
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

#include <iostream.h>
double subp(int n)
{ double s=0;
int i;
for (i=1;i<=n;i++) s+=1./i; // sau s=s+1./i
return s;
}
main()
{ int n,i;
double rez, prod=1;
cout<<"n="; cin>>n;
rez=subp(n);
for (i=1;i<=n;i++) prod*=rez;
cout<<prod;
}

Funcia care calculeaz E1 este:


double subp(int n)
{ double s=0;int i;
for (i=1;i<=n;i++) s+=1./i; // sau s=s+1./i
return s; }

Antetul funciei este double subp(int n);.


Funcia se numete subp.
Funcia are un parametru numit n. Rolul su este important deoarece
precizeaz pentru ce valoare trebuie calculat expresia. Aa cum vom
vedea, exist posibilitatea s avem mai muli parametri, de diferite tipuri.
Manual de informatic pentru clasa a XI-a 43

Funcia are un anumit tip, care precizeaz natura rezultatului. n exemplu,


tipul este double ntruct expresia calculat este de acest tip.
Funcia are variabile proprii - adic variabile care sunt definite n cadrul ei. n
exemplu, s i i. Aceste variabile se numesc variabile locale.
Am vzut c funcia ntoarce un anumit rezultat - n exemplu, de tip double.
Observai mecanismul prin care am obinut aceasta. Calculez expresia n
mod obinuit. Rezultatul este reinut de variabila local s. Prin instruciunea
return s;, funcia a primit ca valoare de retur coninutul variabilei s.

n terminologia utilizat n teoria subprogramelor - n particular, n cazul


funciilor - se utilizeaz termenii parametri formali i parametri efectivi.

Definiia 2.10. Parametrii care se gsesc n antetul funciei se numesc


parametri formali.

Atunci cnd scriem o funcie nu cunoatem valoarea propriu-zis a


parametrilor. Funcia trebuie s ntoarc rezultatul corect, oricare ar fi valoarea lor.
Din acest punct de vedere ei se numesc formali.

Definiia 2.11. Parametrii care se utilizeaz la apel se numesc parametri


efectivi.

La apel, lucrurile stau altfel: valorile acestora sunt cunoscute. Prin urmare,
acetia se numesc parametri efectivi.

Pentru apelul rez=subp(n); , parametrul efectiv este n.

Problema 2.4. Se citete n, numr natural. Se citete un vector cu n


componente numere reale. Se cere s se tipreasc vectorul sortat.

#include <iostream.h>
void citesc(int vt[10],int n)
{ int i;
for(i=0;i<n;i++)
{ cout<<"v["<<i+1<<"]=";
cin>>vt[i];
}
}
void sortez(int vt[10],int n)
{ int gasit,i,man;
do
{ gasit=0;
for (i=0;i<n-1;i++)
if (vt[i]>vt[i+1])
{ man=vt[i];
vt[i]=vt[i+1];
vt[i+1]=man;
gasit=1; }
} while (gasit);
}
44 Capitolul 2. Subprograme

void scriu(int vt[10],int n)


{ int i;
for(i=0;i<n;i++)
cout<<vt[i]<<endl;
}
main()
{ int v[10],n;
cout<<"n=";
cin>>n;
citesc(v,n);
sortez(v,n);
scriu(v,n);
}

Programul conine trei funcii: citesc, sortez i tiparesc.

La apel, controlul programului este transferat la prima instruciune a funciei.


Dup executarea funciei, se revine n programul principal la prima
instruciune care urmeaz celei de apel - n cazul de fa se ntlnete o alt
instruciune de apel.

Cele trei funcii au tipul void, adic nu au valoare de retur. Ele returneaz
rezultatul prin intermediul parametrilor.

2.3.2. Structura unei funcii

n esen, o funcie este alctuit din:

Antet - acesta conine mai multe informaii importante necesare


compilatorului: numele funciei, lista parametrilor formali, tipul rezultatului.

Structura antetului este:


tip nume(lista parametrilor formali)

Lista parametrilor formali este de forma:


parametru1, parametru2, ..., parametrun

Exist i posibilitatea ca lista parametrilor formali s fie vid. Fiecare


parametru are forma:

tip nume.

O instruciune compus - aceasta cuprinde declaraiile variabilelor locale,


i instruciunile propriu-zise.

Poate fi tip al unei funcii orice tip de dat cu excepia masivelor.


Manual de informatic pentru clasa a XI-a 45

Exemple de antete:
- int suma(int a, int b) - funcia se numete suma, returneaz un
rezultat de tip int i are doi parametri formali de tip int, numii a i b.
- void t(int n, float v[20]) - funcia se numete t, este de tip void
(nu returneaz rezultat prin nume), are doi parametri formali, primul numit n, de
tip int, al doilea numit v, de tip float* (un tip care reine adrese de vectori
cu elemente de tipul float).

O funcie returneaz rezultatul la ntlnirea instruciunii return, care are


forma:
return expresie;

Trebuie ca tipul expresiei s coincid cu tipul funciei.

La ntlnirea instruciunii return, dup atribuirea valorii, execuia funciei se


ncheie i se revine la funcia care a apelat-o. n absena instruciunii return,
execuia funciei se ncheie dup execuia ultimei instruciuni. n acest caz nu se
ntoarce nici o valoare.

O funcie poate fi apelat de sine stttor (prin nume i lista parametrilor


efectivi), dar poate fi inclus i n cadrul expresiilor, caz n care, la evaluarea
expresiei este apelat. Aceast ultim form de apel nu este valabil n cazul
funciilor de tip void.

#include <iostream.h> n programul alturat este apelat o


funcie care calculeaz produsul a
int prod (int x, int y) dou numere ntregi.
{ return x*y; }
main() Programul tiprete suma ntre 1 i
{ int x=2,y=3; produsul calculat. n exemplu, 7.
cout<<1+prod(x,y);
}

Apelul funciei s-a realizat din interiorul expresiei: 1+prod(x,y).

Observaii

n cadrul expresiei, apelul este un operand. El intr n calcul cu valoarea


returnat de funcie.

Dup apelul funciei se continu evaluarea expresiei.

La apel, ordinea de evaluare a parametrilor nu este definit. De exemplu,


dac apelm funcia test astfel:

test(2-3,2+3),

nu tim dac nainte se efectueaz scderea sau adunarea.


46 Capitolul 2. Subprograme

2.3.3. Declararea variabilelor

Pn n prezent am declarat variabile doar n corpul funciilor - inclusiv n cel


al funciei main(). Variabilele declarate astfel se numesc locale.

Fiecrui program i se aloc trei zone distincte n memoria intern n care se


gsesc memorate variabilele programului:

Segment de date

Segment de stiv

Heap

Figura 2.5. Cele trei zone n memoria intern

De asemenea, exist posibilitatea ca variabilele s fie memorate ntr-un


anumit registru al microprocesorului. n acest caz timpul de acces la astfel de
variabile este foarte mic, deci se pot obine programe optimizate.

n general, o variabil se caracterizeaz prin 4 atribute. Acestea sunt:

clasa de memorare;
vizibilitate;
durata de via;
tipul variabilei, singurul pe care l-am studiat pn n prezent.

1. Clasa de memorare - precizeaz locul unde este memorat variabila


respectiv. O variabil poate fi memorat n segmentul de date, n cel de
stiv, n heap sau ntr-un registru al microprocesorului.

2. Vizibilitatea - precizeaz liniile textului surs din care variabila respectiv


poate fi accesat.

Astfel avem:

a. Vizibilitate la nivel de bloc (instruciune compus).

b. Vizibilitate la nivel de fiier - n cazul n care programul ocup un


singur fiier surs, singurul caz pe care l tratm acum.

c. Vizibilitate la nivel de clas - este n legtur cu programarea pe


obiecte, pe care o vei studia individual.
Manual de informatic pentru clasa a XI-a 47

3. Durata de via - reprezint timpul n care variabila respectiv are alocat


spaiu n memoria intern. Astfel avem:

a. Durat static - variabila are alocat spaiu n tot timpul execuiei


programului.
b. Durat local - variabila are alocat spaiu n timpul n care se execut
instruciunile blocului respectiv.
c. Durat dinamic - alocarea i dezalocarea spaiului necesar variabilei
respective se face de ctre programator prin operatori sau funcii
speciale.

n C++ variabilele pot fi mprite n trei mari categorii: globale, locale i


dinamice. Noi studiem numai primele dou categorii, variabilele globale i locale.

A) Variabile globale

Acestea se declar n afara corpului oricrei funcii, ca mai jos:


#include <iostream.h>
int a;
int t()
{ a=3;
cout<<a;
}
int b;
main()
{ b=4;
cout<<a<<endl;
t();
}

Variabilele a i b sunt globale. n astfel de cazuri, variabilele respective pot


fi utilizate de toate funciile care urmeaz n textul surs declaraiei variabilei
respective. Din acest motiv, astfel de variabile se numesc globale.

La declarare, variabilele globale sunt iniializate cu 0.

Atributele variabilelor globale sunt:

1. Clasa de memorare - segmentul de date.

2. Vizibilitatea - n cazul n care declaraiile acestora sunt naintea tuturor


funciilor, acestea sunt vizibile la nivelul ntregului program (fiier). Dac anumite
funcii se afl plasate naintea declaraiilor acestor variabile, atunci ele sunt vizibile
doar pentru funciile care sunt plasate dup aceste declaraii. n exemplul anterior,
variabila a poate fi accesat din corpul oricrei funcii, dar variabila b poate fi
accesat doar din funcia main().
48 Capitolul 2. Subprograme

3. Durata de via a variabilelor globale este static. Ele au spaiu rezervat n tot
timpul execuiei programului.

B) Variabile locale

Acestea sunt declarate n corpul funciilor. Mai precis, pot fi declarate n orice
bloc (instruciune compus) al acestora.

Variabilele declarate n corpul funciei main() sunt tot locale. n programul


urmtor, variabilele a i b sunt locale.

Variabila a este declarat n corpul funciei t(), iar variabila b este declarat
n corpul funciei main().

Exemplu:
void t()
{ int a=3;
}
main()
{ int b=4;
}

1. Clasa de memorare a variabilelor locale este implicit segmentul de stiv.


Exist posibilitatea ca acestea s fie alocate n registrele microprocesorului, caz n
care declaraia lor trebuie precedat de cuvntul cheie register.

Exemplu:
register int b=4;

Variabilele locale nu sunt iniializate implicit cu 0. n ipoteza n care acestea


nu sunt iniializate explicit de programator, rein o valoare oarecare, numit
valoare rezidual.

2. Vizibilitatea variabilelor locale este la nivelul blocului n care au fost declarate.

n funcia urmtoare am declarat dou variabile de tip int, numite b i c.


Variabila b este vizibil la nivelul funciei, dar variabila c este vizibil doar la nivelul
blocului n care a fost declarat.

Exemplu:
void t()
{ int b=4;
{ int c=3;
cout<<b<<" "<<c;
}
}
Manual de informatic pentru clasa a XI-a 49

n programul urmtor am declarat trei variabile, toate numite a. Una este


global, iar dou sunt locale, dar declarate n blocuri diferite:
#include <iostream.h>
int a;
void t()
{ int a=4;
{ int a=3;
cout<<a<<endl;
}
cout<<a<<endl;
}
main()
{ a=5;
t();
cout<<a;
}

n program, se afieaz coninutul tuturor acestor variabile (3,4,5).

Observaii

n cazul n care, ntr-un anumit bloc sunt vizibile (se pot accesa) mai multe
variabile, toate cu acelai nume, dar au domenii de vizibilitate diferite, se
acceseaz variabila cu vizibilitatea cea mai mic. De exemplu, dac n
programul anterior se tiprete variabila a din cadrul subblocului funciei, se
tiprete 3, pentru c acesta este coninutul variabilei cu cea mai mic
vizibilitate (cea declarat n subblocul respectiv).

Exist posibilitatea ca, un ciclu for s conin declaraia unei variabile locale.
n secvena urmtoare se calculeaz suma primelor 4 numere naturale.
Variabila i este declarat (i n consecin vizibil) doar n blocul for:

int n=4, s=0;


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

3. Durata de via a variabilelor locale este att timp ct dureaz execuia blocului
respectiv.

2.3.4. Transmiterea parametrilor

Aa cum am artat, parametrii care se gsesc n antetul funciei se numesc


parametri formali, iar cei care se gsesc n instruciunea de apel se numesc
parametri efectivi.

Privii programul urmtor. Acesta conine o funcie care calculeaz suma


a dou numere naturale.
50 Capitolul 2. Subprograme

#include <iostream.h> Parametrii formali sunt a i b.


int suma(int a , int b) Funcia este apelat de mai
{ return a+b; multe ori. Parametrii efectivi sunt,
} pe rnd:
main() 2, 3;
{ int c=4,d=3; 2+7, 3-1*2;
cout<<suma(2,3)<<endl;
cout<<suma(2+7,3-1*2)<<endl;
c, d;
cout<<suma(c,d)<<endl; 1.9, 3.3.
cout<<suma(1.9,3.3)<<endl;
}

Dup fiecare apel al funciei se tiprete suma obinut. ntre parametrii


formali i cei efectivi trebuie s existe o anumit concordan, care este descris
prin regulile urmtoare:

Numrul parametrilor formali trebuie s coincid cu numrul


parametrilor efectivi. La aceast regul exist o excepie care va fi
prezentat ntr-un paragraf separat. n exemplul dat, numrul
parametrilor formali este 2, iar cel al parametrilor efectivi este tot 2
(pentru un apel).

Tipul parametrilor formali trebuie s coincid cu tipul parametrilor


efectivi sau tipul parametrilor efectivi s poat fi convertit implicit
ctre tipul parametrilor formali, la fel ca n cazul atribuirii.

- suma(2,3) - parametrii formali sunt expresii constante de tip ntreg;

- suma(2+7,3-1*2) - parametrii formali sunt expresii constante de tip ntreg;


n acest caz, nainte de apel se evalueaz expresiile de mai sus;

- suma(c,d) - parametrii formali sunt expresii de tip ntreg (dat de tipul


variabilelor);

- suma(1.9, 3.3) - parametrii formali sunt expresii constante de tip real. n


acest caz, ele sunt convertite ctre int la fel ca n cazul atribuirii (se
trunchiaz zecimala). Prin urmare, suma calculat este 4=1+3.

Nu este obligatoriu ca numele parametrilor formali s coincid cu numele


parametrilor efectivi.

Vom analiza modul n care sunt memorai parametrii transmii n momentul


lansrii n execuie a funciei.

Pentru memorarea parametrilor subprogramele folosesc segmentul de


stiv, ntocmai ca pentru variabilele locale.

Memorarea parametrilor transmii se face n ordinea n care acetia


figureaz n antet: de la stnga la dreapta.
Manual de informatic pentru clasa a XI-a 51

n cadrul subprogramului, parametrii transmii i memorai n stiv sunt


variabile. Numele lor este cel din lista parametrilor formali.

Variabilele obinute n urma memorrii parametrilor transmii sunt variabile


locale.

Iat, de exemplu, cum sunt memorai n stiv parametrii n cazul primului apel:

stiva 2 3

a b

Observaii

La revenirea n blocul apelant, coninutul variabilelor memorate n stiv se


pierde - dup cum tim, durata de via a variabilelor locale este local.

Memorarea n stiv are i alte consecine, dar acestea vor fi tratate la


momentul potrivit.

Exist dou mecanisme de transmitere a parametrilor, transmiterea prin


valoare i transmiterea prin referin. Acestea vor fi tratate n continuare.

A. Transmiterea prin valoare se utilizeaz atunci cnd suntem interesai ca


subprogramul s lucreze cu acea valoare, dar n prelucrare, nu ne
intereseaz ca parametrul efectiv (cel din blocul apelant) s rein valoarea
modificat n subprogram.

Se pot transmite prin valoare:

1. Valorile reinute de variabile;


2. Expresii (acestea pot conine i funcii).

1. Valorile reinute de variabile. n acest caz, parametrii efectivi trebuie s fie


numele variabilelor care se trimit prin valoare.

Exemplu:
#include <iostream.h>
void test (int n)
{ n+=1;
cout<<n<<endl;
}
main()
{ int n=1;
test(n);
cout<<n<<endl;
}

Parametrul n este transmis prin valoare.


52 Capitolul 2. Subprograme

n main(), avem declarat variabila n, care este iniializat cu 1.

Apelm funcia. La apel, se rezerv spaiu n stiv, spaiu care are numele
parametrului (deci tot n) i este iniializat cu valoarea memorat de variabila
n a programului principal. n acest moment avem dou variabile n i ambele
rein valoarea 1.

n funcie, variabila n este incrementat (adic la vechiul coninut se adaug 1).


Evident, este vorba de variabila rezervat n cadrul ei.

Tiprim coninutul variabilei n (cea din stiv), deci se tiprete 2.

La ieirea din funcie, variabila n (din stiv) se pierde - adic nu mai are
spaiu alocat. Prin urmare, valoarea 2 este pierdut.

n main() se tiprete coninutul variabilei n, adic 1.

2. Parametrii efectivi sunt expresii care mai nti se evalueaz.

Exemplu:
#include <iostream.h>
void test (int n)
{ cout<<n<<endl;
}
main()
{ test(3);
test(3+4*5);
}

Transmiterea prin valoare a masivelor permite ca funciile s ntoarc noile


valori ale acestora (care au fost atribuite n funcii). De ce? Numele masivului
este un pointer ctre componentele lui. Prin valoare se transmite acest
nume. Cu ajutorul acestuia accesm componentele masivului.

n programul urmtor funcia vector iniializeaz vectorul transmis ca


parametru, iar n main() se afieaz rezultatul:

#include <iostream.h>
void vector (int x[10])
{ for (int i=0;i<10;i++) x[i]=i;
}
main()
{ int a[10];
vector(a);
for (int i=0;i<10;i++) cout<<a[i]<<" ";
}
Manual de informatic pentru clasa a XI-a 53

B. Transmiterea prin referin. Parametrii sunt transmii prin referin atunci


cnd ne intereseaz ca la revenirea din subprogram, variabila transmis s rein
valoarea stabilit n timpul execuiei subprogramului.

n cazul transmiterii prin referin, parametrii efectivi trebuie s fie referine la


variabile.

n cazul transmiterii prin referin, subprogramul reine n stiv adresa


variabilei.

Programul urmtor utilizeaz o funcie care interschimb valorile reinute


de dou variabile. Acestea sunt transmise prin referin.

#include <iostream.h>
void intersc(int &a,int &b)
{ int man=a;
a=b;
b=man;
}
main()
{ int x=2,y=3;
intersc(x,y);
cout<<x<<" "<<y;
}

2.3.5. Definirea i declararea unui subprogram

Dei aparent asemntoare, cele dou noiuni difer. Buna nelegere a lor
ne ajut s evitm anumite erori. Mai mult, aceste noiuni sunt utilizate de orice
limbaj de programare, nu numai n C++.

Definiia 2.12. A defini un subprogram, nseamn a-l scrie efectiv, dup


structura anterior prezentat. O problem important este locul unde se
definete subprogramul.

Definiia 2.13. A declara un subprogram, nseamn a-l anuna. Un


subprogram nedeclarat nu poate fi folosit.

Definiia unui subprogram ine loc i de declaraie!

Programul urmtor conine dou funcii: s1 i s2. Definiiile ambelor


funcii se gsesc naintea funciei main(). Din acest motiv ele pot fi
apelate din main(). Definiia funciei s1 este naintea definiiei lui s2,
deci funcia s1 poate fi apelat din s2. n schimb, din s1 nu poate fi
apelat funcia s2, pentru c definiia lui s2 este dup cea a lui s1.
54 Capitolul 2. Subprograme

#include <iostream.h>
void s1()
{ cout<<"Eu sunt s1"<<endl;
}
void s2()
{ s1(); cout<<"Eu sunt s2"<<endl;
}
main()
{ s1();
s2();
}

n situaia prezentat, totui se poate ca s1 s apeleze pe s2, chiar dac


sunt definite n aceeai ordine.

n astfel de cazuri se folosete prototipul funciei (antetul urmat de ;).


Prototipul are rolul de a declara o funcie. El nu conine definiia acesteia.

#include <iostream.h>
void s2();
void s1()
{ s2(); cout<<"Eu sunt s1"<<endl;
}
void s2()
{ cout<<"Eu sunt s2"<<endl;
}
main()
{ s1();
}

Programatorii n C++ obinuiesc s scrie mai nti prototipurile tuturor


funciilor utilizate de program - fr main() - iar dup funcia main() s le
defineasc. n acest fel, orice funcie - mai puin main() - poate fi apelat
din oricare alta:
#include <iostream.h>
void s1(); // prototip s1
void s2(); // prototip s2
main()
{ s1();
}
void s1()
{ s2(); cout<<"Eu sunt s1"<<endl;
}
void s2()
{ s1(); cout<<"Eu sunt s2"<<endl;
}
Manual de informatic pentru clasa a XI-a 55

2.4. Aplicaii care folosesc subprograme

Aplicaia 2.1. Revenim la exemplul de funcie dat n paragraful 2.1. Acesta va fi


prezentat n totalitate. Se consider funcia:

x +1
1 + x 2 , pentru x [-1,1];

f ( x ) = x + 1, pentru x (-,-1); .
6
1+ x , pentru x (1, ).

Se citesc dou valori reale a i b. S se scrie un program care afieaz care


dintre valorile f(a) i f(b) este cea mai mare.

Rezolvare. Programul este prezentat n continuare:


Varianta Pascal Varianta C++
var a,b,va,vb:real; # include <iostream.h>
function f(x:real):real; float f(float x)
var y:real; { float y;
begin y=x+1;
y:=x+1; if(x<-1) return y;
if x<-1 then f:=y else if(x>1) return 6/y;
else if x>1 then f:=6/y else return y/(1+x*x);
else f:=y/(1+x*x) }
end;
void main()
begin { float a,b,va,vb;
write('a='); readln(a); cout<<"a="; cin>>a;
write('b='); readln(b); cout<<"b="; cin>>b;
va:=f(a); va=f(a);
vb:=f(b); vb=f(b);
if va>vb if (va>vb) cout<<va<<endl;
then writeln(va) else cout<<vb<<endl;
else writeln(vb) }
end.

Aplicaia 2.2. Se citesc dou numere ntregi m i n. Se cere s se tipreasc


cel mai mare divizor comun i cel mai mic multiplu comun al lor.

Rezolvare. Cunoatei deja modul n care se calculeaz cel mai mare divizor
comun pentru dou numere naturale date. Cel mai mic multiplu comun a dou
numere se poate determina mprind produsul lor la cel mai mare divizor comun al
lor. Prin urmare, scriem o funcie cmmdc cu doi parametri formali m i n, care
ntoarce o valoare ntreag - cel mai mare divizor comun al lor.
56 Capitolul 2. Subprograme

Varianta Pascal Varianta C++


var m,n:integer; #include <iostream.h>
function cmmdc(m,n:integer) int cmmdc(int m, int n)
:integer; { while (m!=n)
begin if (m>n) m-=n;
while m<>n do else n-=m;
if m>n then m:=m-n return m;
else n:=n-m; }
cmmdc:=m;
end; void main()
{ int cm,m,n;
begin cout<<"m="; cin>>m;
write ('m='); readln(m); cout<<"n="; cin>>n;
write ('n='); readln(n); cm=cmmdc(m,n);
writeln(cmmdc(m,n)); cout<<cm<<" "<<m*n/cm;
writeln(m*n div cmmdc(m,n)); }
end.

Exerciii
1. Gsii dou motive pentru care parametrii funciei cmmdc trebuie s fie transmii
prin valoare i nu prin referin.
2. Scriei un subprogram cmmmc care s returneze cel mai mic multiplu comun a
dou numere naturale. Stabilii locul unde trebuie integrat acesta n programul dat
i modificai programul principal astfel nct s se utilizeze subprogramul cerut.
3. Cte expresii diferite care au ca rezultat cel mai mare divizor comun al
numerelor naturale nenule a, b i c exist? n orice expresie se utilizeaz numai
apeluri ale funciei cmmdc i variabilele a, b i c. De exemplu, trei expresii sunt:
cmmdc(cmmdc(a,b),c),
cmmdc(cmmdc(b,a),c) i
cmmdc(c,cmmdc(a,b)).

Aplicaia 2.3. Se citete un vector cu n componente numere ntregi. Se cere s


se tipreasc cel mai mare divizor comun al valorilor reinute de vector.

Rezolvare. Dispunem deja de o funcie care calculeaz cel mai mare divizor
comun a dou numere i am testat-o n programul anterior. Pentru varietate, de
data aceasta implementm o funcie cu algoritmul lui Euclid pentru calculul celui
mai mare divizor comun, algoritm prin mpriri, nu prin scderi repetate. Cu
ajutorul ei calculm cel mai mare divizor comun pentru valorile reinute de primele
dou componente i memorm aceast valoare n variabila c. Apoi, pentru fiecare
dintre componentele urmtoare, se calculeaz cel mai mare divizor comun ntre
valoarea curent memorat n c i cea reinut de componenta curent a
vectorului. Cel mai mare divizor comun va fi reinut din nou de c.
Manual de informatic pentru clasa a XI-a 57

Varianta Pascal Varianta C++


var v:array[1..20]of integer; #include <iostream.h>
n,i,c:integer; int v[21],n;
function cmmdc(m,n:integer) int cmmdc(int m, int n)
:integer; { int r;
var r:integer; do { r=m%n;
begin m=n;
repeat n=r;
r:=m mod n; } while (r!=0)
m:=n; n:=r return m;
until r=0; }
cmmdc:=m
end; void main()
{ int i,c;
begin cout<<"n="; cin>>n;
write ('n='); readln(n); cout<<"Componente vector:";
write('Componente vector:'); for(i=1;i<=n;i++)
for i:=1 to n do read(v[i]); cin>>v[i];
c:=cmmdc(v[1],v[2]); c=cmmdc(v[1],v[2]);
for i:= 3 to n do for(i=3;i<=n;i++)
c:=cmmdc(c,v[i]); c=cmmdc(c,v[i]);
writeln(c) cout<<c;
end. }

Exerciiu
n locul secvenei
c cmmdc(v[1],v[2])
pentru i=3,n execut
c cmmdc(c,v[i])
sfrit pentru

dorim s utilizm, mai compact, secvena:


pentru i=1,n execut
c cmmdc(c,v[i])
sfrit pentru

Care este valoarea pe care trebuie s o aib variabila c naintea acestei secvene?

Aplicaia 2.4. Triunghi special. Se citesc dou numere naturale m i n mai


mici dect 10. Se cere ca programul dvs. s afieze un triunghi, dup regulile pe
care le deducei din exemplele urmtoare:
2. m=7 n=9
1. 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
58 Capitolul 2. Subprograme

Rezolvare. Se observ c dac n=5 trebuie s obinem irul 5, 6, 7, 8,


9, 1, 2, 3,... Problema este de a putea numra ncepnd de la n, iar cnd
s-a depit valoarea 9, de a relua numrarea de la 1. Vom utiliza o funcie (succ)
care primete ca parametru de intrare un numr k i returneaz valoarea
urmtoare, n logica prezentat mai sus.
Alt aspect al problemei este realizarea tipririi acestor valori pe m linii, linia 1
avnd o singur valoare, linia a doua dou valori etc. Algoritmul este implementat
n procedura tipar, care are ca parametri formali pe m i n, transmii prin valoare.
Valorile lor sunt citite n programul principal.

Varianta Pascal Varianta C++


var m,n:integer; #include <iostream.h>
function suc(k:integer):integer; int suc(int k)
begin { if (k==9) return 1;
if k=9 then succ:=1 else return k+1;
else succ:=k+1 }
end;
void tipar(int m,int n)
procedure tipar(m,n:integer); { int i,j;
var i,j:integer; for(i=1;i<=m;i++)
begin { for (j=1;j<=i;j++)
for i:=1 to m do begin { cout<<n;
for j:=1 to i do begin n=suc(n);
write (n); }
n:=suc(n) cout<<endl;
end; }
writeln }
end
end; void main()
{ int m,n;
begin cout<<"m="; cin>>m;
write('m='); readln(m); cout<<"n="; cin>>n;
write('n='); readln(n); tipar(m,n);
tipar(m,n) }
end.

Exerciii
1. Dup apelul tipar(m,n) din programul principal, dac se adaug o instruc-
iune care afieaz valoarea lui n, ce valoare estimai c se va tipri?
2. Cum trebuie s arate definiia subprogramului suc, dac n subprogramul
tipar, n loc de nsuc(n), se utilizeaz instruciunea suc(n)?
3. Cum putem defini subprogramul suc astfel nct secvena de afiare din
subprogramul tipar s fie:
pentru i=1,m execut
pentru j=1,i execut
scrie suc(n)
sfrit pentru
scrie EOLN
sfrit pentru
Manual de informatic pentru clasa a XI-a 59

Aplicaia 2.5. Se citesc dou numere naturale m<n. Se cere s se tipreasc


toate numerele palindrom aflate ntre m i n. Un numr este palindrom dac, citit de
la stnga la dreapta i citit de la dreapta ctre stnga, rezultatul este acelai. De
exemplu, numrul 121 este palindrom.

Rezolvare. S analizm problema. Este necesar s fie testate toate numerele


ntre m i n, pentru ca apoi s fie tiprite numai cele care ndeplinesc condiia cerut.

Pentru fiecare numr, avem subproblema verificrii proprietii de numr


palindromic. Pentru aceasta, vom scrie o funcie numit palin care are rolul de a
testa dac un numr este sau nu palindrom. Analiza proprietii de numr
palindromic o efectum construind rsturnatul (inversul) acelui numr i verificm
dac cele dou valori (numrul i rsturnatul su) sunt egale.

Construirea inversului (nu n sens matematic) unui numr natural este i ea


o subproblem a problemei verificrii proprietii de palindrom. Vom scrie o funcie
invers, care va returna inversul unui numr natural transmis ca parametru.

Urmrind apelurile, vom constata c programul principal apeleaz funcia


palin, iar funcia palin apeleaz la rndul ei funcia invers. Programul apelant
al funciei palin este programul principal, n timp ce programul apelant al funciei
invers este subprogramul palin.

Care este avantajul scrierii fiecrei funcii? Acesta este dat de faptul c,
atunci cnd scriem funcia, ne concentrm exclusiv asupra ei, deci posibilitatea de
a grei este mai mic. Dac dispunem de funcie, algoritmul n continuare devine
mult mai simplu. n plus, este posibil ca oricare dintre funcii s poat fi folosit i n
alte cazuri, la rezolvarea altor probleme.

Observaii

Ordinea definirii celor dou subprograme devine important, deoarece funcia


invers, de care are nevoie funcia palin, trebuie declarat anterior acesteia.
Faptul c numrul al crui invers se calculeaz este transmis subprogramului
prin valoare este iari un avantaj. Cu toate c algoritmul ce construiete
inversul lui n distruge de fapt valoarea acestuia, acest lucru nu este resimit de
programul apelant.

Varianta Pascal Varianta C++


function invers(n:longint):longint; #include <iostream.h>
var r:longint;
begin long invers(long n)
r:=0; { long r=0;
while n<>0 do begin while(n!=0) {
r:=r*10+n mod 10; r=r*10+n%10;
n:=n div 10 n/=10;
end; }
invers:=r return r;
end; }
60 Capitolul 2. Subprograme

function palin(n:longint):boolean; int palin(long n)


begin { return invers(n)==n;
palin:=invers(n)=n; }
end;
void main()
var m,n,i:integer; { int m,n,i;
begin cout<<"m="; cin>>m;
write('m='); readln(m); cout<<"n="; cin>>n;
write('n='); readln(n); for(i=m;i<=n;i++)
for i:=m to n do if (palin(i))
if palin(i) then writeln(i); cout<<i<<endl;
end. }

Exerciii
1. Modificai antetul funciei invers, transformnd parametrul n n parametru
transmis prin referin i urmrii efectele modificrii efectuate.

2. Modificai antetul funciei invers, transformnd parametrul n n parametru


transmis prin referin i testai proprietatea de palindrom folosind compararea
n=invers(n) n loc de invers(n)=n. Explicai de ce aceast modificare nu
reprezint o corecie a erorii aprute la cerina 1?

3. Un numr natural este superpalindrom dac este palindrom att el ct i


ptratul su. Scriei, pe baza programului dat, un program care listeaz toate
numerele cu aceast proprietate aflate ntre doi ntregi a i b (a<b<32000).

Aplicaia 2.6. Suma cuburilor cifrelor. Se citete un numr natural


n9999999. Se calculeaz suma cuburilor cifrelor sale. Exemplu: dac se citete
25, se calculeaz 23+53=133. Cu numrul obinut procedm la fel: 13+33+33=55.
Repetm procedeul: 53+53=250. i iar: 23+53+03=133. Repetm procedeul pn
ce obinem un numr care este deja prezent n seria generat. Se cere s se
afieze irul de valori calculate prin acest procedeu.

Exemplu: pentru n=25, se afieaz seria

25 133 55 250 133.

Junior Division

Rezolvare. Suma cuburilor cifrelor unui numr natural este calculat de funcia
suma. Numrul n (citit) va fi memorat de prima component a vectorului seria.
Apoi, suma cuburilor sale, de componenta a doua, suma cuburilor numrului reinut
de a doua component va fi reinut de a treia component, .a.m.d. Dup ce este
memorat o valoare, ea este comparat, pe rnd, cu cele obinute anterior. Dac
este regsit, se tiprete numrul de termeni ai seriei i seria propriu-zis.
Funcia suma calculeaz suma cuburilor cifrelor unui numr. Funcia este verific
dac valoarea n exist deja n irul s format din ls componente.
Manual de informatic pentru clasa a XI-a 61

Varianta Pascal Varianta C++


type vec=array[1..100] of longint; #include <iostream.h>
var n:longint; seria:vec;
ind,i:integer; long n;
int seria[100],ind,i;
function suma(n:longint):longint;
var s,i,c:integer; int suma(int n)
begin { int s=0,c;
s:=0; while (n) {
while n<>0 do c=n%10;
begin s+=c*c*c;
c:=n mod 10; n/=10;
s:=s+c*c*c; }
n:=n div 10 return s;
end; }
suma:=s int este(long n,int s[100],
end; int ls)
function este(n:longint;var s:vec; { int i;
ls:integer):boolean; for(i=0;i<ls;i++)
var i:integer; if(s[i]==n) return 0;
begin return 0;
este:=true; }
for i:=1 to ls do void main()
if s[i]=n then este:=false { cout<<"n="; cin>>n;
end; ind=0;
begin seria[ind]=n;
write('n='); readln(n); do {
seria[1]:=n; n=suma(seria[ind-1]);
ind:=1; seria[++ind]=n;
repeat } while(! este(n,seria,
n:= suma(seria[ind-1]); ind-1));
ind:=ind+1; for (i=1;i<=ind;i++)
seria[ind]:=n; cout<<seria[i]<<endl;
until este(n,seria,ind-1); }
for i:=1 to ind do
writeln(seria[i]);
end.

Aplicaia 2.7. S se scrie o procedur care citete o matrice cu elemente


numere ntregi din fiierul text MAT.TXT. Pe prima linie a fiierului sunt dou valori:
m (numrul de linii ale matricei) i n (numrul de coloane ale matricei). Urmtoarele m
linii ale fiierului conin, n ordine, elementele aflate pe fiecare linie a matricei.
Verificai subprogramul definit, integrndu-l ntr-un program de testare.

Exemplu: fiierul f.in conine:

3 4
3 1 7 9
1 2 3 4
9 1 3 8
62 Capitolul 2. Subprograme

Rezolvare. Pentru a putea fi folosit pentru citirea oricrei matrice de numere


ntregi, procedura va trebui s utilizeze ca parametri de ieire matricea, numrul de
linii i numrul de coloane ale matricei.

n programul urmtor se testeaz procedura, prin listarea matricei:

Varianta Pascal Varianta C++


type mat=array[1..100,1..100] #include <fstream.h>
of integer;
var m:mat; void citesc(int mat[100][100],
linii,coloane,i,j:integer; int& m, int& n)
{ fstream f("fis.txt",ios::in);
procedure citesc(var t:mat; f>>m>>n;
var m,n:integer); for (int i=1;i<=m;i++)
var i,j:integer; for (int j=1;j<=n;j++)
f:text; f>>mat[i][j];
begin f.close();
assign(f,'mat.txt'); }
reset(f);
readln(f,m,n); void main()
for i:=1 to m do { int m,n, mat[100][100];
for j:=1 to n do citmat(m,n,mat);
read(f,t[i,j]); for (int i=1;i<=m;i++)
close(f) { for (int j=1;j<=n;j++)
end; cout<<mat[i][j]<<" ";
cout<<endl;
begin }
citesc(m,linii,coloane); }
for i:= 1 to linii do
begin
for j:=1 to coloane do
write(m[i,j],' ');
writeln
end
end.

Probleme propuse

1. Scriei un program care calculeaz i afieaz valoarea fiecreia dintre expresiile


de mai jos, pentru x citit de la tastatur:

a) G(x) = sin(x)+cos(x)cos(2x);

b) H(x) = 10{x} (am notat prin {x} partea fracionar a lui x).
Manual de informatic pentru clasa a XI-a 63

2. Coifurile NFL . Exist 28 de echipe de fotbal n NFL (Liga Naional


de Fotbal n SUA). Unele magazine au aparate care ofer coifuri n
miniatur ale echipelor contra 0.25$. Cnd pui moneda n aparat, nu
tii ce coif vei obine, oricare din cele 28 de coifuri diferite i poate veni
din aparat (sunt oferite aleator). Scriei un program care simuleaz punerea
monezii ntr-un astfel de aparat pn cnd se obin toate cele 28 de coifuri.
Programul trebuie s tipreasc i totalul cheltuielilor efectuate pentru obinerea
celor 28 de coifuri.
Junior Division

Indicaie. Utilizai subprogramele predefinite Random i Randomize.


3. Scriei o funcie care primete ca parametru lungimea laturii unui ptrat i
returneaz aria sa.
4. Scriei un subprogram care primete ca parametru lungimea laturii unui ptrat i
returneaz att lungimea diagonalei, ct i perimetrul ptratului.
5. Scriei o funcie care primete ca parametri de intrare lungimile celor dou catete
ale unui triunghi dreptunghic i returneaz lungimea ipotenuzei.
6. Scriei o funcie care primete 3 parametri de tip real, cu semnificaia de lungimi
pentru 3 segmente. Funcia va returna 1 dac cele trei segmente pot forma un
triunghi i 0, n caz contrar.

7. Scriei o funcie care returneaz prima cifr a unui numr natural. De exemplu,
dac parametrul efectiv este 127, funcia va returna 1.

8. "Numai ultima cifr". Se citesc n (numr natural, n<100) i n numere naturale


nenule x1 x2 x3 ... xn, numere mai mici ca 30000. Se cere:

ultima cifr a numrului x1 + x 2 +....+ x n ;


ultima cifr a numrului x1 2 3 n .
x x ..... x

Exemplu: n=3; x1=11; x2=4; x3=3. Programul tiprete:

8 pentru c ultima cifr a sumei 11+4+3 este 8;


1 pentru c ultima cifra a lui 1112 este 1.

Concursul Naional al Elevilor, Sinaia

9. S se citeasc dou matrice i s se efectueze suma lor. Programul se va


realiza astfel:

se scrie un subprogram de citire a unei matrice cu m linii i n coloane;


se scrie un subprogram de tiprire a unei matrice cu m linii i n coloane;
se scrie un subprogram care adun dou matrice;
programul principal rezult din apelul acestor subprograme.
64 Capitolul 2. Subprograme

10. S se calculeze coeficienii polinomului P(x)=(x+a)n, n dou variante.

a) Programul utilizeaz un subprogram care nmulete un polinom oarecare


de grad k (coeficienii se gsesc ntr-un vector, transmis prin referin)
cu polinomul x+a.

b) Programul utilizeaz un subprogram care calculeaz coeficienii, aa cum


rezult din formula (binomul lui Newton):
n
( x + a) n = C
k =0
k k nk
na x .

11. S se tipreasc toate numerele prime aflate ntre doi ntregi citii. Programul va
folosi o funcie care testeaz dac un numr este prim sau nu.

12. Scriei un program care tiprete numerele ntregi gsite ntre dou valori citite,
numere care se divid cu suma cifrelor lor. Programul va utiliza o funcie care
returneaz suma cifrelor unui numr ntreg primit ca parametru.

13. S se scrie o procedur care permut dou linii date ale unei matrice ptratice.

14. Scriei o procedur care interclaseaz doi vectori sortai tiind c acetia sunt
ordonai descresctor.

15. Se citesc dou polinoame de grade m i n (coeficienii fiecrui polinom se citesc


ntr-un vector). S se afieze coeficienii polinomului sum i cei ai polinomului
produs. n ambele cazuri se vor utiliza proceduri de citire, adunare, nmulire.

16. Se citesc 3 valori a, b, n (a i b reale, n ntreg). Se cere s se calculeze valorile


unei funcii F, definit pe intervalul [a,b] n n+1 puncte rezultate din mprirea
intervalului [a,b] n n pri egale i s se afieze cea mai mare dintre aceste
valori. Funcia F va fi dat sub forma unui subprogram de tip funcie.

17. Un numr raional este reinut prin utilizarea unui vector cu dou componente.
Scriei subprogramele care adun, scad, nmulesc i mpart dou numere
raionale. Subprogramele trebuie s returneze numrul simplificat.

18. Design triunghiular. Triunghiul de mai jos este generat de un algoritm.


Descoperii acest algoritm i folosii-l pentru a scrie un program care citete
numrul n de linii i genereaz triunghiul cu n linii corespunztor.
1
232
34543
4567654
567898765
67890109876
7890123210987

Junior Division
Manual de informatic pentru clasa a XI-a 65

19. Ce afieaz programul urmtor?

Varianta Pascal Varianta C++


var z:integer; #include <iostream.h>
procedure test(a:integer); void test(int a)
begin { a++;
a:=a+1; write(a); cout<<a;
end; }
begin void main()
z:=5; { int z=5;
test(z); test(z);
write(z) cout<<z;
end. }

a) 6 6; b) 5 6; c) 6 6; d) 6 5.

20. Ce afieaz programul urmtor?

Varianta Pascal Varianta C++


function f(x:integer):integer; #include <iostream.h>
begin int f(int x)
f:=x+1 { return x+1;
end; }
procedure t(a:integer); void t(int a)
begin { a++; cout<<a;
a:=a+1; writeln(a); }
end;
void main()
begin { t(f(f(f(1))));
t(f(f(f(1)))) }
end.

a) 3; b) 4; c) 5; d) eroare de sintax.

21. Ce afieaz programul urmtor:

Varianta Pascal Varianta C++


var a:integer; #include <iostream.h>
function f:integer; int a;
var a:integer; int f()
begin { int a=3;
a:=3; cout<<a;
write(a); return a;
f:=a; }
end;
void main()
begin { a=2;
a:=2; cout<<a;
writeln(a,f); cout<<f();
end. }

a) 233; b) 23; c) 32; d) 232.


66 Capitolul 2. Subprograme

22. tiind c valorile citite ale variabilelor a i b sunt numere naturale nenule, care
dintre secvene utilizeaz subprogramul increment pentru a obine produsul celor
dou numere?

Varianta Pascal Varianta C++


var a,b,i,j,t:integer; #include <iostream.h>
procedure increment(var x:integer); int a,b,i,j,t;
begin void increment(int& x)
x:=x+1 { x++; }
end; void main()
begin { cin>>a;
readln(a); readln(b); cin>>b;
t:=a; t=a;
... ...
writeln(t) cout<<t;
end. }

a) for i:=1 to a do for(i=1;i<=a;i++)


for j:=1 to b do for(j=1;j<=b;j++)
increment(t); increment(t);
b) for i:=1 to a do for(i=1;i<=a;i++)
for j:=1 to b-1 do for(j=1;j<b;j++)
increment(t); increment(t);
c) for i:=1 to b do for(i=1;i<=b;i++)
for j:=1 to t do for(j=1;j<=t;j++)
increment(t); increment(t);
d) for i:=1 to a do for(i=1;i<=b;i++)
increment(t); increment(t);

23. Pentru varianta de limbaj preferat, stabilii care dintre urmtoarele antete de
subprogram sunt corecte:
a) procedure p(n:integer; var void p(int n, int& v[10])
v:array[1..10] of integer);
b) function p(n:integer;var p(int n, float& v)
v:real);
c) procedure p(n:integer;v:real); void p(int n, float v)
d) function p(n):integer; int p(int& n; float v)

24. Pentru funciile definite mai jos, care dintre afirmaii este fals?

Varianta Pascal Varianta C++


function f(x:real):real; float f(float x)
begin { if (x<3) return 3*x-1;
if x<3 then f:=3*x+1 else return x-1;
else f:=x-1 }
end;
function g(x:real):real; float g(float x)
begin { if (x<10) return 4*x;
if x<10 then g:=4*x else return x-1;
else g:=x-1 }
end;
Manual de informatic pentru clasa a XI-a 67

a) Expresia f(g(3)) produce valoarea 11;


b) Expresia g(f(3)) produce valoarea 8;
c) Expresia f(g(f(3))) produce valoarea 7;
d) Expresia (f+g)(3) produce valoarea 14.

25. Fiind dat funcia urmtoare, care dintre afirmaii sunt adevrate?

Varianta Pascal Varianta C++


function Cat(n1,n2:integer): long Cat(int n1,int n2)
longint; { int inv=0;
var inv:integer; while (n2!=0)
begin { inv=inv*10+n2%10;
inv:=0; n2=n2/10;
while n2<>0 do begin }
inv:=inv*10+ n2 mod 10; while (inv!=0)
n2:=n2 div 10 { n1=n1*10+inv%10;
end; inv=inv/10;
while inv<>0 do begin }
n1:=n1*10+inv mod 10; return n1;
inv:=inv div 10 }
end;
Cat:=n1
end;

a) Cat(12,10) returneaz 1210;


b) Cat(10,12) returneaz 1012;
c) Cat(0,12) returneaz 12;
d) Cat(12,0) returneaz 120.

26. Fiind dat funcia urmtoare, care dintre afirmaii este fals?

Varianta Pascal Varianta C++


function Cat(n1,n2:integer) long Cat(int n1,int n2)
:longint; { int n2s;
var n2s:integer; n2s=n2;
begin while (n2!=0)
n2s:=n2; { n2/=10;
while n2 <>0 do begin n1*=10;
n2:=n2 div 10; }
n1:=n1*10 return n1+n2s;
end; }
Cat:=n1+n2s
end;

a) Cat(-12,13) returneaz -1213;


b) Cat(12,0) returneaz 12;
c) Cat(0,12) returneaz 12;
d) Cat(13,12) returneaz 1312.
68 Capitolul 2. Subprograme

27. Se consider declarrile urmtoare:

Varianta Pascal Varianta C++


type vect=array[1..40] of byte; void Concat(int A[20], int
B[20], int m, int n, int C[40],
procedure Concat(A,B:vect; int& p)
m,n:byte;var C:vect;var p:byte);
void Reun(int A[20], int B[20],
procedure Reun(A,B:vect; int m, int n, int C[40], int&
m,n:byte;var C:vect;var p:byte); p)
void Inters(int A[20], int
procedure Inters(A,B:vect;
B[20],int m, int n, int C[40],
m,n:byte;var C:vect;var p:byte);
int& p)
procedure Dif(A,B:vect;m,n:byte; void Dif(int A[20], int B[20],
var C:vect;var p:byte); int m, int n, int C[40], int&
p)
procedure Elimin(var A:vect;
var m:byte); void Elimin (int A[20],int& m)

n care:

- Concat are rolul de a returna vectorul C cu p componente, vector obinut din


concatenarea componentelor vectorului A de m elemente cu ale vectorului B cu n
elemente. De exemplu, din A=(1,2,3,7), m=4, B=(2,5,3), n=3, se obine
C=(1,2,3,7,2,5,3), p=7.
- Reun are rolul de a returna vectorul C cu p componente, vector obinut din
reuniunea mulimilor de numere naturale din vectorii A cu m elemente i B cu n
elemente (presupunnd c A conine valori distincte i B conine valori distincte).
De exemplu, din A=(1,2,3,7), m=4, B=(2,5,3), n=3, se obine
C=(1,2,3,7,5), p=5.
- Inters are rolul de a returna vectorul C cu p componente, vector obinut din
intersecia mulimilor de numere naturale din vectorii A cu m elemente i B cu n
elemente (n aceleai condiii ca i n cazul reuniunii). De exemplu, din
A=(1,2,3,7), m=4, B=(2,5,3), n=3, se obine C=(2,3), p=2.
- Dif are rolul de a returna vectorul C cu p componente, vector obinut din
diferena mulimilor de numere naturale din vectorii A cu m elemente i B cu n
elemente (n aceleai condiii ca i n cazul reuniunii). De exemplu, din
A=(1,2,3,7), m=4, B=(2,5,3), n=3, se obine C=(1,7), p=2.
- Elimin are rolul de a returna vectorul care conine toate elementele distincte
ale vectorului iniial cu m componente, prin eliminarea valorilor multiple. De
exemplu, pentru A=(1,1,2,3,2), m=5, se returneaz A=(1,2,3), m=3.

A. Se citesc doi vectori X cu a numere naturale i Y cu b numere naturale.


Elementele din fiecare vector nu sunt neaprat distincte. Care dintre secvenele de
mai jos nu calculeaz reuniunea mulimilor numerelor naturale reinute de cei doi
vectori? Rezultatul se va gsi n vectorul Z.
Manual de informatic pentru clasa a XI-a 69

a) Concat(X,Y,a,b,Z,p);
Elimin(Z,p);
b) Elimin(X,a); Elimin(Y,b);
Concat(X,Y,a,b,Z,p);
c) Elimin(Concat(X,Y,a,b,Z,p),p);
d) Elimin(X,a); Elimin(Y,b);
Reun(X,Y,a,b,Z,p);

B. Se citesc doi vectori X cu a numere naturale i Y cu b numere naturale.


Elementele din fiecare vector sunt distincte. Care dintre secvenele de mai jos
calculeaz intersecia mulimilor numerelor naturale reinute de cei doi vectori?
Rezultatul se va gsi n vectorul Z.
a) Dif(X,Y,a,b,Z,p);
Dif(X,Z,a,p,Z,p);
b) Dif(X,a,Y,b,Z,p);
Dif(Y,Z,b,p,Z,p);
c) Intersect(X,a,Y,b,Z,p)
d) Intersect(X,Y,a,b,Z,p);
C. Se citesc doi vectori X cu a numere naturale i Y cu b numere naturale.
Elementele din fiecare vector sunt distincte. Care dintre secvenele de mai jos nu
calculeaz nici X-Y, nici Y-X? Rezultatul se va gsi n vectorul Z.
a) Inters(X,Y,a,b,Z,p);
Dif(X,Z,a,p,Z,p);
b) Inters(X,Y,a,b,Z,p);
Dif(Y,Z,b,p,Z,p);
c) Inters(Y,X,a,b,Z,p);
Dif(X,Z,a,b,Z,p);
d) Dif(X,Y,a,b,Z,p);

28. Se consider subprogramul urmtor:

Varianta Pascal Varianta C++


procedure Afis (n:integer); void Afis (int n)
var i,j,k:integer; { int i,j,k;
begin k=2*n-1; i=1;
k:=2*n-1;i:=1; while (i<=n)
while i<=n do {for(j=1;j<=(k-2*i+1)/2;j++)
begin cout<<' ';
for j:=1 to (k-2*i+1)div 2 do for(j=1;j<=2*i-1;j++)
write (' '); cout<<'*';
for j:=1 to 2*i-1 do i++;
write ('*'); cout<<endl;
i:=i+1; }
writeln }
end
end;
70 Capitolul 2. Subprograme

A. Dac n=8, cte caractere * se afieaz pe ultimul rnd tiprit?

a) 8; b) 16; c) 15; d) 17.

B. Dac n=4, cte caractere * se afieaz n total (pe toate rndurile)?

a) 8; b) 16; c) 12; d) 20.

C. Dac n=20, cte spaii se scriu naintea primului caracter *, pe penultimul rnd?

a) 10; b) 20; c) 19; d) 1.

29. Se consider subprogramul urmtor:

Varianta Pascal Varianta C++


type vect=array[1..10] of byte; void xx(int V[10])
{ int A[4][4],i,j,z;
procedure xx(var V:vect); for (i=0;i<4;i++)
var A:array[1..4,1..4] of byte; for (j=3;j>=0;j--)
i,j,z:byte; { A[i][j]=V[i]%2;
begin V[i]/=2;
for i:=1 to 4 do }
for j:=4 downto 1 do for (i=0;i<4;i++)
begin { z=A[0][i];
A[i,j]:=V[i] mod 2; for (j=1;j<4;j++)
V[i]:=V[i] div 2; z=z*2+A[j][i];
end; V[i]=z;
for i:=1 to 4 do }
begin }
z:=A[1,i];
for j:=2 to 4 do
z:=z*2+A[j,i];
v[i]:=z;
end;
end;

A. Care este coninutul lui V dup apel, dac nainte este V=(1,2,3,4)?

a) V=(1,2,3,4); b) V=(4,3,2,1);
c) V=(0,1,6,10); d) V=(0,4,5,9).

B. Care trebuie s fie iniial coninutul lui V, dac dup apel este V=(1,6,10,0)?

a) V=(2,4,6,8); b) V=(1,6,10,0);
c) V=(0,10,6,1); d) V=(1,9,3,2).

C. Pentru care dintre exemplele urmtoare de configuraii ale lui V la intrarea n


subprogram, coninutul acestuia la ieire coincide cu cel de la intrare?
a) V=(13,10,5,11); b) V=(2,3,9,13);
c) V=(0,1,2,12); d) V=(1,4,9,15.
Manual de informatic pentru clasa a XI-a 71

30. Se consider subprogramul urmtor:

Varianta Pascal Varianta C++


type vect=array[1..10] of integer; int Cmp(int m, int n,
int A[10], int B[10])
function Cmp(m,n:integer; { int i=0;
A,B:vect):byte; while (i<=m && i<=n &&
var i:integer; A[i]==B[i]) i++;
begin if(i>n && i>m) return 0;
i:=1; else if(i>n) return 1;
while (i<=m) and (i<=n) else if(i>m) return -1;
and (A[i]=B[i]) do i:=i+1; else if(A[i]<B[i])
if (i>n) and (i>m) return -1;
then Cmp:=0 else
else if i>n then Cmp:=1 return 1;
else if i>m then Cmp:=-1 }
else if A[i]<B[i]
then Cmp:=-1
else Cmp:=1;
end;

A. Ce valoare returneaz funcia apelat pentru fiecare dintre cazurile urmtoare?

a) m=3, n=3, A=(1,2,3), B=(1,2,3);


b) m=2, n=3, A=(1,2), B=(1,2,3);
c) m=2, n=3, A=(1,3), B=(1,2,3);
d) m=1, n=1, A=(1), B=(1).

B. Pentru m=5, n=3, A=(1,2,5,7,2), B=(1,2, ), stabilii ce valoare poate fi


a treia component a vectorului B astfel nct funcia s returneze valoarea 0.

a) 3; b) 5;
c) nu exist o astfel de valoare; d) 2.

C. Pentru X=(1, ,5,7), Y=(1,2, ,7), stabilii ce valori pot fi scrise n casete
astfel nct apelul cmp(4,4,X,Y) s returneze aceeai valoare ca i apelul
cmp(4,4,Y,X).

a) 2 i respectiv 3; b) 2 i respectiv 5;
c) nu exist astfel de valori; d) 3 i respectiv 6.

D. Pentru care ir de vectori, funcia Cmp, apelat pentru oricare doi vectori
consecutivi n ir, returneaz aceeai valoare?

a) A=(1,2,3), B=(1,2,3), C(1,2,2);


b) A=(1,2), B=(1,2), C=(1,3);
c) A=(1,3), B=(1,2), C=(1,3);
d) A=(2), B=(1), c=(0).
72 Capitolul 2. Subprograme

31. Scriei subprograme pentru:

A. Calculul valorilor ce caracterizeaz o sfer (arie i volum) pe baza valorii razei


sale r.
B. Calculul valorilor caracteristice ale unui con (raza bazei, generatoarea i nlimea)
pe baza valorii volumului conului i a ariei sale laterale.
C. Obinerea descompunerii n factori primi a unui numr natural n.
D. Ordonarea componentelor pozitive ale unui ir de valori, valorile negative
rmnnd pe poziiile lor iniiale.
E. Determinarea unei subsecvene palindromice a unui ir de numere naturale de
cel mult dou cifre.
F. Verificarea respectrii 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. Se transmit: viteza maxim admis v, numrul de camere video n, distanele
fa de punctul de nceput al sectorului de drum unde sunt amplasate camerele:
d1,d2,,dn i momentele la care s-a nregistrat trecerea mobilului prin dreptul
fiecrei camere t1,t2,,tn.

Rspunsuri

19. d); 20. c); 21. a); 22. b); 23. c); 24. d); 25. b), c); 26. a);

27. A. b), c); B. a), d); C. c);

28. A. c); B. b); C. d).

29. Indicaie: fiecare numr este scris n binar pe o linie a matricei. Se afieaz
numerele zecimale obinute din numerele n binar de pe fiecare coloan a matricei.
A. c), B. a), C. a).

30. Indicaie: funcia realizeaz compararea lexicografic a doi vectori care rein
numere naturale. A. a) 0; b) -1; c) 1; d) 0; B. c); C. b); D. d).
73

Capitolul 3

iruri de caractere

3.1. Generaliti

Programele nu lucreaz numai cu numere. Ele utilizeaz din plin irurile de


caractere. n absena lor ar fi foarte greu ca, de exemplu, un program s poat citi
sau afia numele unei persoane sau numele unei localiti. Ne putem imagina un
ir de caractere ca un vector n care fiecare component reine un caracter. Dup
cum se tie, fiecare caracter este memorat prin utilizarea codului ASCII. irurile de
caractere pot fi citite, afiate i prelucrate cu ajutorul unor subprograme puse la
dispoziie de limbaj.
irurile de caractere au aplicaii uriae n viaa real. S vedem cteva dintre
acestea:
Cine n-a folosit un editor de texte? Chiar acum, cnd scriem programele
Pascal sau C++ utilizm un editor de texte. Luai codul surs al unui program
(fiiere cu extensii .pas sau .cpp). Observai c sursa este un fiier text n
care fiecare linie a ei, este o linie a fiierului text. Prin urmare, fiecare linie a
fiierului text conine un ir de caractere. Ce operaii putem face cu
editoarele? De exemplu, putem modifica o linie. O astfel de operaie const
n includerea sau tergerea unui subir (caractere consecutive din interiorul
unui ir). Astfel de operaii se fac, i vom vedea cum, prin prelucrarea
irurilor de caractere. Alte operaii: a) Find - este o operaie de identificare
a unui subir n cadrul unui ir de caractere; b) Replace este o operaie de
nlocuire a unui subir cu un altul; c) Cut este o operaie prin care, din cadrul
unui ir se extrage un subir al su i acesta este memorat n Clipboard.
Prin Paste se insereaz un ir de caractere n cadrul altui ir de caractere.
irul exemplelor ar putea continua. Putei gsi altele?
Ai nvat, la Tehnologia Informaiei i a Comunicaiilor, cum, de exemplu,
putem crea tabele (Excel, Word sau Access). Am vzut c liniile tabelului,
pot conine valori numerice, dar i alfabetice, care sunt iruri de caractere.
Putem sorta liniile unui tabel dup o anumit coloan (de exemplu, dup
numele persoanelor, n ordine alfabetic). De aici, rezult c ntre irurile de
caractere exist o relaie de ordine, corezpunztoare ordonrii alfabetice. n
informatic, o astfel de relaie se numete ordine lexicografic. Cum se
ordoneaz alfabetic (lexicografic) mai multe iruri de caractere? Care sunt
principiile care stau la baza acestei ordonri? Despre toate acestea vom
nva n acest capitol.
Limbajele actuale permit numai introducerea datelor sub form de iruri de
caractere. Luai, de exemplu, o component de tip edit, component studiat
la Tehnologia Informaiei i a Comunicaiilor. Chiar dac vrei, de exemplu,
74 Capitolul 3 iruri de caractere

s introducei numrul 12, vei introduce de fapt irul de caractere '12'.


Programul va converti irul de caractere '12' n numrul ntreg 12. Nu
ntotdeauna o astfel de operaie reuete, pentru c este posibil ca persoana
care introduce data respectiv s greeasc. De exemplu, n loc de irul '12',
s introduc irul '1@'. Aceasta nseamn c n timpul operaiei de conversie,
se efectueaz i o anumit validare. n cazul de fa, trebuie efectuat o
validare numeric. Componentele studiate la Tehnologia Informaiei afieaz
iruri de caractere. Prin urmare, dac realizm un astfel de program, este
necesar s convertim valorile numerice n iruri i invers. Cum efectum astfel
de conversii? Conversiile sunt prezentate n acest capitol.
ntruct exist diferene semnificative ntre modul n care sunt reinute i
prelucrate irurile de caractere n cele dou limbaje (Pascal i C++), prezentarea
acestora se va face separat.

3.2. iruri de caractere n Pascal

3.2.1. Noiuni introductive

Poate ai observat c, pn n acest moment, nu am memorat cuvinte.


Acestea au fost doar afiate. n Pascal se poate lucra cu uurin cu ele, datorit
faptului c limbajul este nzestrat cu un tip de date special, numit string.

Definiia 3.1. O succesiune de caractere cuprinse ntre dou caractere


apostrof se numete ir de caractere.

Exemple: 'Un sir'; 'toamna se numara bobocii'.

O ntrebare: ntre caracterele care alctuiesc irul se poate gsi i


apostroful? De exemplu, irul 'Alt 'sir' este scris corect?

Rspunsul comport o anumit nuanare. Pentru a include apostroful, se


folosete un artificiu: acesta este trecut de dou ori. Prin urmare, irul 'Alt
'sir' este incorect, corect este 'Alt ''sir'. Atenie! n memorie, irul 'Alt
''sir' este reinut ca 'Alt 'sir', deci cu un singur apostrof. De ce este
nevoie s se procedeze astfel? Motivul este dat de faptul c, dac am proceda
altfel, compilatorul nu ar putea decide unde se termin irul, cu alte cuvinte, care
este apostroful final.
n Pascal, pentru a putea lucra cu irurile de caractere se folosesc variabilele
de tip string. Tipul string este predefinit, adic este cunoscut, nu avem nevoie
s-l declarm cu type.
var t: string; Programul alturat are declarat o
begin variabil t, de tip string. Ea este
t := 'Iepuras'; iniializat cu irul 'Iepuras', apoi este
writeln(t);
end. afiat.
Manual de informatic pentru clasa a XI-a 75

Pentru nceput, s analizm modul n care o variabil de tip string


memoreaz un ir de caractere. Pentru aceasta, 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 observm? Pentru o variabil de tip string se rezerv automat un vector


cu 256 de octei, numerotai de la 0 la 255. Primul dintre ei are rolul de a reine
numrul de octei ocupai de ir.
n exemplul dat, irul 'Iepuras' este alctuit din apte caractere. Octeii de
la 1 la 7 memoreaz caracterele din care este alctuit irul. Restul octeilor, de la 8
la 255 au un coninut neprecizat. De altfel, nici nu ne intereseaz coninutul lor.
Observai faptul c afiarea s-a realizat, n ansamblu, prin precizarea numelui.
ntruct o variabil de tip string memoreaz cuvintele sub form de vector
de caractere, exist posibilitatea s accesm direct coninutul unui octet, aa cum
suntem obinuii. Astfel, t[1] reine codul caracterului I, t[2] reine codul
caracterului e, .a.m.d. Programul care urmeaz afieaz acelai cuvnt, pe litere:
var t: string;
i: integer;
begin
t := 'Iepuras';
for i := 1 to n do write(t[i]);
end.

Mai mult, putem modifica coninutul unui singur octet, aa cum rezult din
programul urmtor.
var t: string;
begin
t := 'Iepuras';
t[6] := 'i';
write(t);
end.

n loc de 'a', t[6] reine 'i'. Prin urmare, programul afieaz 'Iepuris'.

n cazul variabilelor de tip string exist posibilitatea ca atribuirea s se fac


direct, nu pe litere, aa cum rezult din programul urmtor:
var t, z: string;
begin
t := 'Iepuras';
z := t;
write(z);
end.
n urma atribuirii, variabila z reine cuvntul "iepuras" i acesta este afiat.
76 Capitolul 3 iruri de caractere

Am vzut c pentru o variabil de tip string se rein automat 256 de octei,


din care primul reine lungimea. n multe cazuri, acest numr este prea mare - se
consum memorie inutil. Din acest motiv exist posibilitatea ca o variabil de tip
string s fie declarat n aa fel nct s ocupe un numr mai mic de octei.

Exemplu: var t: string[4];

Variabila t, ocup 5 octei. Primul, cel de indice 0, are rolul de a reine


lungimea cuvntului memorat. n acest caz, variabila poate reine cuvinte ce au cel
mult 4 caractere. Programul afieaz 'mama'.
var t: string[4];
begin
t := 'mama';
write(t);
end.

Ce facem n cazul n care cuvntul care va fi memorat, are un numr mai


mare de litere dect numrul de octei ai variabilei care l memoreaz?
Exemplu:
var t: string[4];
...
t:= 'DANSATOR';

n astfel de cazuri se rein numai primele caractere, attea cte pot fi


memorate. n exemplu, variabila t reine cuvntul 'DANS'.

Dup cum am nvat, caracterul blank se memoreaz ca oricare altul, prin


codul su. Prin urmare, o variabil de tip string poate reine mai multe cuvinte,
separate prin unul sau mai multe blank-uri.
Exemplu:
var nume: string;
...
nume:= 'Ion Zaharia';

n concluzie, prin utilizarea variabilelor de tip string avem avantajul c


putem adresa irul de caractere att n ansamblu, prin utilizarea numelui variabilei,
ct i pe caractere, prin utilizarea parantezelor drepte.

Acesta nu este singurul avantaj. Limbajul este nzestrat cu proceduri i funcii


care uureaz mult lucrul cu irurile de caractere.

3.2.2. Concatenarea irurilor

Pentru a concatena dou iruri de caractere se folosete operatorul '+'.


Operatorul '+' este binar (adic are doi operanzi) i poate aciona asupra datelor
de tip string. Ce nelegem prin concatenare?
Manual de informatic pentru clasa a XI-a 77

Definiia 3.2. Concatenarea este operaia prin care din dou iruri de
caractere se obine un al treilea, format astfel: primul ir (cel aflat n
stnga operatorului), urmat de al doilea ir (cel aflat n dreapta
operatorului).

Exemplu:
program st6;
var t, z: string;
begin
t := 'acest';
z := ' exemplu';
t := t+z;
writeln(t);
end.

n programul st6 se concateneaz dou iruri de caractere (t:=t+z;):

variabila t reine irul 'acest';


variabila z reine irul ' exemplu';
prin operaia (t+z;) se obine irul 'acest exemplu';
irul obinut este atribuit variabilei t.

Observaii

1. Dac am fi scris t:=z+t; programul ar fi afiat ' exempluacest'. De


aici tragem concluzia c operaia de concatenare nu este comutativ (conteaz
ordinea n care sunt trecui cei doi operanzi).
2. Observm ct este de important s inem cont de poziia blank-ului (al
doilea ir este precedat de blank). Observai c dac am fcut concatenarea z+t,
am obinut irul ' exempluacest', care este precedat de blank.

3. Trebuie s avem n vedere c irul obinut n urma concatenrii s poat fi


memorat n ntregime - variabila creia i se atribuie s fie declarat cu un numr de
octei care s permit memorarea sa, altfel irul va fi memorat trunchiat - adic vor
fi memorate numai primele caractere, attea cte ncap.

3.2.3. Compararea irurilor

Orict ar prea de curios, irurile de caractere pot fi comparate. Astfel, dou


iruri de caractere (notate a i b), se pot gsi n una din relaiile:

a=b - cele dou iruri sunt egale;


a>b - irul a este mai mare dect irul b;
a<b - irul a este mai mic dect irul b.
78 Capitolul 3 iruri de caractere

Dar cum se pot compara dou iruri? S nu uitm c i dou caractere se


pot compara dup codul lor. Am vzut modul n care au fost codificate caracterele,
dup o anumit logic: dac un caracter urmeaz altuia, n ordine alfabetic, atunci
el are drept cod un numr mai mare dect al caracterului cruia i urmeaz.
Exemplu: codul caracterului d este mai mare cu o unitate dect codul caracterului c.

irurile sunt succesiuni de caractere. Prin urmare, compararea se face pe


caractere. n continuare prezentm algoritmul de comparare a dou iruri.

Fie dou iruri de caractere, notate cu a (cu m caractere) i b (cu n


caractere).

Se compar codurile primelor dou caractere (aflate n stnga irurilor, de


indice 1). Avem 3 posibiliti:

dac codul primului caracter al irului a este mai mare dect codul
primului caracter al irului b, atunci a>b.

dac codul primului caracter al irului b este mai mare dect codul
primului caracter al irului a, atunci a<b (b>a).

n caz de egalitate se compar codurile caracterelor de indice 2.


...

Dac m<n, i n urma comparrii primelor m caractere a rezultat egalitate, atunci


a<b (b are mai multe caractere).

Dac m>n, i n urma comparrii primelor n caractere a rezultat egalitate,


atunci a>b (a are mai multe caractere).

Dac m=n i n urma comparrii tuturor caracterelor a rezultat egalitate, atunci


cele dou iruri sunt egale (a=b).

Exemple:
a='abc', b='bactr'. Atunci a<b, pentru c a[1] este a i are codul mai
mic dect b[1] care este b.

a='abc', b='aba'. Atunci a>b, codul lui a[1] este egal cu codul lui b[1],
codul lui a[2] este egal cu codul lui b[2], iar codul lui a[3] este mai mare
dect codul lui b[3].

a='abc', b='abca'. Aici m=3, n=4. Atunci a<b, codul lui a[1] este egal cu
codul lui b[1], codul lui a[2] este egal cu codul lui b[2], codul lui a[3]
este egal cu codul lui b[3] i n>m (irul b are mai multe caractere).

Compararea irurilor de caractere este extrem de util n sortarea alfabetic


a cuvintelor (ca n dicionar). Ordinea astfel impus se mai numete i ordine
lexicografic.
Manual de informatic pentru clasa a XI-a 79

n programul urmtor se sorteaz alfabetic mai multe iruri de caractere


(cuvinte). Pentru aceasta, se declar un vector n care fiecare
component este de tip string.

Algoritmul de sortare este unul studiat (care?), motiv pentru care nu revenim
asupra lui. Programul este prezentat n continuare:

type cuvinte = array[1..10] of string;


var v: cuvinte;
man: string;
n, i: byte;
inv: boolean;
begin
write('n= ');
readln(n);
for i := 1 to n do
readln(v[i]);
repeat
inv := false;
for i := 1 to n-1 do
if v[i] > v[i+1] then
begin
man := v[i];
v[i] := v[i+1];
v[i+1] := man;
inv := true;
end
until not inv;
for i := 1 to n do
writeln(v[i]);
end.

3.2.4. Lungimea irurilor de caractere

Definiia 3.3. Prin lungimea unui ir de caractere nelegem numrul de


caractere pe care acesta le conine.

Exemple:
irul 'mama' are lungimea 4;
irul 'mama ' are lungimea 5 (i blank-ul este caracter).

Pentru aflarea lungimii unui ir avem dou posibiliti:

1. Prin utilizarea funciei length, care are forma general:


function length(S: String): Integer;

i ntoarce lungimea irului S - numr ntreg.


80 Capitolul 3 iruri de caractere

Exemple de utilizare

a) var a: string;
c: integer;
...
a := 'un test';
c := length(a);

n urma atribuirii, variabila c reine 7.

b) n condiiile de mai sus, writeln(length(a)) afieaz 7.

2. Dup cum am nvat, octetul de indice 0 reine lungimea irului. Pentru a-l
obine folosim funcia ord astfel: ord(a[0]). De ce aa? Atunci cnd scriem
a[0] ne referim la un caracter. Folosind aceast expresie obinem caracterul care
are codul dat de numr (n binar). Deci, pentru a obine codul su, utilizm
funcia ord.

n programul urmtor se afieaz lungimea unui ir citit. Tiprirea se


face n cele dou moduri prezentate:

var a: string;
begin
write('a= '); readln(a);
writeln('lungimea sirului a este ', length(a));
writeln('lungimea sirului a este ', ord(a[0]));
end.

Un caz aparte de ir este irul vid. Prin ir vid nelegem un ir fr nici un


caracter. Evident, irul vid are lungimea 0.

Exemplu: a:=''. Am iniializat variabila a, de tip string, cu irul vid.

3.2.5. Subiruri

Definiia 3.4. Fiind dat un ir de caractere, prin subir al su se nelege


un ir de caractere consecutive care se regsesc n irul iniial.

Exemple:

irul 'harnic' are ca subir irul 'rni'. Acesta ncepe n poziia a treia din
irul iniial (3 caractere);

irul 'mama' are ca subir irul 'ma'. Observm faptul c subirul apare de
dou ori - ncepnd cu poziiile 1 i 3. Prin urmare, n cadrul unui ir, un subir
poate aprea de mai multe ori.

irul 'harnic' nu are ca subir irul 'rnit', chiar dac primele trei
caractere (rni) se regsesc n irul iniial.
Manual de informatic pentru clasa a XI-a 81

Funcii ce acioneaz asupra irurilor de caractere

Exist mai multe proceduri i funcii care acioneaz asupra irurilor de


caractere. Acestea sunt prezentate n continuare.

Funcia copy are rolul de a extrage un subir din cadrul unui ir dat:
function copy(s:string;inceput,lungime:integer):string;
unde:

variabila s (de tip string) - conine irul din care se face extragerea;
inceput - reine poziia de nceput a subirului care se extrage;
lungime - reine numrul de caractere care se extrag.

n cazul n care prin parametrul lungime specificm mai multe caractere


dect are irul, se extrag caracterele pn la sfritul irului.

Fie programul urmtor:


var a, b: string;
i, j: byte;
begin
write('a= '); readln(a);
write('i= '); readln(i);
write('j= '); readln(j);
b := copy(a, i, j);
writeln(b);
end.

Dac se citete irul 'un text', 4 pentru i i 3 pentru j, variabila b va reine


subirul 'tex'.

Funcia pos are rolul de a verifica dac un ir este subir pentru altul:
function pos(subsir,sir:string):byte;

Se scriu, n ordine, urmtorii parametri:

subsir - subirul cutat;


sir - irul n care se face cutarea.

Funcia returneaz:

0, dac nu a fost gsit subirul cerut;


poziia de nceput a subirului, n cazul n care acesta a fost gsit.

Exemple: 1. Fie irul 'abcde'. Se caut subirul 'bcd'. Acesta este gsit, iar
funcia returneaz 2 (poziia de nceput a subirului). Dac se caut subirul 'cz',
funcia returneaz 0 ('cz' nu este subir al irului 'abcde').
82 Capitolul 3 iruri de caractere

2. Programul urmtor citete dou iruri a i b i verific dac b este subir al lui
a. n ambele cazuri se dau mesaje. Dac b nu a fost identificat ca subir al lui a,
sau, n caz contrar, se afieaz poziia de nceput a sa.
var a, b: string;
n: integer;
begin
write('a= '); readln(a);
write('b= '); readln(b);
n := pos(b,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);
end.

Procedura insert are rolul de a insera un ir de caractere ncepnd cu o


anumit poziie, n alt ir. Pentru aceasta, ea primete ca parametri
urmtoarele:
procedure insert (sir_de_ins: string; var ir_unde_ins:
string; poz: integer);
unde:

sir_de_ins - irul care urmeaz a fi inserat;


sir_unde_ins - irul n care se face inserarea;
poz - poziia din care se face inserarea.

Exemplu:
var a, b: string;
begin
write('a= '); readln(a);
write('b= '); readln(b);
insert(b, a, 3);
writeln(a);
end.

n programul anterior am citit dou variabile de tip string, a i b. irul reinut


de variabila b este inserat n irul reinut de variabila a ncepnd cu poziia 3.

Exemplu: dac a reine irul 'abcd' i b reine irul '123', programul afieaz
'ab123cd'.

Procedura delete are rolul de a terge un subir din cadrul unui ir dat.
Pentru aceasta, ea are ca parametri, n ordine:
procedure delete(var sir: string; indice, nr_car: integer);

unde:

Sir - variabila care conine irul din care se face tergerea;


Indice - indicele primului caracter al subirului care se terge;
nr_car - numrul de caractere pe care l are subirul.
Manual de informatic pentru clasa a XI-a 83

Exemplu:

Programul urmtor citete un ir de caractere ntr-o variabil de tip string,


numit a. irului citit i se terge subirul care ncepe n poziia 3 i are lungimea 2
(dou caractere). De exemplu, dac variabila a reine irul 'abcde', se afieaz
'abe'.
var a:string;
begin
write('a='); readln(a);
delete(a,3,2);
writeln(a);
end.

Aplicaia 3.1. n programul urmtor se listeaz indicii tuturor apariiilor


caracterului citit n ir:
var sir: string;
ch: string[1];
pozn, pozv: byte;
begin
writeln('introduceti sirul'); readln(sir);
writeln('introduceti caracterul '); readln(ch);
pozn := pos(ch, sir);
pozv := 0;
while pozn <> 0 do
begin
pozv := pozv+pozn;
writeln(pozv);
sir := copy(sir, pozn+1, 255);
pozn := pos(ch, sir);
end
end.

Aplicaia 3.2. tergerea tuturor apariiilor unui subir din cadrul unui ir:

var sir, subsir: string;


poz, lung: byte;
begin
writeln('introduceti sirul'); readln(sir);
writeln('introduceti subsirul '); readln(subsir);
lung := length(subsir);
poz := pos(subsir, sir);
while poz <> 0 do
begin
delete(sir, poz, lung);
poz := pos(subsir, sir);
end;
writeln(sir);
end.
84 Capitolul 3 iruri de caractere

Aplicaia 3.3. nlocuirea tuturor apariiilor unui subir cu alt subir. Analizai
programul urmtor pentru a descoperi modul n care se realizeaz aceasta:
var sir, sir_sters, sir_adaugat: string;
poz, lung: byte;
begin
writeln('introduceti sirul '); readln(sir);
writeln('introduceti sirul care se sterge);
readln(sir_sters);
writeln('introduceti sirul care se adauga);
readln(sir_adaugat);
lung := length(sir_sters);
poz := pos(sir_sters, sir);
while poz <> 0 do
begin
delete(sir,poz,lung);
insert(sir_adaugat, sir, poz);
poz := pos(sir_sters, sir);
end;
writeln(sir);
end.

3.2.6. Conversia de la iruri la valori numerice i invers


Fie irul '123'. Evident, acesta este diferit de numrul 123. Dup cum tim,
irul ocup 4 octei (nu uitai, primul octet reine lungimea). Numrul 123, dac este
memorat de o variabil de tip integer, ocup doi octei i este reinut n cod
complementar. Limbajul dispune de dou proceduri care realizeaz conversia de la
valori numerice la iruri i invers.
Procedura str are rolul de a transforma o valoare numeric n ir.
procedure Str(X [: Lg [: Zec ]]; var S:string);

Pentru aceasta i se transmit, n ordine, doi parametri:


X - variabila (valoarea) numeric - poate fi ntreag sau real;
S - variabila de tip string care reine irul convertit.

Exemplu:
var a: string;
n: integer;
begin
write ('n= '); readln(n);
str(n, a);
writeln(a)
end.

n programul de mai sus se citete o valoare ntreag (n). Valoarea este


convertit ctre tipul string - variabila a are acest tip. Rezultatul este afiat. De
exemplu, dac se citete 123 se afieaz 123. Pn aici nimic spectaculos. Dac
Manual de informatic pentru clasa a XI-a 85

am fi afiat coninutul variabilei n, am avea acelai rezultat. Atunci la ce folosesc


astfel de conversii? Pentru a putea da rspunsul trebuie s mai nvm ceva
despre modul n care putem apela procedura str. Am fi putut s scriem apelul i
aa: str(n:4,a);. Observai faptul c, dup numele variabilei care se afieaz
(n) au fost puse caracterul ':' i numrul 4. Aceasta nseamn c am cerut ca
rezultatul (de tip string) s ocupe 5 octei - unde primul reine lungimea, deci 4
octei pentru memorare.

S analizm acum modul de efectuare a conversiei.

Dac variabila n reine numrul 1234, variabila a va reine irul


'1234'.

Dac variabila n reine numrul 123, atunci a va reine irul ' 123'.
Rezult c irul, fr octetul de lungime, ocup 4 octei. n astfel de
cazuri, irul este completat n stnga cu numrul de blank-uri necesar.

Dac variabila n reine numrul 12345 atunci irul va fi '12345'. Cu


alte cuvinte nu se respect cerina noastr, pentru c dac ar fi fost
respectat, rezultatul ar fi fost eronat.

Acum putem rspunde la ntrebarea: la ce folosesc astfel de conversii?


Programatorul va avea grij ca ntotdeauna numrul de octei ai irului s fie mai
mare sau egal cu numrul de octei ai valorii convertite. n acest fel, la afiare, vom
ti care este spaiul ocupat de ir i putem s afim rezultatele aliniate (tabele). n
continuare, ne ocupm de conversia valorilor reale ctre iruri de caractere.
Exemplu:
var a: string;
x: real;
begin
x := -67.789;
str(x: 10: 2, a);
writeln(a)
end.

n programul de mai sus se convertete numrul -67.789 ctre un ir.


Cerina este ca irul efectiv s ocupe 10 caractere, dintre care ultimele dou s fie
zecimale. Evident, dou caractere vor fi semnul '-' i punctul zecimal. n exemplu,
se obine irul ' -67.79'. Ce observm?

ntotdeauna numrul zecimalelor solicitat de programator este respectat. n


cazul n care numrul efectiv de zecimale este mai mare dect numrul
solicitat pentru conversie, naintea conversiei numrul este rotunjit. Dac
numrul solicitat de zecimale ar fi fost 0, s-ar fi afiat -67 (deci punctul
zecimal nu ar fi afiat).
n cazul n care numrul total de cifre al prii ntregi + numrul total de
zecimale + 2 (caracterele de semn i punctul zecimal) ocup mai mult
dect numrul total de octei solicitai pentru afiare, acesta din urm nu este
86 Capitolul 3 iruri de caractere

respectat. Cu alte cuvinte, se trunchiaz numai zecimalele nu i partea


ntreag a valorii reale. De exemplu, dac am fi scris x:1:2, irul obinut ar
fi fost '-67.79'.

n cazul n care numrul care se convertete este pozitiv, semnul '+' nu


este trecut.

Acum studiem conversia invers de la tipul string ctre valori numerice


(ntregi sau reale). De exemplu, irul '123' se poate converti ctre valoarea de tip
integer: 123.

De la nceput precizm c nu ntotdeauna conversia reuete. De exemplu,


dac ncercm s convertim irul '1a2' ctre o valoare de tip integer, conversia
nu reuete, pentru c irul conine caracterul 'a'.

Pentru realizarea conversiei utilizm procedura val. Ea are trei parametri i


anume:
procedure val(s:string; var variabila_numerica; var cod_er:integer);
unde:
s - conine irul ce urmeaz a fi convertit;

variabila_numerica - variabila de tip ntreg sau real care va


reine rezultatul conversiei (valoarea numeric).
cod_er - variabil de tip ntreg. Dup conversie, aceasta va reine 0
dac conversia a reuit sau o valoare diferit de 0, n caz contrar.

S analizm programul urmtor:

var a: string;
x, er: integer;
begin
write('Sirul este '); readln(a);
val(a, x, er);
if er = 0 then writeln(' conversia a reusit ', x)
else
begin
writeln ('conversia nu a reusit');
writeln(x)
end
end.

Programul citete un ir de caractere care este reinut n variabila de tip


string a. Se ncearc conversia irului ctre o variabil de tip integer. n cazul
n care tentativa a reuit, se afieaz mesajul corespunztor i coninutul variabilei,
altfel se afieaz numai un mesaj prin care se anun faptul c tentativa a euat.
Manual de informatic pentru clasa a XI-a 87

Observaii

Dac irul de caractere cifre este precedat de un numr de blank-uri,


conversia reuete.

Exemplu: irul ' 123' se poate converti ctre valoarea 123.

Dac irul de caractere cifre este urmat de un numr de blank-uri, conversia


nu reuete.

Exemplu: irul '123 ' nu poate fi convertit ctre o valoare numeric.

Dac irul conine un singur caracter liter, el nu poate fi convertit ctre o


valoare numeric. De exemplu, irul '12i' nu poate fi convertit. Excepie
fac irurile de caractere care respect sintaxa unei constante reale n form
tiinific (de exemplu, '1.E-3')

Dac variabila care reine rezultatul este de tip ntreg, iar irul conine punctul
zecimal, conversia nu reuete.

Exemplu: irul '1.23' nu poate fi convertit ctre o variabil de tip ntreg,


dar poate fi convertit ctre o variabil de tip real.

Dac n urma conversiei se obine o valoare numeric care nu poate fi


memorat de variabila respectiv, programul se termin anormal, prin eroare
de executare.

Aplicaia 3.4. Programul urmtor testeaz dac o valoare introdus este


numeric i dac este cuprins n intervalul [10,20]:

var sir: string;


eroare: integer;
valoare: real;
begin
writeln('introduceti sirul ');
readln(sir);
val(sir, valoare, eroare);
if eroare<>0
then
writeln('val. introdusa este eronata')
else
if (valoare < 10) or (valoare > 20) then
writeln('val. nu este in intervalul dorit ')
else
writeln('ok!')
end.
88 Capitolul 3 iruri de caractere

3.2.7. Citirea i scrierea datelor de tip String din i


n fiiere text

A) Citirea datelor de tip String

Aceste variabile se citesc ncepnd din poziia curent a cursorului pn este


citit numrul de caractere necesar tipului sau pn s-a ajuns la sfritul de linie.

Programul de mai jos demonstreaz acest fapt (dac linia 1 a fiierului are 3
caractere, se vor afia dou pe un rnd i unul pe al doilea rnd). Dup citirea unei
astfel de variabile, pointerul se afl sau pe caracterul ce urmeaz dup ultimul
caracter citit sau pe CR. n situaia n care pointerul se afl pe CR i se foreaz o
nou citire de tip String, se returneaz irul vid.

var f: text;
a: string[2];
b: string;
begin
assign(f,'f1.dat');
reset(f);
read(f,a);
writeln(a);
read(f,b);
writeln(b);
close(f);
end.

B) Scrierea datelor de tip String

n general, coninutul unei date de tip string se scrie n fiier n totalitate.


Opional, numele variabilei poate fi urmat de ':' i de un parametru, m. Pentru
datele de acest tip, se iau n considerare lungimea efectiv a irului i valoarea
parametrului m ce specific numrul de poziii pe care se face scrierea. n cazul n
care valoarea lui m este mai mic dect lungimea efectiv a irului, aceasta se
ignor, irul va fi scris n ntregime. Dac valoarea lui m este mai mare dect
lungimea efectiv a irului, acesta este scris pe m poziii aliniat dreapta, iar n fa
se pun blank-uri.
Exemplu: se scrie irul ' Marian'.
var f:text;
sir:string[6];
begin
assign(f,'F1.txt');
rewrite (f);
sir:='Marian';
write(f,sir:10);
close(f);
end.
Manual de informatic pentru clasa a XI-a 89

3.3. iruri de caractere n C++

3.3.1. Generaliti

Am nvat faptul c o constant de tip ir de caractere se declar ntre dou


caractere ", de exemplu: "calculator". Dar cum se reine n memoria intern?
Aceasta este reinut sub forma unui vector de caractere. Primul element, cel de
indice 0, reine codul ASCII al caracterului 'c', al doilea reine codul ASCII al
caracterului 'a', ..., .a.m.d. Convenia este ca ultimul octet s rein 0 (codul
caracterului nul). Prin urmare, pentru a reine irul "calculator" trebuie s fie
rezervate cel puin 11 elemente de tip char (10 litere plus caracterul nul) - adic
11 octei. Menionm c pentru fiecare caracter este reinut codul ASCII.

c a l c u 0

a[0] a[1] a[2] ................................... a[10]

Vectorii de caractere pot fi iniializai la declarare, caracterul nul fiind


memorat automat.

Exemple:
char vect[11]="calculator".
char vect[]="calculator". n acest caz, compilatorul face calculul
numrului de octei necesari.
char vect[100]="calculator". Am rezervat mai muli octei dect era
necesar.

3.3.2. Citirea i scrierea irurilor de caractere

Se propune urmtoarea problem: s se citeasc i s se afieze cuvntul


"calculator". Pentru aceasta, ar trebui s procedm astfel:

reinem un vector cu cel puin 11 componente de tip char - n exemplu 20;


citim cuvntul, caracter cu caracter;
l afim.
Programul urmtor realizeaz toate acestea:
#include <iostream.h>
main()
{ char a[20];
int i;
for(i=0;i<10;i++) cin>>a[i];
a[10]=0;
for(i=0;i<10;i++) cout<<a[i];
}
90 Capitolul 3 iruri de caractere

S recunoatem c o astfel de modalitate de lucru este deosebit de greoaie.


Dar dac citim un cuvnt cu patru litere? Sar putea scrie o secven care s
citeasc cuvntul pn la ntlnirea caracterului ENTER, ns i aceast modalitate
este greoaie.

Limbajul C++ permite ca lucrul cu iruri de caractere s fie cu mult mai


simplu. Refacem programul anterior:

#include <iostream.h>
main()
{
char a[20];
cin>>a;
cout<<a;
}

Observaii

Caracterul nul este adugat automat.


Procednd ca mai sus, putem citi orice cuvnt cu un numr de pn la 19
caractere - excluznd caracterul nul.
Putem s rezervm, n limita memoriei pe care o are la dispoziie programul,
un numr mai mare de octei.
Exemplu: char a[1000].

Un vector poate fi adresat pe componente.


Exemplu: a[0]='c', a[1]='a', .a.m.d.

Din pcate, prin metoda de mai sus nu poate fi citit un ir care conine mai
multe cuvinte separate prin spaii. De exemplu, dac la rularea programului anterior
tastm irul " Un om", se va afia "Un". Aceasta nseamn c citirea se face astfel:
Se sar toate caracterele albe. n exemplu, s-au srit blank-urile.

Se citete irul care ncepe cu primul caracter care nu este alb i se


sfrete la ntlnirea primului caracter alb (n exemplu, blank).
Din acest motiv, pentru citirea irurilor de caractere vom utiliza o funcie de
un tip special, pe care o prezentm n continuare.

Funcia:
cin.get(vector_de_caractere, int nr, char='\n')

citete un ir de caractere pn cnd este ndeplinit una dintre condiiile de mai jos:
au fost citite nr-1 caractere;

a fost ntlnit caracterul transmis ca ultim parametru (implicit, "\n").


Manual de informatic pentru clasa a XI-a 91

Observaii

Sunt citite i caracterele albe.


Este inserat caracterul nul.
Caracterul transmis ca ultim parametru nu este inserat n ir.
Al treilea parametru este trecut n mod facultativ. Dac nu este trecut, se
presupune c este '\n'.

Privii urmtoarele exemple:

1. Se citete un ir de maximum 2 caractere:


char a[10];
cin.get(a,3);
cout<<a;

De exemplu, dac tastm 'mama' i Enter se citete irul "ma", care va fi afiat.

2. La fel ca mai sus, dar citirea se ntrerupe la ntlnirea caracterului 'g' sau cnd
au fost citite 9 caractere ale irului.
char a[10];
cin.get(a,10,'g');
cout<<a;

n C++ pot exista mai multe funcii cu acelai nume, dar care difer prin
parametrii primii. Astfel, exist i funcia:
cin.get()
fr parametri. Ea are rolul de a citi un caracter (fie c este alb, fie c nu).
Observaie. n cazul utilizrii repetate a funciei cin.get() cu trei
parametri, apare o problem. Analizai programul urmtor:
#include <iostream.h>
#include <string.h>
main()
{ char sir1[1000], sir2[25];
cout<<"sir 1 ";
cin.get(sir1,1000);
cin.get();
cout<<"sir 2 ";
cin.get(sir2 ,25);
}

Dac dup prima citire nu am fi folosit funcia cin.get() fr parametri, a


doua citire nu ar mai fi fost efectuat. De ce? Sfritul primului ir introdus a fost
marcat prin tastarea Enter. Aceasta a fcut ca n memorie (buffer) s fie pstrat
caracterul '\n'. La a doua citire, noul ir va ncepe cu acesta. Prin logica funciei
92 Capitolul 3 iruri de caractere

cu trei parametri, citirea se face pn la ntlnirea lui. n concluzie, se citete irul


vid. Utilizatorul nu mai apuc s i tasteze textul. Prin utilizarea funciei fr
parametri, acel caracter se citete i noua citire se poate efectua fr probleme.

3.3.3. Tipul char*


Vectorii de caractere au o proprietate deosebit. Privii programul urmtor:
#include <iostream.h>
main()
{
char a[]="masa";
cout<<a+1<<" "<<a+2<<" "<<a+3;
}

n urma executrii, se tiprete: "asa sa a".

De ce? Limbajul C++ permite ca un vector de caractere s fie adresat


ncepnd de la un anumit octet al su:
cnd scriem a, adresm vectorul - n ansamblul lui - ncepnd cu
primul su octet (cel care reine primul caracter al irului de caractere);
echivalent, puteam scrie a+0;
cnd scriem a+1, adresm vectorul ncepnd cu al doilea octet;
cnd scriem a+2, adresm vectorul ncepnd cu al treilea octet;
...

Din acest motiv, programul tiprete pentru a+1 irul "asa", pentru a+2
irul "sa", .a.m.d. Mai mult, vectorii astfel adresai pot fi accesai aa cum suntem
deja obinuii. Astfel, pentru exemplul anterior, (a+1)[0] reine caracterul 'a',
(a+1)[1] reine caracterul 's', etc.

Dup cum observai, a+1, a+2, ... sunt expresii. Orice expresie are un
anumit tip. Care este tipul lor? Tipul acestor expresii este char*. Ce semnificaie
are? Semnificaia este cea de adres.

Definiia 3.5. Numrul de ordine al unui octet n memoria intern se


numete adresa octetului respectiv.

Definiia 3.6. Adresa unui vector de caractere este adresa primului


su octet.

O variabil de tipul char* poate reine adresa unui vector de caractere.

n C++, numele unui vector de caractere este o adres constant de vector


i poate fi atribuit unei variabile de tip char*.
Manual de informatic pentru clasa a XI-a 93

Considerm urmtorul exemplu:

#include <iostream.h>
main()
{ char a[]="Exemplu", *p;
p=a; cout<<p<<endl;
p++; cout<<p<<endl;
p++; cout<<p<<endl;
cout<<p[1]<<endl;
cout<<p-a;
}

Am declarat un vector de caractere numit a, pe care l-am iniializat cu un ir.


De asemenea, am declarat i o variabil de tip char* numit p. n aceste condiii,
putem afirma c:
p=a; este o atribuire corect. Variabila p va reine adresa vectorului de
caractere a, adic adresa primului su octet. n schimb, atribuirea a=p este
incorect, pentru c a este o constant ce reprezint o adres.
dup atribuire, putem utiliza variabila p n aceleai condiii ca variabila v. De
exemplu, putem afia irul reinut prin "cout<<p<<endl;".
dac la coninutul variabilei p se adaug 1, aceasta va reine adresa
vectorului al crui prim octet coincide cu al doilea octet al vectorului v. De
exemplu, dac tipresc vectorul se va afia "xemplu". Noul vector se poate
adresa i pe octei. Exemplu: p[1].
se pot face i scderi ntre adrese. n acest caz, rezultatul este ntreg. De
exemplu, prin p-a se obine indicele n v, al primului octet al vectorului
reinut de p. Testai programul!

3.3.4. Lungimea unui ir de caractere

Pentru a putea fi folosite funciile de prelucrare a irurilor de caractere,


trebuie s fie inclus fiierul antet "string.h", tot aa cum includem fiierul
"iostream.h":
#include <iostream.h>

Funcia strlen are rolul de a returna lungimea efectiv a unui ir (n calculul


lungimii nu intr caracterul nul). Forma general este:
size_t strlen(char*);
unde:
size_t este un tip ntreg, utilizat n adresarea memoriei, definit n "string.h"
(l putem privi ca pe tipul unsigned int);
argumentul este de tip char* (adic o adres ctre un ir).
94 Capitolul 3 iruri de caractere

Programul de mai jos citete un ir i afieaz numrul 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";
}

Aa cum tim din lucrul cu tablouri, atribuirile de forma a=b, unde a i b sunt
vectori de caractere, nu sunt permise. Tot aa, o atribuire de forma a="un sir"
nu este permis. Astfel de operaii ca i multe altele se fac cu anumite funcii, puse
la dispoziie de limbaj. Pentru ca acestea s poat fi folosite, trebuie s fie inclus
fiierul antet "string.h", tot aa cum includem fiierul "iostream.h". Ordinea
de includere nu are importan. n continuare, vom prezenta cele mai uzuale funcii
i modul n care acestea se folosesc.

3.3.5. Copierea i concatenarea irurilor de caractere

Funcia 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. Analizai
codul urmtor:
#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 reinut de b. Programul va afia "alt sir". Aceast copiere simuleaz
atribuirea: a=b.

Funcia standard strcat are forma general:


char* strcat(char* dest, char* sursa);

i rolul de a aduga irului de adres dest irul de adres sursa. irul de adres
sursa rmne nemodificat. Aceast operaie se numete 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 urmtor tiprete "mama merge":


#include <iostream.h>
#include <string.h>
main()
{
char a[20]="mama", b[100]=" merge";
strcat (a,b);
cout<<a;
}

Funcia strncat are forma general:

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

i acelai rol ca strcat cu deosebirea c adaug irului destinaie primii nr octei ai


irului surs. Adugarea caracterelor se face naintea caracterului nul. Funcia
returneaz adresa de nceput a irului destinaie.

3.3.6. Cutarea unui caracter ntr-un ir

Funcia strchr are forma general:

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

i rolul de a cuta caracterul 'c' n irul s. Cutarea se face de la stnga la


dreapta. n cazul n care caracterul este gsit, funcia ntoarce adresa subirului
care ncepe cu prima apariie a caracterului citit i se termin cu caracterul nul al
irului n care se face cutarea. Altfel, ntoarce o expresie de tip char* cu valoarea
0 (adic o adres vid de ir). Exemplele care urmeaz ne vor lmuri.

n programul urmtor se caut n irul a caracterul 't'. Acesta este gsit, iar
programul tiprete irul "ta este". Evident, acesta este subirul care ncepe cu
caracterul 't'.
#include <iostream.h>
#include <string.h>
main()
{
char a[20]="Acesta este";
cout<<strchr (a,'t');
}

Aplicaia 3.5. n unele cazuri ne intereseaz indicele n cadrul vectorului al


caracterului cutat. Acesta se obine ca diferen ntre dou valori de tipul char*.
Desczutul este adresa returnat de funcie, iar scztorul este adresa vectorului n
care se face cutarea.
96 Capitolul 3 iruri de caractere

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

Aplicaia 3.6. n programul urmtor se citete un ir i un caracter. Dac


acesta este gsit n ir, se tiprete indicele primei apariii 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 ";
}

Observaii

Variabila t este de tipul char*.

Testul de apartenen a caracterului la ir s-a fcut prin a vedea dac


variabila t reine sau nu 0 (adic o adres nul de ir).

Aplicaia 3.7. n programul urmtor se listeaz indicii tuturor apariiilor


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 subir, care are ca prim caracter cel reinut de c, intr n
calculul indicelui acelui caracter - din ea se scade adresa de nceput.
Manual de informatic pentru clasa a XI-a 97

Funcia strrchr are forma general:


char *strrchr(const char *s, int c);

i acelai rol cu strchr, deosebirea fiind c ntoarce adresa ultimei apariii a


caracterului (cutarea se face de la dreapta ctre stnga). Ea este utilizat n
ipoteza n care se caut ultima apariie a caracterului n cadrul irului.

3.3.7. Compararea irurilor

Funcia 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 numrul de caractere al irului s1 i n numrul 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 dect codul
caracterului corespunztor irului s2, avem s1>s2.

n cazul n care codul caracterului i+1 al irului s1 este mai mic dect codul
caracterului corespunztor irului s2, avem s1<s2. n cazul n care i la
aceast comparaie avem egalitate, avem patru posibiliti:

ambele iruri au un numr strict mai mare de caractere dect i+1, caz n
care se compar ca nainte caracterele de pe poziia i+2;

s1 are i+1 caractere, iar s2 are un numr de caractere mai mare dect
i+1, n acest caz s1<s2;

s2 are i+1 caractere, iar s1 are un numr de caractere mai mare dect
i+1, n acest caz s1>s2;

att s1 ct i s2 au i+1 caractere, caz n care s1=s2.

Pe scurt, un ir s1 este mai mic ca altul s2, dac n dicionar s1 ar figura


naintea lui s2.

Exemple: "soare">s;
"tata">mama;
98 Capitolul 3 iruri de caractere

Probai relaia de ordine dintre dou cuvinte (iruri care nu conin


caractere albe) prin utilizarea programului urmtor:
#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";
}

Funcia strcmp face distincie ntre literele mari i mici ale alfabetului.

Funcia stricmp are forma general:


int stricmp(char *s1,char *s2);

i acelai rol ca strcmp. Diferena este c nu face distincie ntre literele mari i
mici.
Vectori de cuvinte. Exist aplicaii n care este necesar s se lucreze cu n
cuvinte - nelegnd prin cuvnt o succesiune de caractere care nu sunt albe. n
acest caz avem posibilitatea s declarm 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 reine un ir de caractere. Acesta poate
avea cel mult 25 de caractere (inclusiv caracterul nul). Cuvintele pot fi adresate prin
a[0] (primul cuvnt), a[1] cuvntul al doilea, .a.m.d.

Aplicaia 3.8. n programul urmtor 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 precdere cei fundamentali, pot fi folosii
i ntr-un context diferit de cel n care au fost prezentai.

3.3.8. Subiruri

Funcia strstr are forma general:


char *strstr(const char *s1, const char *s2);

i are rolul de a identifica dac irul s2 este subir (caractere succesive) al irului
s1. Dac acesta este identificat, funcia returneaz adresa de nceput n cadrul
irului s1, altfel returneaz adresa nul (0). Cutarea se face de la stnga la
dreapta. n cazul n care s2 apare de mai multe ori n cadrul lui s1, se returneaz
adresa de nceput a primei apariii.
Exemplu: fie char s1[]="xyzt", s2[]="yz", atunci strstr(s1,s2);
returneaz s1+1 (adresa caracterului y n s1).

Aplicaia 3.9. n programul urmtor se citesc dou iruri de caractere i se


testeaz dac al doilea este subir al primului. n caz afirmativ, programul afieaz
indicele caracterului de nceput al subirului.
#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";
}
100 Capitolul 3 iruri de caractere

Aplicaia 3.10. tergerea tuturor apariiilor unui subir din cadrul unui ir.
Imediat ce am identificat adresa de nceput a subirului, restul irului (fr subir)
este copiat pe poziia de nceput a subirului.
#include <iostream.h>
#include <string.h>
main()
{ char sir[1000],subsir[25],*p;
int lung_subsir;
cout<<"introduceti textul ";
cin.get(sir,1000);
cin.get();
cout<<"introduceti subsirul ";
cin.get(subsir,25);
lung_subsir=strlen(subsir);
p=strstr(sir,subsir);
while (p)
{ strcpy(p,p+lung_subsir);
p=strstr(p,subsir);
}
cout<<sir;
}

Aplicaia 3.11. nlocuirea tuturor apariiilor unui subir cu alt subir. V las pe
dvs. s descoperii modul n care programul urmtor realizeaz aceasta:
#include <iostream.h>
#include <string.h>
main()
{ char sir[100], man[100], sterg[25], adaug[25], *p;
int lung_sterg, lung_adaug;
cout<<"introduceti textul ";
cin.get(sir,100);
cin.get();
cout<<"inlocuim subsirul ";
cin.get(sterg,25);
cin.get();
cout<<"cu subsirul ";
cin.get(adaug,25);
lung_sterg=strlen(sterg);
lung_adaug=strlen(adaug);
p=strstr(sir,sterg);
while (p)
{
man[0]=0;//subsir vid;
strncat(man,sir,p-sir);
strcat(man,adaug);
strcat(man,p+lung_sterg);
strcpy(sir,man);
p=strstr(p+lung_adaug,sterg);
}
cout<<sir;
}
Manual de informatic pentru clasa a XI-a 101

3.3.9. Alte funcii utile n prelucrarea irurilor

Funcia strtok are forma general:


char *strtok(char *s1, const char *s2);

Principiul de executare este urmtorul:

irul s1 este considerat ca fiind alctuit din 0, 1, ..., n entiti separate prin
unul sau mai multe caractere cu rol de separator, iar irul s2 ca fiind alctuit
din unul sau mai multe caractere cu rol de separator;
Exemplu: irul s1 este " mama, tata si bunicul". irul s2 este:
" ,". ntruct caracterele separatoare sunt blank-ul i virgula, rezult c
entitile sunt: "mama", "tata" "si" "bunicul".

la prima apelare care trebuie s fie de forma strtok(s1,s2);, funcia


ntoarce adresa primului caracter al primei entiti (n exemplu "mama"). n
plus, dup prima entitate separatorul este nlocuit automat prin caracterul
nul (cod 0);

urmtoarele apeluri ale funciei sunt de forma strtok(NULL,s2);, iar


funcia ntoarce de fiecare dat adresa primului caracter al urmtoarei entiti
i, dup ea, este adugat caracterul nul - aceasta permite ca entitatea s
poat fi extras cu uurin.
...
n momentul n care irul rmas nu mai conine entiti, funcia ntoarce
adresa nul.

Aplicaia 3.12. n programul urmtor se citete un ir de caractere. Entitile se


consider a fi cuvinte - iruri de caractere care nu sunt albe - separate prin blank-uri
i/sau virgule. Programul listeaz entitile depistate, fiecare pe un rnd.
#include <iostream.h>
#include <string.h>
main()
{ char sir[1000],separator[]=" ,", *p;
cin.get(sir,1000);
p=strtok(sir,separator);
while (p)
{
cout<<p<<endl;
p=strtok(NULL, separator);
}
}

Variabila p reine adresa de nceput a entitii. Dac entitatea este gsit, p


va reine o valoare diferit de 0, caz n care entitatea este afiat i se va cuta
urmtoarea entitate.
102 Capitolul 3 iruri de caractere

Aplicaia 3.13. Programul urmtor citete un ir de caractere i tiprete irul


obinut prin eliminarea blank-urilor. n fapt, se separ entitile, care sunt afiate
una dup alta.
#include <iostream.h>
#include <string.h>
main()
{ char sir[1000], separator[]=" ,", *p;
cin.get(sir,1000);
p=strtok(sir,separator);
while (p)
{ cout<<p;
p=strtok(NULL, separator); }
}

Funcia strcspn are forma general:


size_t strcspn(const char *s1, const char *s2);
i are rolul de a returna numrul de caractere al irului s1 - caractere consecutive
care ncep obligatoriu cu primul caracter - care nu se gsesc n irul s2.

Funcia standard strspn are forma general:


size_t strspn(char *s1, char *s2);
i are rolul de a returna numrul de caractere al irului s1 - caractere consecutive
care ncep obligatoriu cu primul caracter - care se gsesc n irul s2.
Exemple
1. Fie char* s1="AB2def"; *s2="123";. Atunci: strcspn(s1,s2)
returneaz 2 (pentru c primele dou caractere 'A' i 'B' din s1 nu se gsesc n
s2), iar strspn(s1,s2) returneaz 0 (primul caracter din s1 nu se gsete n s2).
2. Fie char* s1="AB2def"; *s2="16A32BF";. Atunci: strcspn(s1,s2)
returneaz 0 (pentru c primul caracter 'A' din s1 se gsete n s2), iar
strspn(s1,s2) returneaz 3 (caracterele 'A', 'B', '2' din s1 se gsesc n s2).

Aplicaia 3.14. Se citete un ir de caractere care nu conine caractere albe. S


se verifice dac irul este alctuit exclusiv din caractere numerice.
Vom reine un vector numit cifre. Componentele lui rein toate caracterele
numerice 0-9. irul de caractere citit are toate caracterele numerice dac toate
caracterele sale - numrul lor este dat de strlen - se regsesc printre
caracterele vectorului cifre.
#include <iostream.h>
#include <string.h>
main()
{ char cuvant[100], cifre[]="0123456789";
cout<<"Introduceti cuvantul "; cin>>cuvant;
if (strspn(cuvant,cifre)==strlen(cuvant)) cout<<"numeric";
else cout<<"nenumeric";
}
Manual de informatic pentru clasa a XI-a 103

Aplicaia 3.15. Se citete un ir de caractere care nu conine caractere albe. S


se verifice dac irul e alctuit exclusiv din caractere nenumerice.
Vom reine un vector numit cifre. Componentele lui rein toate caracterele
numerice: 0-9. Cuvntul citit are toate caracterele nenumerice dac toate
caracterele vectorului cifre - numrul lor este 10 - nu se regsesc 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";
}

Funcia strlwr are forma general:


char *strlwr(char *s);
Ea convertete toate literele mari ('A' ... 'Z') n litere mici ('a' ... 'z').
Restul caracterelor rmn neschimbate. Funcia ntoarce adresa s.
Funcia 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 rmn neschimbate. Funcia ntoarce adresa s. n programul
urmtor se citete un cuvnt. Cuvntul citit se tiprete cu litere mari.
#include <iostream.h>
#include <string.h>
main()
{ char a[20];
cout<<" Introduceti cuvantul "; cin>>a;
cout<<strupr(a);
}

Funcia strpbrk are forma general:


char *strpbrk(char *s1, char *s2);
i acioneaz n felul urmtor:
Caut primul caracter al irului s1 n s2. Dac este gsit, returneaz adresa
sa din cadrul irului s1 i execuia se termin, altfel trece la pasul urmtor.
Caut al doilea caracter al irului s1 n s2. Dac este gsit, returneaz adresa
sa din cadrul irului s1 i execuia se termin, altfel trece la pasul urmtor.
...
dac nici un caracter al irului s1 nu aparine irului s2, funcia 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 gsit n s2).

Aplicaia 3.16. Se citesc dou cuvinte. Se cere s se afieze toate caracterele


primului cuvnt care se regsesc 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 cuvnt, ncepnd de la primul, sunt cutate n al doilea


cuvnt. Orice caracter gsit se tiprete. Aceasta se face pornind de la adresa
irului, returnat de strpbrk. Apoi, variabila care conine aceast adres se
incrementeaz. Aceasta pentru ca, la urmtoarea cutare, s nu fie returnat
aceeai adres de ir.

3.3.10. Conversia irurilor n valori numerice i invers

Urmtoarele funcii au prototipul n "stdlib.h" i folosesc pentru


conversia valorilor numerice n ir i invers.
Funcia atof convertete un ir ctre tipul double. Dac conversia eueaz
(se ntlnete un caracter nenumeric) valoarea ntoars este 0. Dac primele
caractere ale irului sunt albe, acestea sunt ignorate.
double atof(const char *s);

Observaiile sunt valabile i pentru urmtoarele 3 funcii:


Funcia _atold convertete un ir ctre tipul long double:
long double _atold(const char *s);

Funcia atoi convertete un ir ctre tipul int:


int atoi(const char *s);

Funcia atol convertete un ir ctre tipul long:


long atol(const char *s);
Manual de informatic pentru clasa a XI-a 105

Aplicaia 3.17. Se citete un text alctuit din mai multe cuvinte. Se cere s se
calculeze suma valorilor numerice ntlnite n text. Se presupune c valorile
numerice sunt introduse corect.
irul se separ n entiti. Dac o entitate este alctuit exclusiv din '+',
'-', '.' i cifre, atunci ea este convertit n double i adunat la o variabil s,
iniializat 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;
}

Funcia ecvt are rolul de a converti o valoare de tip double ctre un ir.
Caracterul nul este adugat automat irului obinut. Forma general este:
char* ecvt(double valoare, int poz, int* zec, int* semn);
unde:
valoare - valoarea de convertit;
poz - numrul de poziii pe care trebuie s le ocupe irul obinut n urma
conversiei;
zec - adresa unei variabile, de tip int, care reine, dup apel, numrul
zecimalelor pe care le are numrul;
semn - adresa unei variabile, de tip int, care are rolul de a memora,
dup apel, 1, dac numrul este negativ, sau 0, n caz contrar.

Pn n prezent nu am studiat modul n care putem obine adresa unei


variabile. Reinei c dac a este o variabil, &a este adresa ei. Funcia utilizeaz
acest mecanism de transmitere a parametrilor pentru ca s poat returna anumite
rezultate pe care le gsim n variabilele respective. n cazul ei, variabila zec va
reine dup apel numrul ntregilor, iar variabila semn semnul rezultatului.

Funcia nu insereaz n ir nici punctul zecimal, nici semnul numrului. irul


obinut ncepe cu cifra cea mai semnificativ a numrului. Pentru ca irul obinut s
fie corect, este necesar s fie prelucrat, n continuare, de ctre programator. Dac
numrul poziiilor ocupate de ir (poz) este mai mare dect ocup data, irul este
completat n stnga cu un numr de 0 necesar. n cazul n care numrul poziiilor
este mai mic dect cel ocupat de numr, rezultatul este rotunjit.
Exemple: ecvt(val,3,&zec,&semn);
106 Capitolul 3 iruri de caractere

dac val reine 1234, atunci se returneaz irul "123", zec reine 4 i
semn reine 0.
dac val reine 1239, atunci se returneaz irul "124", zec reine 4 i
semn reine 0.
dac val reine 1, atunci se returneaz irul "100", zec reine 1 i semn
reine 0.
dac val reine -0.001, atunci se returneaz irul "100", zec reine -3
i semn reine 1.

Pentru a v convinge, rulai programul urmtor:


#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;
}

Funcia itoa are rolul de a converti o valoare de tip int ntr-un ir, a crui
adres este memorat n variabila sir. Valoarea baza reine baza de numeraie
ctre care s se fac conversia (de obicei baza este 10). n cazul bazei 10, irul
obinut reine i, eventual, semnul -. Funcia ntoarce adresa irului obinut.

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

Funcia ltoa are acelai efect ca itoa, deosebirea fiind dat de faptul c se
convertete ctre ir o valoare de tip long int.

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

Funcia ultoa are acelai efect ca itoa, deosebirea fiind dat de faptul c
se convertete ctre ir o valoare de tip unsigned long.

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

Pentru conversii, se mai pot utiliza urmtoarele funcii:


Manual de informatic pentru clasa a XI-a 107

Funcia
long strtol(const char *s, char **endptr, int baza);

are rolul de a converti un ir ctre long. n afara irului care trebuie convertit,
funcia primete ca parametru de intrare adresa unei variabile de tip char*. Dup
apel, aceast variabil va reine poziia primului caracter din ir care nu poate fi
convertit. De exemplu, dac irul este "12m21", dup apel, variabila a crei adres
a fost transmis ca parametru reine adresa caracterului 'm'. Dar care este rostul
existenei acestei informaii? Cu ajutorul ei se pot depista eventualele erori aprute
atunci cnd se introduc datele. Privii irul dat anterior ca exemplu: e clar c
utilizatorul a dorit s introduc un numr, dar n locul unei cifre a tastat 'm'.

Aplicaia 3.18. Programul urmtor testeaz dac o valoare introdus este


numeric i dac este cuprins n intervalul [10,20]. Testul propriu-zis se face
atunci cnd comparm numrul caracterelor convertite cu lungimea irului.
Egalitatea are semnificaia c ntreg irul este numeric. Variabila radix trebuie s
conin 8, 10 sau 16, adic baza n care este considerat numrul 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;
}

Funcia
double strtod(const char *s, char **endptr);
convertete un ir ctre double.
Funcia
long double _strtold(const char *(s), char **endptr);
convertete un ir ctre long double.
Funcia
unsigned long strtoul(const char *s,char **endptr,int baza);
convertete un ir ctre unsigned long.

n caz de depire - adic numrul nu poate fi memorat deoarece este n


afara tipului - ultimele patru funcii 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


fiiere text

3.3.11.1. Operaia de citire

Pentru a citi o linie a unui fiier text, se poate folosi funcia urmtoare:

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

Funcia citete un ir de caractere, pn cnd una dintre condiiile urmtoare


este ndeplinit:

a) au fost citite Nr_car-1 caractere;


b) a fost detectat sfritul de linie: '\n'.

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

Observaii

Caracterul care marcheaz sfritul liniei '\n' nu este inserat n ir, n


schimb este inserat caracterul nul (marcheaz sfritul irului).
Este suficient s apelm funcia cu primii doi parametri, ultimul fiind implicit.
Dac se dorete, ultimul parametru poate fi apelat explicit cu o valoare
convenabil, caz n care citirea se face pn la ntlnirea ei sau pn cnd
au fost citite Nr_car-1 caractere.

Programul urmtor citete linie cu linie un fiier text ale crui linii nu au
mai mult de 500 de caractere i afieaz liniile pe monitor. Nu se
cunoate lungimea fiecrei 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;
}

Observaie foarte important! Prin f>>Linie_citita, citirea se


efectueaz astfel:
a) ncepnd cu poziia curent a pointerului se sar toate caracterele albe;
b) se citesc toate caracterele pn la ntlnirea unui caracter alb.
De exemplu, dac o linie a fiierului conine irul " Afara ploua", se
citete irul "Afara". Testai!
Manual de informatic pentru clasa a XI-a 109

3.3.11.2. Operaia de scriere

Scrierea se face prin comanda f<< sir;. Prin aceast instruciune se


scrie ntreg irul, ncepnd cu poziia curent a pointerului, inclusiv caracterele albe.
Analizai exemplul urmtor:
#include <fstream.h>
main()
{ fstream f("f.dat",ios::out);
f<<" Afara este"<<endl;
f<<"primavara!";
f.close();
}

3.3.12. O modalitate de conversie de la ir la alt tip

Exist i alte posibiliti de conversie de iruri la alte tipuri de date. Un tip


special, numit istrstream, permite chiar declararea stream-urilor (fluxurilor) de
la iruri ctre variabile. "Citirea" se efectueaz cu ajutorul operatorului >> la fel ca
din fiier.

ir de caractere Variabil

Un ir X reine "1 2 3 4 5". O funcie special (numit constructor)


ataeaz irului X, un stream (flux), numit ins. Ea are doi parametri: irul X i
lungimea lui: istrstream ins(X, strlen(X));.

"Citirea" se efectueaz cu conversie ctre tipul respectiv. Programul va afia


numerele 1,2,3,4,5 cte unul pe linie!
#include <iostream.h>
#include <strstrea.h>
#include <string.h>
main()
{ char X[]="1 2 3 4 5";
istrstream ins(X, strlen(X));
int nr;
while (ins>>nr) cout<<nr<<endl;
}

Observai ct de simplu se detecteaz sfritul irului.

Tot aa, se pot "citi" mai multe cuvinte dintr-un ir. Programul urmtor citete
cuvintele i le afieaz. De aceast dat se consider c este ir valid i cel de
lungime 0, fapt care conduce la o nou citire i deci la ciclare. Pentru aceasta,
de fiecare dat, se testeaz ca lungimea irului citit s fie nevid.
110 Capitolul 3 iruri de caractere

Programul este prezentat n continuare:


#include <iostream.h>
#include <strstrea.h>
#include <string.h>
main()
{
char X[]="1 mama tata 4 bunica";
char cuvant[20];
istrstream ins(X, strlen(X));
while (ins>>cuvant && strlen(cuvant))
cout<<cuvant<<endl;
}

Probleme propuse
1. Se citete de la tastatur un text. Cuvintele se consider separate prin virgul,
spaiu sau punct. Cte cuvinte are textul citit?
2. Se citete de la tastatur un text i o succesiune de caractere. De cte ori
ntlnim aceast succesiune n cadrul textului?
3. Se citete un text. Dou cuvinte pot fi separate printr-unul sau mai multe spaii.
Se cere s se elimine spaiile inutile.
4. Se citete un fiier text care conine o singur propoziie, pe o linie. Programul
rearanjeaz literele n fiecare propoziie, n ordine alfabetic, pstrnd locul
cuvintelor. Numai literele de la 'A' la 'Z' sunt afectate. Celelalte caractere rmn
neschimbate. Ieirea va fi tot un fiier text. Numele fiierelor este ales de dvs.
Exemple:

THE PRICE OF BREAD IS $1.25 PER POUND.


ABC DDEEE EF HIIINO OP $1.25 PPR RRSTU.
THE LICENSE PLATE READ G76-ZA3.
AAA CDEEEEE GHILL NPRS T76-TZ3.
Junior Division
5. Cretere automat. Scriei un program care s mreasc toate numerele care
apar ntr-un document (citit dintr-un fiier text) dup un procent citit de la tastatur.
De exemplu, dac se introduce 12, procentul este: 12%. Toate numerele trebuie
afiate cu dou zecimale. Testai programul dvs. pe fraza:

"Batranul Mc Donald's avea 7 vaci care dadeau 120 de litri de


lapte pe zi. Ele veneau acasa la 4 P.M"

Junior Division
Manual de informatic pentru clasa a XI-a 111

6. Eliminare. "MSSSSPP" este versiunea micorat a cuvntului "MISSISSIPPI",


obinut prin eliminarea tuturor literelor "I". O fraz poate fi micorat i ea:
"TRAVERSAREA CAII FERATE" are versiunea micorat "TRVRSR C FRT", prin
eliminarea literelor "A", "I", "E". Se cere ca programul dvs. s elimine literele dintr-o
fraz citit din fiierul text "input.txt" i scris pe o singur linie. Literele care se
elimin se citesc de la tastatur, iar ieirea este n fiierul "output.txt".
Junior Division

7. Problem de lucru n colectiv - numai pentru elevii care studiaz limbajul


Pascal. iruri generalizate.
Presupunem c nu suntei mulumii de faptul c, n Pascal, irurile cu care
se poate lucra pot avea cel mult 255 de caractere.

De exemplu, dorim s lucrm cu iruri care au cel mult 3000 de caractere.


Un astfel de ir va fi reinut ca un vector de 3000 de caractere, iar sfritul unui ir
va fi marcat prin memorarea valorii 0 (n binar).

Scriei subprogramele urmtoare:


subprogram care determin lungimea unui ir;
subprogram care permite concatenarea a dou iruri;
subprogram care identific dac un ir este sau nu subir pentru altul;
subprogram care convertete un ir ctre un ntreg;
subprogram care convertete un ntreg ctre un ir;
subprogram care convertete un ir ctre un numr real;
subprogram care convertete un real ctre un ir;
subprogram care insereaz un ir pe o anumit poziie a altui ir;
subprogram care terge un subir al unui ir dat (se cunoate poziia
de nceput a subirului);
subprogram care permite scrierea unui ir ntr-un fiier text;
subprogram care permite citirea unui ir dintr-un fiier text.
112

Capitolul 4
Structuri de date neomogene

4.1. Noiuni introductive

La Tehnologia Informaiei i a Comunicaiilor am nvat s lucrm cu


tabele (Excel sau Access). S considerm o linie a unui tabel. De exemplu, ea
conine numele unei persoane (30 caractere alfabetice), vrsta (un ntreg) i
salariul brut (un numr real). Liniile, n totalitatea lor, alctuiesc tabelul. Fcnd
abstracie de faptul c la Tehnologia Informaiei i a Comunicaiilor se folosesc
programe gata fcute, specializate, destinate unor utilizatori crora nu li se pretinde
s fie experi n informatic, se pot pune mai multe ntrebri, cum ar fi:
cum se poate reine n memorie o linie a unui tabel?
cum se poate reine un tabel pe suport extern?

Rspunsul la prima ntrebare este dat de existena unor variabile speciale,


care au o structur neomogen. O astfel de variabil este mprit, la rndul ei, n
mai multe subvariabile numite, uneori, cmpuri. Pentru exemplul considerat, o
variabil, s-o numim Pers, va conine o subvariabil numit Nume, care este un ir
de caractere; o alta, numit Varsta, pentru care se alege un tip ntreg i o alta,
numit Salariu, de un tip real. Astfel, observm c variabila Pers este
neomogen din punct de vedere al structurii, spre deosebire, de exemplu, de un
vector care are toate componentele de acelai tip. Cum se declar i cum se
utilizeaz o astfel de variabil vom nva n acest capitol.

n ceea ce privete rspunsul la a doua ntrebare, o posibilitate de a reine un


tabel este dat de crearea unui fiier cu tip.
Crearea i exploatarea fiierelor cu tip nu face parte din programa dvs., dar
este recomandabil s le studiai n mod individual.

4.2. Structuri neomogene n Pascal

4.2.1. Tipul Record


n practic, apar situaii n care toate tipurile de date nvate pn n prezent
nu ne sunt de mare folos. S analizm un exemplu.

Presupunem c dorim s prelucrm anumite date referitoare la mai muli


elevi. Pentru fiecare elev, cunoatem:
Manual de informatic pentru clasa a XI-a 113

1. Numele i prenumele - se reine n string[20];


2. Nota la matematic variabil de tip real;
3. Nota la fizic - variabil de tip real;
4. Vrsta - variabil de tip byte;
5. Dac este biat sau fat - o variabil de tip char reine 'B' sau 'F'.

Observai faptul c informaiile referitoare la un elev sunt eterogene (de la


numele de tip string, pn vrsta de tip byte). n Pascal, exist posibilitatea ca
toate aceste informaii s se regseasc ntr-un singur tip de nregistrare,
numit RECORD.
n programul urmtor, observm cum se declar (n acest caz, elev), cum
se citete i cum se afieaz o variabil (numit e) de acest tip:
type elev = record
nume: string[20];
n_mat, n_fiz: real;
varsta: byte;
sex: char;
end;
var e: elev;
begin
write('nume elev '); readln(e.nume);
write('nota matematica '); readln(e.n_mat);
write('nota fizica '); readln(e.n_fiz);
write('varsta '); readln(e.varsta);
write ('sexul '); readln(e.sex);
writeln('nume ', e.nume);
writeln('nota matematica ', e.n_mat);
writeln('nota fizica ', e.n_fiz);
writeln('varsta ', e.varsta);
writeln('sexul ', e.sex);
end.

Observaii

Pentru a adresa un anumit cmp al variabilei de tip RECORD, se folosete


numele ei, urmat de '.', apoi de numele cmpului.

Exemplu: e.nume reprezint cmpul nume al variabilei e.


n cazul n care avem mai multe cmpuri adiacente (care urmeaz unul dup
altul) i de acelai tip, le putem declara deodat, separndu-le prin virgul.

Exemplu: cmpurile: Nota la matematic i Nota la fizic sunt adiacente


i de tip real, deci declaraia este:
n_mat, n_fiz: real;
114 Capitolul 4. Structuri de date neomogene

Iat cum arat n memorie o nregistrare de acest tip - ocup 35 de octei:

21 octeti 6 octeti 6 octeti 1 octet 1 octet



nume n.mat n. fiz virsta sex

Cmpurile unei variabile de acest tip pot fi citite sau afiate individual. De
asemenea, li se pot atribui valori, pot intra n calcule, tot individual.
Dac a i b sunt dou variabile de acelai tip RECORD se pot face fr
probleme atribuiri de genul a:=b;.

4.2.2. Accesul simplificat la cmpuri

Aa cum a fost prezentat, modul de acces la cmpurile unei variabile de tip


RECORD este deosebit de greoi - ntotdeauna punem numele variabilei n fa. n
realitate, accesul la cmpurile unei astfel de variabile se poate face mult mai uor,
prin utilizarea instruciunii WITH, cu forma general:
with var1, var2, ..., varn do instruciune

i are rolul ca, n cadrul instruciunii subordonate, adresarea s se fac simplificat,


adic prin utilizarea exclusiv a numelui cmpului.

Relum programul anterior, dar de aceast dat am utilizat instruciunea with:


type elev = record
nume: string[20];
n_mat, n_fiz: real;
varsta: byte;
sex: char;
end;
var e: elev;
begin
with e do
begin
write('nume elev '); readln(nume);
write('nota matematica '); readln(n_mat);
write('nota fizica '); readln(n_fiz);
write('varsta '); readln(varsta);
write ('sexul '); readln(sex);
writeln('nume ',nume);
writeln('nota matematica ',n_mat);
writeln('nota fizica ',n_fiz);
writeln('varsta ',varsta);
writeln('sexul ',sex);
end
end.
Manual de informatic pentru clasa a XI-a 115

4.2.3. nregistrri imbricate

n general, cmpurile care alctuiesc un tip RECORD pot avea orice tip. O
ntrebare: dar RECORD? Rspunsul este afirmativ. Analizai programul urmtor:
program r3;
type elev = record
nume: string[20];
data_n: record
zi, luna: byte;
an: integer;
end;
n_mat, n_fiz: real;
varsta: byte;
sex: char;
end;
var e: elev;
begin
with e do
begin
write('nume elev '); readln(nume);
with data_n do
begin
write('ziua nasterii '); readln(zi);
write('luna nasterii '); readln(luna);
write('anul nasterii '); readln(an);
end;
end
end.

Pentru un elev, se citesc numele i data naterii. Aceasta din urm este de
tipul RECORD (include ziua, luna i anul naterii). Pentru adresarea simplificat a
cmpurilor care o alctuiesc s-a folosit, din nou, instruciunea WITH. Adresarea s-ar
fi putut face i aa: e.data_n.zi, e.data_n.luna, e.data_n.an. Evident, o
astfel de adresare este greoaie i nerecomandat
Putem avea oricte niveluri de imbricare (un tip RECORD include un alt tip
RECORD, care include un altul, .a.m.d.).

4.2.4. Vectori de nregistrri


Aa cum s-a artat, elementele unui vector pot fi de orice tip, deci inclusiv
de tip RECORD.
Adresarea cmpurilor se face prin numele vectorului, urmat de perechea de
paranteze drepte ntre care este trecut indicele componentei, apoi selecia
propriu-zis se face aa cum am nvat.
Dac v este vectorul, iar nregistrarea este de tip elev (ca n programul
urmtor), atunci numele se selecteaz prin v[i].nume, iar ziua naterii
prin v[i].nume.zi.
116 Capitolul 4. Structuri de date neomogene

i de aceast dat, pentru selecia simplificat, se poate utiliza cu succes


instruciunea WITH, aa cum rezult din programul urmtor, unde se citete un
vector cu n nregistrri de tip elev:

type elev = record


nume: string[20];
data_n: record
zi,luna:byte;
an:integer;
end;
end;
vector_inregistrari = array[1..9] of elev;
var v: vector_inregistrari;
n, i: integer;
begin
write('n='); readln(n);
for i := 1 to n do
with v[i] do
begin
write('nume elev '); readln(nume);
with data_n do
begin
write('ziua nasterii '); readln(zi);
write('luna nasterii '); readln(luna);
write('anul nasterii '); readln(an);
end;
end
end.

4.2.5. nregistrare cu variante

Nu toate nregistrrile au o structur fix (acelai numr de cmpuri) aa


cum au fost cele prezentate. Sunt cazuri cnd un tip nregistrare are o parte fix
urmat de o parte variabil.

S presupunem c ne intereseaz o situaie referitoare la studiile unei


persoane. O astfel de nregistrare are o parte fix dat de cmpurile care rein
numele, vrsta i tipul de studii. O persoan poate s nu aib studii, caz n care nu
mai este necesar s avem alte informaii, poate s fi fcut cteva clase de coal
general (ne-ar putea interesa cte clase), s fi terminat liceul (caz n care dorim
s tim anul terminrii i oraul) sau s aib studii superioare (i atunci ne
intereseaz numele facultii i numrul de ani de studiu n cadrul facultii
respective). n concluzie, n funcie de tipul de studii, nregistrarea arat altfel.
Limbajul permite ca nregistrrile s aib o structur variabil. Cum se
realizeaz aceasta?
n primul rnd, trebuie reinut c partea variabil este plasat n cadrul
nregistrrii dup partea fix. O parte variabil se dezvolt n cadrul
nregistrrii dup valorile pe care le ia un cmp situat n cadrul prii fixe.
Manual de informatic pentru clasa a XI-a 117

n programul care urmeaz, se exemplific descrierea unui tip de nregistrare


variabil, selecia prii variabile fcndu-se n funcie de valorile pe care le ia
cmpul studii. Pentru selectare, se folosete o clauz special numit CASE.
Cmpul dup care se face selecia apare descris n aceast clauz.
Cmpul selector trebuie s fie de tip ordinal cu un numr finit de
elemente. n funcie de valorile pe care le poate lua cmpul selector, se va dezvolta
partea variabil. n esen, se scriu pe rnd valorile posibile ale cmpului selector.
n dreptul fiecrei valori se trece partea pe care trebuie s o conin nregistrarea n
acest caz. Aceasta se ncadreaz ntre paranteze rotunde i poate fi chiar vid.
Cmpurile prezente ntre paranteze se scriu separate prin ';'.

Analizai programul urmtor (care citete o singur nregistrare):


type persoana = record
nume: string[30];
varsta: byte;
case studii: char of
'f': ( );
'g': (nr_cl: integer);
'l': (an_t: integer;
oras: string);
's': (fac: record
nume_f: string[20];
an_s: byte
end)
end;
var p: persoana;
begin
write('nume '); readln(p.nume);
write('varsta '); readln(p.varsta);
write('studii '); readln(p.studii);
case p.studii of
'g': begin
write('numar clase '); readln(p.nr_cl);
end;
'l': begin
write('anul terminarii liceului '); readln(p.an_t);
write('orasul '); readln(p.oras);
end;
's': begin
write('numele facultatii '); readln(p.fac.nume_f);
writeln(' ani de studii facultate ');
readln(p.fac.an_s);
end
end {case}
end.

Observaii

Pentru fiecare nregistrare de acest tip compilatorul rezerv numrul de


octei necesari celei mai lungi variante.
Este preferabil ca citirea unei variabile de tip nregistrare cu variante s se
fac prin utilizarea instruciunii CASE.
118 Capitolul 4. Structuri de date neomogene

4.3. Structuri neomogene n C++

4.3.1. Tipul struct

n practic, apar situaii n care toate tipurile de date nvate pn n prezent


nu ne sunt de mare folos. Pentru a nelege aceasta, vom porni de la un exemplu.

Presupunem c dorim s prelucrm date referitoare la mai muli elevi.


Astfel, pentru fiecare elev cunoatem:
1. Numele - char[20];
2. Prenumele - char[20];;
2. Nota matematic - float;
3. Nota informatic - float;
4. Vrsta - int;

Observai faptul c informaiile referitoare la un elev sunt eterogene: iruri de


caractere, numere reale sau ntregi. Cum am putea rezolva problema prin utilizarea
cunotinelor de care dispunem? Ar fi necesari 5 vectori, cte unul pentru fiecare
informaie. Astfel am avea doi vectori cu elemente de baz iruri de caractere
pentru nume i prenume, doi vectori cu elemente de baz de tip float pentru note
i unul cu elemente de baz de tip int pentru vrst. Fiecrui elev i, i corespund
componentele i ale fiecrui vector. O astfel de abordare este greoaie, nenatural.
Ar fi cu mult mai bine dac limbajul ar dispune de un mecanism prin care fiecrui
elev s-i corespund o singur nregistrare.
n C++ exist un tip de date, numit struct, care ne permite acest lucru.
Forma general este (ce este trecut ntre paranteze drepte este considerat facultativ):
struct [nume structura]
{
[<tip> <nume variabila[, nume variabila, ...]>] ;
[<tip> <nume variabila[, nume variabila, ...]>] ;
...
} [lista de variabile] ;

Pentru exemplul dat, structura este:


struct elev
{
char nume[20], prenume[20];
float nota_mate,nota_info;
int varsta;
};

i se numete elev.
Manual de informatic pentru clasa a XI-a 119

Exist dou posibiliti de declarare a variabilelor care alctuiesc structura.

1. Aa cum rezult din forma general, scriind la sfrit numele variabilelor:


struct elev
{ char nume[20], prenume[20];
float nota_mate,nota_info;
int varsta;
}inr1,inr2;

Aici, inr1 i inr2 sunt dou variabile de tipul elev.

2. Clasic, declarnd variabilele aa cum suntem obinuii:


elev inr1, inr2;

Definiia structurii poate figura att n cadrul funciei main() ct i n faa ei,
dup includerile de fiiere antet. n ce ne privete, vom prefera a doua variant.
Se pune urmtoarea ntrebare: fiind dat o variabil de un tip struct, care
este modalitatea de acces la cmpurile ei? Pentru aceasta, se folosete operatorul
de selecie direct, notat cu '.', operator cu prioritate maxim.

Fie inr o variabil de tipul elev. Atunci:

inr.nume - reprezint irul nume al variabilei inr;


inr.nume[0] reprezint primul caracter al irului nume;
inr.nota_mate reprezint cmpul nota_mate al variabilei inr.

n programul urmtor, se citete i se tiprete o variabil de tipul elev:


#include <iostream.h>
struct elev
{ char nume[20], prenume[20];
float nota_mate,nota_info;
int varsta;
};
main()
{ elev inr;
cout<<"Nume "; cin>>inr.nume;
cout<<"Prenume "; cin>>inr.prenume;
cout<<"Nota matematica ";
cin>>inr.nota_mate;
cout<<"Nota informatica ";
cin>>inr.nota_info;
cout<<"Varsta ";
cin>>inr.varsta;
cout<<"Am citit:"<< endl
<<inr.nume<<" "<<inr.prenume <<endl
<<inr.nota_mate<<endl
<<inr.nota_info <<endl
<<inr.varsta;
}
120 Capitolul 4. Structuri de date neomogene

ntre dou variabile de acelai tip struct se poate folosi atribuirea. Astfel,
dac inr1, inr2 sunt dou variabile de tip elev, prin atribuirea inr1=inr2,
variabila inr1 ia aceeai valoare ca variabila inr2. n C++, o astfel de atribuire se
mai numete copiere bit cu bit.

4.3.2. nregistrri imbricate

Exist situaii cnd un tip structurat conine n interiorul su un alt tip


structurat. Privii tipul urmtor:
struct elev
{ char nume[20], prenume[20];
struct
{ int clasa;
float note[20];
}situatie;
int varsta;
};

Structura de baz este elev. n interiorul su se gsete o alt structur, de


aceast dat fr nume, dar pentru care exist declarat "o variabil" numit
situatie. n realitate nu este vorba de o variabil, ci de un nume prin intermediul
cruia poate fi accesat un element al structurii. Am vzut c, n general, elementele
structurii se acceseaz prin numele variabilei de tipul structurii respective.

Fie inr o nregistrare de tipul elev. n aceste condiii, accesarea


elementelor situate n interiorul substructurii se face ca mai jos:

inr.situatie.clasa - se acceseaz cmpul clasa al


substructurii;
inr.situatie.note[0] - se acceseaz prima not a
vectorului inclus n substructur.

Exemplul urmtor prezint o alt posibilitate de declarare a structurilor:


struct elev1
{ char nume[20], prenume[20];
struct
{ int clasa;
float note[20];
}situatie_1, situatie_2;
int varsta;
};

Tipul structurat elev1, subordoneaz, pe lng alte tipuri, dou structuri


situatie_1 i situatie_2. Forma este echivalent cu cea n care cele dou
structuri sunt descrise una dup alta.

n practic, se folosete termenul "imbricate" pentru una sau mai multe


structuri incluse una n alta, ca mai sus.
Manual de informatic pentru clasa a XI-a 121

4.3.3. nregistrri cu structur variabil

Pentru nceput, vom studia un tip aparte de dat structurat, numit union.
Analizai programul urmtor:
#include <iostream.h>
union test
{
int a;
char b[10];
double c;
};
main()
{
test var;int i;
cin>>var.c; cout<<var.c<<endl;
cin>>var.b; cout<<var.b;
}

Variabila var este de tipul union. Ea conine un ntreg, un vector de


caractere i o variabil real. Cele trei cmpuri subordonate ocup respectiv 2
octei, 10 octei i 8 octei. n realitate, pentru toate variabilele s-au reinut 10 octei
- adic octeii necesari pentru a memora cmpul cel mai lung. Aceasta nseamn
c, la un moment dat, se poate memora doar un singur cmp dintre cele
subordonate. n exemplu, am utilizat la nceput variabila ntreag, apoi irul
de caractere.
Iat forma general a unei uniuni:
union [<numele uniunii>]
{
<tip> <nume variabila> ;
...
} [lista de variabile] ;

Cu excepia faptului c numai un cmp poate fi ocupat la un moment dat,


toate celelalte reguli sunt identice cu cele de la structuri. Bine, vei ntreba, dar la
ce folosesc "uniunile"? Sunt situaii n care nregistrrile nu au format fix, ci variabil.
Ca s fiu clar, voi da un exemplu.
S presupunem c ne intereseaz o situaie referitoare la studiile unei
persoane. O astfel de nregistrare are o parte fix dat de cmpurile care rein
numele, vrsta i tipul de studii. O persoan poate s nu aib studii, caz n care nu
mai este necesar s avem alte informaii, poate s fi fcut cteva clase de coal
general (near putea interesa cte clase), s fi terminat liceul (caz n care dorim
s tim anul terminrii i oraul) sau s aib studii superioare (i atunci ne
intereseaz numele facultii i numrul de ani de studiu n cadrul facultii
respective). n concluzie, n funcie de tipul de studii, nregistrarea arat altfel.
Limbajul permite ca nregistrrile s aib o structur variabil. Cum se
realizeaz aceasta?
122 Capitolul 4. Structuri de date neomogene

Uniunile pot fi incluse n structuri. La rndul lor, structurile pot fi incluse n


uniuni. Pentru exemplul nostru, nregistrarea are o parte fix care este alctuit din
numele persoanei respective i o variabil de tip char, numit studii, care reine
tipul studiilor pe care le are persoana respectiv. Astfel, dac persoana respectiv
nu are studii, reine 'f', dac are cteva clase de general, reine 'g', dac are
liceul, reine 'l', iar dac are studii superioare, 's'.

Apoi urmeaz partea variabil care este o uniune. Aceasta conine o


variabil nr_clase - pentru cazul n care studii reine 'g', o structur pentru
cazul n care studii, reine 'l', .a.m.d. n rest, citirea i afiarea nregistrrii se
face, de fiecare dat, sub switch, n funcie de datele citite sau coninute. n
concluzie, nregistrarea are structura variabil, dar ocup un numr fix de octei.
Uniunea poate figura oriunde n interiorul structurii - nu este obligatoriu ca
aceasta s fie scris la sfritul structurii. Analizai programul:
#include <iostream.h>
struct persoana
{ char nume[30], studii;
union
{
int nr_clase;
struct
{int an_t;
char oras[20];
}liceu;
struct
{char nume_f[30];
int nr_ani;
}facultate;
}std;
};

main()
{ persoana pers;
cout<<"Nume persoana ";cin.get(pers.nume,30);
cout<<"Studii f-fara g-generala,l-liceu;s-superioare) ";
cin>>pers.studii;
switch (pers.studii)
{
case 'g': cout<<" numar clase ";
cin>>pers.std.nr_clase;
break;
case 'l': cout<<"anul terminarii liceului ";
cin>>pers.std.liceu.an_t;
cout<<"orasul ";
cin>>pers.std.liceu.oras;
break;
case 's': cout<<"numele facultatii ";cin.get();
cin.get(pers.std.facultate.nume_f,30);
cout<<"nr ani de studiu ";
cin>>pers.std.facultate.nr_ani;
}
Manual de informatic pentru clasa a XI-a 123

//afisez inregistrarea
cout<<pers.nume<<endl;
switch (pers.studii)
{
case 'f': cout<<"n-are saracu' studii "; break;
case 'g': cout<<" numar clase "<<pers.std.nr_clase; break;
case 'l': cout<<"a terminat liceul in "
<<pers.std.liceu.an_t
<<" in orasul "<<pers.std.liceu.oras; break;
case 's': cout<<"numele facultatii "
<<pers.std.facultate.nume_f
<<" nr ani de studiu "
<< pers.std.facultate.nr_ani;
}
}

Probleme propuse
1. Citii o variabil cu urmtoarea structur:

nume_elev: 30 caractere;
data_nasterii:
zi : intreg;
luna : intreg;
an : intreg;
nota matematica real;
nota informatica real;
nota engleza real;
media real;

(media se calculeaz, nu se citete).

Testai dac datele au fost introduse corect. Citirea se va verifica prin afiarea
rezultatului.
2. Citii n nregistrri de tipul celei de mai sus i afiai-le n ordinea alfabetic a
numelui.

3. Aceeai problem ca cea anterioar, numai c afiarea se va face n ordinea


descresctoare a mediilor.

4. Presupunnd c nregistrrile se refer la un examen de admitere dat, s se


afieze n ordine descresctoare a mediilor, n limita unui numr de locuri sau pn
cnd se epuizeaz toate nregistrrile elevilor cu medii mai mari sau egale cu 5. n
cazul n care pe ultimul loc avem mai muli elevi cu aceeai medie, toi acetia sunt
considerai admii. Programul va afia numrul de locuri n plus.
124

Capitolul 5
Structuri de date

5.1. Conceptul de structur de date

Orice algoritm primete date de intrare, le prelucreaz i obine date de


ieire. n fiecare caz, datele de intrare, datele intermediare - cele create n timpul
prelucrrii - i datele de ieire sunt structurate (organizate) ntr-un anumit fel care
corespunde intrrii, necesitilor de prelucrare sau a celor de utilizare ulterioar.

Pentru a veni n sprijinul programatorilor, limbajele de programare evoluate


(de exemplu, C++ sau Pascal) pun la dispoziia acestora posibilitatea organizrii
datelor n anumite "abloane", numite tipuri de date. Mai precis, prin tip de date
se nelege:

o mulime de valori;
o regul de codificare a acestora;
1

o mulime de operaii definite pe mulimea datelor.

La rndul lor, tipurile de date pot fi:

simple - descriu date care aparin unor mulimi care nu sunt


rezultate ca produs cartezian al altor mulimi. Exemplu: int.

structurate - descriu date care aparin unor mulimi rezultate ca


produs cartezian al altor mulimi.

1. Privii declaraia de mai jos:

Varianta Pascal Varianta C++


type rational=record struct rational
p,q:integer; { int p,q;
end };

Prin tipul de mai sus se descrie structura unei variabile capabil s rein
numere raionale. Fie A1 mulimea valorilor care pot fi memorate prin utilizarea
tipului ntreg. Fie A2=A1-{0}. Atunci, o variabil de tip rational poate memora
valori care aparin mulimii A1A2. Evident, este sarcina programatorului ca valoarea
reinut de variabila corespunztoare lui q s fie diferit de 0.

n unele lucrri vei ntlni definiii ale tipului de date care exclud regula de codificare. Aici
1

se pornete de la ideea c nu ntotdeauna este necesar, pentru a lucra cu tipul respectiv,


s cunoatem regula de codificare.
Manual de informatic pentru clasa a XI-a 125

2. Mai jos, este prezentat un alt exemplu:

Varianta Pascal Varianta C++


type vector=array [1..100] of real; double a[100];
var a:vector;

Fie B mulimea valorilor care pot fi reinute de tipul real. Atunci, variabila a
poate reine la un moment dat un element al mulimii:

B
B ...
B .

de n ori

Practica impune utilizarea unor structuri ale datelor de o mare varietate, care
nu se suprapun ntotdeauna peste tipurile care pot fi descrise prin limbaj.

Definiia 5.1. Prin structur de date vom nelege un ansamblu de date


caracterizat prin relaiile existente ntre ele i prin operaiile care pot fi
efectuate cu datele respective.

Vom numi nod, o variabil de un tip oarecare. De obicei, acest tip este
structurat. Dup caz, termenul nod poate fi nlocuit cu articol, nregistrare sau
entitate.

n cele mai multe cazuri, "ansamblul de date" care formeaz structura este
alctuit dintr-o mulime cu un numr variabil de noduri.

Relaiile existente ntre noduri i operaiile care pot fi efectuate cu ele vor fi
prezentate prin exemple.

De reinut!

Tipul variabilei care alctuiete nodul nu caracterizeaz structura de date.

Structura de date este un concept abstract. Mai precis, 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).

n facultile de profil (calculatoare, informatic) se studiaz disciplina numit


"Structuri de date". Ea este una dintre disciplinele care se includ n cadrul
mai larg al informaticii, tot aa cum, de exemplu, algebra se studiaz ca
disciplin aparte a matematicii.

n practic s-au impus anumite structuri. Acest lucru este datorat faptului c
exist muli algoritmi care le utilizeaz. Ele vor fi tratate, pe scurt, n
paragrafele urmtoare.
126 Capitolul 5. Structuri de date

5.2. Structura de tip list liniar

5.2.1. Prezentarea structurii

Definiia 5.2. O list liniar este o colecie de n0 noduri, X1, X2, ..., Xn
aflate ntr-o relaie de ordine. Astfel, X1 este primul nod al listei, X2
este al doilea nod al listei, ..., Xn este ultimul nod.

Operaiile permise sunt:

accesul la oricare nod al listei n scopul citirii sau modificrii informaiei


coninute de acesta;
adugarea unui nod, indiferent de poziia pe care o ocup n list;
tergerea unui nod, indiferent de poziia pe care o ocup n list.
schimbarea poziiei unui nod n cadrul listei.

Faptul c structura este liniar nseamn c fiecare nod, cu excepia


ultimului, are un singur nod succesor (care i urmeaz n list) i, cu excepia
primului nod, are un singur predecesor (care se afl imediat naintea lui
n list).

Dac vorbim la modul general de o structur de date, nu ne intereseaz,


pentru moment, modul n care aceasta va fi implementat (adic unde este
memorat i cum se efectueaz operaiile permise asupra ei). Pur i simplu, ne
imaginm lista ca mai jos, unde fiecare nod i memoreaz informaia infi:

inf 1 inf 2 inf n


nod1 nod2 nodn

Exemple de aplicaii care utilizeaz liste liniare:

a) Evidena situaiei colare a elevilor unei clase. Fie n numrul elevilor. Aici, un
nod reine numele unui elev i notele la diversele materii. Vom avea deci, n noduri.
Nodurile vor fi memorate n ordinea alfabetic a numelor elevilor. n clasa
respectiv pot fi transferai elevi din alte clase, caz n care se adaug noduri. Din
clas, unii elevi pot pleca n alte clase, caz n care se terg noduri.

b) Se dorete s se rein un ir de numere naturale, n ordinea n care au fost


citite de la tastatur. Aici, un nod reine un numr natural.

Nu toate aplicaiile utilizeaz liste liniare. Exemple de structuri care nu sunt


liste liniare:
se dau n orae i oselele care unesc unele dintre aceste orae;
arborele genealogic al unei persoane.
Manual de informatic pentru clasa a XI-a 127

5.2.2. Liste alocate secvenial

Din acest moment ne punem problema s vedem modul n care se poate


implementa o list liniar.

O prim form de alocare este cea secvenial. n cazul acestei alocri,


nodurile listei ocup poziii succesive n memorie. Acest tip de alocare se ntlnete
des, de cte ori utilizm vectorii. Altfel spus, primul nod al listei va fi reinut de
primul element al vectorului, al doilea nod al listei de al doilea element al vectorului,
.a.m.d.

n continuare, urmrim modul n care putem efectua operaiile permise cu o


list liniar.

a) Accesul la oricare nod al listei se poate face cu mare uurin. Dac dorim s
adresm nodul k, atunci scriem V[k] (presupunnd c vectorul care reine nodul
se numete V).

b) tergerea unui nod, indiferent de poziia pe care o ocup n list

Fie lista alocat secvenial:

7 3 1 2 8 9 5 8 3 2 6

Eliminm al doilea nod - coninut 3 (tergem coninutul nodului 2):

7 1 2 8 9 5 8 3 2 6

Pentru a pstra structura i pentru a nu pierde informaii, este obligatoriu ca


informaiile reinute de nodurile care urmeaz nodului ters s fie deplasate ctre
stnga:

7 1 2 8 9 5 8 3 2 6

Practic, de aceast dat, nodul 2 (elementul de indice 2 al vectorului) va


reine informaia celui de-al treilea nod; nodul 3, va reine informaia celui
de-al patrulea nod; .a.m.d. n multe aplicaii, aceast modificare nu prezint
importan.

c) Adugarea unui nod, indiferent de poziia pe care o ocup n list

Fie lista alocat secvenial:

7 3 1 2 8 9 5 8 3 2 6
128 Capitolul 5. Structuri de date

Nodul 2 va conine 5 (practic, adugm un nod). ncepnd cu nodul 2,


deplasm toate informaiile asociate nodurilor ctre dreapta:

7 3 1 2 8 9 5 8 3 2 6

Acum se completeaz valoarea reinut de nodul 2:

7 5 3 1 2 8 9 5 8 3 2 6

Observaii

i de aceast dat, nodurile vor reine informaii diferite. De exemplu, nodul


3 va reine ce anterior reinea nodul 2, .a.m.d.

n concluzie, la alocarea secvenial, accesul la nod este foarte rapid, dar


adugarea sau tergerea unui nod se fac cu efort de calcul, pentru c
necesit deplasri ale coninuturilor nodurilor.

5.2.3. Liste alocate nlnuit

Exist dou feluri de alocare nlnuit: alocare simplu nlnuit i alocare


dublu nlnuit. n acest paragraf prezentm principiile alocrii nlnuite, urmnd ca
n paragraful urmtor s artm modul n care implementm listele alocate nlnuit.

1. O list liniar simplu nlnuit este o structur de forma:

in1 adr2 in2 adr3 inn nil


adr1 adr2 adrn

Semnificaia notaiilor folosite este urmtoarea:

adr1, adr2, adr3, ..., adrn reprezint adresele celor n nregistrri;

in1, in2, ..., inn reprezint informaiile coninute de noduri, de alt natur
dect cele de adres.

nil - are semnificaia "nici o adres" - elementul este ultimul n list.

Dup cum observm, fiecare nod, cu excepia ultimului, reine adresa


nodului urmtor.

2. Alocarea dublu nlnuit. Alocarea simplu nlnuit permite parcurgerea listei


ntr-un singur sens (de la stnga la dreapta). n cazul n care se dorete ca lista s
poat fi parcurs n ambele sensuri, se utilizeaz alocarea dublu nlnuit. Aici,
Manual de informatic pentru clasa a XI-a 129

fiecare nod reine adresele predecesorului i succesorului su, aa cum se vede n


figura urmtoare:

nil in1 adr2 adr1 in2 adr3 adrn-1 inn nil

adr1 adr2 adrn

5.2.4. Implementarea alocrii nlnuite prin utilizarea


vectorilor

Aa cum am nvat, lista liniar este alctuit din mai multe noduri ntre care
exist o relaie de ordine. n cazul alocrii nlnuite, informaia memorat de
fiecare nod va cuprinde i un cmp de adres -n cazul alocrii simplu nlnuit-
sau dou cmpuri de adres -n cazul alocrii dublu nlnuit. n acest paragraf
vom studia implementarea alocrii simplu nlnuit.

Iat cum se descrie informaia dintr-un nod, n cazul listelor alocate simplu
nlnuit, atunci cnd acesta reine un numr ntreg.

info adresa nodului urmtor

Varianta Pascal Varianta C++


type Adresa=Integer; typedef int adresa;
nod=record struct nod
info:integer; { int info;
adr_urm:Adresa; adresa adr_urm;
end; };

Pentru memorarea listei folosim un vector care are componentele de tip Nod,
descris mai jos:

Varianta Pascal Varianta C++


Lista=array[1..1000] of Nod; nod L[1000];
var L:lista;

Din descriere rezult c lista poate avea cel mult 1000 de noduri. Acesta
este spaiul disponibil.

Privii exemplul de mai jos:

L 7 3 5 4 1 5 4 0

1 2 3 4 5 6
130 Capitolul 5. Structuri de date

Dac facem abstracie de implementare, lista este:

7 3 5 4 1 5 4 0

1 3 4 5

Ce observm?

a) Fiecare nod trebuie s rein i adresa nodului urmtor. Adresa este, de fapt,
indicele componentei din vector care reine informaia asociat nodului urmtor.
Prin urmare, necesarul de memorie este mai mare.

b) Nodurile nu ocup adrese succesive n memorie. De exemplu, dei primul nod


al listei este reinut de prima component a vectorului (de indice 1), al doilea nod al
listei este reinut de componenta de indice 3 a vectorului. Din acest motiv, vom face
distincie ntre numrul unui nod (acesta este n cadrul listei) i indicele vectorului
unde este memorat. n exemplul de mai sus, nodul 2 reine 5, iar el este memorat
de componenta de indice 3 a vectorului.

Pentru realizarea practic a implementrii apar o serie de probleme. Acestea


vor fi rezolvate n cele ce urmeaz.

Problema 1. Gestiunea memoriei. Conceptul de list nu precizeaz numrul


de noduri pe care ea le poate avea. n practic, numrul de noduri este limitat.
Aceasta nseamn c, la un moment dat, numrul de noduri poate fi depit. Prin
urmare, programul care lucreaz cu o astfel de list trebuie s gestioneze
spaiul disponibil.

n aceste condiii, vectorul care reine nodurile, va fi dublat de un altul, ale


crui componente rein 1 sau 0, dup cum componenta de acelai indice a
vectorului L reine sau nu un nod. Eventual, se poate utiliza o variabil care
memoreaz numrul de componente ale vectorului care reine nodurile.

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. Accesul la un nod al listei. Spre deosebire de alocarea


secvenial, unde accesul este imediat, la alocarea nlnuit accesul se face
ncepnd cu primul nod al listei. Dac nu acesta este nodul cutat, se trece la nodul
urmtor (orice nod conine adresa nodului urmtor, .a.m.d.).

Problema 3. Adugarea unui nod. S presupunem c n lista de mai jos


dorim s adugm, dup al treilea nod, un nod cu informaia 9.
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, se observ c primul element liber al


vectorului L este cel de indice 2.

a) Marcm nodul ca ocupat (ocupat[2] va reine 1).

b) Memorm informaia: 9.

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 reine ca adres urmtoare, adresa urmtoare reinut 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 reine ca adres urmtoare, adresa nodului nou introdus,


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, lista va fi:

7 3 5 4 1 5 9 5 4 0

1 3 4 5 5
132 Capitolul 5. Structuri de date

Problema 4. tergerea unui nod. S presupunem c n lista de mai jos dorim


s tergem al doilea nod.

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 gsete n elementul de indice 3. Prin urmare, ocupat[3]


va reine 0. n continuare, primul nod va reine ca adres urmtoare adresa nodului
3 (pentru c acesta urmeaz nodului 2). Aceast adres se ia din cmpul de
adres urmtoare a nodului care urmeaz s fie ters.

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

Observai faptul c, dei nu am ters informaia asociat nodului, acesta


devine inaccesibil prin parcurgerea listei ncepnd cu primul nod.

Lista va deveni:

7 4 1 5 4 0

1 4 5

Dezavantajele alocrii nlnuite sunt:

1. Accesul la un nod al listei se face prin parcurgerea nodurilor care l


preced. Aceasta necesit un efort de calcul.

2. Informaiile de adres, prezente n cadrul fiecrui nod ocup memorie.

Avantajele alocrii nlnuite sunt date de faptul c operaiile de adugare


sau eliminare a unui nod se fac rapid.

Exemplele sunt date pentru lista liniar simplu nlnuit, dar bine nelese,
ne permit s deducem singuri modul de efectuare a operaiilor respective pentru
liste dublu nlnuite.
Manual de informatic pentru clasa a XI-a 133

5.3. Structura de tip stiv

Definiia 5.3. Stiva este o list pentru care singurele operaii permise
sunt:

adugarea unui element n stiv;


eliminarea, consultarea, sau modificarea ultimului element introdus
n stiv.

Stiva funcioneaz pe principiul LIFO (Last In First Out) - "ultimul


intrat primul ieit".

Pentru a nelege modul de lucru cu stiva, ne imaginm un numr n de


farfurii identice, aezate una peste alta (o "stiv" de farfurii). Adugarea sau
scoaterea unei farfurii se face, cu uurin, numai n vrful stivei. Dup ce am scos
toate farfuriile din stiv, spunem c aceasta este vid. Orict ar prea de simplu
principiul stivei, el are consecine uriae n programare.

Stivele se pot aloca secvenial (ca vectorii). Fie ST[i] un vector. ST[1],
ST[2], ..., ST[n] pot reine numai litere sau numai cifre. O variabil k indic
n permanen vrful stivei, adic ultimul element introdus.

Exemplificm, n continuare, modul de lucru cu stiva:

A n stiva iniial vid se introduce litera A, vrful stivei va fi la nivelul 1 (k=1).

B
Introducem n stiv litera B, deci k va lua valoarea 2.
A

Scoatem din stiv pe B (A nu poate fi scos deocamdat); k=1.


A

Scoatem din stiv pe A; stiva rmne vid k=0.

Observaii

n mod practic, la scoaterea unei variabile din stiv, valoarea variabilei ce


indic vrful stivei scade cu 1, iar atunci cnd scriem ceva n stiv, o
eventual valoare rezidual se pierde.
134 Capitolul 5. Structuri de date

Pe un anumit nivel se reine, de regul, o singur informaie (liter sau cifr),


ns este posibil s avem mai multe informaii.

n cazul stivei, alocarea secvenial nu prezint mari dezavantaje, ca n cazul


mai general al listelor, pentru c nu se fac operaii de inserare sau tergere
n interiorul stivei, ci numai n vrful ei.
Singurul dezavantaj, n comparaie cu alocarea dinamic nlnuit este dat
de faptul c numrul de noduri care pot fi memorate la un moment dat este
mai mic - depinde de gradul de ocupare al segmentului de date.

n literatura de specialitate vei ntlni termenul PUSH pentru operaia de


adugare n stiv a unei nregistrri i POP, pentru extragere.

Este posibil s v ntrebai: de ce nu putem accesa un element al stivei, chiar


dac nu este ultimul introdus? Nimeni nu ne oprete, doar c, n acest caz,
nu respectm principiul stivei.

Exemple

1. Funcia Manna-Pnueli. Se citete xZ. Se cere programul pentru calculul


funciei:

x 1, x 12
F(x) =
F(F(x + 2)),x < 12

Vom ncepe prin a studia modul de calcul al funciei pentru x=15 i x=8:

f(15)=14;
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.

Algoritmul va folosi o stiv ST i o variabil numit k, ce indic n


permanen vrful stivei.

Algoritmul se bazeaz pe urmtoarele considerente:

la o nou autoapelare a funciei f, se urc n stiv (k se incrementeaz


cu 1) i se pune noua valoare;

n situaia n care, pentru valoarea aflat pe nivelul k, se poate calcula


funcia, se coboar n stiv, punndu-se pe acest nivel noua valoare;

algoritmul se ncheie cnd se ajunge n stiv la nivelul 0.


Manual de informatic pentru clasa a XI-a 135

Pentru exemplul dat, prezentm schematic funcionarea 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..100] of #include <iostream.h>
integer; int st[100],n,k;
n,k:integer; main()
begin { cout<<"n=";
write('n='); readln(n); cin>>n;
k:=1; st[1]:=n; k=1;
while k>0 do st[1]=n;
if st[k]<12 then begin while (k>0)
k:=k+1; if (st[k]<12)
st[k]:=st[k-1]+2 { k++;
end st[k]=st[k-1]+2;
else begin }
k:=k-1; else
if k>0 { k--;
then st[k]:=st[k+1]-1 if (k>0) st[k]=st[k+1]-1;
end; }
writeln('f=',st[1]-1) cout<<"n="<<st[1]-1;
end. }

Se poate demonstra uor c pentru valori mai mici dect 12, funcia ia
valoarea 11. Observaia simplific mult programul, dar exemplul a fost dat n
alt scop.

2. Funcia lui Ackermann. Se d funcia de mai jos, definit pe produsul cartezian


NN. Se citesc m i n. S se calculeze Ack(m,n).

n + 1, m=0

Ack(m, n) = Ack(m 1,1), n=0
Ack(m 1, Ack(m, n 1)), altfel

136 Capitolul 5. Structuri de date

Pentru a elabora algoritmul, studiem un exemplu numeric:


ack(2,1)=ack(1,ack(2,0))=ack(1,ack(1,1))=ack(1,ack(0,ack(1,0)))=
= ack(1,ack(0,ack(0,1)))=ack(1,ack(0,2))=ack(1,3)=ack(0,ack(1,2)) =
=ack(0,ack(0,ack(1,1)))=ack(0,ack(0,ack(0,ack(1,0))))=
=ack(0,ack(0,ack(0,ack(0,1))))=ack(0,ack(0,ack(0,2)))=
ack(0,,ack(0,3))=ack(0,4)=5.

Pentru calculul acestei funcii, folosim o stiv dubl, ST. Iniial, valorile m i n
se rein la nivelul 1. Pe nivelul k al stivei se rein valorile curente m i n. n funcie de
valorile acestora se procedeaz astfel:

pentru m i n diferite de 0, este necesar un nou calcul de funcie, caz n care


se urc n stiv i pe noul nivel se pun argumentele m i n-1;

pentru cazul n=0, se rmne pe acelai nivel n stiv, punnd n locul lui m
valoarea m-1, iar n locul lui n, valoarea 1;

n situaia n care m=0, funcia se poate calcula; se coboar n stiv i se


nlocuiete valoarea lui m cu m-1, valoarea lui n cu valoarea calculat
anterior.

n continuare, prezentm grafic modul de funcionare a algoritmului pentru


exemplul ack(2,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,1)=5.
Manual de informatic pentru clasa a XI-a 137

Varianta Pascal Varianta C++


type stiva=array [1..10000,1..2] int st[10000][2];
of integer;
main()
var st:stiva; { int m,n,k;
m,n,k:integer; cout<<"m="; cin>>m;
cout<<"n="; cin>>n;
begin k=1;
write('m='); st[k][0]=m;
readln(m); st[k][1]=n;
write('n='); while (k>0)
readln(n); if (st[k][0] && st[k][1])
k:=1; { k++;
st[k,1]:=m; st[k][0]=st[k-1][0];
st[k,2]:=n; st[k][1]=st[k-1][1]-1;
while k>0 do }
if (st[k,1]<>0) and else
(st[k,2]<>0) if (!st[k][1])
then { st[k][0]=st[k][0]-1;
begin st[k][1]=1;
k:=k+1; }
st[k,1]:=st[k-1,1]; else
st[k,2]:=st[k-1,2]-1 { k--;
end if (k>0)
else { st[k][0]=st[k][0]-1;
if st[k,2]=0 t[k][1]=st[k+1][1]+1;
then }
begin }
st[k,1]:=st[k,1]-1; cout<<"ac("<<m<<','<<
st[k,2]:=1 n<<")="<<st[1][1]+1;
end }
else
begin
k:=k-1;
if k>0
then
begin
st[k,1]:=st[k,1]-1;
st[k,2]:=st[k+1,2]+1
end
end;
writeln('ac(',m,',',n,')=',
st[1,2]+1)
end.

Funcia lui Ackermann ia valori extrem de mari pentru valori mici ale lui m i
n. De exemplu, nu vei reui s calculai Ack(4,4). ncercai...
138 Capitolul 5. Structuri de date

5.4. Structura de tip coad

Definiia 5.4. O coad este o list pentru care toate inserrile sunt fcute
la unul din capete, toate tergerile (consultrile, modificrile) la cellalt
capt.

Coada funcioneaz pe principiul FIFO (First In First Out) -


"primul intrat primul ieit".

Este cu totul nerecomandabil alocarea secvenial a cozii, deoarece n


aceast situaie, are loc un fenomen de migraie a datelor ctre ultimele
componente ale vectorului (cele de indice mare).

S presupunem c simulm o coad cu ajutorul unui vector cu zece


componente, care rein numere ntregi. Introducem n coad, pe rnd,
numerele 1, 2, 3, 4.

1 2 3 4

Dac scoatem din coad pe 1 i introducem n coad pe 5, coada va arta


n felul urmtor:

2 3 4 5

Scoatem din coad pe 2 i introducem pe 6:

3 4 5 6

Se observ acest fenomen de "migraie".

Probleme propuse
1. Care dintre structurile de mai jos nu este liniar?

a) b) c) d)

2. Un vector reine pe poziiile de la 1 la k nodurile unei liste liniare. Fiecare element


al vectorului (nod) reine un numr natural. Se cere s se scrie un subprogram care
insereaz n list, pe poziia p, un numr natural citit de la tastatur.
Manual de informatic pentru clasa a XI-a 139

Exemplu: k=3; V=(1,2,3); p=2. Numrul citit este 5. Dup rulare trebuie s
avem:
k=4; V=(1,5,2,3).

3. Un vector reine pe poziiile de la 1 la k nodurile unei liste liniare. Fiecare


element al vectorului (nod) reine un numr natural. Se cere s se scrie un
subprogram care terge din list nodul aflat pe poziia p, 1pk.

Exemplu: k=3; V=(1,2,3); p=2. Dup rulare trebuie s avem: k=2; V=(1,3).

4. Pentru problema anterioar, care dintre afirmaiile de mai jos este fals?

a) Pentru a terge un nod aflat pe poziia p sunt necesare k-p deplasri spre
stnga ale coninuturilor celorlalte noduri.
b) Subprogramul va avea parametrul k transmis prin valoare.
c) Subprogramul va avea parametrul k transmis prin referin.
d) Dac k=p, nu se efectueaz deplasri ctre stnga.

5. Lucrare n colectiv. Implementarea listelor alocate simplu nlnuit. Scriei


un set de subprograme care creeaz i gestioneaz o list liniar simplu nlnuit,
alocat secvenial (prin utilizarea unui vector).

6. Lucrare n colectiv. Implementarea listelor alocate simplu nlnuit. Scriei


un set de subprograme care creeaz i gestioneaz o list liniar simplu nlnuit,
alocat nlnuit.

7. Sortai n numere naturale utiliznd algoritmul de sortare prin inserie. Programul


va utiliza o list liniar alocat nlnuit. Care sunt avantajele utilizrii listei alocate
nlnuit n cazul algoritmului de sortare prin inserie?

8. Creai o list liniar cu n noduri alocat secvenial. Nodul 1 va conine numrul 1,


nodul 2 numrul 2, .a.m.d. Se genereaz aleator un numr natural k, mai mare ca
1 i mai mic dect n. Nodurile de la k la n vor fi, n aceast ordine, primele n list,
urmate de nodurile de la 1 la k-1, n aceast ordine. Afiai lista dup p astfel de
inversri. Observaie: aceasta este o modalitate de a genera aleator o permutare a
primelor n numere naturale.

9. n cazul n care, pentru o list liniar alocat nlnuit, cmpul de adres al


ultimului nod reine adresa primului nod, se obine o list circular:

Creai o list circular n care fiecare nod reine un numr natural. De


asemenea, scriei subprograme de inserare i tergere a unui nod al listei create.
140 Capitolul 5. Structuri de date

10. n jurul arbitrului sunt aezai N juctori numerotai n sens orar. Arbitrul,
ncepnd de la un juctor K numr pn la M. Persoana la care s-a oprit
numrtoarea este eliminat din cerc. Arbitrul repet procedeul ncepnd cu
persoana urmtoare celei eliminate. Procedeul se repet pn cnd rmne un
juctor L. S se scrie un program care:
citete M, N, K i-l determin pe L;
citete M, N, L i-l determin pe K.

11. Urmrii secvena urmtoare, care se refer la o stiv, unde Push a este
operaia prin care se pune n stiv valoarea a, iar Pop este operaia prin care se
extrage din stiv.
Push 1; Push 2; Pop; Push 3; Push 4; Pop; Pop
Care din afirmaiile de mai jos nu este adevrat dup executarea secvenei
de mai sus?
a) Din stiv au fost extrase, n aceast ordine, valorile: 2, 4, 3;
b) stiva este vid; c) stiva conine valoarea 1; d) stiva are un singur nivel.

12. Scriei subprogramele care implementeaz o stiv.


B A
13. Se citesc n valori numere naturale. Se cere ca, prin 4
utilizarea unei stive, vedei problema anterioar, valorile citite 2 3
1
s se afieze n ordine invers.
14. n figura alturat avem 4 vagoane, numerotate cu
1,2,3,4. 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. Se cere ca, prin mutri succesive de vagoane, s
avem pe linia B vagoanele ntr-o anumit ordine, n care C
primul vagon este cel aflat la ieirea de pe linia B. Care Figura 5.1.
dintre irurile de vagoane de mai jos nu poate fi obinut?
a) 1 2 3 4; b) 4 3 2 1; c) 3 4 2 1; d) 3 4 1 2.

15. S se scrie un program care, pentru problema de mai sus, citete ca date de
intrare irul vagoanelor aflate pe linia A i irul vagoanelor care trebuie obinut pe
linia B. Se cere s se afieze irul mutrilor de tip Push nr_vagon i Pop
nr_vagon (ai recunoscut, desigur, o structur de tip stiv) prin care se poate
obine irul vagoanelor de pe lina B. Dac acest ir nu se poate obine, n momentul
n care se ajunge n situaia unei mutri imposibile, s se afieze mesajul
Imposibil.

16. Scriei un set de subprograme care gestioneaz o coad. Coada va fi


implementat prin utilizarea alocrii nlnuite.

Rspunsuri

1. d) 4. b) 11. b) 14. d)
141

Capitolul 6

Introducere n recursivitate

6.1. Prezentare general

Recursivitatea este una din noiunile fundamentale ale informaticii.


Utilizarea frecvent a recursivitii s-a fcut dup anii 80. Multe din limbajele de
programare evoluate i mult utilizate - Fortran, Cobol - nu permiteau scrierea
programelor recursive.

Definiia 6.1. Recursivitatea este un mecanism general de elaborare a


programelor. Ea const n posibilitatea ca un subprogram s se
autoapeleze.

Recursivitatea a aprut din necesiti practice date de transcrierea direct a


formulelor matematice recursive. n timp, acest mecanism a fost extins, fiind utilizat
n elaborarea multor algoritmi.

6.2. Modul n care se realizeaz autoapelul


n acest paragraf vom nva modul n care subprogramele se autoapeleaz.
Mecanismul recursivitii i modul cum se gndete un algoritm recursiv vor fi
prezentate n paragrafele urmtoare.

6.2.1. Realizarea autoapelului n limbajul Pascal

Dup cum tim, n limbajul Pascal subprogramele sunt de dou feluri:


proceduri i funcii. Oricare ar fi tipul subprogramului, acesta se poate autoapela,
ns modul n care se realizeaz autotransferul difer.

n cazul procedurilor, autoapelul se realizeaz prin apelul procedurii


respective, din interiorul ei. Apelul se face la fel ca n cazul n care procedura
este apelat din exterior.

Procedura prezentat n continuare este recursiv i afieaz, pe rnduri


separate, numerele 7, 6, ..., 1:
142 Capitolul 6. Introducere n recursivitate

procedure exemplu(n:integer);
begin
if n<>0 then
begin
writeln(n);
exemplu(n-1);
end
end;
begin
exemplu(7);
end.

n cazul funciilor, autoapelul se realizeaz printr-o operaie de atribuire,


operaie prin care numele funciei trebuie s figureze n partea dreapt a
operatorului de atribuire.

Funcia urmtoare calculeaz suma 1+2+...+7:

function suma(n:integer):integer;
begin
suma:=0;
if n<>0 then
suma:=n+suma(n-1);
end;
begin
writeln(suma(7));
end.

6.2.2. Realizarea autoapelului n limbajul C++

Dup cum tim, n C++ funciile pot fi de tipul void sau de un alt tip. n acest
din urm caz, funciile returneaz o anumit valoare. Oricare ar fi tipul funciei,
aceasta se poate autoapela, ns modul n care se realizeaz autotransferul, difer.

n cazul funciilor de tip void, autoapelul se realizeaz prin apelul funciei


respective, din interiorul ei. Apelul se face la fel ca n cazul n care funcia
este apelat din exterior.

Funcia de mai jos este recursiv. Ea afieaz, pe rnduri separate,


numerele 7, 6, ..., 1:
#include <iostream.h>
void exemplu(int n)
{ if (n!=0)
{ cout<<n<<endl;
exemplu (n-1);
}
}
main()
{ exemplu(7);
}
Manual de informatic pentru clasa a XI-a 143

n cazul funciilor care nu sunt de tipul void, autoapelul se realizeaz prin


instruciunea return. Ea este de forma return expresie, dar n
expresia respectiv trebuie s intre i funcia care se autoapeleaz.

Funcia urmtoare calculeaz suma 1+2+...+7:

#include <iostream.h>
int suma (int n)
{ if (n!=0) return n+suma(n-1);
}
main()
{ cout<<suma(7);
}

6.3. Mecanismul recursivitii

Problem. S se calculeze recursiv n!.


A) Pentru a scrie o funcie recursiv care efectueaz acelai calcul, vom porni de
la o definiie recursiv a lui n!. Aceasta este:

1, n=0
n! = fact(n) = cu n N
n fact(n 1), altfel

De exemplu, pentru a calcula 3!, procedm astfel:

3!=fact(3)=3fact(2)=32fact(1)=321fact(0)=3211=6.

Funcia recursiv fact nu face altceva dect s transcrie definiia recursiv


prezentat anterior:

Varianta Pascal Varianta C++


var n:integer; #include <iostream.h>
int fact(int n)
function fact(n:integer): { if (!n) return 1;
integer; else return n*fact(n-1);
begin }
if n=0 then fact:=1
else fact:=n*fact(n-1) main()
end; { int n;
cout<<"n=";
begin cin>>n;
write('n='); cout<<fact(n);
readln(n); }
writeln(fact(n))
end.
144 Capitolul 6. Introducere n recursivitate

Care este mecanismul prin care subprogramele se pot autoapela? S ne


amintim modul n care subprogramele memoreaz parametrii transmii.

Pentru memorarea parametrilor, subprogramele folosesc o zon de


memorie numit stiv (mai exact, aceast zon se numete segment
de stiv).
Memorarea parametrilor transmii se face n ordinea n care acetia
figureaz n antet: de la stnga la dreapta.
Pentru parametrii transmii prin valoare, se memoreaz valoarea
transmis, iar pentru cei transmii prin referin se memoreaz adresa
variabilei.
n cadrul subprogramului, parametrii transmii i memorai n stiv
sunt variabile. Numele lor este cel din lista parametrilor formali.

n capitolul anterior am studiat proprietile structurii numit stiv. Exact


aceleai proprieti le are i segmentul de stiv. Singura diferen este dat de
faptul c gestiunea segmentului de stiv este fcut automat. Mai exact, codul n
limbaj main, obinut n urma compilrii, conine secvene prin care se
gestioneaz segmentul de stiv.

La apelul subprogramului se depun n stiv, n ordine, parametrii transmii.


De asemenea, tot n stiv se rezerv spaiu pentru variabilele locale (cele
declarate n subprogram). Acesta este un prim nivel al stivei.

n cazul n care subprogramul se autoapeleaz pe un al doilea nivel, se


depun din nou parametrii transmii i se rezerv un nou spaiu pentru
variabilele locale.

n continuare, prezentm grafic modul n care funcioneaz recursivitatea n


cazul funciei fact a programului anterior. Aceast funcie nu are variabile locale,
deci n stiv se depune doar parametrul n.

La primul apel al funciei fact se creeaz n


n 3 segmentul de stiv o variabil numit n, care
reine 3 - valoarea parametrului formal.

Funcia se autoapeleaz. De aceast dat


n 2 parametrul n ia valoarea 2. n stiv se creeaz un
nou nivel, care reine n cu valoarea 2.
n 3

n 1 Funcia se autoapeleaz. Parametrul n ia valoarea


1. n stiv se creeaz un nou nivel, care reine n
n 2 cu valoarea 1.
n 3
Manual de informatic pentru clasa a XI-a 145

n 0 Funcia se autoapeleaz. Parametrul n ia valoarea


fact=1
0. n stiv se creeaz un nou nivel, care reine n
n 1
cu valoarea 0. Funcia ia valoarea 1, autoapelul
n 2 nu mai are loc. Se revine pe nivelul 3.

n 3

n 1 Pe nivelul 3 se efectueaz calculul 1*1=1.


fact=1
n Se revine apoi pe nivelul 2.
2

n 3

n 2 Pe nivelul 2 se efectueaz calculul 2*1=2.


fact=2
Se revine apoi pe nivelul 1.
n 3

Pe nivelul 1 se efectueaz calculul 3*2=6.


n 3
Se revine apoi n main().
fact=6

Aici se afieaz 6, adic s-a calculat 3!.

B) Mai puin eficient, n! se poate calcula i printr-o funcie ca n exemplul urmtor:

Varianta Pascal Varianta C++


var val,n,p:integer; #include <iostream.h>
procedure fact(val,n:integer; void fact(int val,int n,int&
var prod:integer); prod)
begin { if (val<=n)
if val<=n then { prod*=val;
begin fact(val+1,n,prod);
prod:=prod*val; }
fact(val+1,n,prod) }
end;
end; main()
{ int val,n,p=1;
begin cout<<"n="; cin>>n;
write ('n='); readln(n); fact(1,n,p);
p:=1; cout<<p;
fact(1,n,p); }
writeln(p);
end.
146 Capitolul 6. Introducere n recursivitate

S analizm i pentru acest exemplu modul n care se efectueaz calculul,


pentru n=3. Funcia are trei parametri: doi transmii prin valoare, unul prin referin.

Iat coninutul variabilelor dup primul apel:

1 1 3 Referin ctre p

p val n prod

Pentru c val este mai mic sau egal ca n se efectueaz: prod=prod*val;.


ntruct variabila p a fost transmis prin referin, programul lucreaz cu aceast
variabil, n loc de prod. n concluzie, se efectueaz p=p*val;. Apoi, funcia se
autoapeleaz:

2 3 Referin ctre p

1 1 3 Referin ctre p

p val n prod

Pentru c val este 2 - mai mic ca 3 - se efectueaz prod=prod*val, dup


care funcia se autoapeleaz.

3 3 Referin ctre p

2 3 Referin ctre p

2 1 3 Referin ctre p

p val n prod

Pentru c val este 3 - mai mic sau egal cu 3 - se efectueaz prod=prod*val, deci p
ia valoarea 6, dup care se revine pe nivelul 2, apoi 1, apoi n programul principal.

Observaii

Dup cum rezult din aceste exemple, subprogramul lucreaz cu datele aflate
pe un anumit nivel al stivei pentru variabilele transmise prin valoare, variabilele
locale i variabilele transmise prin referin.
Exist posibilitatea ca subprogramul s lucreze direct cu variabilele globale fr
ca acestea s fie transmise prin referin. Dup cum tim variabilele globale pot fi
accesate din orice subprogram. n exemplu, am preferat varianta de a trece ca
parametru variabila transmis prin referin, att din motive didactice ct i pentru
a asigura independena subprogramului. Dezavantajul este dat de faptul c stiva
va fi ncrcat suplimentar cu adresa acestei variabile.
Manual de informatic pentru clasa a XI-a 147

n cazul unui numr mare de autoapelri, exist posibilitatea ca segmentul


de stiv s depeasc spaiul alocat, caz n care programul se va termina
cu eroare.

Recursivitatea presupune mai mult memorie n comparaie cu iterativitatea.

6.4. Cum gndim un algoritm recursiv ?


Pentru a ne familiariza cu raionamentul recursiv, vom porni de la cteva
exemple intuitive.

1. Wirth. O camer de luat vederi are n obiectiv un televizor care transmite


imaginile primite de la camer. Evident, n televizor se va vedea un televizor,
iar n acesta un televizor..., .a.m.d.
Pe scurt, n orice televizor se vede un televizor.
2. Anun. n anumite restaurante am ntlnit anunul: Azi nu se fumeaz!.
3. Constatare. tii povestea cu sracul care a prins un petior de aur. Acesta
i spune omului c dac i d drumul i ndeplinete 3 dorine, oricare ar fi ele.
Omul i d drumul, i spune prima dorin, petiorul i-o ndeplinete, i spune
a doua dorin, petiorul i-o ndeplinete. A treia dorin a omului este s i
se ndeplineasc 3 dorine...

Lsnd gluma la o parte, constatm c, n general, o gndire recursiv


exprim concentrat o anumit stare, care se repet la infinit. Aceast gndire se
aplic n elaborarea algoritmilor recursivi, dar cu o modificare esenial: adugarea
condiiei de terminare. n absena acestei condiii, nu poate fi vorba de algoritm,
ntruct algoritmul trebuie s fie finit.

n elaborarea algoritmilor recursivi se aplic raionamentul: ce se ntmpl la


un nivel, se ntmpl la orice nivel.

Subprogramul care se autoapeleaz trebuie s conin instruciunile


corespunztoare unui nivel.

Starea tratat de subprogram se gsete pe un anumit nivel al stivei


(variabilele rezultate n urma transmiterii parametrilor i variabilele locale ale
subprogramului).

Observaii

Un algoritm recursiv se elaboreaz utiliznd acest tip de gndire, nu o


gndire precum cea folosit pn acum, cnd am elaborat algoritmi iterativi.
Pentru orice algoritm recursiv exist unul iterativ care rezolv aceeai
problem.
Nu ntotdeauna alegerea unui algoritm recursiv reprezint un avantaj.
Numeroasele exemple care urmeaz v vor lmuri asupra celor afirmate.
148 Capitolul 6. Introducere n recursivitate

6.5. Aplicaii recursive

6.5.1. Aplicaii la care se transcrie o formul recursiv

Aplicaia 6.1. Se citete xZ. Se cere programul pentru calculul funciei:


x 1, x 12
F(x) =
F(F(x + 2)),x < 12

Rezolvare. Aceast aplicaie a fost tratat iterativ, prin utilizarea stivei. n cazul
tratrii recursive, nu facem altceva dect s transcriem definiia recursiv a funciei.

Varianta Pascal Varianta C++


var x:integer; #include <iostream.h>
int x;
function manna
(x:integer):integer; int manna (int x)
begin { if (x>=12) return x-1;
if x>=12 else
then manna:=x-1 return manna(manna(x+2));
else manna:=manna(manna(x+2)) }
end; main()
begin { cout<<"x=";
write(x=); cin>>x;
readln(x); cout<<manna(x);
writeln(manna(x)) }
end.

n comparaie cu abordarea iterativ, abordarea recursiv prezint avantajul


scrierii concentrate.

Aplicaia 6.2. Se d funcia de mai jos, definit pe NN. Se citesc numerele


m i n. S se calculeze Ack(m,n).

n + 1, m=0

Ack(m, n) = Ack(m 1,1), n=0
Ack(m 1, Ack(m, n 1)), altfel

Rezolvare. i aceast aplicaie a fost tratat iterativ, prin utilizarea stivei. n


cazul tratrii recursive, nu facem altceva dect s transcriem definiia recursiv
a funciei.
Algoritmul recursiv nu necesit comentarii.
Manual de informatic pentru clasa a XI-a 149

Varianta Pascal Varianta C++


var m,n:integer; #include <iostream.h>
int m,n;
function
ack(m,n:integer):integer; int Ack(int m,int n)
begin { if (!m) return n+1;
if m=0 else
then ack:=n+1 if (!n)
else return Ack(m-1,1);
if n=0 then ack:=ack(m-1,1) else
else return
ack:=ack(m-1,ack(m,n-1)) Ack(m-1,Ack(m,n-1));
end; }
begin main()
write(m=); readln(m); { cout<<"m="; cin>>m;
write(n=); readln(n); cout<<"n="; cin>>n;
writeln(ack(m,n)) cout<<Ack(m,n);
end. }

n comparaie cu abordarea iterativ, abordarea recursiv prezint avantajul


scrierii concentrate i acela al scutirii programatorului de un efort suplimentar
n elaborarea algoritmului.

Aplicaia 6.3. irul lui Fibonacci. Se consider irul definit astfel:


0, n=0

U n = 1, n =1

U n 1 + U n 2 altfel
Se citete n, numr natural. S se calculeze Un.

Rezolvare. Funcia fib transcrie definiia recursiv:


Varianta Pascal Varianta C++
var n:integer; #include <iostream.h>
int n;
function
fib (n:integer):integer; int fib (int n)
begin { if (!n) return 0;
if n=0 then fib:=0 else
else if (n==1) return 1;
if n=1 then fib:=1 else
else fib:=fib(n-1)+fib(n-2) return fib(n-1)+fib(n-2);
end; }
begin main()
write(N=); readln(n); { cout<<"n="; cin>>n;
writeln(fib(n)) cout<<fib(n);
end. }
150 Capitolul 6. Introducere n recursivitate

n aceast situaie, este corect s se foloseasc un program care calculeaz


Un iterativ. S ne imaginm cum funcioneaz aceast funcie.

Pentru calculul lui fib(n) este necesar s se cunoasc fib(n-1) i


fib(n-2). Parametrii acestor funcii sunt depui n stiv. Procedeul continu pn
cnd este calculat fib(n-1), apoi se reia calculul pentru fib(n-2). Acest lucru
este extrem de ineficient pentru valori mari ale lui n (ex. n=100). Se calculeaz
u100 ca sum ntre u99 i u98. Pentru calculul lui u99 se calculeaz u98 i u97. Dup
ce calculm u99, relum calculele pentru u98.

O astfel de recursivitate se numete recursivitate n cascad. Rulai


programul pentru o valoare mai mare a lui n i ... ateptai. Ct de simplu se
rezolv problema iterativ i ct este de rapid aceast metod...

Prezentm varianta iterativ, care nu necesit comentarii.

Varianta Pascal Varianta C++


var n,f0,f1,f2,i:integer; #include <iostream.h>
begin
write('n='); readln(n); main()
f0:=0; { int n,f0=0,f1=1,f2;
f1:=1; cout<<"n="; cin>>n;
if n=0 then writeln(f0) if (!n) cout<<f0;
else else
if n=1 then writeln(f1) if (n==1) cout<<f1;
else else
begin { for (int i=2;i<=n;i++)
for i:=2 to n do { f2=f0+f1;
begin f0=f1;
f2:=f0+f1; f1=f2;
f0:=f1; }
f1:=f2 cout<<f2;
end; }
writeln(f2) }
end
end.

Aplicaia 6.4. Se dau dou numere naturale a i b. Se cere s se calculeze cel


mai mare divizor comun al lor.

Rezolvare. Utilizm o definiie recursiv a celui mai mare divizor comun pentru
dou numere naturale a i b:

a, a=b

cmmdc(a, b) = cmmdc(a b, b),a > b
cmmdc(a, b a), a < b

Manual de informatic pentru clasa a XI-a 151

Aceast definiie este transcris n funcia recursiv cmmdc:

Varianta Pascal Varianta C++


var a,b:integer; #include <iostream.h>
int a,b;
function
cmmdc (a,b:integer):integer; int cmmdc (int a,int b)
begin { if (a==b) return a;
if a=b then cmmdc:=a else
else if a>b if (a>b)
then cmmdc:=cmmdc(a-b,b) return cmmdc(a-b,b);
else cmmdc:=cmmdc(a,b-a) else return cmmdc(a,b-a);
end; }
begin
write(a=); readln(a); main()
write(b=); readln(b); { cout<<"a="; cin>>a;
writeln(cmmdc(a,b)) cout<<"b="; cin>>b;
end. cout<<cmmdc(a,b);
}

Rezolvm aceeai problem iterativ (utiliznd definiia de mai sus):

Varianta Pascal Varianta C++


var a,b:integer; #include <iostream.h>
main()
begin { int a,b;
write('a='); readln(a); cout<<"a="; cin>>a;
write('b='); readln(b); cout<<"b="; cin>>b;
while a<>b do while (a!=b)
if a>b then a:=a-b if (a>b) a=a-b;
else b:=b-a; else b=b-a;
writeln('cmmdc=',a) cout<<"cmmdc="<<a;
end. }

Pentru aceast problem, este indiferent ce variant de rezolvare se alege.

Aplicaia 6.5. S se scrie o funcie recursiv pentru a calcula suma cifrelor unui
numr natural.

Rezolvare. Iniial, prezentm varianta iterativ:


Varianta Pascal Varianta C++
var n,s:integer; #include <iostream.h>
begin main()
write('n='); readln(n); { int n,s=0;
s:=0; cout<<"n="; cin>>n;
while n<>0 do begin while (n)
s:=s+n mod 10; { s+=n%10;
n:=n div 10; n/=10; }
end; cout<<"s="<<s;
writeln('s=',s) }
end.
152 Capitolul 6. Introducere n recursivitate

Reinem ideea: se izoleaz ultima cifr, iar lui n i se atribuie ctul ntreg
dintre vechea valoare i 10. Aceast idee folosete pentru a gsi o relaie de
recuren, necesar elaborrii variantei recursive. Relaiile sunt scrise prin
utilizarea operatorilor din Pascal (stnga) i C++ (dreapta):

0, n=0 0, n=0
S(n) = S(n) =
n mod 10+ S(n div 10), altfel n % 10+ S(n / 10), altfel

Programul de mai jos calculeaz suma utiliznd relaia prezentat:

Varianta Pascal Varianta C++


var n:integer; #include <iostream.h>
int n;
function s(n:integer):integer;
begin int s(int n)
if n=0 then s:=0 { if (!n) return 0;
else s:=n mod 10 + s(n div 10) else return n%10 + s(n/10);
end; }
begin main()
write('n='); readln(n); { cout<<"n="; cin>>n;
writeln(s(n)) cout<<s(n);
end. }

Exist posibilitatea ca un subprogram s se autoapeleze prin intermediul altui


subprogram. Din moment ce s-a realizat autoapelul, este vorba de recursivitate i
cum acesta nu s-a realizat direct - ca n exemplele anterioare - o astfel de
recursivitate se numete indirect.

Aplicaia 6.6. Se consider irurile definite recurent astfel: a0=a; b0=b; a,b>0:

a n1 + b n1
an = , b n = a n1b n1 .
2

S se scrie un program care s citeasc a, b i n i s se calculeze an i bn.

Varianta Pascal Varianta C++


#include <iostream.h>
var a,b:real; #include <math.h>
n:integer; double a,b;
function bn(n:integer):real; int n;
forward;
function an(n:integer):real; double bn(int n);
begin double an(int n)
if n=0 { if (!n) return a;
then an:=a else
else an:=(an(n-1+bn(n-1))/2 return (an(n-1)+bn(n-1))/2;
end; }
Manual de informatic pentru clasa a XI-a 153

function bn(n:integer):real; double bn (int n)


begin { if (!n) return b;
if n=0 then bn:=b else return
else bn:=sqrt(an(n-1)*bn(n-1) sqrt(an(n-1)*bn(n-1));
end; }

begin main()
write(a=); readln(a); { cout<<"a="; cin>>a;
write(b=); readln(b); cout<<"b="; cin>>b;
write(n=); readln(n); cout<<"n="; cin>>n;
writeln(an(n):5:10, , cout<<an(n)<<' '<<bn(n);
bn(n):5:10) }
end.

6.5.2. Aplicaii la care nu dispunem de o formul de


recuren

Aplicaia 6.7. S se scrie o funcie recursiv care citete caractere i le


afieaz n ordinea invers citirii. Sfritul irului este marcat de caracterul 0.

Rezolvare. Conform principiului, ce se ntmpl la un nivel se ntmpl la orice


nivel, vom gndi funcia recursiv astfel:

se citete un caracter;
dac este diferit de 0, se reapeleaz funcia;
se tiprete caracterul.

Raionamentul este suficient pentru a scrie programul.

Varianta Pascal Varianta C++


procedure inv; #include <iostream.h>
var a:char; void inv()
begin { char a; cin>>a;
read(a); if (a!='0') inv();
if a<>'0' then inv; cout<<a;
write(a) }
end;
main()
begin { inv();
inv; cout<<endl;
writeln; }
end.

Exerciiu. Modificai programul astfel nct caracterul 0 - care marcheaz


sfritul irului - s nu fie tiprit.
154 Capitolul 6. Introducere n recursivitate

Aplicaia 6.8. S se scrie o funcie recursiv pentru a transforma un numr


natural n, din baza 10 n baza k (1<k<10).

Rezolvare. S ne amintim algoritmul clasic de trecere din baza 10 n baza k.


Numrul se mparte la k, se reine restul, ctul se mparte la k, se reine restul...
pn cnd ctul este mai mic dect mpritorul. Rezultatul se obine prin scrierea
n ordine invers a resturilor obinute. Practic, tiprirea restului se face dup
autoapel (ca i la problema anterioar).

Varianta Pascal Varianta C++


var n,b:integer; #include <iostream.h>
int n,b;
procedure
transform(n,b:integer); void transform(int n,int b)
var rest:integer; { int rest=n%b;
begin if (n>=b) transform(n/b,b);
rest:=n mod b; cout<<rest;
if n>b }
then transform(n div b,b);
write(rest) main()
end; { cout<<"n="; cin>>n;
cout<<"baza="; cin>>b;
begin transform(n,b);
write('n='); readln(n); }
write('baza='); readln(b);
transform(n,b);
writeln;
end.

Aplicaia 6.9. Se d mulimea {1,2,...,n} i se cer toate permutrile acesteia.


Exemplu: pentru n=3 avem:
{3,1,2}, {2,3,1}, {2,1,3}, {3,2,1}, {1,3,2}, {1,2,3}.

Rezolvare. n vederea rezolvrii problemei, observm urmtoarele:

mulimea cu un singur element {1} are o singur permutare: {1};

din fiecare permutare a mulimii {1,2,...,n-1} ({a1,a2,...,an-1}), se


obin urmtoarele permutri ale mulimii {1,2,...,n}:
{n,a2,a3,...,an-1,a1};
{a1,n,a3,...,an-1,a2};
{a1,a2,n,...,an-1,a3};
...
{a1,a2,a3,...,an-1,n}.
Manual de informatic pentru clasa a XI-a 155

Pentru n=3, privii reprezentarea din 321


figura alturat:
21 231

213
1
312

12 132
Figura 6.1.
Cazul n care n=3
123

Avem n vedere posibilitatea de revenire la situaia iniial: dup ce am


operat o interschimbare a elementelor ai i aj, urmat de o reapelare a funciei
pentru valoarea k+1, interschimbm din nou ai cu aj.

Varianta Pascal Varianta C++


var p: array [1..8] of integer; #include <iostream.h>
n,i:integer; int p[10],n;
procedure tipar; void tipar()
var i:integer; { for (int i=1;i<=n;i++)
begin cout<<p[i];
for i:=1 to n do write(p[i]); cout<<endl;
writeln }
end;
void permut(int k,int n,
procedure permut(k,n:integer; int p[10])
var p:vector); { int i,c;
var i,j,c:integer; if (k==n+1) tipar();
begin else
if k=n+1 { p[k]=k;
then tipar for (i=1;i<=k;i++)
else { c=p[i];
begin p[i]=p[k];
p[k]:=k; p[k]=c;
for i:=1 to k do permut(k+1,n,p);
begin c=p[i];
c:=p[i]; p[i]=p[k];
p[i]:=p[k]; p[k]=c;
p[k]:=c; }
permut(k+1,n,p); }
c:=p[i]; }
p[i]:=p[k];
p[k]:=c main()
end { cout<<"n=";
end cin>>n;
end; permut(1,n,p);
}
begin
write('n='); readln(n);
permut(1,n,p)
end.
156 Capitolul 6. Introducere n recursivitate

Aplicaia 6.10. Algoritmul Fill. Se d o matrice binar. Valorile 1 delimiteaz


o anumit suprafa nchis n cadrul matricei (elementele aparinnd acestei
suprafee sunt marcate cu 0). De asemenea, se dau coordonatele x i y ale unui
element al matricei, semnificnd un punct din interiorul acestei suprafee.

Fie matricea de mai jos:

0 1 1 0

0 0 0 1
A=
0 1 1 1

1 0
0 0

Suprafaa nchis este dat de elementele A(1,1), A(2,1), A(2,2),


A(2,3), A(3,1).

Considerm coordonatele (2,3) ale unui punct situat n interiorul acestei


suprafee. Dup executarea programului, matricea trebuie s arate astfel:

1 1 1 0

1 1 1 1
A=
1 1 1 1

1 0 0 0

Algoritmul se dovedete extrem de util n colorarea unei suprafee nchise


atunci cnd sunt cunoscute coordonatele unui punct situat n interiorul ei.
Acest algoritm este cunoscut i sub denumirea de algoritmul FILL.

Rezolvare. Pentru rezolvare se folosete funcia Scriu(), care se autoape-


leaz. Iniial, matricea se bordeaz cu dou linii i dou coloane ce conin elemente
care au valoarea 1. Aceasta are ca scop evitarea testului de ieire din matrice.

Funcia Scriu() funcioneaz astfel:

testeaz dac elementul matricei la care s-a ajuns (de coordonate


(x,y)) are valoarea 0;

n caz afirmativ, acesta ia valoarea 1, iar funcia se autoapeleaz


pentru fiecare dintre elementele nvecinate (sus, jos, dreapta, stnga);

n caz contrar, se iese din funcie.

Programul este prezentat n continuare:


Manual de informatic pentru clasa a XI-a 157

Varianta Pascal Varianta C++


program umplere; #include <iostream.h>
var a:matrice: array int a[10][10],i,j,m,n,x,y;
[0..9,0..9] of integer;
i,j,m,n,x,y:integer; void scriu (int x,int y,
int a[10][10])
procedure scriu (x,y:integer; { if (!a[x][y])
var a:matrice); { a[x][y]=1;
begin scriu(x+1,y,a);
if a[x,y]=0 then scriu(x,y+1,a);
begin scriu(x-1,y,a);
a[x,y]:=1; scriu(x,y-1,a);
scriu(x+1,y,a); }
scriu(x,y+1,a); }
scriu(x-1,y,a);
scriu(x,y-1,a) main()
end { cout<<"M="; cin>>m;
end; cout<<"N="; cin>>n;
for (i=1;i<=m;i++)
begin for (j=1;j<=n;j++)
write('M='); readln(m); { cout<<"a["<<i<<','
write('N='); readln(n); <<j<<"]=";
for i:=1 to m do cin>>a[i][j];
for j:=1 to n do }
begin
for (i=1;i<=n;i++)
write('a[',i,',',j,']=');
{ a[0][i]=1;
readln(a[i,j])
a[m+1][i]=1;
end;
}
for i:=1 to n do
begin for (i=1;i<=m;i++)
a[0,i]:=1; { a[i][0]=1;
a[m+1,i]:=1 a[i][n+1]=1;
end; }
for i:=1 to m do cout<<"X="; cin>>x;
begin cout<<"Y="; cin>>y;
a[i,0]:=1; for (i=1;i<=m;i++)
a[i,n+1]:=1 { for (j=1;j<=n;j++)
end; cout<<a[i][j];
write('X='); readln(x); cout<<endl;
write('Y='); readln(y); }
for i:=1 to m do scriu(x,y,a);
begin cout<<endl<<endl;
for j:=1 to n do for (i=1;i<=m;i++)
write(a[i,j]); { for (j=1;j<=n;j++)
writeln cout<<a[i][j];
end; cout<<endl;
scriu(x,y,a); }
writeln; }
writeln;
for i:=1 to m do
begin
for j:=1 to n do
write(a[i,j]);
writeln
end
end.
158 Capitolul 6. Introducere n recursivitate

Aplicaia 6.11. Problema fotografiei (aplicaie Fill). O fotografie alb-negru


este reprezentat sub forma unei matrice binare. Ea nfieaz unul sau mai multe
obiecte. Poriunile corespunztoare obiectului (sau obiectelor) n matrice au valoarea
1. Se cere s se determine dac fotografia reprezint unul sau mai multe obiecte.

n matricea urmtoare sunt reprezentate dou obiecte:

1 1 0 0

0 0 0 1
A= ,
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= .
0 1 1 1

1 0 0 0

Ca i n problemele anterioare, pentru a evita testul ieirii din matrice,
aceasta este bordat cu linii i coloane avnd valoarea 0. Algoritmul este tot cel
din problema anterioar (Fill), dar aici cutarea se face pe 8 direcii.
n programul principal se citete matricea i se caut primul element 1
printre elementele acesteia. Se apeleaz apoi funcia compact() care are rolul de
a marca cu 0 toate elementele matricei care aparin acestui prim obiect identificat.
La revenire, se testeaz dac mai exist elemente cu valoarea 1 n matrice. n
caz afirmativ, se poate trage concluzia c n fotografie aveam iniial mai multe
obiecte (altfel, fotografia coninea un singur obiect).

Varianta Pascal Varianta C++


var a: array [0..9,0..9] of #include <iostream.h>
integer; int a[10][10], i,j,m,n,
i,j,m,n,x,y:integer; x,y,gasit;
gasit:boolean;
procedure compact void compact(int x,int y,
(x,y:integer; var a:matrice); int a[10][10])
begin { if (a[x][y])
if a[x,y]=1 then begin { a[x][y]=0;
a[x,y]:=0; compact(x-1,y,a);
compact(x-1,y,a); compact(x-1,y+1,a);
compact(x-1,y+1,a); compact(x,y+1,a);
compact(x,y+1,a); compact(x+1,y+1,a);
compact(x+1,y+1,a); compact(x+1,y,a);
compact(x+1,y,a); compact(x+1,y-1,a);
compact(x+1,y-1,a); compact(x,y-1,a);
compact(x,y-1,a); compact(x-1,y-1,a);
compact(x-1,y-1,a); }
end }
end;
Manual de informatic pentru clasa a XI-a 159

begin main()
write('M='); readln(m); { cout<<"M=";cin>>m;
write('N='); readln(n); cout<<"N=";cin>>n;
for i:=1 to m do for (i=1;i<=m;i++)
for j:=1 to n do for (j=1;j<=n;j++)
begin { cout<<"a["<<i<<','
write('a[',i,',',j,']='); <<j<<"]=";
readln(a[i,j]) cin>>a[i][j];
end; }
for i:=1 to n do for (i=1;i<=n;i++)
begin { a[0][i]=0;
a[0,i]:=0; a[m+1][i]=0;
a[m+1,i]:=0; }
end; for (i=1;i<=m;i++)
for i:=1 to m do { a[i][0]=0;
begin a[i][n+1]=0;
a[i,0]:=0; }
a[i,n+1]:=0 x=0;
end; do
x:=0; { x++;
repeat y=0;
x:=x+1; do
y:=0; { y++; }
repeat while (y!=n && a[x][y]!=1);
y:=y+1 }
until (y=n) or (a[x,y]=1) while ((x!=m) && a[x][y]!=1);
until (x=m) or (a[x,y]=1); compact(x,y,a);
compact(x,y,a); gasit=0;
gasit:=false; for (i=1;i<=m;i++)
for i:=1 to m do for (j=1;j<=n;j++)
for j:=1 to n do if (a[i][j]==1) gasit=1;
if a[i,j]=1 then if (gasit)
gasit:=true; cout<<"mai multe obiecte";
if gasit else cout<<"un obiect";
then writeln('mai multe }
obiecte')
else writeln('un obiect')
end.

Probleme propuse
1. Calculai recursiv suma a n numere naturale citite.

2. Calculai recursiv expresiile:

a) 12+23+...+n(n+1);
b) 1+1/2+...+1/n;
c) 1/(23)+2/(3*4)+...+n/((n+1)(n+2)).
160 Capitolul 6. Introducere n recursivitate

3. Se citesc n i k (numere naturale n>k). Calculai recursiv Cnk , prin utilizarea


formulei de recuren: Cnk = Cnk11 + Cnk1 . Este eficient?
4. Scriei un program iterativ care rezolv problema anterioar utiliznd aceeai
formul.

5. Calculai recursiv Cnk prin utilizarea formulei:

1, k = 0;
C = n k + 1 k 1
k
n
Cn altfel.
k

Comparai timpul de rezolvare cu cel necesar pentru rezolvarea problemei 4.

6. Scriei un subprogram recursiv prin care calculatorul ghicete un numr natural


ascuns de dumneavoastr (numrul este cuprins ntre 1 i 30000). Atunci
cnd calculatorul propune un numr i, se va rspunde prin:
1, dac numrul este prea mare;
2, dac numrul este prea mic;
0, dac numrul a fost ghicit.

7. Scriei un subprogram recursiv care calculeaz cte cuvinte distincte cu 2n


caractere se pot forma cu n caractere A i n caractere B.

8. Calculai conform formulei urmtoare valoarea maxim reinut de un vector de


numere naturale cu n componente:

V[1], n = 1;
max(V[1], V[2],...V[n]) =
max(max(V[1], V[2],...V[n 1]), V[n]) altfel.
9. Se citete un numr natural n. Se cere s se scrie o funcie recursiv care
returneaz cea mai mic baz n care se poate considera n.

10. Scriei o funcie recursiv care testeaz dac un numr natural n>1 este prim.

11. Scriei o funcie recursiv care returneaz suma elementelor pare ale unui
vector citit.
Exemplu: Pentru n=4 i V=(2,2,5,6), se returneaz 10.

12. Scriei o funcie recursiv prin care se testeaz dac un numr natural x se
regsete ntre componentele unui vector V cu n numere naturale.

13. Scriei o funcie recursiv care primete ca parametri dou numere naturale
i<j i un numr real x[i,j] i returneaz [x] (parte ntreag din x). Nu se
vor folosi funciile specializate ale limbajului.
Manual de informatic pentru clasa a XI-a 161

14. Scriei o funcie recursiv care verific dac un vector cu componente numere
naturale este palindrom (afiarea componentelor de la 1 la n coincide cu afiarea
lor de la n la 1).

15. Scriei o funcie recursiv care returneaz numrul cifrelor pe care le are un
numr natural primit ca parametru.

16. Scriei un subprogram recursiv care afieaz, cifr cu cifr, oglinditul unui
numr natural.
Exemplu: pentru n=123, se afieaz 321.

17. Scriei un subprogram recursiv care returneaz, oglinditul unui numr natural.
Exemplu: pentru n=123, se returneaz 321.

18. Scriei o funcie recursiv care testeaz dac un vector cu n numere naturale
reine numai valori distincte, caz n care funcia returneaz true, iar n caz contrar,
returneaz false.

19. Scriei un subprogram recursiv care descompune n toate modurile posibile un


numr natural n n dou numere n1 i n2, n1n2 a cror sum este n.

20. Pentru un vector cu n componente 0 sau 1 care are semnificaia de numr


binar, se cere s se scrie o funcie recursiv care afieaz numrul n baza 10.
Exemplu: pentru n=4 i V=(1011), se va returna 11.

21. Scriei o funcie recursiv care afieaz valoarea unui polinom n punctul a.
Coeficienii polinomului sunt dai ntr-un vector. Astfel, pentru V=(1,2,3) avem
polinomul P=x2+2x+3.

22. Fie funcia definit pe N*N*. Se citesc n i k. Se cere s se scrie o funcie


recursiv care evalueaz funcia:

0, k>n

S(n, k) = 1, k {1, n}
S(n 1, k 1) + kS(n 1, k) 1 < k < n

23. Calculai S(n,k) nerecursiv.

24. Calculai recursiv i nerecursiv funcia definit pe N*N:

0, k = 0 sau k > n;

F(n, k) = 1 k = n;
F(n 1, k 1) nF(n 1, k) 1 < k < n.

Comparai eficiena celor dou moduri de calcul.
162 Capitolul 6. Introducere n recursivitate

25. S se calculeze recursiv i nerecursiv P(n,k), definit pe N*N*:

P(n+k,k)=P(n,1)+P(n,2)+...+P(n,k), dac 1<k<n;


P(n,k)=1, dac k=1 sau k=n;
P(n,k)=0, dac k>n.

26. Calculai iterativ i recursiv cel mai mare divizor comun pentru dou numere
naturale m i n, utiliznd algoritmul lui Euclid:

cmmdc(n, m mod n), n 0 cmmdc(n, m % n), n 0


cmmdc(m, n) = cmmdc(m, n) =
m, n=0 m, n=0
Pascal C++

27. Ce se afieaz la: writeln(t(12)); / cout<<t(12); ?

Varianta Pascal Varianta C++


function t(n:integer):integer; int t(int n)
begin { if (n) return 10*t(n/10)+
if n<>0 then t:=10*t(n div 10) n%10;
+(n mod 10) else return 0;
else t:=0; }
end;

a) 12;
b) 21;
c) eroare de executare;
d) 0.

28. Ce calculeaz funcia urmtoare?

Varianta Pascal Varianta C++


function t(n:integer):integer; int t(int n)
begin { if (n)
if n<>0 then return (n%2==0)*n +t(n-1);
t:=ord(n mod 2=0)*n+t(n-1) else return 0;
else t:=0; }
end;

a) suma primelor n numere naturale impare;


b) suma primelor n numere naturale pare;
c) suma numerelor naturale pare strict mai mici dect n;
d) suma numerelor naturale pare mai mici sau egale cu n.
Manual de informatic pentru clasa a XI-a 163

29. Pentru care dintre numerele de mai jos, care sunt parametri de intrare pentru
funcia urmtoare, ultimele 2 numere afiate vor fi 0?

Varianta Pascal Varianta C++


procedure t(n:integer); void t(int n)
begin { if (n>15)
if n>15 then { t(n/16);
begin cout<<n%16<<endl;
t(n div 16); }
writeln(n mod 16) else cout<<n<<endl;
end }
else writeln (n);
end;

a) 295;
b) 1024;
c) 1000;
d) 10000.

Testele de la 30 la 33 se refer la funcia de mai jos, unde V1 i V2 sunt vectori


cu n componente numere naturale:

Varianta Pascal Varianta C++


procedure t(n,i,j:integer); void t(int n, int i, int j)
begin { if (i<=n && j<=n)
if (i<=n) and (j<=n) then { if (V1[i]<V2[j])
begin cout<<V1[i++]<<" ";
if V1[i]<V2[j] then else cout<<V2[j++]<<" ";
begin t(n,i,j);
write (V1[i], ); }
i:=i+1 }
end
else // V1 si V2 contin date
begin // incepand cu indicele 1
write (V2[j], );
j:=j+1
end;
t(n,i,j);
end;
end;

30. Dac n este egal cu 3, V1=(1,2,3) i V2=(4,5,1) ce se afieaz la


apelul t(n,1,1)?

a) 1 2 3;
b) 1 4 2 5 3 1;
c) cerina nu este corect;
d) 4 5 1.
164 Capitolul 6. Introducere n recursivitate

31. Care dintre afirmaiile de mai jos sunt corecte dac n este 3 i apelul este
t(n,1,1)?

a) ntotdeauna se vor afia cel mult 6 numere;


b) ntotdeauna se vor afia cel puin 3 numere;
c) ntotdeauna se vor afia cel mult 3 numere;
d) ntotdeauna se vor afia cel puin 6 numere.

32. Dac n este 3, pentru care din datele de mai jos se afieaz un numr maxim
de valori distincte, dac apelul este t(n,1,1)?

a) V1=(1,2,8) i V2=(4,5,6);
b) V1=(1,2,5) i V2=(4,5,6);
c) V1=(4,5,6) i V2=(1,2,3);
d) V1=(1,2,3) i V2=(4,5,6).

33. Ce va afia funcia la apelul t(1,1,n) dac n=3, V1=(1,2,3) i


V2=(4,5,6)?

a) 1 2 3; b) 1 2 3 4 5 6;
c) 4 5 6; d) nici o valoare.

Testele de la 34 la 36 se refer la programul urmtor:

Varianta Pascal Varianta C++


type vector=array[1..10] of #include <iostream.h>
integer; int m,n,i,V[20];
var m,n,i:integer;
V:vector; void b(int k, int n)
{ if (n)
procedure b(k,n:integer); { b(k+1,n/2);
begin if (n%2) cout<<V[k]<<endl;
if n<>0 then }
begin }
b(k+1,n div 2);
if n mod 2=1 then main()
writeln(V[k]);; { int i,n;
end cin>>n;
end; for (i=1;i<=n;i++)
cin>>V[i];
begin cin>>m;
readln (n); b(1,m);
for i:=1 to n do readln(V[i]); }
readln(m);b(1,m);
end.

34. Dac n=4, V=(1,2,3,4) i m=2, ce afieaz programul?

a) 4; b) 3; c) 2; d) 1.
Manual de informatic pentru clasa a XI-a 165

35. Dac n=4, pentru care dintre valorile de mai jos ale lui m se afieaz dou
valori?

a) 4; b) 3; c) 2; d) 1.

36. Dac n=4, pentru care valoare a lui m se afieaz toate cele 4 valori ale
vectorului?

a) 15; b) 16; c) 0; d) Nu exist o astfel de valoare.

37. n funcia de mai jos nlocuii linia ... cu una dintre instruciunile urmtoare,
astfel nct funcia s-i ncheie execuia fr eroare pentru orice valoare admisibil
a argumentului:

Varianta Pascal Varianta C++


function x(n:integer):integer; int x(int n)
begin {
if n<>0 then if (n)
begin {
writeln(n); cout<<n;
... ...
end }
end; }

a) x:=x(n-2); a) return x(n-2);


b) x:=x(n mod 2); b) return x(n%2);
c) x:=x(n-1); c) return x(n-1);
d) x:=x(n div 2). d) return x(n/2).

Testele 38 i 39 se refer la funcia de mai jos:

Varianta Pascal Varianta C++


function an(n:integer):integer; int an(int n)
begin {
if n=0 then an:=1 if (n==0) return 1;
else an:=3*an(n-1)+7 else return 3*an(n-1)+7;
end; }

38. Dac funcia este apelat prin an(4), de cte ori se autoapeleaz?

a) de 4 ori; b) de 3 ori; c) de 5 ori; d) depinde de stiv.

39. Pentru care dintre valorile de mai jos, care sunt parametri de intrare pentru
funcia an, executarea funciei se termin cu eroare?

a) 0; b) -1; c) 1; d) nici una dintre valorile de mai sus.


166 Capitolul 6. Introducere n recursivitate

40. Pentru programul de mai jos, de cte ori se autoapeleaz funcia an?

Varianta Pascal Varianta C++


function an(n:integer):integer; #include <iostream.h>
begin int an(int n)
if n=0 then an:=2 { if (n==0) return 2;
else else
if n=1 then an:=1 if (n==1) return 1;
else an:=an(n-1)-an(n-2)+1 else
end; return an(n-1)-an(n-2)+1;
}
begin
writeln(an(4)); main()
end. { cout<<an(4); }

a) de 8 ori; b) de 4 ori; c) de 9 ori; d) de 2 ori.

Indicaii / Rezolvri

1. Sn=1+2+....n-1+n; SnSn-1+n;

0, daca n = 0
Sn =
S n1 + n, altfel

7. Exist (2n)! permutri ale literelor. ntruct pentru fiecare permutare nu


conteaz dac s-a inversat A cu A, (2n)! se mparte la n!. Pentru c nu
conteaz dac s-a inversat B cu B (2n)!, se mparte din nou la n!. Se obine:

(2n )! = C n
n!n!
2n

8.
Varianta Pascal Varianta C++
function int Maxim(int n)
Maxim(n:integer):integer; { int max;
var max:integer; if (n==1) return V[1];
begin else
if n=1 then Maxim:=V[1] { max=Maxim(n-1);
else begin if (max<V[n]) return V[n];
max:=Maxim(n-1); else return max;
if max<V[n] then }
Maxim:=V[n] }
else Maxim:=max;
end;
end;
Manual de informatic pentru clasa a XI-a 167

9. Se calculeaz cifra maxim a numrului i se adun 1.

10. De exemplu, funcia de mai jos se apeleaz Prim(n,2):

Varianta Pascal Varianta C++


function int Prim(int n,int i)
Prim(n,i:integer):boolean; { if (i>(int)(sqrt(n)+1))
begin return 1;
if i>trunc(sqrt(n))+1 else
then Prim:=true if (n%i==0) return 0;
else else return Prim(n,i+1);
if n mod i=0 }
then Prim:=false
else Prim:=Prim(n,i+1);
end;

11.

Varianta Pascal Varianta C++


function int Suma(int n)
Suma(n:integer):integer; { if (n==0) return 0;
begin else return Suma(n-1)+
if n=0 (V[n]%2==0)*V[n];
then Suma:=0 }
else Suma:=Suma(n-1)+
ord(V[n] mod 2=0)*V[n];
end;

12.

Varianta Pascal Varianta C++


function int Apartine(int n, int x)
Apartine(n,x:integer):boolean; { if (n==0) return 0;
begin else
if n=0 if (V[n]==x) return 1;
then else
Apartine:=false return Apartine(n-1,x);
else }
if V[n]=x
then Apartine:=true
else
Apartine:=Apartine(n-1,x);
end;
168 Capitolul 6. Introducere n recursivitate

13. Este clasicul algoritm de cutare binar.

Varianta Pascal Varianta C++


function int PInt(int i,int j,float x)
PInt(i,j:integer;x:real):integer; { int Mijloc;
var Mijloc:integer; if (j-i>1)
begin { Mijloc=(i+j)/2;
if j-i>1 if (x==Mijloc)
then return Mijloc;
begin else
Mijloc:=(i+j) div 2; if (x<Mijloc)
if x=Mijloc return
then Pint:=Mijloc PInt(i,Mijloc,x);
else else
if x<Mijloc then PInt(Mijloc,j,x);
Pint:=Pint(i,Mijloc,x) }
else Pint:=Pint(Mijloc,j,x) else return i;
end }
else Pint:=i
end;

14. Iniial, se apeleaz cu Palindrom(1,n):

Varianta Pascal Varianta C++


function int Palindrom(int i, int j)
Palindrom(i,j:integer):boolean; {
begin if (i>=j) return 1;
if i>=j else
then Palindrom:=true if (V[i]!=V[j]) return 0;
else else return
if V[i]<>V[j] Palindrom(i+1,j-1);
then Palindrom:=false }
else Palindrom:=
Palindrom(i+1,j-1);
end;

15.

Varianta Pascal Varianta C++


function Nrcif (n:longint): int Nrcif(long n)
integer; { if (n<10) return 1;
begin else return Nrcif(n/10)+1;
if n<10 }
then Nrcif:=1
else Nrcif:=NrCif(n div 10)+1;
end;
Manual de informatic pentru clasa a XI-a 169

16.

Varianta Pascal Varianta C++


procedure Oglinda(n:integer); void Oglinda(int n)
begin { if (n)
if n<>0 then { cout<<n%10;
begin Oglinda (n/10);
write(n mod 10); }
Oglinda(n div 10); }
end;
end;

17. Variabila ninv, transmis prin referin, trebuie s rein, iniial, 0.

Varianta Pascal Varianta C++


procedure Oglinda(n:integer; void Oglinda(int n, int& ninv)
var ninv:integer); { if (n)
begin { ninv=ninv*10+n%10;
if n<>0 then Oglinda (n/10,ninv);
begin }
ninv:=ninv*10+n mod 10; }
Oglinda(n div 10, ninv);
end;
end;

18. Funcia se apeleaz cu Distincte(1,n):

Varianta Pascal Varianta C++


function int Distincte(int i,int n)
Distincte(i,n:integer):boolean; { int gasit,j;
var gasit:boolean; if (i==n) return 1;
j:integer; else
begin { gasit=0;
if i=n for (j=i+1;j<=n;j++)
then Distincte:=true if (V[i]==V[j]) gasit=1;
else if (gasit) return 0;
begin else return
gasit:=false; Distincte(i+1,n);
for j:=i+1 to n do }
if V[i]=V[j] }
then gasit:=true;
if gasit
then Distincte:=false
else Distincte:=
Distincte(i+1,n);
end
end;
170 Capitolul 6. Introducere n recursivitate

19. Apelai cu Descompun(1,n):

Varianta Pascal Varianta C++


procedure Descompun(i,n:integer); void Descompun(int i,int n)
begin { if (i<=n/2)
if i<= n div 2 then { cout<<i<<" "<<n-i<<endl;
begin Descompun(i+1,n);
writeln (i, ' ', n-i); }
Descompun(i+1,n); }
end
end;

20. Apelai cu Refac(n,n):

Varianta Pascal Varianta C++


function int Refac(int i,int n)
Refac(i,n:integer):integer; { if (i==1) return V[1];
begin else
if i=1 then Refac:=V[1] return 2*Refac(i-1,n)+V[i];
else }
Refac:=2*Refac(i-1,n)+V[i];
end;

21. Apelai cu Valoare(n,n,a):

Varianta Pascal Varianta C++


function int Valoare(int i,int n,int a)
Valoare(i,n,a:integer):integer; { if (i==1) return V[i];
begin else return a*Valoare(i-1,
if i=1 then Valoare:=V[1] n,a)+V[i];
else Valoare:=a*Valoare(i-1, }
n,a)+V[i];
end;

22. Este ineficient s calculm recursiv.

23. S presupunem c avem de calculat S(8,5). Mai nti calculm:

S(1,1)=1, S(1,2)=0, S(1,3)=0, ..., S(1,8)=0. Urmeaz:

S(2,1)=1, S(2,2)=1,S(2,3)=0, ..., S(2,8)=0


S(3,1)=1, S(3,2)=S(2,1)+2S(2,2)=3, S(3,3)=1, S(3,4)=0, ...
S(4,1)=1, S(4,2)=S(3,1)+2S(3,2)=7, ...

Cu excepia primei linii S(1,x), pentru calculul elementelor de pe linia i, S(i,k),


se folosesc rezultatele aflate pe linia i-1, adic S(i-1,x).
Manual de informatic pentru clasa a XI-a 171

25. n cazul n care k<n, avem:


P(n,k)=P(n-k+k,k)=P(n-k,1)+P(n-k,2)+...+P(n-k,k).

Varianta Pascal Varianta C++


function int P(int n,int k)
P(n,k:integer):integer; { int i,s=0;
var i,s:integer; if (k>n) return 0;
begin else
if k>n then P:=0 if (k==1 || k==n) return 1;
else else
if (k=1) or (k=n) { for (i=1;i<=n-k;i++)
then P:=1 s+=P(n-k,i);
else return s;
begin }
s:=0; }
for i:=1 to n-k do
s:=s+P(n-k,i);
P:=s;
end
end;

27. a); 28. d); 29. b); 30. a); 31. a), b); 32. a); 33. d);

34. c); 35. b); 36. a); 37. d);

38. a); 39. b);


40. a) Autoapelurile se efectueaz dup schema de mai jos:

3 2

2 1 1 0

1 0

Figura 6.2. Exemplu

Acum, vedei pe viu motivul pentru care, n cazul unor astfel de formule de
recuren, n care, n expresie, intervin mai muli operanzi ce se calculeaz
recursiv, este de preferat metoda iterativ.
172

Capitolul 7

Metoda DIVIDE ET IMPERA

7.1. 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 uoare), care se rezolv, iar soluia pentru problema iniial se obine
combinnd soluiile problemelor n care a fost descompus. Se presupune c
fiecare dintre problemele n care a fost descompus problema iniial, se poate
descompune n alte subprobleme, la fel cum a fost descompus problema iniial.
Procedeul se reia pn cnd (n urma descompunerilor repetate) se ajunge la
probleme care admit rezolvare imediat.

Evident, nu toate problemele pot fi rezolvate prin utilizarea acestei tehnici.


Fr teama de a grei, putem afirma c numrul lor este relativ mic, tocmai datorit
cerinei ca problema s admit o descompunere repetat.

DIVIDE ET IMPERA este o tehnic ce admite o implementare recursiv. Am


nvat principiul general prin care se elaboreaz algoritmii recursivi: ce se ntmpl
la un nivel, se ntmpl la orice nivel (avnd grij s asigurm condiiile de
terminare). Tot aa, se elaboreaz un algoritm prin DIVIDE ET IMPERA. La un anumit
nivel, avem dou posibiliti:

1) am ajuns la o problem care admite o rezolvare imediat, caz n care se


rezolv i se revine din apel (condiia de terminare);

2) nu am ajuns n situaia de la punctul 1, caz n care descompunem problema n


dou sau mai multe subprobleme, pentru fiecare din ele reapelm funcia,
combinm rezultatele i revenim din apel.

7.2. Aplicaii

7.2.1. Valoarea maxim dintr-un vector

Problema 7.1. Se citete un vector cu n componente, numere naturale. Se


cere s se tipreasc valoarea maxim.

Problema de mai sus este binecunoscut. Cum o rezolvm utiliznd tehnica


DIVIDE ET IMPERA?
Manual de informatic pentru clasa a XI-a 173

Rezolvare. Trebuie tiprit valoarea maxim dintre numerele reinute n vector de


la i la j (iniial i=1 i j=n).

Pentru aceasta, procedm astfel:

dac i=j, valoarea maxim va fi v[i];

contrar, vom mpri vectorul n doi vectori (primul vector va conine


componentele de la i la (i+j) div 2, al doilea va conine componentele
de la (i+j) div 2 + 1 la j, rezolvm subproblemele (aflm maximul
pentru fiecare din ele) iar soluia problemei va fi dat de valoarea maxim
dintre rezultatele celor dou subprobleme.

Programul este urmtorul:

Varianta Pascal Varianta C++


var v:array[1..10] of #include <iostream.h>
integer; int v[10],n;
n,i:integer;
int max(int i,int j)
function max(i,j:integer) { int a,b;
:integer; if (i==j) return v[i];
var a,b:integer; else
begin { a=max(i,(i+j)/2);
if i=j b=max((i+j)/2+1,j);
then max:=v[i] if (a>b) return a;
else else return b;
begin }
a:=max(i,(i+j) div 2); }
b:=max((i+j) div 2+1,j);
if a>b main()
then max:=a { cout<<"n="; cin>>n;
else max:=b; for (int i=1;i<=n;i++)
end; { cout<<"v["<<i<<"]=";
end; cin>>v[i];
}
begin
cout<<"max="<<max(1,n);
write('n=');
}
readln(n);
for i:=1 to n do
begin
write('v[',i,']=');
readln(v[i])
end;
writeln('max=',max(1,n))
end.

Algoritmul prezentat este exclusiv didactic, n practic este preferat


algoritmul clasic.
174 Capitolul 7. Metoda DIVIDE ET IMPERA

7.2.2. Sortarea prin interclasare

Problema 7.2. Se consider vectorul a cu n componente numere ntregi (sau


reale). S se sorteze cresctor, utiliznd sortarea prin interclasare.

Interclasarea a doi vectori a fost studiat. Dac dispunem de dou iruri de


valori, primul cu m elemente, al doilea cu n elemente, ambele sortate, atunci se
poate obine un vector care conine toate valorile sortate. Algoritmul de interclasare
este performant, pentru c efectueaz cel mult m+n-1 comparri.

n cele ce urmeaz, vom utiliza algoritmul de interclasare n vederea sortrii


unui vector prin interclasare.

Rezolvare. Algoritmul de sortare prin interclasare se bazeaz pe urmtoarea


idee: pentru a sorta un vector cu n elemente l mprim n doi vectori care, odat
sortai, se interclaseaz.

Conform strategiei generale DIVIDE ET IMPERA, problema este descompus n


alte dou subprobleme de acelai tip i, dup rezolvarea lor, rezultatele se combin
(n particular se interclaseaz). Descompunerea unui vector n ali doi vectori care
urmeaz a fi sortai are loc pn cnd avem de sortat vectori de una sau
dou componente.

n aplicaie, funcia sort sorteaz un vector cu maximum dou elemente;


interc interclaseaz rezultatele; divimp implementeaz strategia general a
metodei studiate.

Varianta Pascal Varianta C++


type vector=array [1..10] of #include <iostream.h>
integer;
int a[10],n;
var a:vector;
n,i:integer; void sort(int p,int q,
int a[10])
procedure sort(p,q:integer; {
var a:vector); int m;
if (a[p]>a[q])
var m:integer; { m=a[p];
begin a[p]=a[q];
if a[p]>a[q] a[q]=m;
then }
begin }
m:=a[p]; void interc(int p,int q,
a[p]:=a[q]; int m,int a[10])
a[q]:=m { int b[10],i,j,k;
end i=p; j=m+1; k=1;
end;
Manual de informatic pentru clasa a XI-a 175

procedure interc while (i<=m && j<=q)


(p,q,m:integer; var a:vector); if (a[i]<=a[j])
var b:vector; { b[k]=a[i];
i,j,k:integer; i=i+1;
k=k+1;
begin }
i:=p; j:=m+1; k:=1; else
while (i<=m) and (j<=q) do { b[k]=a[j];
if a[i]<=a[j] then j=j+1;
begin k=k+1;
b[k]:=a[i]; }
i:=i+1; k:=k+1 if (i<=m)
end for (j=i;j<=m;j++)
else
{ b[k]=a[j];
begin
k=k+1;
b[k]:=a[j];
j:=j+1; k:=k+1 }
end; else
if i<=m then for (i=j;j<=q;j++)
for j:=i to m do { b[k]=a[i];
begin k=k+1;
b[k]:=a[j]; k:=k+1 }
end k=1;
else for (i=p;i<=q;i++)
for i:=j to q do { a[i]=b[k];
begin k=k+1;
b[k]:=a[i]; k:=k+1 }
end; }
k:=1;
for i:=p to q do void divimp (int p,int q,
begin int a[10])
a[i]:=b[k]; k:=k+1 { int m;
end if ((q-p)<=1) sort(p,q,a);
end; else
{ m=(p+q)/2;
procedure divimp divimp(p,m,a);
(p,q:integer;var a:vector); divimp(m+1,q,a);
var m:integer;
interc(p,q,m,a);
begin
}
if (q-p)<=1 then sort(p,q,a)
else }
begin
m:=(p+q) div 2; main()
divimp(p,m,a); { int i;
divimp(m+1,q,a); cout<<"n="; cin>>n;
interc(p,q,m,a) for (i=1;i<=n;i++)
end { cout<<"a["<<i<<"]=";
end; cin>>a[i];
}
begin divimp(1,n,a);
write('n='); readln(n); for (i=1;i<=n;i++)
for i:=1 to n do cout<<a[i]<<" ";
begin }
write('a[',i,']=');
readln(a[i])
end;
divimp(1,n,a);
for i:=1 to n do writeln(a[i])
end.
176 Capitolul 7. Metoda DIVIDE ET IMPERA

n continuare, calculm numrul aproximativ de comparri efectuat de


algoritm. Fie acesta T(n). Mai simplu, presupunem n=2k.

O problem se descompune n alte dou probleme, fiecare cu n/2


componente, dup care urmeaz interclasarea lor, care necesit n/2+n/2=n
comparaii:

0, n = 1;

T(n) = n
2T + n, altfel.

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

k
+ +
2k +2
... = n+
k
n + ...
+ n = n k = n log2n

de k ori de k ori

7.2.3. Sortarea rapid

Problema 7.3. Fie vectorul a cu n componente numere ntregi (sau reale). Se


cere ca vectorul s fie sortat cresctor.

Rezolvare. Este necesar o funcie POZ care trateaz o poriune din vector,
cuprins ntre indicii dai de li (limita inferioar) i ls (limita superioar). Rolul
acestei funcii este de a poziiona prima component a[li] pe o poziie k cuprins
ntre li i ls, astfel nct toate componentele vectorului cuprinse ntre li i k-1
s fie mai mici sau egale dect a[k] i toate componentele vectorului cuprinse
ntre k+1 i ls s fie mai mari sau egale dect a[k].

n aceast funcie exist dou moduri de lucru:

a) i rmne constant, j scade cu 1;


b) i crete cu 1, j rmne constant.

Funcia este conceput astfel:


iniial, i va lua valoarea li, iar j va lua valoarea ls (elementul care iniial
se afl pe poziia li se va gsi mereu pe o poziie dat de i sau de j);
se trece n modul de lucru a);
att timp ct i<j, se execut:
dac a[i] este strict mai mare dect a[j], atunci se inverseaz
cele dou numere i se schimb modul de lucru;
i i j se modific corespunztor modului de lucru n care se afl
programul;
k ia valoarea comun a lui i i j.
Manual de informatic pentru clasa a XI-a 177

Pentru a=(6,9,3,1,2), li=1, ls=5; modul de lucru a):

i=1, j=5;
a[1]>a[5], deci se inverseaz elementele aflate pe poziiile 1 i 5,
deci a=(2,9,3,1,6) i programul trece la modul de lucru b);
i=2, j=5;
a[2]>a[5], deci a=(2,6,3,1,9) i se revine la modul de lucru a);
i=2, j=4;
a[2]>a[4], deci a=(2,1,3,6,9); se trece la modul de lucru b);
i=3, j=4;
funcia se ncheie, elementul aflat iniial pe poziia 1 se gsete
acum pe poziia 4, toate elementele din stnga lui fiind mai mici
dect el, totodat toate elementele din dreapta lui fiind mai mari
dect el (k=4).

Alternana modurilor de lucru se explic prin faptul c elementul care trebuie


poziionat se compar cu un element aflat n dreapta sau n stnga lui, ceea ce
impune o modificare corespunztoare a indicilor i i j.

Dup aplicarea funciei POZ, este evident c elementul care se afl iniial n
poziia li va ajunge pe o poziie k i va rmne pe acea poziie n cadrul
vectorului deja sortat, fapt care reprezint esena algoritmului.

Funcia QUICK are parametrii li i ls (limita inferioar i limita superioar).


n cadrul ei se utilizeaz metoda DIVIDE ET IMPERA, dup cum urmeaz:

se apeleaz POZ;
se apeleaz QUICK pentru li i k-1;
se apeleaz QUICK pentru k+1 i ls.

Varianta Pascal Varianta C++


type vector=array [1..100] of #include <iostream.h>
integer; int a[100],n,k;

var i,n,k:integer; void poz (int li,int ls,int&


a:vector; k,int a[100])
{ int i=li,j=ls,c,i1=0,j1=-1;
procedure poz (li,ls:integer; while (i<j)
var k:integer; { if (a[i]>a[j])
var a:vector); { c=a[j]; a[j]=a[i];
a[i]=c; c=i1;
var i,j,c,i1,j1:integer; i1=-j1; j1=-c;
begin }
i=i+i1;
i1:=0; j=j+j1;
j1:=-1; }
i:=li; k=i;
j:=ls; }
178 Capitolul 7. Metoda DIVIDE ET IMPERA

while i<j do void quick (int li,int ls)


begin { if (li<ls)
if a[i]>a[j] { poz(li,ls,k,a);
then quick(li,k-1);
begin quick(k+1,ls);
c:=a[j]; }
a[j]:=a[i]; }
a[i]:=c;
c:=i1; main()
i1:=-j1; { int i;
j1:=-c cout<<"n="; cin>>n;
end; for (i=1;i<=n;i++)
i:=i+i1; { cout<<"a["<<i<<"]=";
j:=j+j1 cin>>a[i];
end; }
k:=i quick(1,n);
end; for (i=1;i<=n;i++)
procedure quick(li,ls:integer); cout<<a[i]<<endl;
begin }
if li<ls
then
begin
poz(li,ls,k,a);
quick(li,k-1);
quick(k+1,ls)
end
end;
begin
write('n=');
readln(n);
for i:=1 to n do
begin
write('a[',i,']=');
readln(a[i])
end;
quick(1,n);
for i:=1 to n do
writeln(a[i])
end.

Reinei! Sortarea rapid efectueaz n medie n log 2 n operaii.

Demonstraia necesit cunotine de matematic pe care nu le avei la nivelul


acestui an de studiu
Manual de informatic pentru clasa a XI-a 179

7.2.4. Turnurile din Hanoi

Problema 7.4. Se dau 3 tije simbolizate prin a, b, c.


Pe tija a se gsesc discuri de diametre diferite, aezate
n ordine descresctoare a diametrelor privite de jos n
sus. Se cere s se mute discurile de pe tija a pe tija b, utiliznd ca tij intermediar
tija c, respectnd urmtoarele reguli:

la fiecare pas se mut un singur disc;


nu este permis s se aeze un disc cu diametrul mai mare peste un disc
cu diametrul mai mic.

Rezolvare.
Dac n=1, se face mutarea ab, adic se mut discul de pe tija a pe tija b.

Dac n=2, se fac mutrile ac, ab, cb.

n cazul n care n>2, problema se complic. Notm cu H(n,a,b,c) irul


mutrilor celor n discuri de pe tija a pe tija b, utiliznd ca tij intermediar, tija c.

Conform strategiei DIVIDE ET IMPERA, ncercm s descompunem problema


n alte dou subprobleme de acelai tip, urmnd apoi combinarea soluiilor. n acest
sens, observm c mutarea celor n discuri de pe tija a pe tija b, utiliznd ca tij
intermediar tija c, este echivalent cu:

mutarea a n-1 discuri de pe tija a pe tija c, utiliznd ca tij intermediar tija b;


mutarea discului rmas pe tija b;
mutarea a n-1 discuri de pe tija c pe tija b, utiliznd ca tij intermediar tija a.

Parcurgerea celor trei etape permite definirea recursiv a irului


H(n,a,b,c) astfel:

ab, n =1
H(n, a, b, c) =
H(n 1, a, c, b), ab, H(n 1, c, b, a), n >1

Privii urmtoarele exemple:

1) pentru n=2, avem: H(2,a,b,c)=H(1,a,c,b),ab,H(1,c,b,a)=ac,ab,cb;

2) pentru n=3, avem:


H(3,a,b,c)=H(2,a,c,b),ab,H(2,c,b,a)=H(1,a,b,c),ac,H(1,b,c,a),
ab,H(1,c,a,b),cb,H(1,a,b,c)=ab,ac,bc,ab,ca,cb,ab.
180 Capitolul 7. Metoda DIVIDE ET IMPERA

Varianta Pascal Varianta C++


var a,b,c:char; #include <iostream.h>
n:integer; char a,b,c;
procedure han (n:integer; int n;
a,b,c:char);
begin void han (int n,char a,
if n=1 then char b,char c)
writeln(a,b) { if (n==1) cout<<a<<b<<endl;
else else
begin { han(n-1,a,c,b);
han(n-1,a,c,b); cout<<a<<b<<endl;
writeln(a,b); han(n-1,c,b,a);
han(n-1,c,b,a) }
end }
end; main()
begin { cout<<"N="; cin>>n;
write('N='); readln(n); a='a'; b='b'; c='c';
a:='a'; b:='b'; c:='c'; han(n,a,b,c);
han(n,a,b,c) }
end.

7.2.5. Problema tieturilor

Problema 7.5. Se d o bucat dreptunghiular de tabl cu lungimea l i


nlimea h, avnd pe suprafaa ei n guri de coordonate numere ntregi. Se cere
s se decupeze din ea o bucat de arie maxim care nu prezint guri. Sunt
permise numai tieturi verticale i orizontale.

Rezolvare. Coordonatele gurilor sunt reinute n doi vectori XV i YV.


Dreptunghiul iniial, precum i dreptunghiurile care apar n procesul tierii sunt
memorate n program prin coordonatele colului din stnga-sus (X,Y), prin
lungime i nlime (L,H).

Pentru un dreptunghi (iniial pornim cu toat bucata de tabl), verificm dac


avem sau nu o gaur n el (se caut practic prima din cele n guri). n situaia cnd
acesta prezint o gaur, problema se descompune n alte patru probleme de
acelai tip. Dac bucata nu prezint guri, se compar aria ei cu aria unei alte
buci fr gaur, gsit n fazele precedente.

Menionm c dreptunghiul de arie maxim fr guri este reinut prin


aceiai parametri ca i dreptunghiul cu guri, n zonele XF, YF, LF, HF.

n concluzie, problema iniial se descompune n alte patru probleme de


acelai tip, mai uoare, ntruct fiecare nou dreptunghi are cel mult n-1 guri, dac
dreptunghiul iniial avea n guri. La aceast problem compararea soluiilor const
n a reine dreptunghiul cu aria maxim dintre cele fr guri.
Manual de informatic pentru clasa a XI-a 181

Fie dreptunghiul cu o gaur:

h
xv(i),yv(i)

x,y l
Pentru a se afla n interiorul dreptunghiului, gaura trebuie s ndeplineasc
simultan condiiile:
1) xv(i)>x;
2) xv(i)<x+l;
3) yv(i)>y;
4) yv(i)<y+h.

Dac facem o tietur vertical prin aceast gaur, obinem dou dreptunghiuri:
1) x, y, xv(i)-x, h;
2) xv(i), y, l+x-xv(i), h.

n urma unei tieturi pe orizontal se obin cele dou dreptunghiuri:


1) x, y ,l ,yv(i)-y;
2) x, yv(i), l, h+y-yv(i).

Programul este urmtorul:

Varianta Pascal Varianta C++


type vect=array [1..9] of #include <iostream.h>
integer; int l,h,i,n,xf,yf,lf,
hf,xv[10],yv[10];
var l,h,i,n,xf,yf,
lf,hf:integer; void dimp(int x,int y,int l,
xv,yv:vect; int h, int& xf, int& yf,
int& lf,int& hf,
procedure dimp int xv[10],int yv[10])
(x,y,l,h:integer; { int gasit=0,i=1;
var xf,yf,lf,hf:integer; while (i<=n && !gasit)
var xv,yv:vect); if (xv[i]>x && xv[i]<l &&
var gasit:boolean; yv[i]>y && yv[i]<y+h)
gasit=1;
i:integer;
else i++;
begin
if (gasit)
i:=1;
{ dimp(x,y,xv[i]-x,h,xf,
gasit:=false;
yf,lf,hf,xv,yv);
while (i<=n) and (not gasit) dimp(xv[i],y,l+x-xv[i],
do h,xf,yf,lf,hf,xv,yv);
if (xv[i]>x) and (xv[i]<l) dimp(x,y,l,yv[i]-y,xf,
and (yv[i]>y) and yf,lf,hf,xv,yv);
(yv[i]<y+h) dimp(x,yv[i],l,h+y-yv[i],
then gasit:=true xf,yf,lf,hf,xv,yv);
else i:=i+1; }
182 Capitolul 7. Metoda DIVIDE ET IMPERA

if gasit else
then if (l*h>lf*hf)
begin { xf=x;
dimp(x,y,xv[i]-x, yf=y;
h,xf,yf,lf,hf,xv,yv); lf=l;
dimp(xv[i],y,l+x-xv[i], hf=h;
h,xf,yf,lf,hf,xv,yv); }
dimp(x,y,l,yv[i]-y, }
xf,yf,lf,hf,xv,yv);
dimp(x,yv[i],l,h+y-yv[i], main()
xf,yf,lf,hf,xv,yv) { cout<<"n="; cin>>n;
end for (int i=1;i<=n;i++)
else { cout<<"x["<<i<<"]=";
if (l*h)>(lf*hf) cin>>xv[i];
then cout<<"y["<<i<<"]=";
begin cin>>yv[i];
xf:=x; }
yf:=y; cout<<"l="; cin>>l;
lf:=l; cout<<"h="; cin>>h;
hf:=h dimp(0,0,l,h,xf,yf,lf,
end hf,xv,yv);
end; cout<<"x="<<xf<<" y="<<yf
<<" l="<<lf<<" h="<<hf;
begin }
write('n=');
readln(n);
for i:=1 to n do
begin
write('x[',i,']=');
readln(xv[i]);
write('y[',i,']=');
readln(yv[i])
end;
write('l=');
readln(l);
write('h=');
readln(h);
lf:=0;
hf:=0;
dimp(0,0,l,h,xf,yf,
lf,hf,xv,yv);
writeln('x=',xf,' y=',yf,'
l=',lf,' h=',hf)
end.
Manual de informatic pentru clasa a XI-a 183

7.3. Fractali

Fractalii au fost introdui n anul 1975 prin lucrarea revoluionar a


matematicianului francez Benoit Mandelbrot, O teorie a seriilor fractale, ce
reunete totodat diversele teorii dinaintea sa. El este cel care a inventat cuvntul
fractal, de provenien latin (frngere a sparge n fragmente neregulate).

Noiunea de fractal a aprut ca urmare a studiului vieii reale, n care


informaia genetic coninut n nucleul unei celule se repet la diferite scri.
Calculatorul permite ca o anumit figur (de exemplu, un segment) s se
transforme ntr-o alta, format din mai multe figuri iniiale (de exemplu, o linie
frnt) i fiecare figur obinut s se transforme n mod asemntor (aceste
transformri necesit foarte multe calcule).

Aceste forme geometrice au fost considerate n trecut haotice sau aberaii


geometrice, iar multe dintre ele erau att de complexe nct necesitau
calculatoare performante pentru a le vizualiza. Pe parcurs, domenii tiinifice ca
fizica, chimia, biologia sau meteorologia descopereau elemente asemntoare cu
fractalii n viaa real. Acetia au proprieti matematice extrem de interesante,
care de multe ori contrazic aparena, dar acestea depesc cu mult cunotinele de
matematic din liceu.

nainte de a prezenta cteva exemple, trebuie cunoscute mai nti noiunile


de baz pentru a lucra n mod grafic. Acestea vor fi prezentate n continuare.

7.3.1. Elemente de grafic

7.3.1.1. Generaliti (varianta Pascal)

Limbajul Pascal conine o serie de proceduri i funcii care permit realizarea


unor aplicaii grafice. Acestea sunt reunite n unitatea GRAPH, ce se gsete n
subcatalogul UNITS.

Pentru ca o imagine s poat aprea pe ecran, calculatorul utilizeaz placa


video, care difer n funcie de memoria video i ali parametri. Pentru a accesa o
plac video, trebuie s folosim anumite rutine speciale, specifice lor, numite
Driver-e. Limbajul Pascal deine o colecie de astfel de componente software i n
funcie de placa ce a fost detectat n sistem, se ncarc un driver sau altul. Aceste
fiiere au extensia bgi. Deoarece performanele componentelor hardware au
depit cu mult capacitile CGA sau EGA, ne vom referi n continuare doar la
driver-ul VGA (Video Graphics Array), dezvoltat de firma IBM. Driver-ul VGA poate
lucra n mai multe moduri, ns vom prefera modul standard de nalt rezoluie
VGAHI (constant de tip ntreg), ce poate afia 640 x 480 puncte n 16 culori.
184 Capitolul 7. Metoda DIVIDE ET IMPERA

Selectarea driver-ului i a modului de lucru se face prin utilizarea procedurii


initgraph. Aceasta are trei parametri: gdriver (de tip integer) care conine
codul asociat driver-ului, gmode (de tip integer) care reine modul de lucru i o
variabil de tip string, care arat calea ctre unitatea GRAPH. Forma general a
acestei proceduri este

initgraph(gdriver,gmode,cale);.

Primii doi parametri sunt transmii prin referin.

Iniializarea sistemului grafic se poate face n dou feluri:

1) prin a solicita s se identifice automat placa grafic i corespunztor ei s


se ncarce un anumit driver i s se selecteze modul de lucru cel mai bun din
punct de vedere al performanelor:
procedure initg;
begin
gdriver := detect;
initgraph(gdriver,gmode,c:\tp\bgi);
if graphresult<>0 then
begin
writeln(Tentativa esuata!);
halt
end
end;

Constanta detect are valoarea 0 i se specific procedurii identificarea


automat a driver-ului i a modului de lucru.

Vom reine aceast procedur pentru c o vom utiliza n exemplele ulterioare.

2) prin indicarea cu ajutorul primilor doi parametri a unui driver i a unui mod
de lucru solicitate de programator (n acest caz, nu se poate executa programul pe
un calculator ce nu este dotat cu placa grafic specificat):
gdriver := VGA;
gmode := VGAHI;
initgraph(gdriver,gmode,c:\tp\bgi);
if graphresult<>0 then
begin
writeln(Tentativa esuata!);
halt
end;

Tentativa de iniializare grafic poate eua din diverse motive, cum ar fi: lipsa
unitii GRAPH, calea indicat greit, etc. Testarea se realizeaz cu funcia ntreag
graphresult care returneaz 0 n caz afirmativ i o valoare diferit de 0, n
caz contrar.

Odat intrai n modul grafic, nu se mai poate scrie pe monitor, ca pn


acum (cu write sau writeln). Ieirea din modul grafic se face prin
utilizarea procedurii closegraph.
Manual de informatic pentru clasa a XI-a 185

7.3.1.2. Generaliti (varianta C++)

Limbajul C++ (n varianta Borland), conine o serie de funcii care permit


realizarea unor aplicaii grafice. Acestea sunt reunite n fiierul GRAPHICS.H, ce se
gsete n folderul INCLUDE.

Pentru ca o imagine s poat aprea pe ecran, calculatorul utilizeaz placa


video, care difer n funcie de memoria video i ali parametri. Pentru a accesa o
plac video, trebuie s folosim anumite rutine speciale, specifice lor, numite
Driver-e. Limbajul C++ deine o colecie de astfel de componente software i n
funcie de placa ce a fost detectat n sistem, se ncarc un driver sau altul. Aceste
fiiere au extensia bgi. Deoarece performanele componentelor hardware au
depit cu mult capacitile CGA sau EGA, ne vom referi n continuare doar la
driver-ul VGA (Video Graphics Array), dezvoltat de firma IBM. Driver-ul VGA
poate lucra n mai multe moduri, ns vom prefera modul standard de nalt
rezoluie VGAHI (constant de tip ntreg), ce poate afia 640 x 480 puncte n
16 culori. Fiierul ce conine driver-ul utilizat este EGAVGA.CGI.

Selectarea driver-ului i a modului de lucru se face prin utilizarea funciei


initgraph. Aceasta are trei parametri: gdriver (de tip integer) care conine
codul asociat driver-ului, gmode (de tip integer) care reine modul de lucru i o
variabil de tip string, care arat calea ctre unitatea GRAPH. Forma general a
acestei funcii este

initgraph(&gdriver,&gmode,"cale");.

Primii doi parametri sunt transmii prin referin.

Iniializarea sistemului grafic se poate face n dou feluri:

1) prin a solicita s se identifice automat placa grafic i corespunztor ei s


se ncarce un anumit driver i s se selecteze modul de lucru cel mai bun din
punct de vedere al performanelor:
void init()
{ gdriver = DETECT;
initgraph(&gdriver,&gmode,"E:\\BORLANDC\\BGI");
if (graphresult())
{ cout<<"Tentativa nereusita.";
cout<<"Apasa o tasta pentru a inchide...";
getch();
exit(1);
}
}

Constanta DETECT are valoarea 0 i se specific funciei identificarea


automat a driver-ului i a modului de lucru.

Vom reine funcia init() pentru c o vom utiliza n exemplele ulterioare.


186 Capitolul 7. 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 se poate executa programul pe
un calculator ce nu este dotat cu placa grafic specificat):

gdriver := VGA; gmode := VGAHI;


initgraph(&gdriver,&gmode,"E:\\BORLANDC\\BGI");
if (graphresult())
{ cout<<"Tentativa nereusita.";
cout<<"Apasa o tasta pentru a inchide...";
getch();
exit(1);
}

Tentativa de iniializare grafic poate eua din diverse motive, cum ar fi: lipsa
unitii GRAPHICS, calea indicat greit, etc. Testarea se realizeaz cu funcia
ntreag graphresult() care returneaz 0 n caz afirmativ i o valoare diferit
de 0, n caz contrar.

Odat intrai n modul grafic, nu se mai poate scrie pe monitor ca pn acum


(de exemplu, cu cout). Ieirea din modul grafic se face prin utilizarea
procedurii closegraph().

Atenie! Pentru a putea scrie i rula programe C++ ce utilizeaz modul grafic al
limbajului, trebuie bifat urmtoarea opiune, din meniu:

Options / Linker / Libraries / Graphics library.

7.3.1.3. Setarea culorilor i procesul de desenare (Pascal i C++)

Cu siguran, placa video utilizat de dvs. are performane superioare


modului standard VGA, ce se regsete n driver-ul limbajului Pascal sau C++.
Pentru a generaliza ns, vom considera modul menionat anterior, ce poate reda
16 culori, reprezentate pe 4 bii.

Fiecare culoare de baz are atribuit o constant de la 0 la 15, precum


urmeaz: 0 black (negru); 1 blue (albastru); 2 green (verde); 3 cyan
(turcoaz); 4 red (rou); 5 magenta (violet); 6 brown (maro); 7 lightgrey
(gri deschis); 8 darkgrey (gri nchis); 9 lightblue (albastru deschis); 10
lightgreen (verde deschis); 11 lightcyan (turcoaz deschis); 12
lightred (rou deschis); 13 lightmagenta (violet deschis); 14 yellow
(galben) i 15 white (alb).

Aceste culori sunt cele implicite. Pentru a utiliza mai multe culori (dar nu n
acelai timp), se poate schimba setul (paleta) de culori. ntruct n acest
moment nu sunt necesare o multitudine de culori, nu vom prezenta n detaliu
acest aspect.
Manual de informatic pentru clasa a XI-a 187

Pentru a seta culoarea de fundal, se utilizeaz procedura (n Pascal) sau


funcia (n C++)
setbkcolor(culoare);.

Exemple: setbkcolor(6); sau setbkcolor(RED);.

Selectarea culorii cu care se deseneaz se face cu ajutorul procedurii (n


Pascal) sau funciei (n C++)
setcolor(culoare);.

Exemplu: setcolor(15); sau setcolor(WHITE);.

Observaii

Schimbarea culorii nu afecteaz ce am desenat anterior, ci doar ce este scris


dup apelul acestei rutine.
n cazul limbajului C++, numele simbolic al culorii se scrie obligatoriu cu
majuscule.

Operaia de desenare

Oricare ar fi modul de lucru ales, un punct se reprezint printr-un pixel de


coordonate x (linia) i y (coloana), ambele valori ntregi. Punctul din stnga-sus are
coordonatele (0,0). Pentru a ne muta la poziia (x,y), vom folosi procedura (n
Pascal) sau funcia (n C++) moveto(x,y). Pentru a trasa o linie de la punctul
curent, determinat anterior, pn la o nou poziie, vom utiliza procedura (n
Pascal) sau funcia (n C++) lineto(x1,y1). Astfel, vom obine o linie ntre
punctele de coordonate (x,y) i (x1,y1).

Exemplu. Mai jos, este prezentat un program ce deseneaz o linie pe diagonala


principal a ecranului (de la colul din stnga-sus la colul din dreapta jos):

Varianta Pascal Varianta C++


... ...
begin main()
initg; { init();
setcolor(red); setcolor(RED);
moveto(0,0); moveto(0,0);
lineto(getmaxx,getmaxy); lineto(getmaxx(),getmaxy());
readln; getch();
end. }

De asemenea, dou funcii foarte utile sunt getmaxx i getmaxy (n


Pascal) sau getmaxx() i getmaxy() (n C++). Acestea ntorc valoarea minim
i respectiv, maxim a coordonatelor de pe ecran. Astfel, cu ajutorul lor se poate
obine o independen relativ a programelor fa de modul grafic al sistemului.
188 Capitolul 7. Metoda DIVIDE ET IMPERA

7.3.2. Curba lui Koch pentru un triunghi echilateral

Se consider un triunghi echilateral. Fiecare latur a sa se transform aa


cum se vede n figura urmtoare (se mparte n trei segmente congruente, se
elimin segmentul din mijloc i se construiete deasupra un triunghi echilateral):

Figura 7.1. Exemplu de transformare

Fiecare latur a acestui poligon se transform din nou, dup aceeai regul.
S se vizualizeze figura obinut dup ls pai (numr citit de la tastatur).

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

Programul principal va apela, pentru fiecare segment care constituie o latur


a triunghiului, o procedur numit generator. Aceasta execut transformarea de
ls ori, avnd ca parametri de intrare coordonatele punctelor care constituie
extremitile segmentului, numrul de transformri fcute (n) i numrul de
transformri care trebuie efectuate (ls). Pentru a nelege funcionarea procedurii,
trebuie s avem un minimum de cunotine specifice geometriei analitice (ce se
poate face fr matematic?).

Fie AB un segment de dreapt, unde A este un punct de coordonate (x1,y1),


iar B are coordonatele (x2,y2). Dou puncte P1 i P2 mpart segmentul ntr-un
anumit raport, notat cu k:
x1 k x2 y1 k y 2
xp = , yp = .
1 k 1 k

Demonstrai singuri aceste formule!

Fie segmentul AB cu A(x1,y1) i B(x2,y2). Considerm punctele C i D care


mpart segmentul n trei segmente congruente.

Aflm coordonata punctului D:


DA x1 + 2 x 2 y1 + 2 y 2
k= = 2; xp = , yp = .
DB 3 3

Problema const n stabilirea coordonatelor vrfului noului triunghi


echilateral. Acestea se obin dac se rotete punctul C n jurul punctului D cu
unghiul / 3 . Rotaia o efectueaz procedura rotplan.
Manual de informatic pentru clasa a XI-a 189

S prezentm algoritmul care st la baza procedurii generator:

se pornete de la segmentul AB;


se determin coordonatele punctului care constituie vrful triunghiului
echilateral (s-l notm cu V);
n cazul n care segmentul nu a fost transformat de ls ori, se apeleaz
generator pentru segmentele AC, CV, VD i DB;
contrar, se apeleaz procedura care traseaz linia frnt ACVDB.

n programul principal au fost alese punctele care determin triunghiul


echilateral iniial plasat n centrul ecranului i pentru fiecare segment ce
constituie o latur a acestuia s-a apelat procedura generator. Odat tras curba,
se coloreaz interiorul acesteia.

Programul este urmtorul:

Varianta Pascal Varianta C++


uses graph,crt; #include "graphics.h"
var L,gdriver,gmode, #include <iostream.h>
ls:integer; #include <stdlib.h>
xmax,ymax:integer; #include <conio.h>
#include <math.h>
procedure initg; int gdriver,gmode,ls,L;
...
void init()
procedure rotplan(xc,yc,x1, { ... }
y1:integer; var x,y:integer;
unghi:real); void rotplan(int xc,int yc,
begin int x1, int y1,int &x,
x := round(xc+(x1-xc)* int &y,float unghi)
cos(unghi)-(y1-yc)* {x = ceil(xc+(x1-xc)*cos(unghi)-
sin(unghi)); (y1-yc)*sin(unghi));
y := round(yc+(x1-xc)* y = ceil(yc+(x1-xc)*sin(unghi)+
sin(unghi)+(y1-yc)* (y1-yc)*cos(unghi));
cos(unghi)) }
end; void desenez(int x1,int y1,
procedure desenez(x1,y1,x2, int x2,int y2,int x3,int y3)
y2,x3,y3:integer); { moveto(x1,y1);
begin lineto(div((2*x1+x2),3).quot,
moveto(x1,y1); div((2*y1+y2),3).quot);
lineto((2*x1+x2) div 3, lineto(x3,y3);
(2*y1+y2) div 3); lineto(div((x1+2*x2),3).quot,
lineto(x3,y3); div((y1+2*y2),3).quot);
lineto((x1+2*x2) div 3, lineto(x2,y2); }
(y1+2*y2) div 3); void generator(int x1,int y1,
lineto(x2,y2); int x2,int y2,int n, int ls)
end; { int x,y;
procedure rotplan(div((2*x1+x2),3).quot,
generator(x1,y1,x2,y2, div((2*y1+y2),3).quot,
n,ls:integer); div((x1+2*x2),3).quot,
var x,y:integer; div((y1+2*y2),3).quot,
x,y,M_PI/3);
190 Capitolul 7. Metoda DIVIDE ET IMPERA

begin if (n<ls)
rotplan((2*x1+x2) div 3, {generator(x1,y1,div((2*x1+x2),
(2*y1+y2) div 3,(x1+2*x2) div 3).quot,div((2*y1+y2),
3,(y1+2*y2) div 3,x,y,pi/3); 3).quot,n+1,ls);
if n<ls then generator(div((2*x1+x2),
begin 3).quot,div((2*y1+y2),
generator(x1,y1,(2*x1+x2) 3).quot,x,y,n+1,ls);
div 3,(2*y1+y2) div 3, generator(x,y,div((x1+2*x2),
n+1,ls); 3).quot,div((y1+2*y2),
generator((2*x1+x2) div 3, 3).quot,n+1,ls);
(2*y1+y2) div 3, generator(div((x1+2*x2),
x,y,n+1,ls); 3).quot,div((y1+2*y2),
generator(x,y,(x1+2*x2) div 3).quot,x2,y2,n+1,ls);
3,(y1+2*y2) div 3,n+1,ls); }
generator((x1+2*x2) div 3, else desenez(x1,y1,x2,y2,x,y);
(y1+2*y2) div 3, }
x2,y2,n+1,ls);
end main()
else desenez(x1,y1,x2,y2,x,y); { cout<<"ls= "; cin>>ls;
end; init();
setcolor(6);
begin L = getmaxx()-320;
write('ls= '); readln(ls); generator(160,getmaxy()-150,
initg; 160+L,getmaxy()-150,1,ls);
setcolor(red); generator(160+L,getmaxy()-
L:=getmaxx-320; 150,160+div(L,2).quot,
generator(160,getmaxy-150, getmaxy()-150-
160+L,getmaxy-150,1,ls); ceil(L*(sqrt(3)/2)),1,ls);
generator(160+L,getmaxy-150, generator(160+div(L,2).quot,
160+L div 2,getmaxy-150 getmaxy()-150-
L*round(sqrt(3)/2),1,ls); ceil(L*(sqrt(3)/2)),160,
generator(160+L div 2,getmaxy- getmaxy()-150,1,ls);
150-L*round(sqrt(3)/2),160, setfillstyle(1,4);
getmaxy-150,1,ls); floodfill(div(getmaxx(),2)
setfillstyle(1,blue); .quot,div(getmaxx(),
floodfill(getmaxx div 2, 2).quot,6);
getmaxy div 2, red); getch();
readln closegraph();
end. }

Privii mai jos rezultatele obinute pentru diferite valori ale lui ls:

ls = 2 ls = 3 ls = 4
Figura 7.2. Exemple de fractali formai cu ajutorul curbei lui Koch (triunghi echilateral)
Manual de informatic pentru clasa a XI-a 191

7.3.3. Curba lui Koch pentru un ptrat

Se consider un ptrat. Fiecare latur a sa se transform dup cum se vede


n figura de mai jos:

Figura 7.3. Exemplu de transformare

Fiecare segment al liniei frnte astfel formate se transform din nou dup
aceeai regul. Se cere s se vizualizeze curba dup ls transformri (valoare
citit de la tastatur).

Transformarea i desenarea unui segment sunt realizate de procedura


desen. Aceasta are ca parametri de intrare coordonatele punctului care determin
segmentul, numrul de transformri efectuate (n) i numrul de transformri
cerut (ls).

Procedura conine urmtorul algoritm:

dac nu a fost efectuat numrul de transformri necesar, se


calculeaz coordonatele punctelor care determin linia frnt obinut
pornind de la segment i pentru fiecare segment din aceast linie se
reapeleaz procedura desen;

contrar, se deseneaz linia frnt obinut.

n final, figura se coloreaz. Programul este prezentat n continuare:

Varianta Pascal Varianta C++


uses graph,crt; #include "graphics.h"
var gdriver,gmode,ls:integer; #include <iostream.h>
#include <stdlib.h>
procedure initg; #include <conio.h>
... #include <math.h>
procedure rotplan(xc,yc,x1, int gdriver,gmode,ls,L;
y1:integer;
var x,y:integer; void init()
unghi:real); { ...
... }
192 Capitolul 7. Metoda DIVIDE ET IMPERA

procedure desen(x1,y1,x2, void rotplan(int xc,int yc,


y2,n,ls:integer); int x1,int y1,int &x,int &y,
var x3,x4,x5,x6,x7,x8,xc, float unghi)
y3,y4,y5,y6,y7,y8,yc:integer; { ...
begin }
if n<=ls then void desen(int x1,int y1,int
begin x2,int y2,int n,int ls)
x3:=(3*x1+x2) div 4; { int x3,x4,x5,x6,x7,x8,
y3:=(3*y1+y2) div 4; xc,y3, y4,y5,y6,y7,y8,yc;
rotplan(x3,y3,x1,y1,x4,y4, if (n<=ls)
-pi/2); { x3=div(3*x1+x2,4).quot;
xc:=(x1+x2) div 2; y3=div(3*y1+y2, 4).quot;
yc:=(y1+y2) div 2; rotplan(x3,y3,x1,y1,x4,y4,
rotplan(xc,yc,x3,y3,x5,y5, -M_PI/2);
-pi/2); xc=div(x1+x2,2).quot;
rotplan(xc,yc,x3,y3, yc=div(y1+y2,2).quot;
x6,y6,pi/2); rotplan(xc,yc,x3,y3,x5,y5,
x8:=(x1+3*x2) div 4; -M_PI/2);
y8:=(y1+3*y2) div 4; rotplan(xc,yc,x3,y3,x6,y6,
rotplan(x8,y8,xc,yc,x7,y7, M_PI/2);
pi/2); x8=div(x1+3*x2, 4).quot;
desen(x1,y1,x3,y3,n+1,ls); y8=div(y1+3*y2,4).quot;
desen(x3,y3,x4,y4,n+1,ls); rotplan(x8,y8,xc,yc,x7,y7,
desen(x4,y4,x5,y5,n+1,ls); M_PI/2);
desen(x5,y5,xc,yc,n+1,ls); desen(x1,y1,x3,y3,n+1,ls);
desen(xc,yc,x6,y6,n+1,ls); desen(x3,y3,x4,y4,n+1,ls);
desen(x4,y4,x5,y5,n+1,ls);
desen(x6,y6,x7,y7,n+1,ls);
desen(x5,y5,xc,yc,n+1,ls);
desen(x7,y7,x8,y8,n+1,ls);
desen(xc,yc,x6,y6,n+1,ls);
desen(x8,y8,x2,y2,n+1,ls); desen(x6,y6,x7,y7,n+1,ls);
if n = ls then begin desen(x7,y7,x8,y8,n+1,ls);
moveto(x1,y1); desen(x8,y8,x2,y2,n+1,ls);
lineto(x3,y3); if (n == ls)
lineto(x4,y4); { moveto(x1,y1);
lineto(x5,y5); lineto(x3,y3);
lineto(x6,y6); lineto(x4,y4);
lineto(x7,y7); lineto(x5,y5);
lineto(x8,y8); lineto(x6,y6);
lineto(x2,y2); lineto(x7,y7);
end lineto(x8,y8);
end lineto(x2,y2); }
end; }
}
begin
write('ls= '); readln(ls); main()
{ cout<<"ls= "; cin>>ls;
initg; setcolor(red);
init(); setcolor(6);
desen(100,100,300,100,1,ls);
desen(100,100,300,100,1,ls);
desen(300,100,300,300,1,ls); desen(300,100,300,300,1,ls);
desen(300,300,100,300,1,ls); desen(300,300,100,300,1,ls);
desen(100,300,100,100,1,ls); desen(100,300,100,100,1,ls);
setfillstyle(1,blue); setfillstyle(1,3);
floodfill(getmaxx div 2, floodfill(div(getmaxx(),2)
getmaxy div 2, red); .quot,div(getmaxy(),2).quot,6);
readln getch(); closegraph();
end. }
Manual de informatic pentru clasa a XI-a 193

Sunt prezentate mai jos imaginile obinute n urma rulrii programului, pentru
diferite valori ale lui ls:

ls=1 ls=2 ls=3

Figura 7.4. Exemple de fractali formai cu ajutorul curbei lui Koch (ptrat)

7.3.4. Arborele

Se d un segment AB. Cu ajutorul lui se construiete un arbore, aa cum se


vede n figura de mai jos:

Figura 7.5. Exemplu de transformare n cazul unui arbore

Lungimea fiecrei ramuri este o treime din lungimea iniial a segmentului.


Fiecare latur se transform n mod asemntor. Se cere s se vizualizeze figura
astfel rezultat, dup ls transformri.

Pentru obinerea 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; xc = = , yp = = .
CB 1 3 2 1 3 2

se rotete acest punct n jurul punctului B(x2,y2) cu un unghi de / 4 ;


se rotete punctul n jurul lui B cu unghiul / 4 .
194 Capitolul 7. Metoda DIVIDE ET IMPERA

n urma acestor rotaii se obin coordonatele punctelor care, mpreun cu


punctul B, determin segmentele ce costituie ramurile arborelui.

Procedura desenez are ca parametri de intrare coordonatele unui segment,


numrul de transformri efectuate (n) i numrul de transformri care trebuie
efectuate (ls). n cazul n care nu s-au efectuat toate transformrile, se traseaz
segmentul (cu o culoare oarecare), se calculeaz coordonatele punctelor care
determin ramurile i, pentru fiecare segment, se reapeleaz procedura.

Programul este prezentat mai jos:

Varianta Pascal Varianta C++


uses graph,crt; #include "graphics.h"
#include <iostream.h>
var gdriver,gmode,ls:integer; #include <stdlib.h>
xmax,ymax:integer; #include <conio.h>
#include <math.h>
procedure initg;
int gdriver,gmode,ls,L;
...
void init()
procedure rotplan(xc,yc,x1, { ... }
y1:integer; var x,y:integer;
unghi:real); void rotplan(...)
... { ... }
procedure desenez(x1,y1,x2,y2, void desenez(int x1,int y1,
n,ls:integer); int x2,int y2,int n,int ls)
var x,y:integer; { int x,y;
begin if (n<=ls)
if n<=ls then { setcolor(1+random(15));
begin moveto(x1,y1);
setcolor(1+random(15)); lineto(x2,y2);
moveto(x1,y1); rotplan(x2,y2,div(3*x2-
lineto(x2,y2); x1,2).quot,div(3*y2-y1,2)
rotplan(x2,y2,(3*x2-x1) div .quot,x,y,M_PI/4);
2,(3*y2-y1) div 2,x,y,pi/4); desenez(x2,y2,x,y,n+1,ls);
rotplan(x2,y2,div(3*x2-
desenez(x2,y2,x,y,n+1,ls);
x1,2).quot,div(3*y2-y1,
rotplan(x2,y2,(3*x2-x1) div
2).quot,x,y,-M_PI/4);
2,(3*y2-y1) div 2,x,y,-pi/4);
desenez(x2,y2,x,y,n+1,ls);
desenez(x2,y2,x,y,n+1,ls); }
end }
end;
main()
begin { randomize();
randomize; cout<<"ls= "; cin>>ls;
write('ls= '); readln(ls); init(); setcolor(6);
initg; desenez(div(getmaxx(),2)
setbkcolor(white); .quot,getmaxy(),
desenez(getmaxx div 2, div(getmaxx(),2).quot,
getmaxy, getmaxx div 2, getmaxy()-250,1,ls);
getmaxy-250,1,ls); getch();
readln closegraph();
end. }
Manual de informatic pentru clasa a XI-a 195

Pentru diverse valori ale parametrului de intrare ls, vom obine arborii:

ls = 3 ls = 5 ls = 7

Figura 7.6. Exemple de fractali de tip arbore

Observaii

Exemplele grafice prezentate au fost generate pentru valori mici ale lui ls
deoarece la tiprire, detaliile sunt greu de observat peste o anumit limit.
Generarea fractalilor reprezint o aplicaie a recursivitii, tehnica aplicat
fiind DIVIDE ET IMPERA. Pentru valori mari ale lui ls, timpul de efectuare al
calculelor poate fi i de ordinul zecilor de secunde, ceea ce poate fi
considerat un inconvenient major.

Probleme propuse
1. Se citete a1, numr real. Se cere s se scrie o funcie care calculeaz ln(a)
cu 3 zecimale exacte. Nu este permis utilizarea funciei logaritmice a limbajului.

2. Scriei o funcie care calculeaz prin metoda DIVIDE ET IMPERA suma numerelor
reinute dintr-un vector.

3. Referitor la problema anterioar: care este complexitatea algoritmului folosit?


Se va considera ca operaie de baz adunarea.

4. Se citete un numr real x(-10000, 10000). S se afieze partea


fracionar. Exemple: pentru x=1.23, se va afia: 0.23; pentru x=-12.7, se va
afia 0.7. Nu se vor folosi funcii specializate ale limbajului.

5. Se tie c ecuaia x3+x-1=0 are o singur rdcin real n intervalul (0,1).


Scriei un program, care o afieaz cu 4 zecimale exacte.
196 Capitolul 7. Metoda DIVIDE ET IMPERA

6. Problema seleciei. Se consider un vector cu n componente numere naturale


i 1tn. Se cere s se determine al t-lea cel mai mic element. Imaginai o
rezolvare care utilizeaz funcia Poz de la sortarea rapid!

7. Se consider un vector care reine n numere naturale. Se cere s se determine


dac exist un element majoritar (adic un numr care se gsete n mai mult de
[n / 2] + 1 elemente).
Victor Mitrana
8. Fiind dat x real, s se calculeze [ x ] cu patru zecimale exacte! Nu se vor folosi
3

funcii specializate ale limbajului.


9. Se pleac de la un ptrat a crui suprafa se divide n 9 pri egale prin
mprirea fiecrei laturi n 3 pri egale. Ptratul din mijloc se elimin. Cu ptratele
rmase se procedeaz la fel. Vizualizai figura dup ls astfel de transformri
(Covorul lui Sierpinski).

Rspunsuri
1. ln(a)=x a=ex ex-a=0. Dac notm cu f(x)=ex-a, atunci trebuie
rezolvat ecuaia f(x)=0. Avem f(0)=e0-a=1-a<0 i f(a)=ea-a>0. De aici,
rezult c f(x) are o rdcin n intervalul (0,a). Cum f(x) este strict
cresctoare (ca diferen ntre funcia strict cresctoare ex i o constant),
rdcina este unic. Algoritmul pe care l folosim se numete n matematic
metoda njumtirii intervalului, dar, din punct de vedere informatic, corespunde
metodei DIVIDE ET IMPERA.
Fie li=0 i ls=a, m=(a+b)/2. Dac f(li)f(m)<0, rdcina se gsete n
(li,m), altfel rdcina este n [m,ls). Condiia de terminare este ca
li ls < 0.0001 , pentru c trebuie s avem 3 zecimale exacte.

Varianta Pascal Varianta C++


var a:real; #include <iostream.h>
function LogN(a,li,ls:double): #include <math.h>
double; double a;
begin double LogN(double a,double li,
if a=1 then LogN:=0 double ls)
else { if (a==1) return 0;
if abs(li-ls)<0.0001 else
then LogN:=(li+ls)/2 if (fabs(li-ls)<0.0001)
else return (li+ls)/2;
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,li,(li+ls)/2) return LogN(a,li,
else (li+ls)/2);
LogN:=LogN(a,(li+ls)/2,ls) else return LogN(a,
(li+ls)/2,ls);
end;
}
Manual de informatic pentru clasa a XI-a 197

begin main()
write ('a='); readln(a); { cout<<"a="; cin>>a;
writeln(' rezultat cout<<"rezultat calculat "
calculat:',LogN(a,0,a):3:3); <<LogN(a,0,a)<<endl;
writeln(' rezultat preluat ', cout<<"rezultat preluat "
ln(a):3:3); <<log(a)<<endl;
end. }

Practic, la fiecare pas se njumtete intervalul n care se caut soluia i


aceasta corespunde strategiei generale DIVIDE ET IMPERA.

2. Programul este prezentat mai jos:

Varianta Pascal Varianta C++


type vector=array[1..9] of #include <iostream.h>
integer;
int n,i,v[10];
var v:vector; int Suma(int li, int ls)
n,i:integer; { if (li==ls) return v[li];
else return
function
Suma(li, (li+ls)/2)+
Suma(li,ls:integer):integer;
Suma((li+ls)/2+1,ls);
begin }
if li=ls then Suma:=v[li]
main()
else
{ cout<<"n=";
Suma:=Suma(li,(li+ls) div 2)
cin>>n;
+ Suma((li+ls) div 2+1,ls);
for (i=1;i<=n;i++)
end;
cin>>v[i];
begin cout<<Suma(1,n);
write('n='); }
readln(n);
for i:=1 to n do
readln(v[i]);
writeln(suma(1,n));
end.

3. Fiecare problem se descompune n alte dou i rezultatul se adun. Pentru


simplitate, considerai n=2k. n final, se obine O(n). Putei scrie i funcia recursiv
care calculeaz T(n), dar, pentru a obine rezultatul corect, luai n=2k:

0 n = 1;

T(n) = n
2T + 1 altfel.
2

4. A calcula [x] se reduce la DIVIDE ET IMPERA. Partea fracionar se obine uor,


dac calculm x [x ] .
198 Capitolul 7. Metoda DIVIDE ET IMPERA

5. Vedei problema 1.

6. Funcia Poz returneaz poziia k pe care se va gsi, dup rularea ei, primul
element al vectorului. n plus, toate elementele de indice mai mic dect k sunt mai
mici sau egale dect A[k] i toate elementele de indice mai mare dect k sunt mai
mari sau egale dect A[k]. Altfel spus: elementul A[1], care se afl dup rularea
funciei pe poziia k, este al k-lea cel mai mic element din vectorul A. Atunci, n
cazul n care k=t, problema este rezolvat. Dac t<k, elementul cutat are
indicele cuprins ntre li i k-1 i relum rularea funciei Poz ntre aceste limite, iar
dac t>k, elementul cutat are indicele ntre k+1 i ls i relum rularea funciei
Poz ntre aceste limite. Datorit faptului c, la fiecare pas, se restrnge numrul
valorilor de cutare, se ajunge n situaia n care t=k.

Secvena este:

Varianta Pascal Varianta C++


li:=1; li=1;
ls:=n; ls=n;
repeat do
poz(li,ls,k,a); { poz(li,ls,k,a);
if t<k then ls:=k-1; if (t<k) ls=k-1;
if t>k then li:=k+1; if (t>k) li=k+1;
until t=k; }while (t!=k);
writeln('Elementul cautat ', cout<<"elementul cautat "
a[t]); <<a[t];

7. Dup aplicarea algoritmului de la problema anterioar, elementul din mijloc


trebuie s fie majoritar.
199

Capitolul 8
Metoda BACKTRACKING

8.1. Prezentarea metodei

8.1.1. Cnd se utilizeaz metoda backtracking ?

Metoda backtracking se folosete n rezolvarea problemelor care ndeplinesc


simultan urmtoarele condiii:

soluia lor poate fi pus sub forma unui vector S=x1,x2,...,xn, cu


x1A1, x2A2, ..., xnAn;
mulimile A1, A2, ..., An sunt mulimi finite, iar elementele lor se consider
c se afl ntr-o relaie de ordine bine stabilit;
nu se dispune de o alt metod de rezolvare, mai rapid.

n continuare, este prezentat un exemplu de problem care poate fi


rezolvat prin utilizarea tehnicii backtracking.

Generarea permutrilor. Se citete un numr natural n. S se


123
genereze toate permutrile mulimii {1,2,...,n}. De exemplu, pentru 132
n=3, permutrile mulimii {1,2,3} sunt prezentate alturat. 213
231
Pentru aceast problem, A1=A2=A3={1,2,3}. Fie permutarea 213. Ea 312
este scris sub form de vector, unde 2A1, 1A2 i 3A3. 321

8.1.2. Principiul care st la baza metodei backtracking

Principiul care st la baza metodei backtracking va fi prezentat printr-un


exemplu, acela al generrii permutrilor. Cum se poate rezolva aceast
problem?
O prim soluie ar fi s generm toate elementele produsului cartezian:

{1,2,3}{1,2,3}{1,2,3}={11, 12, 13, 21, 22, 23, 31, 32, 33}


{1,2,3}= {111, 112, 113, 121, 122, 123, 131, 132, 133, 211,
212, 213, 221, 222, 223, 231, 232, 233, 311, 312, 313, 321,
322, 323, 331, 332, 333}.
200 Capitolul 8. Metoda backtracking

Apoi, urmeaz s vedem care dintre elementele acestui produs cartezian


sunt permutri, adic s conin numai numere distincte. Astfel, 111, 112
nu sunt permutri, dar 123 este permutare, .a.m.d. Produsul cartezian are 27 de
elemente i dintre ele, doar 6 sunt permutri. n general, produsul cartezian are
nn elemente, din care permutri sunt doar n!. V dai seama c un astfel de
algoritm de generare a permutrilor este ineficient

ntrebarea este dac problema nu se poate rezolva eficient? S observm


c nu are rost s generm un element al produsului cartezian pentru ca apoi, s ne
dm seama c nu este permutare, deoarece nu este alctuit din numere distincte.
De exemplu, dac la un pas al algoritmului am generat 22, e clar c nu se poate
obine o permutare, oricare ar fi numrul care urmeaz pe poziia 3.

Principiul metodei

Metoda backtracking are la baz un principiu simplu: dac n procesul de


generare a unui vector soluie S=x1x2,...,xn, pentru componenta k, atunci
cnd am generat deja x1x2...xk, constatm c valoarea xk nu este bine
aleas (pstrnd-o nu se va ajunge la o soluie), nu trecem componenta k+1
ci relum cutarea pentru alt valoare pentru componenta k, iar dac
aceast valoare nu exist, relum cutarea pentru componenta k-1.

Observai faptul c dup ce am analizat posibilele valori pe care le poate lua


componenta k, avem dou posibiliti: ori trecem la componenta k+1 (facem
pasul nainte), ori mergem la componenta k-1 (facem pasul napoi).

Trecem la exemplificarea algoritmului pentru generarea permutrilor, n


cazul n care n=3.

Componenta 1 va memora numrul 1. ntruct exist


1
permutri care ncep cu 1, trecem la elementul 2 - facem
pasul nainte.

Componenta 2 va memora numrul 1. 1 1

Nu exist permutri care ncep cu 1,1, motiv pentru care,


1 2
pentru aceeai component, vom reine valoarea urmtoare,
adic 2. ntruct exist permutri care ncep cu 1,2, vom trece la elementul
3 (nainte).

Componenta 3 va memora numrul 1. 1 2 1

Nu exist permutri care sunt de forma 1,2,1, motiv pentru


1 2 2
care aceeai component va reine numrul urmtor, 2.

Nu exist permutri care sunt de forma 1,2,2, motiv pentru


1 2 3
care aceeai component va memora numrul urmtor, adic
3. Am obinut deja o prim soluie i o afim.
Manual de informatic pentru clasa a XI-a 201

Pentru componenta 3, nu exist o alt valoare pe care o


1 3 0
putem utiliza. Din acest motiv, vom trece la elementul 2,
(napoi). Componenta 2 are deja memorat valoarea 2. Alegem valoarea
urmtoare, 3. ntruct exist permutri care ncep cu 1,3, vom trece la
elementul urmtor, 3 (nainte).

Prima valoare care poate fi memorat este 1. ntruct nu


1 3 2
exist permutri de forma 1,3,1 trecem la valoarea
urmtoare 2. Dar 1,3,2 este soluie i o afim.
...

Algoritmul continu pn cnd se ajunge la componenta de indice 0. n acel


moment, au fost deja afiate toate permutrile.

Exerciiu. Artai cum funcioneaz algoritmul pn se ajunge la


componenta de indice 0.

8.1.3. O modalitate de implementare a metodei backtracking

Pentru uurarea nelegerii metodei, mai nti vom prezenta un subprogram


general, aplicabil oricrei probleme. Subprogramul va apela alte subprograme care
au ntotdeauna acelai nume i parametri i care, din punct de vedere al
metodei, realizeaz acelai lucru. Sarcina celui care face programul este s scrie
explicit, pentru fiecare problem n parte, subprogramele apelate de acesta.

Evident, o astfel de abordare conduce la programe cu multe instruciuni. Din


acest motiv, dup nelegerea metodei backtracking, vom renuna la aceast
form standardizat. Dar, principiul rmne nemodificat.

Iat subprogramul care implementeaz metoda. El va fi apelat prin


back(1).

Varianta Pascal Varianta C++


procedure back(k:integer); void back(int k)
begin {
if solutie(k) if (solutie(k)) tipar();
then tipar else
else { init(k);
begin while(succesor(k))
init(k); if (valid(k)) back(k+1);
while succesor(k) do }
if valid(k) then back(k+1) }
end
end;
202 Capitolul 8. Metoda backtracking

S-l analizm! Subprogramul are parametrul k de tip ntreg. Acest parametru


are semnificaia de indice al componentei vectorului pentru care se caut o valoare
convenabil. Algoritmul va porni cu componenta de indice 1. Din acest motiv,
subprogramul se va apela cu back(1). Dup cum observai, subprogramul
este recursiv.

Iniial se testeaz dac s-a generat o soluie. Pentru aceasta, se apeleaz


subprogramul solutie(k).

Pentru permutri, vom avea o soluie cnd s-a generat o secven alctuit din n
numere distincte. Cum subprogramul este recursiv, acest fapt se ntmpl atunci
cnd s-a ajuns pe nivelul n+1.

Dac s-a obinut o soluie, aceasta se afieaz. Pentru aceast operaie se


va utiliza subprogramul tipar.

n situaia n care nu a fost obinut o soluie, se iniializeaz nivelul k.


Iniializarea se face cu valoarea aflat naintea tuturor valorilor posibile. Se
va folosi subprogramul init.

Pentru permutri, iniializarea se face cu 0.

Dup iniializare, se genereaz, pe rnd, toate valorile mulimii Ak. Pentru


aceasta se utilizeaz subprogramul succesor. Rolul su este de a atribui
componentei k valoarea urmtoare celei deja existente.

Pentru fiecare valoare generat, se testeaz dac aceasta


ndeplinete anumite condiii de continuare. Acest test este realizat
de subprogramul valid:

n cazul n care condiiile sunt ndeplinite, se trece la


componenta k+1, urmnd ca generarea valorilor pe nivelul k s
continue atunci cnd se revine pe acest nivel;

dac condiiile de continuare nu sunt ndeplinite, se


genereaz urmtoarea valoare pentru componenta k.

Dup ce au fost generate toate valorile mulimii Ak se trece, implicit, la


componenta k-1, iar algoritmul se ncheie cnd k=0.

Pentru permutri, pe fiecare nivel, valorile posibile sunt Ak={1,2,...,n}.


Condiia de continuare, n acest caz este ca valoarea aflat pe nivelul k s fie
distinct n raport cu valorile aflate pe nivelurile inferioare.

Programul de generare a permutrilor este prezentat n 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 nn, se cer toate


soluiile de aranjare a n dame, astfel nct s nu se afle dou dame pe
aceeai linie, coloan sau diagonal (damele s nu se atace reciproc).

De exemplu, dac n=4, o soluie este reprezentat n figura 8.1., a). Modul de
obinere al soluiei este prezentat n figurile urmtoare, 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) Observm c o dam trebuie s fie plasat singur pe linie. Poziionm prima
dam pe linia 1, coloana 1.
c) A doua dam nu poate fi aezat dect n coloana 3.
d) Observm c a treia dam nu poate fi plasat n linia 3. ncercm atunci
plasarea celei de-a doua dame n coloana 4.
e) A treia dam nu poate fi plasat dect n coloana 2.
f) n aceast situaie dama a patra nu mai poate fi aezat. ncercnd s
avansm cu dama a treia, observm c nu este posibil s o plasm 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. Avansm cu prima dam n
coloana a-2-a.
g) A doua dam nu poate fi aezat dect n coloana a 4-a.
h) Dama a treia se aeaz n prima coloan.
i) Acum este posibil s plasm a patra dam n coloana 3 i astfel am obinut o
soluie a problemei.

Algoritmul continu n acest mod pn cnd trebuie scoas de pe tabl


prima dam.

Pentru cutarea i reprezentarea unei soluii folosim un vector cu n componente,


numit sol (avnd n vedere c pe fiecare linie se gsete o singur dam). Prin
sol[i] nelegem coloana n care se gsete dama de pe linia i.

Alturat, putei observa modul n care este reprezentat


2 4 1 3
soluia cu ajutorul vectorului sol.

Dou dame se gsesc pe aceeai diagonal dac i numai dac este


ndeplinit condiia:
|sol(i)-sol(j)|=|i-j|
(diferena, n modul, dintre linii i coloane este aceeai).

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.

ntruct dou dame nu se pot gsi n aceeai coloan, rezult c o soluie


este sub form de permutare. O prim idee ne conduce la generarea tuturor
permutrilor i la extragerea soluiilor pentru problem (ca dou dame s nu fie
plasate n aceeai diagonal). Dac procedm astfel, nseamn c nu lucrm
conform strategiei backtracking. Aceasta presupune ca imediat ce am gsit dou
dame care se atac, s relum cutarea n alte condiii. Fa de programul de
generare a permutrilor, programul de generare a tuturor soluiilor problemei celor
n dame are o singur condiie suplimentar, n subprogramul valid. Mai jos,
putei observa noua versiune a subprogramului valid. Dac l utilizai n locul
subprogramului cu acelai nume din programul de generare a permutrilor, vei
obine 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 lucrrile n care este


prezentat metoda backtracking.

n ceea ce ne privete, dincolo de un exemplu de backtracking, am avut


ocazia s vedem ct de mult seamn rezolvrile (prin aceast metod) a
dou probleme ntre care, aparent, nu exist nici o legtur.

Exerciii
1. Desenai configuraia tablei corespunztoare vectorului sol=(3,1,4,2,5) i
verificai dac aceasta reprezint o soluie a problemei damelor.
Manual de informatic pentru clasa a XI-a 207

2. Explicai de ce configuraia corespunztoare vecorului sol=(3,1,3,4,5) nu


este o soluie a problemei damelor. Care este poziia din sol la care nu s-a fcut
alegerea corect a unei valori valide?

3. Explicai de ce nu orice permutare a mulimii {1,2, , n} este o soluie a


problemei damelor pe o tabl cu n linii i n coloane.

4. Determinai, folosind metoda backtracking, o soluie care se obine pe o tabl cu


ase linii i ase coloane, tiind c dama plasat pe ultima linie trebuie s se afle
pe a doua coloan. Considerai c mai este util s completm vectorul sol
pornind de la poziia 1? Dac pornim de la poziia 1, ce adaptri trebuie fcute?

8.2. Mai puine linii n programul surs

Pn n prezent, am rezolvat dou probleme prin metoda backtracking:


generarea permutrilor i problema celor n dame. n ambele cazuri, am utilizat
subprogramul standard.

Este ntotdeauna necesar s-l folosim pe acesta sau putem reine numai
ideea i, dup caz, s scriem mai puin?

Evident, dup ce am neles bine metoda, putem renuna la subprogramul


standard. Pentru aceasta, ncorporm 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 uor de realizat aceast


operaie. n locul apelului vom scrie instruciunea 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 secvena


prin care se afieaz st.

Putem elimina subprogramul succesor. n locul apelului vom testa dac


st[k]<n i avem grij s incrementm valoarea aflat pe nivelul curent.

Putei observa n continuare programul obinut. Este cu mult mai scurt!


Oricum, ideea de rezolvare rmne aceeai. Subprogramele prezentate au fost
numai ncorporate, nu s-a renunat 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 vei ntlni i o rezolvare precum urmtoarea, care n subprogramul


back, pentru succesor se folosete o instruciune repetitiv de tip for:
Manual de informatic pentru clasa a XI-a 209

Varianta Pascal Varianta C++


procedure back(k:integer); void back(int k)
var i:integer; { int i;
begin if (k==n+1)
if k=n+1 { for (i=1;i<=n;i++)
then cout<<sol[i];
begin cout<<endl;
for i:=1 to n do }
write(sol[i]); else
writeln; for (i=1;i<=n;i++)
end { sol[k]=i;
else if (valid(k))
for i:=1 to n do back(k+1);
begin }
sol[k]:=i; }
if valid(k)
then back(k+1)
end
end;

Exerciii
1. Testai subprogramul anterior pentru problema generrii permutrilor.
2. Adaptai rezolvarea problemei permutrilor astfel nct s se afieze numai
permutrile n care oricare dou numere consecutive nu sunt alturate.
3. Observai c ordinea de afiare a soluiilor depinde de ordinea n care se
consider elementele mulimilor A1, A2, Ce modificri trebuie aduse
procedurii recursive back astfel nct permutarile de 4 elemente s fie afiate n
ordinea: 4321, 4312, 4231, 4213, 4132, 4123, 3421, 3412 1243, 1234?
4. Renunai la utilizarea subprogramului valid, utiliznd un vector folosit, n
care folosit[i] are valoarea 0 dac numrul i nu este deja folosit n soluie
i are valoarea 1 n caz contrar. Astfel, plasarea valorii i n vectorul soluie
(sol[k]i) trebuie nsoit de memorarea faptului c i este utilizat
(folosit[i]1), la revenirea din recursie (cand se nltur valoarea de pe
poziia curent) fiind necesar memorarea faptului c i nu mai este utilizat n
soluie (folosit[i]0). Condiia de validare se reduce n acest caz la:
Dac folosit[i]=0 atunci ...
5. Urmrii toate modalitile diferite de a aeza patru obiecte identificate prin
numerele 1, 2, 3, 4 pe un cerc, la distane egale. Vom observa c nu toate
permutrile de patru obiecte sunt configuraii distincte, datorit distribuiei pe
cerc. Astfel permutrile 1234, 2341, 3412 i 4123 reprezint una i aceeai
configuraie. Scriei un program care afieaz numai permutrile distincte
conform aezrii pe un cerc. Indicaie: se va considera sol[1]=1 i se vor
permuta doar celelalte elemente.
210 Capitolul 8. Metoda backtracking

8.3. Cazul n care se cere o singur soluie.


Exemplificare: problema colorrii hrilor

Sunt probleme care se rezolv cu metoda backtracking i n care se cere o


singur soluie.

Implementarea ca la carte presupune utilizarea unei variabile de


semnalizare (de exemplu, variabila gata) care s fie iniial 0, la obinerea soluiei
dorite aceasta primind valoarea 1. Orice succesor va fi condiionat n plus de
valoarea variabilei gata.

De aceast dat, pentru simplitate, vom renuna la programarea structurat


i vom opri n mod forat programul.

n Pascal, vei utiliza procedura halt.

n C++, vei folosi funcia exit, cu parametrul EXIT_SUCCESS


(o constant). Pentru a o putea utiliza, trebuie s includei fiierul antet
stdlib.h:
#include<stdlib.h>.

n ambele cazuri, secvena care determin oprirea forat este trecut


imediat dup ce prima soluie a fost afiat.

Exerciiu. Modificai programul care rezolv problema celor n dame, astfel


nct acesta s afieze o singur soluie.

Problema colorrii hrilor. Fiind dat o hart cu n ri, se cere o soluie de


colorare a hrii, utiliznd cel mult 4 culori, astfel nct dou ri cu frontier
comun s fie colorate diferit.

Este demonstrat faptul c sunt suficiente numai 4 culori pentru ca orice hart
s poat fi colorat.

Pentru exemplificare, vom considera harta din figura 8.4., unde rile sunt
numerotate cu cifre cuprinse ntre 1 i 5.

O soluie a acestei probleme este urmtoarea:


1
ara 1 - culoarea 1; 4
ara 2 - culoarea 2; 3
ara 3 - culoarea 1; 2
ara 4 - culoarea 3; 5
Figura 8.4.
ara 5 - culoarea 4.
Manual de informatic pentru clasa a XI-a 211

Harta este furnizat programului cu ajutorul unei matrice (tablou) An,n:

1, ara i are frontiera comun cu ara j


A(i, j) =
0, altfel

Matricea A este simetric. Pentru rezolvarea problemei se utilizeaz


vectorul sol, unde sol[k] reine culoarea ataat rii k. Evident, orice soluie
are exact n componente.

Varianta Pascal Varianta C++


var sol:array[1..9] of #include <iostream.h>
integer; #include <stdlib.h>
a:array[1..10,1..10] of int n,i,j,sol[10],a[10][10];
integer;
n,i,j:integer; int valid(int k)
{ for (int i=1;i<k;i++)
function
if (sol[k]==sol[i] &&
valid(k:integer):boolean;
a[i][k]==1) return 0;
begin
valid:=true; return 1;
for i:=1 to k-1 do }
if (sol[k]=sol[i]) void back(int k)
and (a[k,i]=1) { int i;
then valid:=false if (k==n+1)
end; { for (int i=1;i<=n;i++)
procedure back(k:integer); cout<<sol[i];
var i:integer; exit(EXIT_SUCCESS);
begin }
if k=n+1 then else
begin for (i=1;i<=4;i++)
for j:=1 to n do { sol[k]=i;
write(sol[j]); if (valid(k))
halt; back(k+1);
end }
else }
for i:=1 to n do
begin main()
sol[k]:=i; { cout<<"Numarul de tari=";
if valid(k) cin>>n;
then back(k+1) for (int i=1;i<=n;i++)
end; for (int j=1;j<=i-1;j++)
end; { cout<<"a["<<I
begin <<','<<j<<"]=";
write('Numarul de tari='); cin>>a[i][j];
readln(n); a[j][i]=a[i][j];
for i:=1 to n do }
for j:=1 to i-1 do back(1);
begin }
write('a[',i,',',j,']=');
readln(a[i,j]);
a[j,i]:=a[i,j]
end;
back(1)
end.
212 Capitolul 8. Metoda backtracking

Exerciii
1. Soluia afiat este i soluia care utilizeaz un numr
minim de culori?
2. Dac rile din centrul figurii alturate sunt numerotate
cu 1, 2, 3, 4, iar cele de la exterior cu 5 i 6, care este
soluia afiat de programul dat? Este acesta numrul
minim de culori necesare?
3. Cte culori sunt suficiente pentru colorarea unei hri
particulare n care orice ar se nvecineaz cu cel Figura 8.5.
mult dou ri?
4. Dai exemplu de particularitate pe care poate s o aib o hart pentru a fi
suficiente dou culori pentru colorarea tuturor rilor?

8.4. Aplicaii ale metodei backtracking n


combinatoric

8.4.1. O generalizare util

Acum, c am nvat s generm permutrile mulimii {1,2...n}, se pune


problema s vedem de ce este util acest algoritm. La ce folosete faptul c putem
aranja numerele {1,2...n} n toate modurile posibile?

S observm c acest algoritm poate fi folosit pentru a aranja oricare n


elemente distincte n toate modurile posibile.

Exemple

1. Se d o mulime alctuit din n litere distincte. Se cer toate cuvintele care se


pot forma cu ele, astfel nct fiecare cuvnt s conin n litere distincte. De
exemplu, dac mulimea este {a,b,c}, vom avea cuvintele: abc, acb, bac,
bca, cab i cba.

2. Se dau numele a n persoane. Se cere s se afieze toate modurile posibile n


care acestea se pot aeza pe o banc. De exemplu, dac n=3, iar persoanele sunt
Ioana, Costel i Mihaela, atunci soluiile sunt:

Ioana Costel Mihaela;


Ioana Mihaela Costel;
Costel Ioana Mihaela;
...
Manual de informatic pentru clasa a XI-a 213

n astfel de cazuri, cele n elemente distincte se memoreaz ntr-un vector V,


aa cum vedei mai jos:

a b c Ioana Costel Mihaela


1 2 3 1 2 3

Atunci cnd s-a generat o permutare, de exemplu 213, vom afia


V[2]V[1]V[3], adic bac, n primul caz sau Costel Ioana Mihaela, n al
doilea caz.
Procedeul de mai sus poate fi folosit pentru oricare alt aplicaie din
combinatoric, n probleme cum ar fi: generarea tuturor submulimilor unei mulimi,
generarea aranjamentelor, a combinrilor sau a tuturor prilor unei mulimi.

Exerciiu. Scriei programul care rezolv exemplul 2.


Mulimea permutrilor mulimii {1,2,...n} reprezint toate funciile
bijective f:{1,2,...,n}{1,2,...,n}. De exemplu, dac n=3,
permutarea 213 este funcia f:{1,2,3}{1,2,3} definit astfel:
f(1)=2; f(2)=1; f(3)=3.

8.4.2. Produs cartezian

Enun. Se dau n mulimi: A1, A2,... An, unde Ai={1,2,...,ki}, pentru


k=1,2,...,n. Se cere produsul cartezian al celor n mulimi.

Exemplu: A1={1,2}, A2={1,2,3}, A3={1,2,3}.


A1A2A3={(1,1,1),(1,1,2),(1,1,3),(1,2,1),(1,2,2),(1,2,3),
(1,3,1),(1,3,2),(1,3,3),(2,1,1),(2,1,2),(2,1,3),(2,2,1),
(2,2,2),(2,2,3),(2,3,1),(2,3,2),(2,3,3)}.

Rezolvare. De la nceput observm c este necesar s afim toate soluiile.


S observm c o soluie este de forma x1,x2,...,xn, cu x1A1, x2A2, ...,
xnAn. De aici rezult necesitatea folosirii unui vector sol, cu n componente,
unde sol[1] va conine numerele naturale ntre 1 i k1, sol[2] va conine
numerele naturale ntre 1 i k2, ..., sol[n] va conine numerele naturale ntre 1 i
kn. Observm c valorile care pot fi luate sol[1] sunt ntre 1 i k1, valorile care
pot fi luate sol[2] sunt ntre 1 i k2, ..., valorile care pot fi luate sol[n] sunt ntre
1 i kn. Pentru a putea reine aceste valori, vom utiliza un vector a, cu n
componente, unde a[1]= k1, a[2]= k2, , a[n]= kn. Pentru exemplul dat,
vectorul a va reine (2,3,3).
Important! S observm c orice valoare reinut de sol[i] ntre 1 i ki
ndeplinete condiiile de continuare (este valid). Din acest motiv, nu mai este
necesar s utilizm subprogramul valid.
214 Capitolul 8. Metoda backtracking

Mai jos, putei observa programul care genereaz produsul cartezian al


mulimilor date:

Varianta Pascal Varianta C++


var n,i:integer; #include <iostream.h>
sol,a:array[1..10] of int n, sol[10],a[10],i;
integer; void back(int k)
{ if (k==n+1)
procedure back(k:integer);
{ for (i=1;i<=n;i++)
begin
cout<<sol[i];
if k=n+1 then
cout<<endl; }
begin
else
for i:=1 to n do
{ sol[k]=0;
write(sol[i]);
while(sol[k]<a[k])
writeln
{ sol[k]++; back(k+1); }
end
}
}
else
begin main()
sol[k]:=0; {
while sol[k]<a[k] do cout<<"Numarul de multimi=";
begin cin>>n;
sol[k]:=sol[k]+1; for(int i=1;i<=n;i++)
back(k+1) { cout<<"a["<<i<<"]=";
end cin>>a[i];
end }
end; back(1);
}
begin
write('Numarul de multimi=');
readln(n);
for i:=1 to n do
begin
write('a[',i,']=');
readln(a[i])
end;
back(1)
end.

Observaii

1. Avem k1k2...kn elemente ale produsului cartezian. De aici rezult c


algoritmul este exponenial.

2. O alt interpretare pentru metoda backtracking: fiind date n mulimi: A1, A2, ...,
An, produsul cartezian al lor A1A2...An se mai numete spaiul soluiilor. n
acest context, metoda backtracking caut una sau toate soluiile, care sunt
elemente ale produsului cartezian i care ndeplinesc anumite condiii. Astfel, se
poate justifica faptul c, n generarea produsului cartezian, nu este necesar
subprogramul valid pentru c se genereaz toate elementele produsului
cartezian, fr a verifica anumite condiii.
Manual de informatic pentru clasa a XI-a 215

Dei algoritmul este exponenial, exist aplicaii utile, evident, atunci cnd
fiecare mulime Ai poate lua numai cteva valori i unde n este suficient de mic.

Exerciii
1. Tabelarea anumitor funcii. Se d funcia

f:A1A2...AnR,

unde fiecare mulime Ai este dat de numerele ntregi din intervalul [ai,bi] i

f=c1x1+c2x2+...+cnxn, ciR.

Se cere s se realizeze un tabel, n care pentru fiecare valoare din


domeniul de definiie s se afieze valoarea funciei corespunztoare acelei valori.

2. Scriei programul care genereaz toate cuvintele cu patru litere care au prima
i ultima liter vocale, litera a doua consoan din mulimea {P, R, S, T}, iar a treia
liter consoan din mulimea {B, M, R, T, V}.

3. Scriei programul care genereaz i numr cte cuvinte de cinci litere ale
alfabetului englez se pot forma, cu condiia s nu existe dou consoane alturate
i nici dou vocale alturate.

8.4.3. Generarea tuturor submulimilor unei mulimi

Enun. Fiind dat mulimea A={1,2,...,n}, se cere s se afieze toate


submulimile ei.

Rezolvare. S ne amintim c submulimile unei mulimi A se pot reprezenta


prin vectorul caracteristic V, unde:

1, pentru i A
V[i] =
0, pentru i A
De exemplu, dac A={1,2,3}, pentru submulimea {1,3} vom avea
V=(1,0,1). De aici, rezult c problema se reduce la generarea tuturor valorilor
posibile pe care le poate reine vectorul caracteristic.
Aceasta nseamn c o soluie este de forma x1,x2,...,xn, unde
xi{0,1}. i n acest caz, orice valoare ar reine componenta i, ea nu trebuie s
ndeplineasc nici o condiie de continuare, motiv pentru care subprogramul valid
nu este necesar.

n continuare, putei observa programul care genereaz toate valorile pe


care le poate reine vectorul caracteristic:
216 Capitolul 8. Metoda backtracking

Varianta Pascal Varianta C++


var n,i:integer; #include <iostream.h>
sol:array[1..10] of int n, sol[10],i;
integer;
void back(int k)
procedure back (k:integer); { if (k==n+1)
begin { for (i=1;i<=n;i++)
if k=n+1 then cout<<sol[i];
begin cout<<endl;
for i:=1 to n do }
write(sol[i]); else
writeln { sol[k]=-1;
end while(sol[k]<1)
else { sol[k]++;
begin back(k+1);
sol[k]:=-1; }
while sol[k]<1 do }
begin }
sol[k]:=sol[k]+1;
back(k+1) main()
end { cout<<"n="; cin>>n;
end back(1);
end; }

begin
write('n=');
readln(n);
back(1)
end.

Exerciii
1. Problema nu este rezolvat n totalitate. Programul afieaz numai toate valorile
pe care le poate lua vectorul caracteristic. Completai-l astfel nct programul s
afieze toate submulimile mulimii {1,2...n}!

2. Se citesc numele a 6 elevi. Afiai toate submulimile mulimii celor 6 elevi.

3. S se afieze toate numerele scrise n baza 10 a cror reprezentare n baza 2


are n cifre, dintre care exact k sunt egale cu 1. Valorile n i k se citesc de la
tastatur (n<12, k<n). De exemplu, pentru n=3 i k=2, se obin valorile: 5 i 6.

4. Realizai un program care genereaz combinaii de n cifre 0 i 1 cu proprietatea


c n orice grup de 3 cifre consecutive exist cel puin o cifr de 1. De exemplu,
dac n=4, se afieaz combinaiile: 0010, 0011, 0100, 0101, 0110, 0111, 1001,
1010, 1011, 1100, 1101, 1110, 1111.

5. Se citesc dou numere naturale n i s (n<10, s<1000). S se afieze mulimile


formate din n numere prime cu proprietatea c suma elementelor din fiecare
mulime este exact s.
Manual de informatic pentru clasa a XI-a 217

Observaii

Fiind dat o mulime cu n elemente, avem 2n submulimi ale ei. Mulimea


dat i submulimea vid sunt submulimi ale mulimii date! De ce? Fiecare
component a vectorului caracteristic poate reine dou valori. Prin urmare,
numrul de submulimi este

2
2 2...2
=2 .
n

de n ori

De aici rezult c algoritmul care genereaz toate submulimile mulimii 1,


2, ..., n este exponenial.

Uneori, vei rezolva probleme n care se d o mulime i se cere o submulime


a sa care ndeplinete anumite caracteristici. n anumite situaii, problema se
poate rezolva prin utilizarea unor algoritmi mai rapizi (polinomiali). Greeala
tipic care se face este c se genereaz toate submulimile, dup care se
selecteaz cea (cele) care ndeplinete condiiile date.

Exemplu. Se d o mulime de numere reale. Se cere s se determine o


submulime a sa care are suma maxim. Problema se rezolv uor: se
consider ca fcnd parte din submulime numai numerele pozitive. Altfel,
dac am genera toate submulimile...

8.4.4. Generarea combinrilor

Fiind dat mulimea A={1,2,...,n}, se cer toate submulimile ei cu p


elemente. Problema este cunoscut sub numele de generarea combinrilor de
n, luate cte p. Se tie c numrul soluiilor acestei probleme este

n!
C pn = .
(n p)! p!

De exemplu, dac n=4 i p=3, soluiile sunt urmtoarele:


{1,2,3}, {1,2,4}, {1,3,4} i {2,3,4}.

Enun. Se citesc n i p numere naturale, np. Se cere s se genereze toate


submulimile cu p elemente ale mulimii A={1,2,...,n}.

Rezolvare. O soluie este de forma x1,x2,...,xp, unde x1, x2, ..., xpA.
n plus, x1, x2, ..., xp trebuie s fie distincte. Cum la o mulime ordinea elementelor
nu prezint importan, putem genera elementele ei n ordine strict cresctoare.
Aceast observaie ne ajut foarte mult n elaborarea algoritmului.
a) Pentru k>1, sol[k]>sol[k-1].
218 Capitolul 8. Metoda backtracking

b) Pentru fiecare k{1,2,...,p}, sol[k]n-p+k. S presupunem, prin


absurd, c aceast ultim relaie nu este respectat. Aceasta nseamn c k,
astfel nct sol[k]>n-p+k. Deci:

sol[k+1]>n-p+k+1,
...
sol[p]>n-p+p=n.

Absurd. De aici rezult c:

1sol[1]n-p+1,
sol[1]<sol[2]n-p+2,
...
sol[n-1]<sol[n]n-p+p=n.

Relaiile de mai sus simplific mult algoritmul, pentru c innd cont de ele, nu mai
este necesar s se testeze nici o condiie de continuare.

Varianta Pascal Varianta C++


var sol:array[1..9] of integer; #include <iostream.h>
n,p:integer; int n,p,sol[10];
procedure back(k:integer); void back(int k)
var i:integer; { int i;
begin if (k==p+1)
if k=p+1 { for (i=1;i<=p;i++)
then cout<<sol[i];
begin cout<<endl;
for i:=1 to p do }
write(sol[i]); else
writeln; { if (k>1) sol[k]=sol[k-1];
end else sol[k]=0;
else while(sol[k]<n-p+k)
begin { sol[k]++;
if k>1 then sol[k]:=sol[k-1] back(k+1); }
else sol[k]:=0; }
while sol[k]<n-p+k do }
begin main()
sol[k]:=sol[k]+1; { cout<<"n="; cin>>n;
back(k+1); cout<<"p="; cin>>p;
end back(1);
end }
end;
begin
write('n=');
readln(n);
write ('p=');
readln(p);
back(1);
end.

Examinnd raionamentul propus putem observa c, n anumite cazuri,


analiza unei probleme conduce la un algoritm cu mult mai rapid.
Manual de informatic pentru clasa a XI-a 219

Exerciii
1. Se dau coordonatele din plan a n puncte. Afiai coordonatele vrfurilor tuturor
ptratelor care au ca vrfuri puncte din mulimea considerat.

2. Se dau n substane chimice. Se tie c, n anumite condiii, unele substane intr


n reacii chimice cu altele. Fiind date p perechi de forma (i,j) cu semnificaia c
substana i intr n reacie cu substana j, se cer toate grupurile de s<n substane
astfel nct oricare dou substane din grup nu intr n reacie.

8.4.5. Generarea aranjamentelor

Se dau dou mulimi A={1,2,...,p} i B={1,2,...,n}. Se cer toate


funciile injective definite pe A cu valori n B. O astfel de problem este una de
generare a aranjamentelor de n luate cte p ( A pn ).

Exemplu: p=2, n=3. Avem: 12, 21, 13, 31, 23, 32. De exemplu, 21 este funcia
f:AB dat astfel: f(1)=2; f(2)=1. Avem relaiile:

n!
A pn = = n(n 1)...(n p + 1) .
(n p)!

Enun. Se citesc n i p. S se genereze toate aranjamentele de n luate cte p.


S observm c dac se cunoate fiecare submulime de p elemente a
mulimii de n elemente, atunci aranjamentele se pot obine permutnd n toate
modurile posibile elementele unei astfel de mulimi. Pornind de la aceast
observaie, suntem tentai s generm toate submulimile cu p elemente ale
mulimii cu n elemente i, din fiecare astfel de submulime, s obinem permutrile
ei. Exerciiu!

Pe de alt parte, se poate lucra mult mai eficient. O soluie este de forma:
x1x2...xp, unde x1, x2, ..., xpB. n plus, x1, x2, ..., xp trebuie s fie distincte.
Spre deosebire de algoritmul de generare a combinrilor, aici ne intereseaz toate
permutrile unei soluii (acestea sunt, la rndul lor, alte soluii). Aceasta nseamn
c nu mai putem pune n soluie elementele n ordine cresctoare. S recapitulm:

- o soluie are p numere din B;

- numerele trebuie s fie distincte.

Rezult de aici c algoritmul este acelai de la permutri, diferena fiind dat


de faptul c soluia are p numere, nu n ca n cazul permutrilor.
220 Capitolul 8. Metoda backtracking

Varianta Pascal Varianta C++


var sol:array[1..9]of integer; #include <iostream.h>
n,p:integer; int n,p,sol[10];
function int valid(int k)
valid(k:integer):boolean; { for (int i=1;i<k;i++)
var i:integer; if (sol[k]==sol[i])
begin return 0;
valid:=true; return 1;
for i:=1 to k-1 do }
if sol[k]=sol[i] then
valid:=false void back(int k)
end; { int i,j;
if (k==p+1)
procedure back(k:integer); { for (j=1;j<=p;j++)
var i,j:integer; cout<<sol[j];
begin cout<<endl;
if k=p+1 then }
begin else
for j:=1 to p do for (i=1;i<=n;i++)
write(sol[j]); { sol[k]=i;
writeln if (valid(k))
end back(k+1);
else }
for i:=1 to n do }
begin
main()
sol[k]:=i;
{ cin>>n;
if valid(k) then
cin>>p;
back(k+1)
back(1);
end
}
end;
begin
readln(n);
readln(p);
back(1)
end.

Exerciii
1. Se citesc n, p i apoi n litere distincte. Afiai toate cuvintele care se pot forma
cu p dintre ele.

2. Se citesc n i apoi numele mici a n persoane. tiind c toate numele care se


termin cu a reprezint nume de fat, celelalte fiind nume de biei, s se afieze
toate mulimile de perechi fat-biat care se pot forma. Dou mulimi sunt distincte
dac cel puin una dintre perechi difer. De exemplu, pentru n=5, Maria, Ana,
Doina, Doru, Cosmin, se afieaz mulimile: {Maria-Doru, Ana-Cosmin},
{Ana-Cosmin, Maria-Doru}, {Maria-Doru, Doina-Cosmin}, {Doina-Doru,
Maria-Cosmin}, {Ana-Doru, Doina-Cosmin}, {Doina-Doru, Ana-Cosmin}.
Manual de informatic pentru clasa a XI-a 221

3. Cei n acionari ai unei firme trebuie s organizeze un numr maxim de edine


tip mas rotund la care s participe exact p dintre ei. tiind c oricare dou
edine trebuie s difere fie prin acionarii prezeni, fie prin vecinii pe care i au
acetia la mas, stabilii numrul de edine pe care le pot organiza. De exemplu,
dac n=4 i p=3, atunci sunt posibile 5 configuraii diferite ale celor 3 acionari
aezai la masa rotund: 1-2-3; 1-3-2; 1-3-4; 1-4-3; 2-3-4; 2-4-3
(configuraiile 2-3-1 i 3-1-2 nu se consider, deoarece sunt echivalente, la
masa rotund, cu configuraia 1-2-3).

8.4.6. Generarea tuturor partiiilor mulimii {1, 2, ..., n}

Definiia 8.1. Fie mulimea A={1,2,...,n}. Se numete partiie a


mulimii A, un set de kn mulimi care ndeplinesc condiiile de mai jos:
a) A1A2...Ak=A;
b) AiAj=, ij{1,2...n}.

Exemplu. Considerm mulimea A={1,2,3}. Avem partiiile:


{1,2,3}
{1,2} {3}
{1,3} {2}
{2,3} {1}
{1} {2} {3}

Enun. Se citete un numr natural, n. Se cer toate partiiile mulimii


A={1,2,...,n}.

Rezolvare. Chiar dac tim s generm toate submulimile unei mulimi, tot nu ne
ajut s generm toate partiiile.

1. Pentru a putea genera toate partiiile, trebuie s gsim o metod prin care s
putem reine o partiie. O prim idee ne conduce la folosirea unui vector, sol, astfel:
dac sol[i]=k, atunci elementul i se gsete n mulimea k a partiiei. Totui, nu
tim cte mulimi sunt n partiia respectiv. Exist o partiie care conine n mulimi
atunci cnd fiecare element este ntr-o mulime i una care conine toate mulimile,
adic tocmai mulimea A. Cu alte cuvinte, numrul mulimilor dintr-o partiie este
ntre 1 i n.

2. Pentru a avea o ordine n generarea soluiilor, elementele mulimii A trebuie s


aparin de submulimi consecutive ale partiiei.

Din acest motiv, sol[i] va lua valori ntre 1 i


1+max{sol[1], sol[2], ..., sol[i-1]}.
222 Capitolul 8. Metoda backtracking

Prin aceast condiie se evit situaia n care, de exemplu, vectorul sol reine
(1,3,1). Aceasta ar avea semnificaia c elementele 1 i 3 se gsesc n
submulimea 1 a partiiei, iar elementul 2 se gsete n submulimea 3 a partiiei. n
acest caz, lipsete submulimea 2 a partiiei.

S exemplificm funcionarea algoritmului pentru cazul n=3:

- sol=(1,1,1) - A1={1,2,3);
- sol=(1,1,2) - A1={1,2} A2={3};
- sol=(1,2,1) - A1={1,3} A2={2};
- sol=(1,2,2) - A1={1} A2={2,3};
- sol=(1,2,3) - A1={1} A2={2} A3={3}.

S observm c nici n cazul acestei probleme nu trebuie s verificm existena


anumitor condiii de continuare.

Analizai programul astfel obinut!

Varianta Pascal Varianta C++


var sol:array[0..10]of integer; #include <iostream.h>
n,i,j,maxim:integer; int n, sol[10],
max[10],i,j,maxim;
procedure tipar;
void tipar()
begin
{ maxim=1;
maxim:=1;
for (i=2;i<=n;i++)
for i:=2 to n do
if (maxim<sol[i])
if maxim<sol[i]
maxim=sol[i];
then maxim:=sol[i];
cout<<"Partitie "<<endl;
writeln('Partitie ');
for (i=1;i<=maxim;i++)
for i:=1 to maxim do
{ for (j=1; j<=n;j++)
begin
if (sol[j]==i)
for j:=1 to n do
cout<<j<<" ";
if sol[j]=i
cout<<endl;
then write (j,' ');
}
writeln;
}
end;
end; void back(int k)
{ int i,j,maxprec;
procedure back (k:integer); if (k==n+1) tipar();
var i,j,maxprec:integer; else
begin { maxprec=0;
if k=n+1 for (j=1;j<=k-1;j++)
then tipar if (maxprec<sol[j])
else maxprec=sol[j];
begin for (i=1;i<=maxprec+1;i++)
maxprec:=0; { sol[k]=i; max[k]=sol[k];
for j:=1 to k-1 do back(k+1);}
if maxprec<sol[j] then }
maxprec:=sol[j]; }
Manual de informatic pentru clasa a XI-a 223

for i:=1 to maxprec+1 do main()


begin { cout<<"n=";
sol[k]:=i; cin>>n;
back(k+1) back(1);
end; }
end
end;

begin
write('n='); readln(n);
back(1);
end.

Exerciiu. Putei arta c oricrei partiii i aparine un unic coninut al


vectorului sol, obinut ca n program?

Indicaie. Observai c ntotdeauna elementul 1 aparine primei submulimi a


partiiei, elementul 2 poate aparine submulimilor 1 sau 2 ale partiiei, ...,
elementul n poate aparine submulimilor, 1, 2 sau n ale partiiei. Pornind de aici,
construii vectorul sol!

innd cont de faptul c oricrei partiii i corespunde un unic coninut al


vectorului sol i oricrui coninut al vectorului sol i corespunde o unic
partiie, am obinut, practic, o funcie bijectiv de la mulimea partiiilor
mulimii A la mulimea coninuturilor generate de algoritm ale vectorului sol.
Pornind de la aceast bijecie, n loc ca algoritmul s genereze partiiile, el
va determina coninuturile vectorului sol. Apoi, pentru fiecare coninut al
vectorului sol, se obine o partiie.

Exerciiu. Modificai programul precedent pentru ca acesta s afieze toate


partiile care conin exact 3 submulimi.

8.5. Alte tipuri de probleme care se rezolv prin


utilizarea metodei backtracking

8.5.1. Generaliti

Toate problemele pe care le-am ntlnit pn acum admit soluii care


ndeplinesc urmtoarele caracteristici:
soluiile sunt sub form de vector;
toate soluiile unei probleme au aceeai lungime, unde prin lungime
nelegem numrul de componente ale vectorului soluie.
224 Capitolul 8. Metoda backtracking

Exemple. Fie mulimea A={1,2...n}. Atunci:

a) Toate permutrile mulimii A au lungimea n.

b) Toate submulimile cu p elemente ale mulimii A (generarea combinrilor) au


lungimea p.

c) Toate soluiile sub form de vector ale problemei generrii tuturor partiiilor
mulimii A au lungimea n.

n realitate, cu ajutorul metodei backtracking se pot rezolva i probleme care nu


ndeplinesc condiiile de mai sus. Astfel, exist probleme n care nu se cunoate de la
nceput lungimea soluiei, exist probleme care admit mai multe soluii de lungimi
diferite, exist probleme n care soluia este sub forma unei matrice cu dou sau trei
linii etc. Exemplele urmtoare v vor convinge.

8.5.2. Generarea partiiilor unui numr natural

Enun. Se citete un numr natural n. Se cere s se tipreasc toate modurile


de descompunere a lui ca sum de numere naturale. De exemplu, pentru n=4,
avem: 4, 31, 22, 211, 13, 121, 112, 1111.

Ordinea numerelor din sum este important. Astfel, se tiprete 112 dar
i 211, 121.

Rezolvare. De la nceput, observm c nu se cunoate lungimea unei soluii.


Ea poate fi cuprins ntre 1, n cazul n care numrul n sine constituie o
descompunere a sa i n, atunci cnd numrul este descompus ca sum a n
numere egale cu 1.

Trecem la stabilirea algoritmului pe care l vom folosi.

1. Fiecare component a vectorului sol trebuie s rein o valoare mai


mare sau egal cu 1.

2. Mai nti s observm c, n procesul de generare a soluiilor, trebuie ca


n permanen s fie respectat relaia

sol[1]+sol[2]+...sol[k]n.

3. Avem soluie atunci cnd

sol[1]+sol[2]+...sol[k]=n.

Rezult de aici c trebuie s cunoatem, la fiecare pas k, suma


s= sol[1]+sol[2]+...sol[k-1].
Manual de informatic pentru clasa a XI-a 225

O prim posibilitate ar fi ca la fiecare pas s calculm aceast sum. Dar, se


poate lucra eficient. Suma va fi reinut n permanen ntr-o variabil global,
numit s.
Mai jos, este prezentat funcionarea algoritmului pentru n=4:

soluie

1 0 0 0 1 1 0 0 1 1 1 0 1 1 1 1
s=0, k=1 s=1, k=2 s=2, k=3 s=3, k=4

soluie soluie soluie

1 1 2 1 2 0 0 1 2 1 1 3
s=2, k=3 s=1, k=2 s=3, k=3 s=1, k=2

Observai modul n care calculm suma la fiecare pas. De cte ori se trece
la componenta urmtoare (k+1), la s se adun sol[k], de cte ori se face
pasul napoi (se trece la componenta k-1), din s se scade sol[k].

Programul este prezentat n continuare:

Varianta Pascal Varianta C++


var sol:array[1..100] of integer; #include <iostream.h>
n,i,s:integer; int sol[100], n,i,s;
procedure back (k:integer); void back(int k)
begin { if (s==n)
if s=n then begin { for (i=1;i<=k-1;i++)
for i:=1 to k-1 do cout<<sol[i];
write(sol[i]); cout<<endl;
writeln; }
end else
else begin { sol[k]=0;
sol[k]:=0; while (sol[k]+s<n)
while sol[k]+s<n do { sol[k]++;
begin s+=sol[k];
sol[k]:=sol[k]+1; back(k+1);
s:=s+sol[k]; back(k+1); s-=sol[k];
s:=s-sol[k] }
end; }
end }
end;
main()
begin { cout<<"n="; cin>>n;
write('n='); readln(n); back(1);
back(1) }
end.
226 Capitolul 8. Metoda backtracking

Exerciii
1. Cum trebuie procedat n cazul n care se cere ca soluiile s fie afiate o singur
dat? Spre exemplu, dac s-a afiat descompunerea 1,1,2 s nu se mai afieze
2,1,1 sau 1,2,1?
Indicaie: procedeul a mai fost ntlnit, de exemplu la generarea combinrilor.
Soluiile se vor genera n ordine cresctoare. Modificai programul n acest sens.
2. Adaptai metoda de rezolvare astfel nct s se genereze numai partiiile formate
din numere naturale distincte.
3. Adaptai metoda de rezolvare astfel nct s se genereze numai partiiile formate
din cel puin p numere naturale distincte (n i p citite de la tastatur).
4. Adaptai metoda de rezolvare astfel nct s se genereze numai partiiile formate
din numere naturale aflate n intervalul [a,b] (n, a i b citite de la tastatur).
5. Rezolvai problema scrierii numrului natural n ca sum de numere naturale
alese dintr-o mulime format din k valori date {v1, v2, , vk}. Astfel, 10 se
poate scrie ca sum de numere alese din mulimea {2,3,6} n felul urmtor:
10=2+2+2+2+2, 10=2+2+3+3, 10=2+2+6.

8.5.3. Plata unei sume cu bancnote de valori date

Enun. Se dau suma s i n tipuri de monede avnd valori de a1,a2,...,an lei. Se


cer toate modalitile de plat a sumei s utiliznd aceste monede. Se presupune
c se dispune de un numr nelimitat de exemplare din fiecare tip de moned.

Iat soluiile pentru Suma=5, n=3 (trei tipuri de monede) cu valorile 1, 2, 3:

1) 1 de 2, 1 de 3; 2) 1 de 1, 2 de 2; 3) 2 de 1, 1 de 3;
4) 3 de 1, 1 de 2; 5) 5 de 1;

Rezolvare. Valorile celor n monede sunt reinute de vectorul a. Astfel, a[1] va


reine valoarea monedei de tipul 1, a[2] valoarea monedei de tipul 2, .a.m.d.
Numrul de monede din fiecare tip va fi reinut de vectorul sol. Astfel, sol[1] va
reine numrul de monede de tipul 1, sol[2] va reine
numrul de monede de tipul 2, .a.m.d. n aceste condiii, o sol 2 0 1
soluie pentru exemplul anterior arat ca alturat, unde suma 5 a 1 2 3
se formeaz cu dou monede de 1 i o moned de 3.

Ce observm?
Manual de informatic pentru clasa a XI-a 227

1. Exist componente ale vectorului sol care rein 0. Aceast situaie corespunde
cazului n care moneda respectiv nu este luat n calcul. Din acest motiv, fiecare
component a vectorului sol va fi iniializat cu o valoare aflat naintea tuturor celor
posibile, adic cu -1.

2. Orice soluie are exact n componente (n este numrul de tipuri de monede).


Acest numr include toate monedele, chiar i cele care nu sunt luate n calcul.
3. Ca i la problema anterioar, vom reine n permanen suma obinut la un
moment dat. Astfel, la pasul k, avem la dispoziie suma:
s = a[1]*sol[1] + a[2]*sol[2] + ... + a[k-1]*sol[k-1]

4. Avem soluie dac:


s = a[1]*sol[1] + a[2]*sol[2] + ... + a[n]*sol[n] = Suma

Exerciiu. ncercai s artai, prin desene, ca la problema anterioar, modul


de obinere a tuturor soluiilor pentru Suma=5 i n=3.

n continuare, este prezentat programul:

Varianta Pascal Varianta C++


var sol,a:array[1..100] #include <iostream.h>
of integer; int sol[100], a[100],
n,i,s,Suma:integer; n,i,s,Suma;
procedure back (k:integer); void back(int k)
begin { if (s==Suma)
if s=suma then { cout<<"Solutie "<<endl;
begin for(i=1;i<=k-1;i++)
writeln('Solutie'); if(sol[i])
for i:=1 to k-1 do cout<<sol[i]<<"monede de "
if sol[i]<>0 then <<a[i]<<endl;
writeln (sol[i],' monede cout<<endl;
de ',a[i]); }
writeln; else
end { sol[k]=-1;
else while(sol[k]*a[k]+s<Suma
begin && k<n+1)
sol[k]:=-1; {
while (sol[k]*a[k]+s<Suma) sol[k]++;
and (k<n+1) do s+=sol[k]*a[k];
begin back(k+1);
sol[k]:=sol[k]+1; s-=sol[k]*a[k];
s:=s+sol[k]*a[k]; }
back(k+1); }
s:=s-sol[k]*a[k] }
end;
main()
end
{ cout<<"suma="; cin>>Suma;
end;
cout<<"n="; cin>>n;
228 Capitolul 8. Metoda backtracking

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


write ('suma='); readln(suma); { cout<<"a["<<i<<"]=";
write('n='); readln(n); cin>>a[i];
for i:=1 to n do }
begin back(1);
write ('a[',i,']='); }
readln(a[i]);
end;
back(1)
end.

Exerciiu. Adaptai rezolvarea problemei plii unei sumei cu bancnote date,


cunoscnd, n plus, pentru fiecare valoare ai numrul limit bi de bancnote cu
valoarea respectiv disponibile. Astfel, pentru s=100, a=(2,5,50), b=(10,6,3),
varinata s=10x5+1x50 nu corespunde cerinei deoarece nu avem la dispoziie 10
monede de 5, ci doar 6.

8.5.4. Problema labirintului

Enun. Se d un labirint sub form de matrice cu m linii i n coloane. Fiecare


element al matricei reprezint o camer a labirintului. ntr-una din camere, de
coordonate lin i col, se gsete un om. Se cere s se gseasc toate ieirile
din labirint. Nu este permis ca un drum s treac de dou ori prin aceeai camer.

O prim problem care se pune este precizarea modului de codificare a


ieirilor din fiecare camer a labirintului.

Fie l(i,j) un element al matricei. Acesta poate lua valori ntre 0 i 15. Se
consider ieirile spre nord, est, sud i vest, luate n aceast ordine. Pentru fiecare
direcie cu ieire se reine 1, iar n caz contrar, se reine 0. Un ir de patru cifre 1
sau 0 formeaz un numr n baza 2. Acest numr este convertit n baza 10 i
reinut n l(i,j). De exemplu, pentru o camer care are ieire n nord i vest,
avem 1001(2)=9(10).

Exemplu. Alturat este prezentat un labirint. Acolo


15 11 10 14
unde nu este permis trecerea dintr-o camer n alta, se
marcheaz cu o linie oblic. De asemenea, matricea 11 12 11 12
reine i valorile corespunztoare ieirilor, aa cum sunt
11 6 15 7
ele cerute de program.

Rezolvare
1. O camer vizitat se reine prin coordonatele ei: lin (linia) i col(colana). Din
acest motiv, pentru a reine un traseu vom utiliza o matrice cu dou coloane i mai
multe linii: sol. De exemplu, dac camera iniial este cea de coordonate (2,2)
o soluie este (2,2), (2,3), (1,3).
Manual de informatic pentru clasa a XI-a 229

2. Nu toate soluiile au aceeai lungime, ntruct exist trasee de lungime diferit.


Se obine o soluie atunci cnd 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). Evident, atunci cnd
s-a gsit o situaie, aceasta se afieaz.
3. Spunem c o camer este accesibil dac exist intrare din camera curent
ctre ea. Atenie la modul (vedei programul) n care testm dac o camer este
accesibil sau nu. Este o operaie n care se testeaz coninutul unui anumit bit.
Acesta se obine efectund un I logic ntre dou valori. De exemplu, dac testm
ieirea spre sud, atunci efectum I logic ntre 0010(2)=2(10) i valoarea reinut
n matrice pentru camera curent. Dac valoarea obinut este diferit de 0, atunci
avem ieire din camera curent ctre sud.
4. nainte de a intra ntr-o camer accesibil se testeaz dac respectiva camer
a mai fost vizitat sau nu. Pentru aceasta utilizm funcia vizitat. n caz c a
fost vizitat, se face pasul napoi.
Analizai programul:

Varianta Pascal Varianta C++


var sol:array [1..100,1..2] of #include <iostream.h>
integer; int sol[100][2],l[10][10],
l:array [0..10,0..10] of m,n,i,j,lin,col;
integer;
int vizitat(int k,int lin,
m,n,i,j,lin,col:integer;
int col)
function vizitat(k,lin, { int v=0;
col:integer):boolean; for (i=1;i<=k;i++)
begin if (sol[i][0]==lin &&
vizitat:=false; sol[i][1]==col) v=1;
for i:=1 to k-1 do return v;
if (sol[i,1]=lin) and }
(sol[i,2]=col)
void tipar(int k,int lin,
then vizitat:=true;
int col)
end;
{ cout <<" Solutie "<<endl;
procedure for (i=1;i<=k-1;i++)
tipar(k,lin,col:integer); cout<<sol[i][0]<<" "
begin <<sol[i][1]<<endl;
writeln('Solutie'); if (lin==0)
for i:=1 to k-1 do cout<<"iesire prin nord"
writeln(sol[i,1],' ', <<endl;
sol[i,2]); else
if lin=0 then if (lin==m+1)
writeln('iesire prin nord') cout<<"iesire prin sud"
else <<endl;
if lin=m+1 then else
writeln('iesire prin sud') if (col==0)
else cout<<"iesire prin vest"
if col=0 then <<endl;
writeln('iesire prin vest') else
else cout<<"iesire prin est"
writeln('iesire prin est'); <<endl;
readln; }
end;
230 Capitolul 8. Metoda backtracking

procedure void back(int k, int lin,


back(k,lin,col:integer); int col)
var i:integer; { if (lin==0 || lin==m+1 ||
begin col==0 || col==n+1)
if (lin=0) or (lin=m+1) or tipar(k,lin,col);
(col=0) or (col=n+1) else
then tipar(k,lin,col) {
else sol[k][0]=lin;
begin sol[k][1]=col;
sol[k,1]:=lin; for (int i=1;i<=4;i++)
sol[k,2]:=col; switch(i)
for i:=1 to 4 do
{
case i of
case 1:
1:if (l[lin,col]
if (l[lin][col] & 8 &&
and 8<>0) and not
vizitat(k,lin-1,col) ! vizitat(k, lin-1,col))
then back(k+1,lin-1,col);
back(k+1,lin-1,col); break;
2:if (l[lin,col] case 2:
and 4<>0) and not if (l[lin][col] & 4 &&
vizitat(k,lin,col+1) ! vizitat(k, lin,col+1))
then back(k+1,lin,col+1);
back(k+1,lin,col+1); break;
3:if (l[lin,col] case 3:
and 2<>0) and not if (l[lin][col] & 2 &&
vizitat(k,lin+1,col) ! vizitat(k, lin+1,col))
then back(k+1,lin+1,col);
back(k+1,lin+1,col); break;
4:if (l[lin,col] case 4:
and 1<>0) and not if (l[lin][col] & 1 &&
vizitat(k,lin,col-1) ! vizitat(k, lin,col-1))
then back(k+1,lin,col-1);
back(k+1,lin,col-1) break;
end; {case} }
end }
end; }
begin main()
write('M='); { cout<<"M=";
readln(m); cin>>m;
write('N='); cout<<"N=";
readln(n); cin>>n;
for i:=1 to m do for (i=1;i<=m;i++)
for j:=1 to n do for(j=1;j<=n;j++)
begin { cout<<"l["<<i<<","
write('l[',i,',',j,']='); <<j<<"]=";
readln(l[i,j]) cin>>l[i][j];
end; }
write('lin='); cout<<"Linie=";
readln(lin); cin>>lin;
write('col='); cout<<"Coloana= ";
readln(col); cin>>col;
back(1,lin,col) back(1,lin,col);
end. }
Manual de informatic pentru clasa a XI-a 231

Intrebare. Cum s-ar putea gsi un drum de lungime minim de la camera


iniial ctre ieirea din labirint? Prima idee care ne vine n minte este s
generm toate ieirile, ca n program, pentru a o putea depista pe cea de lungime
minim. Ei bine, rspunsul nu este satisfctor. Nu uitai, tehnica backtracking
necesit un timp exponenial. Problema se poate rezolva cu mult mai eficient. Dar,
pentru asta, trebuie s studiem teoria grafurilor. Toate la timpul lor...

Exerciii
1. Adaptai rezolvarea pentru un labirint n care fiecare csu reine valoarea 1
sau 0 (1 semnificnd csu plin, prin care nu se poate trece, iar 0 csu liber,
pe unde se poate trece). Ca i n problema prezentat, deplasarea se poate face
dintr-o csu n orice alt csu alturat, orizontal sau vertical, cu condiia ca ea
s existe i s fie liber. Validai poziia iniial a omului (lin, col), astfel nct
aceasta s corespund unei csue libere. Estimai spaiul de memorie utilizat
n aceast variant.

2. Realizai o variant a rezolvrii de la 1, adugnd cte o linie sau coloan


suplimentar pe fiecare margine a labirintului, astfel nct s nu se mai testeze ca
celula n care se trece s existe. Plasarea ntr-o celul de pe margine este
echivalent cu obinerea unei soluii.

8.5.5. Problema bilei

Enun. Se d un teren sub form de matrice cu m linii i n coloane. Fiecare


element al matricei reprezint un subteren cu o anumit altitudine dat de valoarea
reinut de element (numr natural). ntr-un astfel de subteren, de coordonate
(lin,col) se gsete o bil. tiind c bila se poate deplasa n orice poriune de
teren aflat la nord, est, sud sau vest, de altitudine strict inferioar poriunii pe care
se gsete bila. Se cere s se gseasc toate posibilitile ca bila s prseasc
terenul.
6 8 9 3
Exemplu. Fie terenul alturat. Iniial, bila se afl n subterenul
de coordonate (2,2). O posibilitate de ieire din teren este dat 9 7 6 3
de drumul: (2,2), (2,3), (3,3), (3,4). n program, 5 8 5 4
altitudinile subterenului vor fi reinute de matricea t.
8 3 7 1

Analiza problemei i rezolvarea ei. Problema seamn cu cea anterioar,


deci putem gndi c dac nlocuim testul de intrare ntr-o camer cu cel de
altitudine mai mic, am rezolvat-o! Este adevrat, se obine o rezolvare, dar se
poate i mai uor. S analizm: mai este necesar s testm dac bila nu a ajuns
pe un teren pe unde a mai trecut? Nu, deoarece, la fiecare pas, bila se deplaseaz
pe un teren de altitudine strict inferioar. Prin urmare, problema este mai uoar
dect precedenta.
232 Capitolul 8. Metoda backtracking

n rest, vom propune o rezolvare n care sol este o matrice cu 3 coloane i


un numr mare de linii. Astfel, sol(k,1) va reine direcia n care pleac bila (1 pt
nord, 2 pentru est, 3 pentru sud i 4 pentru vest), sol(k,2) va reine linia
subterenului, iar sol(k,3) va reine coloana subterenului.

Exerciiu. Artai, prin reprezentare grafic, modul de funcionare a


algoritmului, pe baza structurii de date propus.

Varianta Pascal Varianta C++


var sol:array [1..100,1..3] #include <iostream.h>
of integer;
t:array [0..10,0..10] int sol[100][3],t[10][10],
of integer; m,n,i,j,lin,col;
m,n,i,j,lin,col:integer;
procedure tipar(k:integer); void tipar(int k)
begin { cout<<"Solutie "<<endl;
for(i=1;i<=k-1;i++)
writeln('Solutie');
cout<<sol[i][1]<<" "
for i:=1 to k-1 do
<<sol[i][2]<<endl;
writeln(sol[i,2],' ',
}
sol[i,3]);
end; void back(int k, int lin, int col)
procedure { if (lin==0 || lin==m+1 ||
back(k,lin,col:integer); col==0 || col==n+1)
begin tipar(k);
if (lin=0) or (lin=m+1) or else
(col=0) or (col=n+1) { sol[k][0]=0;
then tipar(k) sol[k][1]=lin;
else begin sol[k][2]=col;
sol[k,1]:=0; while (sol[k][0]<4)
sol[k,2]:=lin; {
sol[k,3]:=col; sol[k][0]++;
while sol[k,1]<4 do switch(sol[k][0])
begin {
sol[k,1]:=sol[k,1]+1; case 1:
case sol[k,1] of if(t[lin-1][col]<t[lin][col])
1:if t[lin-1,col]< back(k+1,lin-1,col); break;
t[lin,col] then case 2:
back(k+1,lin-1,col); if(t[lin][col+1]<t[lin][col])
2:if t[lin,col+1]< back(k+1,lin,col+1); break;
t[lin,col] then case 3:
back(k+1,lin,col+1); if(t[lin+1][col]<t[lin][col])
3:if t[lin+1,col]< back(k+1,lin+1,col); break;
t[lin,col] then case 4:
back(k+1,lin+1,col); if(t[lin][col-1]<t[lin][col])
4:if t[lin,col-1]< back(k+1,lin,col-1); break;
t[lin,col] then }
back(k+1,lin,col-1); }
end; {case} }
end end end; }
begin main()
write('M='); readln(m); { cout<<"m="; cin>>m;
write('N='); readln(n); cout<<"n="; cin>>n;
Manual de informatic pentru clasa a XI-a 233

for i:=1 to m do for (i=1;i<=m;i++)


for j:=1 to n do for (j=1;j<=n;j++)
begin { cout<<"t["<<i<<","<<j<<"]=";
write('t[',i,',',j, cin>>t[i][j];
']='); }
readln(t[i,j]) cout<<"lin="; cin>>lin;
end; cout<<"col="; cin>>col;
write('lin=');readln(lin); back(1,lin,col);
write('col=');readln(col); }
back(1,lin,col)
end.

Exerciiu. Modificai programul astfel nct s se afieze i direcia n care


bila prsete terenul.

8.5.6. Sritura calului

Enun. Se consider o tabl de ah nxn i un cal plasat 1 16 11 20 3



n colul din stnga, sus. Se cere s se afieze o posibilitate 10 21 2 17 12
de mutare a acestei piese de ah, astfel nct s treac o 15 24 19 4 7

singur dat prin fiecare ptrat al tablei. Alturat, observai o 22 9 6 13 18
soluie pentru n=5. 25 14 23 8 5

Analiza problemei i rezolvarea ei. Fiind dat o poziie n care se gsete
calul, acesta poate sri n alte 8 poziii. Pentru a scrie mai puin cod, cele 8 poziii
sunt reinute de vectorii constani x i y. Astfel, dac lin i col sunt coordonatele
poziiei unde se gsete calul, acesta poate fi mutat n:
(lin+x[0],col+y[0])...(lin+x[7],col+y[7]).

Reinei acest procedeu! De altfel, acesta poate fi folosit pentru problemele


deja prezentate. Matricea t reine poziiile pe unde a trecut calul. n rest, s-a
procedat ca la problema anterioar.

Varianta Pascal Varianta C++


const x:array[1..8] of #include <iostream.h>
integer=(-1,1,2,2,1,-1,-2,-2); #include <stdlib.h>
y:array[1..8] of
integer=(2,2,1,-1,-2,-2,-1,1); const int x[8]={-1,1,2,2,1,-1,
var n:integer; -2,-2};
st: array[1..1000,1..2] of const int y[8]={2,2,1,-1,-2,-2,
integer; -1,1};
t: array[-1..25,-1..25] of
integer; int n,sol[1000][2],t[25][25];

procedure back(k,lin, void back(int k,int lin,


col:integer); int col)
var i,linie,coloana:integer; { int linie,coloana,i;
234 Capitolul 8. Metoda backtracking

begin if (k==n*n)
if k=n*n { for (i=1;i<=k-1;i++)
then cout<<sol[i][0]<<" "
begin <<sol[i][1]<<endl;
for i:=1 to k-1 do cout<<lin<<" "<<col;
writeln(st[i,1],' ', exit(EXIT_SUCCESS);
st[i,2]); }
writeln(lin,' ',col); else
halt; { sol[k][0]=lin;
end sol[k][1]=col;
else for (i=0;i<=7;i++)
begin { linie=lin+x[i];
st[k,1]:=lin; coloana=col+y[i];
st[k,2]:=col; if (linie<=n && linie>=1
for i:=1 to 8 do && coloana<=n &&
begin coloana>=1 &&
linie:=lin+x[i]; t[linie][coloana]==0)
coloana:=col+y[i]; {
if (linie<=n) and t[linie][coloana]=1;
(linie>=1) and back(k+1,linie,coloana);
(coloana<=n) and t[linie][coloana]=0;
(coloana>=1) and
}
(t[linie,coloana]=0)
}
then
}
begin
}
t[linie,coloana]:=1;
back(k+1,linie, main()
coloana);
{ cout<<"n=";
t[linie,coloana]:=0;
cin>>n;
end;
back(1,1,1);
end
}
end
end;

begin
write ('n=');
readln(n);
back(1,1,1);
end.
Manual de informatic pentru clasa a XI-a 235

Probleme propuse

1. Avem la dispoziie 6 culori: alb, galben, rou, verde, albastru i negru. S se


precizeze toate drapelele tricolore care se pot proiecta, tiind c trebuie
respectate regulile:

orice drapel are culoarea din mijloc galben sau verde;


cele trei culori de pe drapel sunt distincte.

2. Dintr-un grup de n persoane, dintre care p femei, trebuie format o delegaie


de k persoane, din care l femei. S se precizeze toate delegaiile care se
pot forma.

3. La o mas rotund se aeaz n persoane. Fiecare persoan reprezint o


firm. Se dau k perechi de persoane care aparin unor firme concurente. Se
cere s se determine toate modalitile de aezare la mas a persoanelor,
astfel nct s nu stea alturi dou persoane de la firme concurente.

4. Se d o permutare a primelor n numere naturale. Se cer toate permutrile care


se pot obine din aceasta astfel nct nici o succesiune de dou numere,
existent n permutarea iniial, s nu mai existe n noile permutri.

5. Se cer toate soluiile de aezare n linie a m cini i n pisici astfel nct s nu


existe o pisic aezat ntre doi cini.

6. Anagrame. Se citete un cuvnt cu n litere. Se cere s se tipreasc toate


anagramele cuvntului citit. Se poate folosi algoritmul pentru generarea
permutrilor?

7. Se dau primele n numere naturale. Dispunem de un algoritm de generare a


combinrilor de n elemente luate cte p pentru ele. Se consider un vector cu
n componente iruri de caractere, unde, fiecare ir reprezint numele unei
persoane. Cum adaptai algoritmul de care dispunei pentru a obine
combinrile de n persoane luate cte p?

8. Se citesc n numere naturale distincte. Se cere o submulime cu p elemente


astfel nct suma elementelor sale s fie maxim n raport cu toate
submulimile cu acelai numr de elemente.

9. S se determine 5 numere de cte n cifre, fiecare cifr putnd fi 1 sau 2, astfel


nct oricare dintre aceste 5 numere s coincid exact n m poziii i s nu
existe o poziie n care s apar aceeai cifr n toate cele 5 numere.
236 Capitolul 8. Metoda backtracking

10. Fiind dat un numr natural pozitiv n, se cere s se produc la ieire toate
descompunerile sale ca sum de numere prime.

11. Attila i regele. Un cal i un rege se afl pe o tabl de ah. Unele cmpuri
sunt arse, poziiile lor fiind cunoscute. Calul nu poate clca pe cmpuri arse,
iar orice micare a calului face ca respectivul cmp s devin ars. S se afle
dac exist o succesiune de mutri permise (cu restriciile de mai sus), prin
care calul s poat ajunge la rege i s revin la poziia iniial. Poziia iniial
a calului, precum i poziia regelui sunt considerate nearse.

12. Se dau n puncte n plan prin coordonatele lor. Se cer toate soluiile de unire a
acestor puncte prin exact p drepte, astfel nct mulimea punctelor de
intersecie ale acestor drepte s fie inclus n mulimea celor n puncte.

13. Gsii toate soluiile naturale ale ecuaiei 3x+y+4xz=100.

14. S se ordoneze n toate modurile posibile elementele mulimii {1,2,...,n}


astfel nct numerele i, i+l, ..., i+k s fie unul dup cellalt i n aceast
ordine (l=1,i+kn).

15. Se consider o mulime de n elemente i un numr natural k nenul. S se


calculeze cte submulimi cu k elemente satisfac, pe rnd, condiiile de mai jos
i s se afieze aceste submulimi.
conin p obiecte date;
nu conin nici unul din q obiecte date;
conin exact un obiect dat, dar nu conin un altul;
conin exact un obiect din p obiecte date;
conin cel puin un obiect din p obiecte date;
conin r obiecte din p obiecte date, dar nu conin alte q obiecte date.

16. Se d un numr natural par N. S se determine toate irurile de N paranteze


care se nchid corect.
Exemplu: pentru N=6: ((( ))),(()()),()()(),()(()),(())().

17. Se dau N puncte albe i N puncte negre n plan, de coordonate ntregi. Fiecare
punct alb se unete cu cte un punct negru, astfel nct din fiecare punct, fie el
alb sau negru, pleac exact un segment. S se determine o astfel de
configuraie de segmente astfel nct oricare dou segmente s nu se
intersecteze. Se citesc N perechi de coordonate corespunznd punctelor albe
i N perechi de coordonate corespunznd punctelor negre.

18. S se genereze toate permutrile de N cu proprietatea c oricare ar fi 2iN,


exist 1ji astfel nct
V(i)-V(j)=1.
Manual de informatic pentru clasa a XI-a 237

Exemplu: pentru N=4, permutrile cu proprietatea de mai sus sunt:


2134, 2314, 3214, 2341, 3241, 3421, 4321.

19. O trup cu N actori i propune s joace o pies cu A acte astfel nct:

oricare dou acte s aib distribuie diferit;


n orice act exist, evident, cel puin un actor;
de la un act la altul, vine un actor pe scena sau pleac un actor de pe
scen (distribuia a dou acte consecutive difer prin exact un actor).

S se furnizeze o soluie, dac exist vreuna.

20. Fiind dat un numr natural N i un vector V cu N componente ntregi, se cere:

s se determine toate subirurile cresctoare de lungime [N/5];


s se calculeze p(1)+p(2)+...+p(k), unde p(k) reprezint numrul
subirurilor cresctoare de lungime k.

21. Pe malul unei ape se gsesc c canibali i m misionari. Ei urmeaz s treac


apa i au la dispoziie o barc cu 2 locuri. Se tie c, dac att pe un mal, ct
i pe cellalt avem mai muli canibali dect misionari, misionarii sunt mncai
de canibali. Se cere s se scrie un program care s furnizeze toate soluiile de
trecere a apei, astfel nct s nu fie mncat nici un misionar.

22. Se d un careu sub form de matrice cu m linii i n coloane. Elementele


careului sunt litere. Se d, de asemenea, un cuvnt. Se cere s se gseasc
n careu prefixul de lungime maxim al cuvntului respectiv. Regula de cutare
este urmtoarea:

a) se caut litera de nceput a cuvntului;


b) litera urmtoare se caut printre cele 4 elemente nvecinate cu elementul
care conine litera de nceput, apoi printre cele 4 elemente nvecinate cu
elementul care conine noua liter, .a.m.d.

23. Se dau coordonatele a n puncte din plan. Se cere s se precizeze 3 puncte


care determin un triunghi de arie maxim. Ce algoritm vom folosi?

a) Generarea aranjamentelor; b) Generarea combinrilor;


c) Generarea permutrilor; d) Generarea tuturor submulimilor.

24. Fiind date numele a n soldai, ce algoritm vom utiliza pentru a lista toate
grupele de cte k soldai? Se tie c ntr-o grup, ordinea prezint importan.

a) Generarea aranjamentelor; b) Generarea combinrilor;


c) Generarea permutrilor; d) Generarea tuturor partiiilor.
238 Capitolul 8. Metoda backtracking

25. Fiind date n numere naturale, ce algoritm vom utiliza pentru a determina
eficient o submulime maximal de numere naturale distincte?

a) se genereaz toate submulimile i se determin o submulime maximal


care ndeplinete condiia cerut;
b) se genereaz toate partiiile i se caut o submulime maximal care
aparine unei partiii oarecare i care ndeplinete condiia cerut;
c) se compar primul numr cu al doilea, al treilea, al n-lea, al doilea cu al
treilea, al patrulea, al n-lea, ... i atunci cnd se gsete egalitate se elimin un
numr dintre ele.
d) nici un algoritm dintre cei de mai sus.

26. Dispunem de un algoritm care genereaz permutrile prin backtracking.


Primele dou permutri afiate sunt: 321, 312. Care este urmtoarea
permutare care va fi afiat?

a) 321; b) 123; c) 213; d) 231.

Indicaii

6. Dei algoritmul este asemntor, nu este acelai, trebuie pus o condiie


suplimentar. De exemplu, n cuvntul mama nu se poate inversa a de pe poziia
2 cu a de pe poziia 4.

7. Dac vectorul care reine numele persoanelor este V, n loc s se afieze i, se


va afia V[i].

23. b)

24. a)

25. d)
Explicaie: primele dou variante prezint soluii exponeniale, a treia este n
O(n2). Dar dac sortm numerele, atunci le putem afia pe cele distincte dintr-o
singur parcurgere. Sortarea se poate efectua n O(nlog(n)), iar parcurgerea
n O(n). Prin urmare, complexitatea este O(nlog(n)).

26. d)
Explicaie: pe fiecare nivel al stivei se caut succesorii n ordinea n, n-1, ..., 1.
239

Capitolul 9

Introducere n teoria grafurilor

9.1. Grafuri neorientate

9.1.1. Introducere

Uneori, algoritmii trebuie s prelucreze date referitoare la anumite


elemente ntre care exist anumite relaii. S analizm exemplele urmtoare:

1. Se dau n orae. Unele dintre ele sunt unite prin osele directe (care nu mai
trec prin alt ora).
2. Se cunosc relaiile de prietenie dintre n persoane.
3. Se dau n ri i se cunoate relaia de vecintate ntre ele.
4. Se dau n triunghiuri, iar unele dintre ele sunt asemenea.

Pentru fiecare dintre aceste exemple se poate imagina o reprezentare


grafic care s exprime relaiile existente.
Convenim ca fiecare element s-l numim nod sau vrf.
Astfel, n cazul 1. nodul este oraul, n cazul 2. nodul este persoana, n
cazul 3. nodul este ara i n cazul 4. nodul este triunghiul. Convenim ca un nod
(vrf) s-l notm cu un cercule n care s nscriem numrul lui (de la 1 la n).

Relaia existent ntre dou noduri o vom reprezenta grafic unindu-le


printr-un segment de dreapt. Convenim ca un astfel de segment s-l numim
muchie i dac ea unete nodurile i i j, s-o notm cu (i,j).

n cazul 1., muchia (i,j) are


semnificaia c ntre oraele i i j exist o osea 1
direct. n cazul 2., muchia (i,j) are
semnificaia c persoanele i i j sunt prietene, n 6
cazul 3. muchia (i,j) are semnificaia 2
c rile i i j sunt vecine, iar n cazul 4., c
triunghiurile i i j sunt asemenea. 5
3 4
Procednd aa, obinem o descriere grafic
precum cea din figura 9.1 i convenim ca o astfel
de reprezentare s-o numim graf neorientat. Figura 9.1.
Exemplu de graf neorientat
240 Capitolul 9. Introducere n teoria grafurilor

Desigur, aceast abordare este intuitiv. Teoria grafurilor este fundamentat


matematic i n cele ce urmeaz ne propunem s-o prezentm sistematic.

9.1.2. Definiia grafului neorientat


1
Definiia 9.1. . Un graf neorientat este o pereche ordonat G=(V,E),
unde:
V = {v1, v2,... vn} este o mulime finit i nevid. Elementele
mulimii V se numesc noduri (vrfuri).

E este o mulime finit de perechi neordonate de forma (vi, vj),


unde ij, i vi,vjV. Elementele mulimii E se numesc muchii.
Semnificaia unei muchii este aceea c unete dou noduri.

Un graf poate fi desenat aa cum se observ


n exemplul urmtor (vezi figura 9.2.), unde
1 6
G=(V,E),
V = {1,2,3,4,5,6};
E = {(1,2),(1,3),(1,5),(2,3),(3,4), 2
(4,5)} 5
3
4
Notaie: n graful G=(V,E), vom nota
cu n numrul nodurilor i cu m numrul
muchiilor. Figura 9.2.
Alt exemplu de graf neorientat

Observaii

Dou noduri distincte pot fi unite prin cel mult o muchie. n exemplul de
mai sus, (1,2) este muchia care unete nodul 1 cu nodul 2. Dac scriem
(2,1), ne referim la aceeai muchie (perechea este neordonat).

Nu exist o muchie care unete un nod cu el nsui (o muchie unete


dou noduri distincte).

Definiia 9.2. n graful G=(V,E), nodurile distincte vi,vjG sunt


adiacente dac exist muchia (vi, vj)E.
Vom spune c muchia (vi,vj)E este incident la nodurile vi i vj.

n exemplul dat anterior, nodurile 1 i 5 sunt adiacente, dar nodurile 2 i 5 nu


sunt adiacente. Muchia (4,5) este incident la nodurile 4 i 5.

Definiia este restrictiv, n unele lucrri vei ntlni definiii mai puin restrictive, de
1

exemplu, poate exista o muchie de la un nod la el nsui sau nu se cere ca mulimea


nodurilor s fie finit.
Manual de informatic pentru clasa a XI-a 241

Definiia 9.3. ntr-un graf neorientat, prin gradul unui nod v se nelege
numrul muchiilor incidente cu nodul v i se noteaz cu d(v). Un nod
cu gradul 0 se numete nod izolat, iar unul cu gradul 1 se numete nod
terminal.

n exemplul dat, d(2)=2, d(1)=3, iar d(6)=0 (6 este nod izolat).

O relaie util: fie un graf neorientat cu n noduri i m muchii. Dac notm cu d1,
d2, ..., dn gradele celor n noduri, atunci avem relaia:

d 1 + d 2 + d 3 + ...d n = 2m.

Demonstraie: fiecare muchie face s creasc gradele celor dou noduri la


care este incident cu cte o unitate. Prin urmare, se obine relaia anterioar.

Pentru a nelege bine noiunile prezentate n acest paragraf, ne vom referi la


exemplele din 9.1.1:

- fie afirmaia: gradul nodului i este k. Pentru exemplul 1., ea are


semnificaia c din oraul i pleac (sosesc) k osele, pentru exemplul 2., are
semnificaia c persoana i are k prieteni, pentru exemplul 3., are semnificaia c
ara i se nvecineaz cu k ri, iar pentru exemplul 4., are semnificaia c pentru
triunghiul i se cunosc k triunghiuri asemenea. Aici trebuie fcut observaia c ar
putea s existe i alte triunghiuri asemenea cu el, dar modul n care putem afla
aceasta va fi tratat separat.
- fie afirmaia: nodurile i i j sunt adiacente. Pentru exemplul 1., ea are
semnificaia c oraele i i j sunt unite printr-o osea care nu trece prin alte
orae, pentru exemplul 2., are semnificaia c persoanele i i j sunt prietene,
pentru exemplul 3., are semnificaia c rile i i j sunt vecine, iar pentru
exemplul 4., are semnificaia c triunghiurile i i j sunt asemenea.

- fie afirmaia: nodul i este izolat. Pentru exemplul 1., nseamn c nu exist nici
o osea care leag oraul i cu alt ora, pentru exemplul 2., nseamn c
persoana i nu are nici un prieten, pentru exemplul 3., nseamn c ara i nu se
nvecineaz cu nici o ar (este situat pe o insul), pentru exemplul 4., nseamn
c nu exist nici un triunghi dintre celelalte n-1 triunghiuri care s fie asemenea cu
triunghiul i.

Exerciiu

Dai un exemplu inspirat din viaa real, pentru care s gsii graful asociat.
Astfel, vei rspunde la ntrebrile: ce semnificaie au nodurile sau muchiile i ce
nseamn gradul unui nod.
242 Capitolul 9. Introducere n teoria grafurilor

9.1.3. Memorarea grafurilor

n acest paragraf, prezentm principalele structuri de date prin care grafurile pot
fi memorate n vederea prelucrrii lor. De la nceput, precizm faptul c vom alege o
2
structur sau alta n funcie de :
a) algoritmul care prelucreaz datele referitoare la graf;

b) memoria intern pe care programul o are la dispoziie;

c) dac graful conine multe muchii sau nu.

Pentru fiecare structur de date pe care o vom folosi, vom avea cte o
procedur (funcie) care citete datele respective. Toate aceste subprograme se
gsesc grupate n unitatea de program grafuri.pas (pentru Pascal) i n
grafuri.cpp (pentru C++). Vom fi astfel scutii ca, pentru fiecare program pe care l
realizm, s fim nevoii s adugm liniile de cod necesare citirii i ne permite s ne
concentrm exclusiv asupra algoritmului.

Toate subprogramele pe care le utilizm citesc datele dintr-un fiier text, n


care, pe prima linie vom scrie numrul de noduri (n), iar pe urmtoarele linii,
cte o muchie (i,j), ca n exemplul de mai jos, n care este prezentat un
graf i liniile fiierului text care este citit pentru el:

6
Fiierul text:
1 6 1 2
1 3
1 5
2 2 3
3 4
5
4 5
3 Figura 9.3.
4 Exemplu de graf neorientat

Trecem la prezentarea structurilor prin care putem memora datele referitoare la


un graf.

A. Memorarea grafului prin matricea de adiacen

An,n - o matrice ptratic, unde elementele ei, ai,j au semnificaia:

1, pentru (i, j) E
a i, j =
0, pentru (i, j) E

2
Modul de alegere a structurii l vei nelege pe parcursul studiului acestui capitol.
Manual de informatic pentru clasa a XI-a 243

Pentru graful din figura 9.3, 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, 6 =
0 0 1 0 1 0
1 0 0 1 0 0

0 0 0 0 0 0

Observaii

1. ntruct, din modul n care a fost definit graful, rezult c nu exist muchii de la un
nod la el nsui, rezult c elementele de pe diagonala principal rein 0:

ai ,i = 0, i {1,2,..., n} .

2. Matricea de adiacen este simetric:

ai , j = a j ,i , i, j {1,2,..., n} .

Evident, deoarece muchia (i,j) coincide cu muchia (j,i).

3. Suma elementelor de pe linia i, i{1,2,...,n}, are ca rezultat gradul nodului


i, d(i), pentru c astfel se obine suma nodurilor j, j {1,2,...,n}, pentru care
exist muchie de la i la j, adic suma muchiilor incidente la i.

De exemplu, pentru graful de mai sus, suma elementelor de pe linia 1 este 3,


adic gradul nodului 1, d(1).

4. Tot aa, suma elementelor de pe coloana j, j {1,2,...,n}, are ca rezultat


gradul nodului j, d(j).

! Raionamentul este asemntor cu cel de la observaia precedent.

5. Suma tuturor elementelor matricei de adiacen este, de fapt, suma gradelor


tuturor nodurilor, adic dublul numrului de muchii (2m).

6. Dac graful citit are un numr mic de muchii, atunci matricea de adiacen este o
form ineficient de memorare a lui, pentru c ea va reine o mulime de 0.

Subprogramele pe care le vom utiliza pentru aceast modalitate de memorare


sunt prezentate n continuare:
244 Capitolul 9. Introducere n teoria grafurilor

Varianta Pascal Varianta C++

type mat_ad=array[1..50,1..50] of void CitireN


integer; (char Nume_fis[20],
procedure CitireN int A[50][50], int& n)
(Nume_Fis:string; {
var A:Mat_ad; var n:integer); int i,j;
var f:text; fstreamf (Nume_fis,ios::in);
i,j:byte; f>>n;
begin while (f>>i>>j)
Assign(f,Nume_Fis); A[i][j]=A[j][i]=1;
Reset(f); Readln(f,n); f.close();
while(not eof(f)) do }
begin
readln(f,i,j);
A[i,j]:=1; A[j,i]:=1;
end;
close(f);
end;

Programul urmtor citete datele referitoare la un graf i afieaz matricea


sa de adiacen:

Varianta Pascal Varianta C++

uses grafuri; #include "grafuri.cpp"


var A:mat_ad; int A[50][50],n;
n,i,j:integer; main()
begin {
CitireN('Graf.txt',A,n); CitireN("Graf.txt",A,n);
for i:=1 to n do for (int i=1;i<=n;i++)
begin { for (int j=1;j<=n;j++)
for j:=1 to n do cout<<A[i][j]<<" ";
write (A[i,j],' '); cout<<endl;
writeln; }
end; }
end.

B. Memorarea grafului prin liste de adiacen


Listele de adiacen reprezint o alt form de memorare a grafurilor, n care
pentru fiecare nod se cunoate lista nodurilor adiacente cu el. De exemplu, pentru
graful din figura 9.4., listele de adiacen sunt:

1 1 -> 2,3,5
6
2 -> 1,3
3 -> 1,2,4
2 4 -> 3,5
5 5 -> 1,4
3 6 ->
4
Figura 9.4.
Manual de informatic pentru clasa a XI-a 245

n acest caz, se utilizeaz un vector cu n componente, pe care l vom numi


Start i o matrice T cu 2 linii i 2m coloane. Semnificaiile sunt:
Start pentru fiecare nod i, Start[i] specific coloana din T unde
ncepe lista nodurilor adiacente cu i. Dac reine 0, nseamn c nodul i nu
are noduri adiacente.
T(0,i) reprezint un nod al listei nodurilor adiacente.
T(1,i) reprezint indicele coloanei din T unde se gsete urmtorul
element din list. Dac reine 0, atunci acesta este ultimul elerment din lista
succesorilor.
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. Start[3]=9,


adic lista ncepe n coloana 9 a matricei T. Primul nod adiacent cu nodul 3 este 4.
Urmtorul se gsete la indicele 8. Urmtorul nod adiacent cu nodul 3 este nodul 2.
Urmtorul se gsete la indicele 4. Acesta este nodul 1. El este ultimul din list,
pentru T(1,4)=0 (T[1][4]).

n continuare, vom arta modul n care se construiesc listele de adiacen.


Pentru fiecare muchie (i,j) se completeaz dou coloane ale matricei T, pentru c
trebuie nregistrat faptul c i este adiacent cu j i j este adiacent cu i. Astfel,
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.

Considerm primul caz, n care vom pune pe j, ca prim element n lista


nodurilor adiacente cu i. Variabila k, care are iniial valoarea 0, va reine indicele
ultimei coloane din T completat. O astfel de operaie se realizeaz n patru pai:

1. Pentru c trebuie completat o nou coloan n T, se incrementeaz k;


2. T(0,k) va reine j, pentru c succesorul lui i este j;
3. T(1,k) va reine Start[i], pentru c indicele coloanei n care se gsete
primul nod adiacent (pn la aceast nou introducere, cnd devine al doilea din list)
este n Start[i];
4. Start[i] va reine k, pentru c, de acum, primul nod din lista nodurilor
adiacente cu i, este j, care se gsete n coloana de indice k.
246 Capitolul 9. Introducere n teoria grafurilor

Mai jos, putei observa subprogramele care construiesc listele de adiacen


(funciile au fost adugate n grafuri.pas i respectiv, grafuri.cpp):

Varianta Pascal Varianta C++


type void Citire_LA_Astatic
lista=array[0..1,1..50]of integer; (char Nume_fis[20],
pornire=array[1..50] of integer; int T[2][50],
... int Start[50], int& n)
procedure Citire_LA_Astatic {
(Nume_fis:string;var T:Lista; int i,j,k=0;
var Start:pornire;var n:integer); fstream
var i,j,k:integer; f(Nume_fis,ios::in);
f:text; f>>n;
begin while (f>>i>>j)
k:=0; { k++;
Assign(f,Nume_Fis); Reset(f); T[0][k]=j;
Readln(f,n); T[1][k]=Start[i];
while(not eof(f)) do Start[i]=k;
begin k++;
readln(f,i,j); k:=k+1; T[0][k]=i;
T[0,k]:=j; T[1,k]:=Start[i]; T[1][k]=Start[j];
Start[i]:=k; k:=k+1; Start[j]=k;
T[0,k]:=i; T[1,k]:=Start[j]; }
Start[j]:=k; f.close();
end; }
close(f);
end.

Programul urmtor citete datele despre un graf i afieaz, pentru fiecare


nod, lista nodurilor adiacente:

Varianta Pascal Varianta C++

uses grafuri; #include "grafuri.cpp"


var Start:pornire; T:lista; int T[2][50],Start[50],
n,i,man:integer; n,i,man;
begin main()
Citire_LA_Astatic {Citire_LA_Astatic
('Graf.txt',T,Start,n); ("Graf.txt",T,Start,n);
for i:=1 to n do for (int i=1;i<=n;i++)
begin {
writeln ('Noduri adiac. cu ',i); cout<<"Noduri adiac. cu "<<
man:=Start[i]; i<<endl;
while (man<>0)do man=Start[i];
begin while (man)
write(T[0,man],' '); { cout<<T[0][man]<<" ";
man:=T[1,man]; man=T[1][man]; }
end; cout<<endl;
writeln }
end; }
end.
Manual de informatic pentru clasa a XI-a 247

C. Memorarea grafului prin lista muchiilor

Se utilizeaz un vector cu m componente, unde m este numrul muchiilor.


Fiecare component va reine cele dou noduri la care muchia respectiv este
incident i, n anumite cazuri alte informaii referitoare la muchia respectiv.
Reinei c exist algoritmi pentru grafuri care prelucreaz datele pornind de la
aceast reprezentare (vedei arbori pariali). Uneori, va fi necesar s sortm
muchiile, fie dup nodul de pornire, fie dup alt informaie asociat lor.

Mai jos, putei observa cum se descrie un vector (V) care reine muchiile
unui graf:

Varianta Pascal Varianta C++

type muchie=record struct muchie


x,y:integer; { int x,y;
end; };
var V:array[1..50] of muchie; muchie V[50];

Exerciiu. Realizai un subprogram care citete i memoreaz datele


referitoare la muchiile unui graf memorat prin lista muchiilor.

Observaie foarte important

Uneori, nodurilor unui graf li se asociaz anumite informaii. De exemplu,


dac nodurile unui graf reprezint orae, pentru fiecare astfel de nod se poate
memora numrul obiectivelor turistice. n astfel de cazuri, pe lng una dintre
metodele de memorare prezentate mai sus, vom asocia grafului un vector cu n
(numrul de noduri ale grafului) componente, unde fiecare component va reine
informaiile referitoare la nodul respectiv. Pentru exemplul dat, fiecare component
a grafului reine numrul de obiective turistice.

9.1.4. Graf complet

S considerm mulimea elevilor unei clase. Teoretic, oricare doi elevi din
clas se cunosc. Pentru a transpune n limbaj specific teoriei grafurilor aceast
situaie, vom considera c fiecare elev este un nod al grafului. Pentru c oricare doi
elevi se cunosc, nseamn c oricare dou noduri sunt unite printr-o muchie.
Astfel, am obinut un graf aparte, pe care-l vom numi graf complet.

Definiia 9.4. Prin graf complet vom nelege un graf neorientat n care
oricare dou noduri sunt adiacente. Vom nota un graf complet prin Kn, unde
n este numrul de noduri ale grafului.
248 Capitolul 9. Introducere n teoria grafurilor

Alturat, avei un graf complet cu 4 noduri (K4):


1

2 3

Figura 9.5. 4
Exemplu de graf complet

Relaii utile:

1. ntr-un graf complet, gradul oricrui nod este n-1. Evident, din fiecare nod,
pleac (sosesc) n-1 muchii.
n(n 1)
2. ntr-un graf complet, avem relaia: m= , unde m este numrul de
2
muchii, iar n, numrul de noduri.

Demonstraie: fiecare muchie unete 2 noduri. Numrul muchiilor va fi egal


cu numrul de submulimi cu 2 elemente ale mulimii celor n noduri.
Acest numr este C2 = n(n 1) .
n
2
n(n1)
3. Avem 2 2 grafuri neorientate cu n noduri.

Demonstraie: am vzut c numrul maxim de muchii pe care le poate avea


un graf neorientat este:

n(n 1)
2
i corespunde unui graf complet. Se tie c, fiind dat o mulime A cu n elemente,
avem 2n submulimi disjuncte ale acesteia (aici este inclus i submulimea vid
i A). Prin urmare, avem:
n(n1)

2 2

submulimi ale numrului maxim de muchii. Ori, fiecrei submulimi de muchii i


corespunde un graf neorientat, pentru c nodurile sunt aceleai.

9.1.5. Graf parial, subgraf

Definiia 9.5. Un graf parial al unui graf neorientat dat G=(V,E) este un
graf G1=(V,E1), unde E1E.
Manual de informatic pentru clasa a XI-a 249

Un graf parial al unui graf dat, este el nsui sau se obine din G prin
suprimarea anumitor muchii. Privii exemplul de mai jos:

Figura 9.6.
Obinerea unui 1 1
graf parial

2 3 3
rezult 2

4 4

G=(V,E) G1=(V,E1)

Exemple din viaa real

1. Fiind date n persoane, ntre unele dintre ele exist o relaie de prietenie. Asociem
acestei situaii un graf G. Dup un timp, unele persoane se ceart. n teoria grafurilor,
aceasta nseamn c n G se suprim anumite muchii i astfel, se obine un graf
parial G1.
2. Fiind date n orae, unele dintre ele sunt unite printr-o osea direct (care nu mai
trece prin alte orae). Asociem situaiei date un graf G. Datorit precipitaiilor, anumite
osele se inund i nu mai pot fi utilizate. Aceasta nseamn c n G se suprim
anumite muchii i se obine un graf parial G'.

Intrebare: cte grafuri pariale are un graf neorientat cu n noduri? Indicaie:


Cte submulimi are mulimea muchiilor {1,2,...,m}?

Definiia 9.6. Un subgraf al unui graf neorientat G=(V,E) este un graf


G1=(V1,E1), unde V1V, E1E, iar muchiile din E1 sunt toate muchiile din
E care sunt incidente numai la noduri din mulimea V1.

Un subgraf al unui graf G este el nsui sau se obine din G prin suprimarea
anumitor noduri i a tuturor muchiilor incidente cu acestea. Privii exemplul de
mai jos:

Figura 9.7. 1
Obinerea unui 1
subgraf

2 3 3
rezult

4 4

G=(V,E) G1=(V1,E1)
250 Capitolul 9. Introducere n teoria grafurilor

Exemple din realitate

1. Se dau n firme. ntre unele din acestea se stabilesc relaii de colaborare. Asociem
situaiei date un graf G. ntre timp, anumite firme se desfiineaz. Aceasta nseamn
c n G vom elimina anumite noduri i muchiile incidente lor, obinnd un subgraf al
lui G, G1.

2. Mai multe calculatoare (n) sunt legate n reea cu ajutorul unor cabluri. Asociem
situaiei date un graf G. ntre timp, anumite calculatoare se defecteaz. Astfel, se
obine un subgraf al lui G, G1.

ntrebare: cte subgrafuri are un graf neorientat cu n noduri? Indicaie: care


este numrul de submulimi ale mulimii {1,2,...,n}? ntruct mulimea
nodurilor, V, este nevid, vom face abstracie de mulimea vid.

9.1.6. Parcurgerea grafurilor neorientate


3
Prin parcurgerea grafurilor nelegem o modalitate de vizitare a nodurilor
acestuia. Parcurgerea eficient a grafurilor este esenial n teoria grafurilor,
deoarece o mulime de algoritmi consacrai au la baz o astfel de parcurgere.
Din acest motiv, n acest paragraf nu vom insista pe aplicaiile parcurgerilor i ne vom
mrgini numai la prezentarea algoritmilor de parcurgere. n paragrafele urmtoare,
vei gsi multe exemple utile, n care parcurgerea grafurilor joac un rol fundamental.

Exist dou modaliti generale de parcurgere i anume: parcurgerea n


lime (BF) i parcurgerea n adncime (DF). Acestea vor fi tratate separat.

9.1.6.1. Parcurgerea n lime (BF - Breadth First)

Parcurgerea n lime se face ncepnd de la un anumit nod i, pe care l


considerm vizitat.
Vizitm apoi toate nodurile adiacente cu el - fie ele j1, j2, ... jk, vizitate n
aceast ordine.
Vizitm toate nodurile adiacente cu j1, apoi cu j2, , apoi cu jk.

...
Parcurgerea continu n acest mod pn cnd au fost vizitate toate nodurile
accesibile.

3
n acest paragraf vom exemplifica parcurgerile doar n cazul grafurilor conexe. Cum
noiunea nu a fost prezentat pn n acest moment, precizm doar c vom exemplifica
parcurgerea grafurilor n care oricare dou noduri sunt "legate" printr-o succesiune
de muchii.
Manual de informatic pentru clasa a XI-a 251

Exemple de parcurgeri BF ale aceluiai graf, pornind de la noduri diferite:

1
Pentru graful alturat 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.8.

Parcurgerea BF se efectueaz prin utilizarea structurii numit coad, avnd grij


ca un nod s fie vizitat o singur dat. Atunci cnd un nod a fost introdus n coad
se marcheaz ca vizitat. Coada va fi alocat prin utilizarea unui vector.

Algoritmul este urmtorul:

Nodul de pornire este introdus n coad i este marcat ca vizitat.


Ct timp coada este nevid
Pentru nodul aflat la nceputul cozii:
se trec n coad toate nodurile adiacente cu el, care nu au fost vizitate i
se marcheaz ca vizitate;
se afieaz;
se extrage din coad.

Vom utiliza urmtoarele notaii:

- i_c - indicele primei componente a cozii;


- s_c - indicele ultimei componente a cozii;
- coada - vectorul care memoreaz coada propriu-zis;
- s - vector ce reine nodurile vizitate:

0, nodul i nu a fost vizitat


s[i] =
1, nodul i a fost vizitat

n continuare, vor fi prezentate dou exemple de implementri ale


parcurgerii n lime (BF) a unui graf, memorat prin liste de adiacen i
matrice de adiacen:
252 Capitolul 9. Introducere n teoria grafurilor

1. Programul urmtor parcurge BF un graf memorat prin liste de adiacen:

Varianta Pascal Varianta C++


uses grafuri; #include "grafuri.cpp"
var n,i_c,sf_c,p:integer; int n, coada[50],s[50],i_c=1,
coada,s:array[1..50] of sf_c=1,T[2][50],Start[50],p;
integer;
T:lista; void bf(int nod)
Start:pornire; { coada[i_c]=nod;
s[nod]=1;
procedure bf(nod:integer); while (i_c<=sf_c)
begin { p=Start[coada[i_c]];
i_c:=1;sf_c:=1; while (p)
coada[i_c]:=nod; { if (s[T[0][p]]==0)
s[nod]:=1; {sf_c++;
while i_c<=sf_c do coada[sf_c]=T[0][p];
begin s[T[0][p]]=1;
p:=Start[coada[i_c]]; }
while p<>0 do p=T[1][p];
begin }
if s[T[0,p]]=0 then cout<<coada[i_c]<<endl;
begin i_c++;
sf_c:=sf_c+1; }
coada[sf_c]:=T[0,p]; }
s[T[0,p]]:=1;
end; main()
p:=T[1,p]; {
end; Citire_LA_Astatic("Graf.txt",T
writeln(coada[i_c]); ,Start,n);
i_c:=i_c+1; bf(6);
end }
end;
begin
Citire_LA_Astatic('Graf.txt',T,
Start,n);
bf(3);
end.

2. Programul urmtor parcurge BF un graf memorat prin matricea de adiacen:

Varianta Pascal Varianta C++


uses grafuri; #include "grafuri.cpp"
var n,i_c,sf_c,i:integer;
coada,s:array[1..50] of int n, coada[50],s[50],i_c=1,
integer; sf_c=1,A[50][50],i;
A:mat_ad;
void bf(int nod)
procedure bf(nod:integer); {
begin coada[i_c]=nod;
i_c:=1; sf_c:=1; s[nod]=1;
Manual de informatic pentru clasa a XI-a 253

coada[i_c]:=nod; while (i_c<=sf_c)


s[nod]:=1; { i=1;
while (i_c<=sf_c) do while (i<=n)
begin {if (A[coada[i_c]][i]==1
i:=1; && s[i]==0)
while i<=n do { sf_c++;
begin coada[sf_c]=i;
if ((A[coada[i_c],i]=1) s[i]=1;
and (s[i]=0)) }
then i++;
begin }
sf_c:=sf_c+1;; cout<<coada[i_c]<<endl;
coada[sf_c]:=i; i_c++;
s[i]:=1; }
end; }
i:=i+1;
main()
end;
{ CitireN("Graf.txt",A,n);
writeln(coada[i_c]);
bf(1);
i_c:=i_c+1;
}
end
end;
begin
CitireN('Graf.txt',A,n);
bf(1);
end.

9.1.6.2. Parcurgerea n adncime (DF - Depth First)

Parcurgerea n adncime se face ncepnd de la un anumit nod i, pe care l


considerm vizitat.
Dup vizitarea unui nod, se viziteaz primul dintre nodurile adiacente,
nevizitate nc, apoi urmtorul nod adiacent, pn cnd au fost vizitate toate
nodurile adiacente cu el.
...
Parcurgerea continu n acest mod pn cnd au fost vizitate toate nodurile
accesibile.

Exemple de parcurgeri DF ale aceluiai graf, pornind de la noduri diferite:

1
Pentru graful alturat, 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.9.
254 Capitolul 9. Introducere n teoria grafurilor

Exemple de implementri ale parcurgerii DF

1. Programul urmtor parcurge DF un graf memorat prin liste de adiacen:

Varianta Pascal Varianta C++

uses grafuri; #include "grafuri.cpp"


var n:integer; int s[50],n;
s:array[1..50] of integer; T[2][50],Start[50];
T:lista;
void df(int nod)
Start:pornire;
{ int p;
procedure df(nod:integer); cout<<nod<<" ";
var p:integer; p=Start[nod];
begin s[nod]=1;
writeln(nod,' '); while (p)
p:=Start[nod]; { if (s[T[0][p]]==0)
s[nod]:=1; df(T[0][p]);
while p<>0 do p=T[1][p];
begin }
if s[T[0,p]]=0 }
then df(T[0,p]);
p:=T[1,p]; main()
end {
end; Citire_LA_Astatic("Graf.txt",
T,Start,n);
begin df(1);
Citire_LA_Astatic('Graf.txt',T, }
Start,n);
df(1);
end.

2. Programul urmtor parcurge DF un graf memorat prin matricea de adiacen:

Varianta Pascal Varianta C++


uses grafuri; #include "grafuri.cpp"
var n:integer; int s[50],A[50][50],n;
s:array[1..50]of integer;
void df_r(int nod)
A:mat_ad;
{ int k;
procedure df_r(nod:integer); cout<<nod<<" ";
var k:integer; s[nod]=1;
begin for (k=1;k<=n;k++)
write(nod,' '); if(A[nod][k]==1 && s[k]==0)
s[nod]:=1; df_r(k);
for k:=1 to n do }
if (A[nod,k]=1) and (s[k]=0)
then df_r(k); main()
end; { CitireN("Graf.txt",A,n);
df_r(1);
begin }
CitireN('Graf.txt',A,n);
df_r(1);
end.
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 gsite 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 gsesc deja grupate n lista asociat nodului respectiv i numrul
lor corespunde numrului de muchii incidente acestuia. Algoritmul va selecta,
pe rnd, toate muchiile, de unde rezultatul de mai sus.

9.1.7. Lanuri

Relum exemplele de la paragraful 9.1.1.

Pentru exemplul 1. ntrebarea este: fiind date dou orae a i b, se poate


ajunge cu maina din a n b?
Pentru exemplul 2. O persoan, a, afl o informaie important. Persoana
transmite informaia tuturor prietenilor, acetia, la rndul lor, transmit informaia tuturor
prietenilor lor, .a.m.d. ntrebarea este: informaia ajunge la persoana b?
Pentru exemplul 4. Fiind date dou triunghiuri, a i b, sunt ele asemenea? S
observm c nu este obligatoriu ca s se fi introdus de la nceput faptul c triunghiul a
este asemenea cu triunghiul b. Aceast informaie poate fi dedus, de exemplu, prin
faptul c a este asemenea cu k, k cu l i l cu b.

Analiznd aceste ntrebri pe graful asociat fiecrei situaii 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, rspunsul este negativ, altfel rspunsul este pozitiv.
Sau, exprimat n teoria grafurilor, aceasta nseamn c rspunsul depinde de
existena unui lan de la a la b. De acum, putem prezenta definiia lanului.

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

Vrfurile v1 i vp se numesc extremitile lanului.


Numrul p-1 se numete lungimea lanului. Acesta este dat de numrul de
muchii ce unesc nodurile lanului.
256 Capitolul 9. Introducere n teoria grafurilor

Definiia 9.8. Se numete lan elementar un lan care conine numai noduri
distincte.
1
Exemple: pentru graful din figura alturat:
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 afieze lanul.

Rezolvare. Exist un lan de la a la b, dac i numai dac o parcurgere DF sau


BF, care pornete de la nodul a, va ajunge s viziteze nodul b. Rmne de rezolvat
problema modului n care reinem lanul de la a la b. S observm 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
reine seleciile astfel efectuate, vom utiliza un vector T, iar elementele acestuia au
semnificaia urmtoare:
j, dac i este descendent al lui j
T[i] =
0, dac i nu a fost selectat
S mai observm c un nod este selectat o singur dat, deci, n final, T va
reine, 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
afieaz b, apoi T[b], apoi T[T[b]] ... pn cnd se obine valoarea 0. Pentru ca
drumul s nu fie afiat n ordine invers fa de modul n care a fost obinut,
subprogramul refac care l reconstituie i l afieaz este recursiv. Programul de mai
jos afieaz 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. }

Observaii foarte importante

1. S observm c vectorul T poate fi folosit pentru a obine lanuri de la nodul a la


oricare alt nod al grafului.
2. Dac refacem rezolvarea prin utilizarea parcurgerii n lime, vom observa c
lanul obinut are lungimea minim. Prin natura ei, parcurgerea BF selecteaz nodurile
n ordinea "deprtrii" lor fa de nodul de la care a nceput parcurgerea. Astfel, la
nceput se viziteaz primul nod (a), apoi nodurile pentru care lungimea lanului de la a
la ele este 1, apoi nodurile pentru care lungimea lanului de la a la ele este 2, .a.m.d.

Programul care urmeaz afieaz 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 lanurilor. ntrebri referitoare la situaiile prezentate la 9.1.1:

a) pentru exemplul 1, ntrebarea este: cum putem afla, pentru fiecare ora n parte,
oraele n care putem ajunge cu maina?
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 situaii, 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 reinute ntr-o matrice cu n linii i n coloane (matrice ptratic). Aceast
matrice se numete matricea lanurilor, iar elementele ei au semnificaia:

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 obine matricea lanurilor?
Rspunsul este uor de dat. Parcurgem graful ncepnd cu nodul 1. Pentru toate
nodurile j vizitate, vom avea L(1,j)=1, completnd 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 ncepnd cu nodul 3.... .a.m.d.
O anumit mbuntire a algoritmului se obine dac inem cont de faptul c matricea
lanurilor este simetric (de ce?). Lsm ca exerciiu scrierea acestui program.
ntrebare: care este complexitatea acestui algoritm?

9.1.8. Graf conex

Revenind la exemplele de la 9.1.1, putem pune ntrebrile:


a) pentru exemplul 1: se poate ajunge cu maina din orice ora n oricare altul?
b) pentru exemplul 4: toate triunghiurile sunt asemenea ntre ele?

Dac la ambele ntrebri rspunsul este afirmativ, ce semnificaie are el pentru


grafurile asociate? Aceasta nseamn c pentru orice pereche de noduri, exist un
lan care le are ca extremiti. Sau, n limbajul specific teoriei grafurilor, cele dou
grafuri sunt conexe.

Definiia 9.9. Un graf neorientat G=(V,E) este conex, dac pentru orice
pereche de noduri x,yV, exist un lan n care extremitatea iniial este x
i extremitatea final este y.

Exemple Figura 9.11.


1
1. Graful alturat este conex. De exemplu,
ntre nodurile 1 i 5 exist lanul [1,2,3,4,5], dar
i lanul [1,3,4,5]. ntre nodurile 3 i 5 exist 2 3
lanul [3,4,5], .a.m.d.

Oricum am alege dou noduri, exist lanul cerut de


4 5
definiie.

2. Graful alturat 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 definiie, conex. Aceasta pentru c nu exist
dou noduri diferite pentru care s se pun problema existenei 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. innd cont de cele nvate, problema nu este grea. Putem utiliza
una din metodele de parcurgere nvate, DF sau BF. Ideea este urmtoarea: dac,
pornind de la un nod, printr-una din metodele de parcurgere, ajungem s vizitm toate
celelalte noduri, atunci graful dat este conex. Cum putem ti dac am vizitat toate
nodurile? Simplu, dup parcurgere, toate componentele vectorului s rein 1. Putei
scrie acest program?

9.1.9. Componente conexe


Analizm, din nou, exemplele de la 9.1.1.
a) Pentru exemplul 1: se cere o mulime de orae, astfel nct s se poat circula cu
maina ntre oricare dou orae din mulime, iar dac un ora nu aparine acestei
mulimi, atunci nu putem ajunge cu maina de la el la oricare ora din mulime.
b) Pentru exemplul 4: se cere o mulime de triunghiuri astfel nct oricare dou
triunghiuri din aceast mulime sunt asemenea, iar dac un triunghi nu aparine
acestei mulimi, el nu este asemenea cu nici unul din mulime.
Observm c fiecare astfel de mulime este maximal n raport cu relaia de
incluziune. Dac nu ar fi aa, ar exista o alt mulime 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.

Definiia 9.10. Fie G=(V,E) un graf neorientat i G1=(V1,E1) un subgraf


al su. Atunci G1=(V1,E1) este o component conex a grafului
G=(V,E) dac sunt ndeplinite condiiile de mai jos:
a) Oricare ar fi x,yV1, exist un lan de la x la y.
b) Nu exist un alt subgraf al lui G, G2=(V2,E2) cu V1V2 care
ndeplinete condiia a).
Figura 9.13.
Exemple 4
1
1. Graful alturat este alctuit din dou componente
conexe. Prima este alctuit 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 unete.
Figura 9.14.
2. Graful din figura 9.14. conine 3 componente 4
conexe. Aa cum un graf, cu un singur nod, este 1
conex, tot aa un nod izolat alctuiete el singur o
component conex.
2 3 5
Manual de informatic pentru clasa a XI-a 261

Observaii

1. Cte componente conexe poate avea un graf neorientat cu n noduri? Numrul


lor este cuprins ntre 1, pentru un graf conex i n corespunztor unui graf cu toate
nodurile izolate.

2. n unele probleme, se d un graf neorientat, care nu este conex i se cere s


se adauge un numr minim de muchii, astfel nct graful s devin conex. n
astfel de cazuri, se determin componentele conexe, fie ele C1, C2, ... Cp. Fie p
numrul lor. Vom aduga p-1 muchii, prima unete un nod din C1 cu unul din C2, a
doua unete un nod din C2 cu unul din C3, ..., ultima unete un nod din Cp-1 cu
unul din Cp.

Problema (de programare).9.4. Fiind dat un graf neorientat, se cere s se


afieze nodurile fiecrei componente conexe.

Rezolvare. Dup cum uor v putei da seama, o parcurgere a grafului (DF sau
BF) pornind de la un anumit nod, viziteaz toate nodurile componentei conexe care
l conine. Pentru fiecare nod vizitat, s[i] reine 1. Dac, dup o parcurgere, mai
rmn noduri nevizitate, parcurgerea se reia ncepnd de la primul nod nevizitat.
Evident, numrul componentelor conexe este egal cu numrul de parcurgeri
necesare pentru a fi vizitate toate nodurile.

Varianta Pascal Varianta C++


uses grafuri; #include "grafuri.cpp"
var n,i:integer; int s[50],A[50][50],n,i;
s:array[1..50]of integer;
void df_r(int nod)
A:mat_ad;
{ int k;
procedure df_r(nod:integer); cout<<nod<<" ";
var k:integer; s[nod]=1;
begin for (k=1;k<=n;k++)
write (nod,' '); if((A[nod][k]==1)&&
s[nod]:=1; (s[k]==0))
for k:=1 to n do df_r(k);
if (A[nod,k]=1) and (s[k]=0) }
then df_r(k);
end; main()
{ CitireN("Graf.txt",A,n);
begin for (i=1;i<=n;i++)
CitireN('Graf.txt',A,n); if (s[i]==0)
for i:=1 to n do { cout <<"Comp
if s[i]=0 then conexa"<<endl;
begin df_r(i);
writeln('Comp conexa'); cout<<endl;
df_r(i); }
writeln; }
end
end.
262 Capitolul 9. Introducere n teoria grafurilor

9.1.10. Cicluri

Revenim la exemplul cu oraele legate prin osele. ntrebarea este urmtoarea:


exist vreun ora din care putem face o excursie cu maina, s nu trecem dect o
singur dat pe o osea, s vizitm mai multe orae i s ne ntoarcem de unde am
plecat?
Problema se reduce la a afla dac graful asociat are sau nu cel puin un ciclu.
Mai nti, s definim noiunea.

Definiia 9.11. Un lan L care conine numai muchii distincte i pentru


care nodul iniial coincide cu nodul final se numete ciclu. Dac, cu
excepia ultimului nod, care coincide cu primul, lanul este elementar,
atunci ciclul este elementar (adic, cu excepia ultimului nod, care
coincide cu primul, conine numai noduri distincte).

Exemple: pentru graful neorientat din Figura 9.15.


figura 9.15. avem:
4
a. [1,2,3,1] este ciclu elementar. 1

b. [1,2,1] nu este ciclu, pentru c (1,2) i


(2,1) reprezint o aceeai muchie, deci nu conine 2 3 5
numai muchii distincte.
c. [1,2,3,2,1] nu este ciclu, pentru c nu conine numai muchii distincte.

Problema 9.5. Fiind dat un graf conex, G=(V,E), s se scrie un program care
decide dac graful conine cel puin un ciclu.

Rezolvare. ncepem prin a observa c dac graful nu este conex, putem rezolva
problema verificnd dac exist un ciclu ntr-o component conex a sa. Pentru
simplitate, am preferat s considerm c graful este conex. i aici, problema se poate
rezolva pornind de la o parcurgere DF. Graful conine cel puin un ciclu dac, n
timpul parcurgerii, algoritmul va ajunge n situaia de a vizita un nod de dou ori
(tentativ oricum respins, pentru c algoritmul testeaz acest lucru, vedei rolul
vectorului s). Vom da un exemplu, cu graful de mai jos, pe care l parcurgem DF.

Vizitm nodul 1, apoi primul nod adiacent lui, fie el nodul


2, apoi primul nod adiacent al lui 2, fie el 3, apoi se ncearc 1
vizitarea nodului adiacent cu 3, i anume 1. Dar acest nod a mai
fost vizitat i tentativa este respins. De ce s-a ajuns n situaia
s se ncerce vizitarea nodului 3 de dou ori? Pentru c nodul 2 3
1, a fost vizitat ca nod de pornire i pentru c se ncearc
vizitarea lui prin lanul [1,2,3].
4
Astfel, s-a obinut ciclul [1,2,3,1].
Figura 9.16.
Manual de informatic pentru clasa a XI-a 263

Programul urmtor testeaz dac un graf conine sau nu cicluri. S observm


c, odat vizitat un nod, accesat prin intermediul muchiei (nod,k), se terge din
matricea de adiacen muchia (k, nod), pentru c altfel ar fi selectat i aceast
muchie i s-ar ajunge n situaia s fie semnalat un ciclu fals.

Varianta Pascal Varianta C++


uses grafuri,wincrt; #include "grafuri.cpp"
var n:integer; int s[50],A[50][50],gasit,n;
s:array[1..50]of integer;
void df(int nod)
A:mat_ad;
{ int k;
gasit:boolean;
s[nod]=1;
procedure df(nod: integer); for(k=1;k<=n;k++)
var k:integer; if (A[nod][k]==1)
begin {
s[nod]:=1; A[k][nod]=0;
for k:=1 to n do if (s[k]==0) df(k);
if A[nod,k]=1 then else gasit=1;
begin }
A[k][nod]:=0; }
if s[k]=0 then df(k)
main()
else gasit:=true;
{
end
CitireN("Graf.txt",A,n);
end;
df(1);
begin if (gasit) cout<<"Da";
CitireN('Graf.txt',A,n); else cout<<"Nu";
df(1); }
if gasit then writeln('Da')
else writeln('Nu')
end.

Observaie

Dei parcurgerea se face n timp polinomial i cu toate c programul este


simplu, se putea proceda ntr-un mod cu mult mai inteligent. Mai mult, aproape c nu
este cazul s facem un program. Graful fiind conex, este suficient s verificm relaia
m=n-1,

unde m este numrul de muchii, iar n este numrul de noduri. Dac relaia este
verificat, nseamn c graful nu conine cicluri, altfel, dac m>n-1 nseamn c
graful conine cel puin un ciclu, iar dac m<n-1 nseamn c nu este conex, i ar
contrazice cerina.

De unde aceast observaie? Pentru a o nelege, trebuie s studiem arborii


264 Capitolul 9. Introducere n teoria grafurilor

9.1.11. Arbori

9.1.11.1. Noiunea de arbore

S presupunem c o firm dorete s conecteze la TV, prin cablu, cele n case


ale unui sat. Cum vor fi conectate casele la cablu? Logic, va trebui ca fiecare cas s
fie conectat. Apoi, la o cas va sosi cablul dintr-un singur loc i, eventual, de la ea va
porni cablul ctre alt cas. Dac analizm situaia prezentat prin prisma teoriei
grafurilor, vom avea un graf conex (fiecare cas trebuie conectat), iar graful nu va
conine cicluri. Dac ar conine un ciclu, atunci, evident, putem renuna la cablul care
unete dou case care aparin ciclului respectiv. Astfel, obinem un graf cu
proprietile de mai sus, numit arbore.

Definiia 9.12. Se numete arbore un graf


neorientat care este conex i nu conine 1 2
cicluri.

n figura 9.17. avei un exemplu de arbore cu 5


noduri. 3 4
5
Problema 9.6. Se citete un graf. S se scrie un
Figura 9.17.
program care verific dac este arbore.

Rezolvare. Conexitatea tim s o verificm. Dac ntr-o parcurgere, DF (BF) se


viziteaz toate nodurile, atunci graful este conex. Dac un graf are cicluri, este, din
nou, uor de verificat, cu aceeai parcurgere DF(BF). S ne amintim c n cazul n
care graful are cicluri, n timpul parcurgerii, va exista cel puin o a doua tentativ de
vizitare a unui nod. Prin urmare, printr-o simpl parcurgere DF (BF) se poate stabili
dac graful este conex sau nu.

Varianta Pascal Varianta C++


uses grafuri; #include "grafuri.cpp"
var n,i,suma:integer; int s[50],A[50][50],gasit,n,i,
s:array[1..50]of integer; suma;
A:mat_ad;
void df(int nod)
gasit:boolean;
{
procedure df(nod:integer); int k;
var k:integer; s[nod]=1;
begin for(k=1;k<=n;k++)
s[nod]:=1; if (A[nod][k]==1)
for k:=1 to n do {
if A[nod,k]=1 then A[k][nod]=0;
begin if (s[k]==0) df(k);
A[k,nod]:=0; else gasit=1;
if s[k]=0 then df(k) }
else gasit:=true; }
end
end;
Manual de informatic pentru clasa a XI-a 265

begin main()
CitireN('Graf.txt',A,n); { CitireN("Graf.txt",A,n);
df(1); df(1);
suma:=0; suma=0;
for i:=1 to n do for (i=1;i<=n;i++)
suma:=suma+s[i]; suma+=s[i];
if suma<>n if (suma!=n)
then writeln('Nu este conex') cout<<"Nu este conex";
else else
if gasit if (gasit)
then writeln('Are ciclu') cout<<"Are ciclu";
else writeln('Arbore'); else cout<<"Arbore";
end. }

Teorema 9.1. Fie G un graf neorientat cu n noduri. G este arbore dac i


numai dac are n-1 muchii i nu conine cicluri.

Demonstraie
Fie G un arbore (graf neorientat, conex i fr cicluri). Trebuie s demonstrm
c are n-1 muchii. Vom demonstra prin inducie. Dac n=1, numrul muchiilor
este 0 (se verific, are n-1 muchii). Vom presupune proprietatea adevrat
pentru arbori cu n noduri (adic au n-1 muchii). Fie un arbore cu n+1 noduri.
Exist cel puin un nod terminal (nod care are o singur muchie incident).
Dac nu ar exista un astfel de nod, s considerm un lan care pornete
dintr-un nod oarecare. La fiecare pas, vom selecta o muchie. Pn la urm,
pentru c mulimea nodurilor este finit i pentru c nu exist nod terminal,
lanul va trece de dou ori printr-un acelai nod. Asta nseamn c arborele ar
conine cicluri (absurd, se contrazice definiia). Eliminm nodul terminal i
muchia care i este incident. Obinem un arbore cu n noduri. Conform ipotezei
fcute, acesta va avea n-1 muchii. nseamn c arborele cu n+1 noduri va
avea n muchii (n-1+1).

Fie G un graf cu n-1 muchii, care nu conine cicluri. Rmne de dovedit c G


este conex. Vom demonstra prin reducere la absurd. Presupunem c G
nu este conex. Fie G1, G2, , Gp componentele conexe ale grafului. Fiecare
dintre ele ndeplinete condiiile:
a) este conex (aa a fost aleas);

b) nu conine cicluri (pentru c G nu conine cicluri).

Rezult c fiecare dintre ele este arbore. Fie mi numrul muchiilor i ni


numrul nodurilor fiecrui arbore Gi. Avem mi=ni-1. Dar m1+m2+...+mp=n-1.
Rezult: n1-1+n2-1+...+np-1=n-1, deci n1+n2+...+np=n+p-1. Dar G are n
noduri. Rezult: n=n+p-1, deci p=1. n concluzie, exist o singur component
conex, care nu conine cicluri. Rezult c G este arbore.
266 Capitolul 9. Introducere n teoria grafurilor

9.1.11.2. Noiunea de arbore parial


S presupunem c ntr-un jude se impune repararea tuturor oselelor care
unesc diversele localiti. Pentru aceasta s-a obinut un credit extern care permite ca
toate oselele s fie recondiionate. Desigur, se dorete ca repararea oselelor s se
fac ct mai repede, dar, n acelai timp, trebuie ca n timpul reparaiilor s se poat
circula, astfel nct s nu rmn localiti inaccesibile pentru traficul rutier. Se cere
un numr minim de osele care s nu intre n reparaii n prima faz, astfel nct
condiia de mai sus s poat fi respectat.
Dac considerm graful n care nodurile sunt localitile, iar muchiile sunt
oselele, va trebui s pstrm un numr minim de muchii, astfel nct graful s
rmn conex. Care este acel numr minim de muchii care conserv conexitatea?
Evident, n-1. Cum graful rmne conex, n urma eliminrii anumitor muchii se obine,
aa cum rezult din teorema 9.1. (dat n paragraful anterior), un arbore.
Problema 9.7. Se d un graf conex,
G=(V,E). Se cere un graf parial al su, 1 1
care este arbore. O astfel de structur se
numete arbore parial. Evident, exist
posibilitatea ca dintr-un graf conex s se 2 3 2 3
poat obine mai muli arbori pariali.
Alturat, putei observa un graf
conex (stnga) i un arbore parial al su. 4 5 4 5

Pentru a rezolva problema, vom folosi, Figura 9.18.


din nou, o metod de parcurgere a unui
graf, mai precis parcurgerea DF. Vom afia numai muchiile selectate de algoritm,
adic cele care nu determin cicluri. Programul urmtor citete datele referitoare la un
graf conex i afieaz muchiile unui arbore parial.

Varianta Pascal Varianta C++


uses grafuri,wincrt; #include "grafuri.cpp"
var n:integer; int s[50],A[50][50],n;
s:array[1..50]of integer;
void df_r(int nod)
A:mat_ad;
{int k;
procedure df_r(nod:integer);
s[nod]=1;
var k:integer;
for (k=1;k<=n;k++)
begin
if (A[nod][k]==1 && s[k]==0)
s[nod]:=1;
{ cout<<nod<<" "<<k<<endl;
for k:=1 to n do
df_r(k);
if (A[nod,k]=1) and (s[k]=0)
}
then begin
}
writeln(nod,' ',k);
df_r(k); main()
end; { CitireN("Graf.txt",A,n);
end; df_r(1);
begin }
CitireN('Graf.txt',A,n);
df_r(1);
end.
Manual de informatic pentru clasa a XI-a 267

9.2. Grafuri orientate

9.2.1. Noiunea de graf orientat


Nu ntotdeauna grafurile neorientate pot exprima "relaiile" existente ntre
anumite elemente. Pentru a ne da seama de acest fapt, vom da cteva exemple.

1. Considerm un grup de n persoane, unde fiecare persoan deine un telefon


mobil. n agenda telefonului mobil al fiecrei persoane se gsesc numerele de telefon
ale altor k0 persoane din grup. S observm c dac persoana i are n agenda
telefonic numrul de telefon al persoanei j, nu este obligatoriu ca persoana j s
aib n agend numrul de telefon al persoanei i.
2. Un autor de carte dorete s prezinte anumite noiuni. Pentru a obine o lucrare
valoroas, acesta dorete ca orice noiune pe care o introduce s fie precedat n
lucrare de noiunile pe care le presupune deja prezentate.
3. Un program este alctuit din n instruciuni, atribuiri, apeluri de subprograme,
afiri, citiri sau instruciuni decizionale. Aici sunt excluse instruciunile repetitive.
Acestea rezult n urma instruciunilor decizionale i a ordinii de executare a
instruciunilor. Pentru a prezenta ordinea n care se execut instruciunile, unui
program i se poate asocia o schem logic.

Elementele ntre care exist anumite relaii se numesc i de aceast dat


noduri sau vrfuri. Dou vrfuri pot fi unite printr-un arc. Arcul (i,j) este
reprezentat ca o sgeat de la i la j i are semnificaia general c exist o relaie
de la i la j (atenie, nu i de la j la i).
Pentru exemplul 1, arcul (i,j) are
semnificaia c i are pe agenda telefonic 1
numrul lui j. Pentru exemplul 2, arcul (i,j)
are semnificaia c este necesar cunoaterea 6
noiunii i pentru a putea nelege noiunea j. 2
Pentru exemplul 3, arcul (i,j) are semnificaia
c dup executarea instruciunii i este posibil s 5
se efectueze instruciunea j. 3 4

Procednd aa cum am artat, obinem un Figura 9.19.


Exemplu de graf orientat
graf orientat aa cum este cel alturat.
Definiia 9.13. Se numete graf orientat perechea ordonat G=(V, A),
unde:
V={v1, v2, ..., vn} este o mulime finit de elemente numite vrfuri sau
noduri.
A este o mulime de arce. Vom nota un arc prin perechea ordonat
(vi,vj) cu ij.
268 Capitolul 9. Introducere n teoria grafurilor

De exemplu, pentru graful din figura 9.19., avem V={1,2,3,4,5,6} i


A={(1,6),(6,1),(6,5),(4,5),(2,1),(2,3)}.

Observaii

1. n cazul grafurilor orientate, dac exist arcul (vi,vj) nu nseamn c exist


automat i arcul (vj,vi). Din acest motiv, n definiie, s-a precizat c un arc este o
pereche ordonat de forma (vi,vj).
2. Din definiie, rezult c nu exist arce de la un nod la el nsui. Astfel, un arc a
fost definit (vi,vj) cu ij.
3. S observm c mulimea arcelor A, respect relaia AVV, unde VV este
produsul cartezian al mulimii V cu ea nsi.
4. Grafurile neorientate sunt cazuri particulare
de grafuri orientate, mai precis acele grafuri 1 1
orientate n care pentru orice arc (vi,vj) exist
arcul (vj,vi). Alturat, putei observa un graf 2 2
neorientat (stnga) reprezentat ca un graf
orientat (dreapta).
Figura 9.20.
5. Grafurile orientate se mai numesc i
digrafuri.

Definiia 9.14. n graful orientat G=(V,A), vrfurile distincte vi,vjG sunt


adiacente dac exist cel puin un arc care le unete.
Astfel, avem urmtoarele cazuri:
a) Exist numai arcul (vi, vj)A - n acest caz spunem c arcul (vi, vj)A
este incident spre exterior cu vi i spre interior cu vj.
b) Exist numai arcul (vj, vi)A - n acest caz spunem c arcul (vj, vi)A
este incident spre interior cu vi i spre exterior cu vj.
c) Exist arcul (vi, vj)A i arcul (vj,vi)A.

Definiia 9.15. ntr-un graf orientat, prin gradul exterior al unui vrf v vom
nelege numrul arcelor incidente spre exterior cu v. Gradul exterior al
unui nod va fi notat cu d+(v).

Definiia 9.16. ntr-un graf orientat, prin gradul interior al unui nod v vom
nelege numrul arcelor incidente spre interior cu v.
Gradul interior al unui nod va fi notat cu d-(v).

Pentru vrful i din figura alturat, avem: i

d+(i)=3 i
d-(i)=2. Figura 9.21.
Manual de informatic pentru clasa a XI-a 269

O relaie util: fie un graf orientat cu n vrfuri i m arce. Avem relaia:

d + (1) + d + (2) + ... + d + (n) = d (1) + d (2) + ... + d (n) = m .

Demonstraie. Relaia este adevrat, pentru c fiecare arc este incident spre
exterior cu un vrf i fiecare arc este incident spre interior cu un vrf.

Revenim la exemplele prezentate la nceputul acestui paragraf.


Pentru exemplul 1, dac gradul exterior al nodului i este k, nseamn c
persoana i are pe agenda telefonic numerele a k persoane din grup, iar dac gradul
interior al nodului i este l nseamn c numrul persoanei i este pe agenda
telefonic a l persoane.
Pentru exemplul 2, dac gradul exterior al nodului i este k, nseamn c
noiunea i este necesar pentru nelegerea a altor k noiuni, iar dac gradul interior
al nodului i este l, nseamn pentru a nelege noiunea i, este necesar ca alte l
noiuni s fie nelese.
Pentru exemplul 3, dac gradul exterior al nodului i este k, nseamn c dup
instruciunea i se pot efectua alte k instruciuni (i este instruciune decizional), iar
dac gradul interior al nodului i este l, nseamn c instruciunea i se poate executa
dup executarea uneia din cele l instruciuni.
n ( n 1)
2
O relaie util: avem 4 grafuri orientate cu n noduri.

Demonstraia se face prin inducie. Dac n=1, avem 1 graf orientat. Dac
n=2, cele dou noduri pot s nu fie sau s fie adiacente. n acest din urm caz,
putem avea arcul (v1,v2) sau arcul (v2,v1) sau putem avea ambele arce
(v1,v2) i (v2,v1). n total, avem 4 grafuri orientate, valoare care rezult i din
formul, dac nlocuim n cu 1.
Presupunem formula adevrat, adic dac sunt n vrfuri, avem
n ( n 1)
2
4
grafuri orientate. Trebuie s demonstrm c dac sunt n+1 vrfuri, avem
n ( n +1)
2
4
grafuri orientate. Adugm vrful n+1. Acest vrf poate fi adiacent cu fiecare
dintre celelalte n vrfuri n exact 3 moduri (vedei adiacena) sau poate s nu fie
adiacent. Atunci, numrul 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 .

Definiia 9.17. Un graf orientat este complet dac oricare dou vrfuri i
i j (ij) sunt adiacente.
270 Capitolul 9. Introducere n teoria grafurilor

n ( n 1)
2
O relaie util: avem 3 grafuri complete. Demonstraia se face prin inducie!
Exerciiu!

9.2.2. Memorarea grafurilor orientate

Memorarea grafurilor orientate se face la fel precum memorarea grafurilor


neorientate.

Pentru fiecare structur de date pe care o vom folosi, vom avea cte o
procedur (funcie) care citete datele respective. Toate aceste subprograme se
gsesc grupate n unitatea de program grafuri.pas (pentru Pascal) i n
grafuri.cpp (pentru C++).

Toate subprogramele pe care le utilizm citesc datele dintr-un fiier text, n care
pe prima linie vom scrie numrul de noduri (n), iar pe urmtoarele linii cte o muchie
(i, j) ca n exemplul urmtor, n care este prezentat un graf i liniile fiierului text
care este citit pentru el:

Fiierul 6
1 text: 1 2
6
1 3
1 5
2 2 3
5 3 4
3 4 5
4
Figura 9.22.

Trecem la prezentarea structurilor prin care putem memora datele referitoare


la un graf.

A. Memorarea grafului prin matricea de adiacen

An,n - o matrice ptratic, unde elementele ei, ai,j au semnificaia:

1, pentru (i, j) A
a i, j =
0, pentru (i, j) A
0 1 1 0 1 0
0 0 1 0 0 0
Pentru graful din figura 9.22., matricea de adiacen
este prezentat alturat.
0 0 0 1 0 0
Observaii
0 0 0 0 1 0
0 0 0 0 0 0
1. ntruct, din modul n care a fost definit graful, rezult c nu
exist arce de la un nod la el nsui, rezult c elementele de 0 0 0 0 0 0
Manual de informatic pentru clasa a XI-a 271

pe diagonala principal rein 0 (ai,i=0, oricare ar fi i{1,2,...,n}).


2. Matricea de adiacen nu este n mod obligatoriu simetric.
3. Suma elementelor de pe linia i, i {1,2,...,n} are ca rezultat gradul
exterior al nodului i, d+(i).
4. Tot aa, suma elementelor de pe coloana i, i {1,2,...,n} are ca rezultat
gradul interior al nodului i, d-(i).
5. Suma tuturor elementelor matricei de adiacen este, de fapt, suma gradelor
exterioare (sau interioare) adic suma arcelor, m.
6. Dac graful citit are un numr mic de muchii, atunci matricea de adiacen este o
form ineficient de memorare a lui, pentru c ea va reine o mulime de 0.

Subprogramele pe care le utilizm sunt:

Varianta Pascal Varianta C++


procedure CitireO void CitireO(char Nume_fis[20],
(Nume_Fis:string; int A[50][50], int& n)
var A:Mat_ad; var n:integer); {
var f:text; int i,j;
i,j:byte; fstream f(Nume_fis,ios::in);
begin f>>n;
Assign(f,Nume_Fis); while (f>>i>>j) A[i][j]=1;
Reset(f); Readln(f,n); f.close();
while(not eof(f)) do }
begin
readln(f,i,j);
A[i,j]:=1;
end;
close(f);
end;

B. Memorarea grafului prin liste de adiacen

Se face la fel ca n cazul grafurilor neorientate, diferena este dat de faptul c


arcul (i,j) este nregistrat o singur dat (nu ca n cazul grafurilor neorientate cnd
reineam (i,j) i (j,i)).

Mai jos, putei observa subprogramele care construiesc listele de adiacen:

Varianta Pascal Varianta C++


procedure Citire_LA_AstaticO void Citire_LA_AstaticO
(Nume_fis:string;var T:Lista; (char Nume_fis[20],
var Start:pornire; int T[2][50],
var n:integer); int Start[50], int& n)
var i,j,k:integer; {
f:text; int i,j,k=0;
272 Capitolul 9. Introducere n teoria grafurilor

begin fstream f(Nume_fis,ios::in);


k:=0; f>>n;
Assign(f,Nume_Fis); Reset(f); while (f>>i>>j)
Readln(f,n); { k++;
while(not eof(f)) do T[0][k]=j;
begin T[1][k]=Start[i];
readln(f,i,j); Start[i]=k;
k:=k+1; }
T[0,k]:=j; f.close();
T[1,k]:=Start[i]; }
Start[i]:=k;
end;
close(f);
end;

C. Memorarea grafului prin lista arcelor se face la fel ca n cazul grafurilor


neorientate.

9.2.3. Graf parial, subgraf

Definiia 9.18. Un graf parial al unui graf orientat dat G=(V,A) este un
graf G1=(V,A1), unde A1A.

Un graf parial al unui graf dat, este el nsui, sau se obine din G prin
suprimarea anumitor arce.

1 1

2 3 3
2
rezult
4 4
Figura 9.23.
Obinerea unui
graf parial G=(V,A) G1=(V,A1)

Referitor la exemplul 1 din 9.2.1, unele persoane i terg din agend


numerele altor persoane din grup. Aceasta nseamn c noul graf nu va mai avea
anumite arce, deci va deveni un graf parial al grafului iniial.

Exerciiu! Cte grafuri pariale are un graf cu m arce?

Definiia 9.19. Un subgraf al unui graf orientat G=(V,A) este un graf


G1=(V1,A1), unde V1V, A1A, iar arcele din A1 sunt toate arcele din A
care sunt incidente numai la vrfuri din mulimea V1.
Manual de informatic pentru clasa a XI-a 273

Un subgraf al unui graf G este graful G sau se obine din G prin suprimarea
anumitor vrfuri i a tuturor arcelor incidente cu acestea.

1 1

2 3 3
rezult

4 4
Figura 9.24.
Obinerea unui G1=(V1,A1)
subgraf G=(V,A)

Referitor la exemplele din paragraful 9.2.1, avem:


1. Pot exista persoane din grup care i pierd telefonul mobil. Astfel, numerele de
telefon ale respectivelor persoane aflate n agenda altora, pentru moment, nu mai
folosesc. De asemenea, ceilali din grup nu mai pstreaz numerele de telefon ale
acestora. n graful iniial se renun la vrfurile respective i la arcele adiacente lor.
Astfel, se obine un subgraf al grafului iniial.
2. Autorul renun la prezentarea anumitor noiuni. Din nou, se obine un subgraf al
grafului iniial.

Exerciiu! Cte subgrafuri are un graf cu n vrfuri?

9.2.4. Parcurgerea grafurilor. Drumuri. Circuite

Parcurgerea grafurilor orientate se face la fel precum parcurgerea grafurilor


neorientate. Aceasta nseamn c parcurgerea se poate face n dou feluri, n
adncime (DF) i n lime (BF). Subprogramele sunt aceleai.
Pentru graful orientat G=(V,A), un drum D=[v1,v2,...,vp] este o succesiune
de vrfuri (v1,v2)A, (v2,v3)A, ..., (vp-1,vp)A. Vrfurile v1 i vp se
numesc extremitile drumului. Numrul p-1 se numete lungimea
drumului. Acesta este dat de numrul arcelor ce unesc vrfurile drumului.
Determinarea existenei unui drum ntre dou vrfuri i determinarea unui drum
ntre dou vrfuri date, eventual a unui drum de lungime minim, se face la fel
ca n cazul grafurilor neorientate.
Un drum D=[v1,v2,...,vp] este elementar dac conine numai vrfuri
distincte.
Un circuit ntr-un graf orientat este un drum n care vrful iniial coincide cu
vrful final i care conine numai arce distincte. Printr-o simpl parcurgere DF
putem determina, ca i n cazul grafurilor neorientate, dac un graf conine sau
nu un circuit.
274 Capitolul 9. Introducere n teoria grafurilor

Anumite noiuni prezentate n cazul grafurilor neorientate se regsesc i n


cazul grafurilor orientate.
Pentru graful orientat G=(V,A), un lan D=[v1,v2,...,vp] este o succesiune
de vrfuri astfel nct ntre oricare vrfuri distincte din vi,vi+1 exist fie arcul
(vi,vi+1), fie arcul (vi+1,vi).
Un lan L=[v1,v2,...,vp] este elementar dac conine numai vrfuri distincte.

Relum exemplele din paragraful 9.2.1.


Pentru exemplul 1: persoana i are un mesaj de transmis persoanei j. Dac
exist posibilitatea ca ea s sune o persoan al crei numr l are n agend, aceasta
o alt persoan .a.m.d pn este sunat persoana j, nseamn c exist un drum
de la i la j. De asemenea, dac exist posibilitatea ca persoana i s ajung s
primeasc mesajul pe care l-a transmis, nseamn c exist un circuit de la i la j.
Pentru exemplul 2: dac graful are un circuit nseamn c exist cel puin o
noiune care nu poate fi explicat dect prin
intermediul altora care, la rndul lor, ar trebui 1
explicate exact prin noiunea care nu poate fi
explicat fr ele. Se mai ntmpl i aa. Un
exemplu de limbaj care nu poate fi predat n mod
clasic este Java. De exemplu, cel mai simplu 2
program utilizeaz din plin programarea orientat
pe obiecte, care se studiaz dup ce am nvat s 3 4
scriem programe simple.
Pentru exemplul 3: dac dup execuia 5
instruciunii i, pentru cel puin un set de date de
intrare, se ajunge s se execute instruciunea j,
nseamn c exist un lan de la i la j. De 6
asemenea, dac dup ce se execut instruciunea
i, se ajunge s se execute din nou instruciunea i,
atunci programul conine structuri repetitive, iar
7
graful asociat conine circuite.
Aa cum am definit pentru grafuri neorientate
matricea lanurilor, similar, se poate forma pentru Figura 9.25.
grafuri orientate matricea drumurilor:
1, dac exist drum de la i la j
D(i, j) =
0, n caz contrar
Matricea drumurilor nu este, n cazul general, simetric. Pentru a o determina,
pentru fiecare nod i, parcurgem graful i aflm toate nodurile pentru care exist drum
de la i la j. Pentru toate nodurile atinse (mai puin i), vom avea L(i,j)=1. Astfel,
se completeaz linia i.

Exerciiu! Scriei programul care, pornind de la un graf, afieaz matricea


drumurilor.
Manual de informatic pentru clasa a XI-a 275

9.2.5. Graf tare conex. Componente tare conexe

Relum exemplul 1 din paragraful 9.2.1. S presupunem c grupul celor n


persoane efectueaz o excursie la munte i, din pcate, s-au rtcit. ntrebarea este:
exist posibilitatea ca oricare membru al grupului, s propun un loc de ntnire i
s-i anune telefonic prietenii din agend, acetia s-i sune pe alii .a.m.d., astfel
nct toi membrii grupului s afle de acest loc?

Judecnd dup graful orientat asociat, ar trebui ca de la oricare membru al


grupului s existe un drum ctre oricare alt membru al grupului. Aceasta nseamn c
oricare ar fi nodurile i i j, exist un drum de la i la j i exist i un drum de j la i.
Un graf cu aceast proprietate se numete graf tare conex.

Definiia 9.20. Graful orientat G=(V,A) este tare conex dac x,yV,
drum de la x la y i drum de la y la x.

Definiia 9.21. Subgraful G1=(V1,A1) al grafului G=(V,A) este o


component tare conex dac:
1. x, y V1, drum de la x la y i drum de la y la x.
2. Nu exist un alt subgraf al lui G, G2=(V2,A2) cu V1V2 care
ndeplinete condiia 1.

Graful alturat are patru componente


5 tare conexe:
1 4 - subgraful care conine vrfurile:
1 2 3
- subgraful care conine vrfurile:
5 7
7
2 3
- subgraful care conine vrful 4
6 - subgraful care conine vrful 6

Figura 9.26.

Problema 9.8. Fie un graf orientat G=(V,A), memorat prin intermediul matricei de
adiacen. Se cere s se determine vrfurile fiecrei componente tare conex.

Rezolvare
a) Vom numi succesori ai vrfului i, toate nodurile j, pentru care exist drum de la
i la j, la care se adaug i. De exemplu, pentru graful dat., succesorii vrfului 1 sunt
vrfurile 1, 2, 3 i 4. Pentru a determina toi succesorii vrfului i, vom efectua o
parcurgere DF a grafului pornind de la acest vrf. Succesorii nodului i vor fi reinui n
vectorul suc.
276 Capitolul 9. Introducere n teoria grafurilor

b) Fie i un vrf al grafului. Vom numi predecesori ai vrfului i, toate vrfurile j,


pentru care exist drum de la j la i, la care se adaug i. Pentru graful dat,
predecesorii vrfului 1 sunt: 1, 2 i 3.

c) Dac un vrf este simultan succesor i predecesor al lui i, atunci el va face parte
din componenta tare conex a vrfului i. Mulimea nodurilor cu aceast proprietate va
fi o component tare conex a grafului. De ce? Pentru c ntre dou vrfuri k i l,
exist att drum de la k la l (de la k la i i de la i la l) ct i drum de la l la k (de la
l la i i de la i la k). Mulimea nodurilor cu aceast proprietate este maximal n
raport cu relaia de incluziune. Dac, prin absurd, ar mai exista un vrf cu aceast
proprietate, care nu aparine acestei mulimi, atunci ar trebui s existe drum de la i la
el, i de la el la i, caz n care acesta ar fi fost gsit prin procedeul dat.

d) De acum, putem redacta algoritmul. Variabila nrc, cu valoarea iniial 1, va reine


numrul curent al componentei tare conexe care urmeaz s fie identificat. Fiecare
component a vectorilor suc i pred reine, iniial, valoarea 0.

pentru fiecare vrf i


dac suc[i]=0
toi succesorii lui i, inclusiv i, vor reine nrc;
toi predecesorii lui i, inclusiv i, vor reine nrc;
toate componentele i, pentru care suc[i]pred[i] vor reine 0;
se incrementeaz nrc.

se afieaz vrfurile fiecrei componente conexe.

Mai jos, putei observa evoluia 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
Manual de informatic pentru clasa a XI-a 277

Programul este prezentat n continuare:

Varianta Pascal Varianta C++


uses grafuri; #include "grafuri.cpp"
var suc,pred:array[1..50] of int suc[50], pred[50],
byte; A[50][50], n,nrc,i,j;
A:mat_ad;
n,nrc,i,j:integer; void df_r1(int nod)
{ int k;
procedure df_r1(nod:byte);
suc[nod]=nrc;
var k:byte;
for (k=1;k<=n;k++)
begin
if (A[nod][k]==1 &&
suc[nod]:=nrc;
suc[k]==0)
for k:=1 to n do
df_r1(k);
if (A[nod,k]=1) and (suc[k]=0)
}
then df_r1(k);
end;
void df_r2(int nod)
procedure df_r2(nod:byte); { int k;
var k:byte; pred[nod]=nrc;
begin for (k=1;k<=n;k++)
pred[nod]:=nrc; if (A[k][nod]==1 &&
for k:=1 to n do pred[k]==0)
if (A[k,nod]=1) and (pred[k]=0) df_r2(k);
then df_r2(k); }
end;
main()
begin {
CitireO('Graf.txt',A,n); CitireO("Graf.txt",A,n);
nrc:=1; nrc=1;
for i:=1 to n do for (i=1;i<=n;i++)
if suc[i]=0 if (suc[i]==0)
then { suc[i]=nrc;
begin df_r1(i);
suc[i]:=nrc; df_r2(i);
df_r1(i); for (j=1;j<=n;j++)
df_r2(i); if(suc[j]!=pred[j])
for j:=1 to n do suc[j]=pred[j]=0;
if suc[j]<>pred[j] then nrc++;
begin }
suc[j]:=0; for (i=1;i<nrc;i++)
pred[j]:=0; {
end; cout<<"Componenta"<<i<<endl;
nrc:=nrc+1 for (j=1;j<=n;j++)
end; if (suc[j]==i)
for i:=1 to nrc-1 do cout<<j<<" ";
begin cout<<endl;
writeln ('Componenta ',i); }
for j:=1 to n do }
if suc[j]=i
then write (j,' ');
writeln;
end;
end.
278 Capitolul 9. Introducere n teoria grafurilor

i n cazul grafurilor orientate se pstreaz noiunea de graf conex i noiunea


de component conex.

Definiia 9.22. Graful orientat G=(V,A) este conex dac x,yV,


lan de la x la y.

Definiia 9.23. Subgraful G1=(V1,A1) al grafului G=(V,A) este o


component conex dac:
1. x, y V1, lan de la x la y.
2. Nu exist un alt subgraf al lui G, G2=(V2,A2) cu V1V2 care ndepli-
nete condiia 1.

Probleme propuse

1. O cunotin mi-a zis: la mine n birou suntem 5 persoane. Fiecare dintre noi
colaboreaz cu exact 3 persoane. A zis adevrul?
2. Demonstrai c ntr-un graf neorientat numrul nodurilor de grad impar este par.
3. Fiind date n persoane i m relaii de prietenie ntre ele de forma: persoana i este
prieten cu persoana j, se cere s se stabileasc corespondenele ntre afirmaiile
din stnga i cele din dreapta.

1. n grupul celor n persoane, fiecare a. Graful asociat are noduri


persoan are cel puin un prieten. terminale.
2. Fiecare persoan este prieten cu b. Graful asociat conine un subgraf
oricare alta din grup. complet cu k noduri.
3. Exist persoane care nu au dect c. Graful asociat este complet.
un singur prieten.
d. Graful asociat nu are noduri
4. Exist k persoane din grup astfel izolate.
nct oricare dintre ele este prieten
cu toate celelalte k-1.

4. Se dau n drepte i m relaii de forma: di||dj - dreapta i este paralel cu dreapta


j. Se tie c graful asociat este conex. Care dintre afirmaiile de mai jos este fals?
a) Pentru orice dreapt i, exist o dreapt j care este paralel cu ea.
b) Toate dreptele sunt paralele ntre ele.
c) Mulimea punctelor de intersecie ale acestor drepte este nevid.

5. ntre n firme exist relaii de colaborare. O relaie de colaborare este dat ca o


pereche de forma ij i are semnificaia c firma i colaboreaz cu firma j.
Stabilii corespondena ntre afirmaiile din stnga i cele din dreapta.
Manual de informatic pentru clasa a XI-a 279

1. Orice firm colaboreaz cu toate a. Exist un lan de la i la j.


celelalte.
b. Graful asociat are un nod de
2. Anumite firme i ntrerup relaia grad n-1.
de colaborare.
c. Graful asociat se transform
3. Anumite firme dau faliment i nu ntr-un graf parial al su.
mai funcioneaz.
d. Graful asociat este complet.
4. O firm colaboreaz cu toate
e. Graful asociat se transform
celelalte.
ntr-un subgraf al su.
5. Cu ajutorul unor firme interme-
diare dintre cel n firme, firma i poate
face afaceri cu firma j.

6. La un trand exist 6 bazine. Unele dintre ele sunt unite printr-o eav prin care
poate circula apa. Astfel, bazinul 1 este unit cu bazinul 2, bazinul 4 cu bazinul 5 i
bazinul 2 cu bazinul 3.
6.1. tiind c fiecare bazin poate fi dotat cu un robinet, se cere numrul minim de
robinete care asigur umplerea tuturor bazinelor.
6.2. Care este numrul minim de evi prin care se pot uni dou bazine, astfel nct s
se poat umple toate bazinele cu un singur robinet. Dai exemple de bazine unite care
asigur cerina problemei.
7. Fiind dat un grup de n persoane, n care dintre situaiile de mai jos se poate
folosi pentru modelare un graf neorientat?
a) Unele persoane din grup cunosc alte persoane din grup;
b) Unele persoane din grup simpatizeaz alte persoane din grup;
c) n cazul n care toate persoanele lucreaz ntr-o firm, unele persoane din grup
sunt efi pentru alte persoane din grup;
d) Unele persoane din grup sunt prietene cu 1
2
alte persoane din grup.

Exerciiile 8 i 9 se refer la graful din 3


5
figura 9.27. 4
6
8. Care este matricea de adiacen a 7
grafului? Figura 9.27.

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)
280 Capitolul 9. Introducere n teoria grafurilor

9. Care este valoarea de adevr a afirmaiilor de mai jos (A, adevrat, iar F, fals):

9.1. Graful este alctuit din 2 componente conexe.


9.2. [1,7,4] este un lan.
1
9.3. [2,1,4,3,1] este un ciclu. 2

9.4. Nodul 2 este izolat.


3
9.5. Graful din problem are ca graf parial graful 4
din figura 9.28. 6
9.6. Graful din problem are ca subgraf graful din 7
figura 9.28. Figura 9.28.

9.7. Nodul 1 are gradul 2.


9.8. Nodul 4 are gradul 3. 1 2 3 4
2 1
9.9. Graful din problem conine un
ciclu de lungime 3. 3 1 4
9.10. Graful alturat, reprezentat cu 4 1 3
ajutorul listelor de adiacen, este
Figura 9.29.
subgraf al grafului din problem.

10. Care dintre matricele de mai jos ar putea fi matricea de


adiacen a grafului alturat?

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

a) b) c) d)

11. 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)
Manual de informatic pentru clasa a XI-a 281

12. Cte muchii are graful neorientat reprezentat de matricea de 0 1 0 1 1



adiacen alturat? 1 0 0 1 1
0 0 0 1 1
13. Care este numrul 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 dintre matricele de mai jos este matricea de
adiacen a unui subgraf al grafului din figura 9.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.31.
a) b) c) d)

15. Care este numrul minim i care este numrul maxim de componente conexe
pe care le poate avea un graf neorientat cu 8 noduri i 6 muchii?
16. Care este numrul de cifre 0 pe care l reine matricea de adiacen a unui graf
neorientat cu n noduri i m muchii?
17. Care este numrul minim i numrul maxim de noduri izolate pe care l poate
avea un graf neorientat cu 10 noduri i 10 muchii?
18. Care este numrul de grafuri neorientate cu 5 noduri?
19. Precizai care dintre afirmaiile urm-
toare sunt adevrate i care sunt false. 1 3
Toate afirmaiile se refer la graful din 2
figura 9.32.
19.1. "6 1 2 3 5 4 7" reprezint o 5
parcurgere n adncime a grafului. 4
6
19.2. "3 2 5 1 4 6 7" reprezint o 7
parcurgere n lime a grafului. Figura 9.32.

19.3. Exist dou noduri din graf pentru care nu exist un lan care le unete.
19.4. "6 1 2 3 5 4 7" este un lan n graful dat.
19.5. "3 2 5 1 4 6 7" este un lan n graful dat.
19.6. Numrul minim de muchii care trebuie eliminate pentru a obine un arbore
parial este 2.
19.7. Numrul maxim de muchii care pot fi eliminate astfel nct graful s rmn
conex este 3.
19.8. Numrul minim de muchii care pot fi eliminate pentru ca graful s nu conin
cicluri este 3.
19.9. Un arbore parial al grafului dat are 7 muchii.
282 Capitolul 9. Introducere n teoria grafurilor

20. Precizai dac afirmaiile de mai jos sunt adevrate sau false.
20.1. Cu ajutorul parcurgerii n adncime se poate determina dac un graf neorientat
are cel puin un ciclu.
20.2. Orice graf neorientat are un graf parial care este arbore.
20.3. Cu ajutorul parcurgerii n lime se poate determina dac un graf este conex.
20.4. Orice graf neorientat cu 10 noduri i 9 muchii este un arbore.

20.5. Un arbore conine exact o component conex.


20.6. Un graf este alctuit din dou componente conexe. Pentru ca graful s devin
conex, este suficient s eliminm o anumit muchie.
20.7. Un graf este alctuit din dou componente conexe. Fiecare dintre ele
alctuiete un graf parial al grafului dat.
20.8. Un graf este alctuit din dou componente conexe. Fiecare dintre ele
alctuiete un subgraf al grafului dat.
20.9. Cu ajutorul parcurgerii n lime se poate determina, dac exist, un lan ntre
dou noduri ale grafului.
20.10. Cu ajutorul parcurgerii n adncime se poate determina, dac exist, un lan
ntre dou noduri ale grafului.
20.11. Exist graf complet cu n>2 noduri care nu conine cicluri.

20.12. Orice graf complet este alctuit dintr-o singur component conex.
20.13. Din orice graf complet, prin eliminarea anumitor muchii se poate obine un
arbore.
20.14. Orice graf complet are un subgraf care este arbore.
21. Se d un graf neorientat memorat sub forma matricei de adiacen. S se afieze
toate nodurile care au gradul maxim.
22. S se scrie un subprogram care transform matricea de adiacen a unui graf n
liste de adiacene.
23. S se scrie scrie un subprogram care transform listele de adiacen n matrice
de adiacen.
24. Se d un graf neorientat i o succesiune de noduri ale lui. Se cere s se scrie un
subprogram care decide dac succesiunea dat este sau nu un lan.
25. Se d un graf neorientat memorat prin liste de adiacen. S se scrie un
subprogram care decide dac graful dat conine sau nu cicluri.
26. Se d un graf memorat prin matricea de adiacen i un nod al su, v. Se cere
s se parcurg graful n lime, pornind de la nodul v. Algoritmul va utiliza coada
creat ca list liniar simplu nlnuit implementat static.
Manual de informatic pentru clasa a XI-a 283

27. Se d un graf memorat sub forma matricei de adiacen. Se cere s se


afieze matricea drumurilor. Algoritmul va utiliza parcurgerea n adncime.
28. Fiind dat matricea drumurilor unui graf, se cere s se scrie programul care
afieaz componentele conexe.

29. Partiia determinat de o relaie de echivalen


Se consider o mulime A. O relaie oarecare R ntre elementele acestei mulimi,
este o relaie de echivalen dac respect urmtoarele trei condiii:
oricare ar fi xA, x R x (x este echivalent cu x), proprietate numit reflexivitate;
oricare ar fi x,yA, din x R y, rezult y R x, proprietate numit simetrie;
oricare ar fi x,y,z mulimii A, din x R y i y R z, rezult x R z, proprietate
numit tranzitivitate.

Se citete o mulime de numere ntre 0 i 255 prin citirea a n perechi (x,y)


de numere de acest tip. Printr-o astfel de pereche se nelege c x este echivalent
cu y. Se cere s se determine partiia generat de relaia de echivalen
considerat pe mulime.

Exemplu: citim (1 2), (4 5), (2 3), (6 7), (7 1).


Se obine partiia: {1,2,3,6,7} {4,5} a mulimii {1,2,...,7}.

30. Se dau n puncte distincte n plan: Pi(xi,yi) cu 0xi,yi200, pentru orice


i=1, 2, ..., n. Considerm c fiecare punct este unit cu cel mai apropiat punct
diferit de el (dac exist mai multe puncte la distan minim, se unete cu fiecare
dintre acestea). Numim regiune o mulime maximal de puncte cu proprietatea c
oricare dintre ele sunt unite printr-un lan. S se determine numrul de regiuni i s
se vizualizeze regiunile (punctele i legturile dintre ele).
31. Se d matricea de adiacen a unui graf neorientat. Se cere s se afieze toate
ciclurile de lungime k. Un ciclu se va afia o singur dat.
32. Se d matricea de adiacen a unui graf neorientat. Se cere s se afieze
toate ciclurile de lungime 4. Se cere un algoritm eficient.
33. ntr-un grup de n persoane, anumite persoane, mprumut altor persoane diverse
sume de bani! Modelnd problema cu ajutorul grafurilor orientate, se cere s stabilii
corespondena dintre afirmaiile din stnga i cele din dreapta.
Observaie: dac persoana i mprumut cu bani persoana j, atunci exist un arc de
la i la j.

1. Persoana i nu a mprumutat cu a) gradul interior al nodului i este 0.


bani alte persoane din grup.
b) gradul exterior al nodului i este 0.
2. Persoana i nu a mprumutat bani
de la alte persoane din grup.
284 Capitolul 9. Introducere n teoria grafurilor

34. Se dau n mulimi de numere naturale: A1, A2...An. Acestor mulimi li se asociaz
un graf orientat astfel: dac mulimea Ai este inclus n mulimea Aj, n graful asociat
vom avea arcul (Ai,Aj). Nu vom considera cazul de incluziune a unei mulimi n ea
nsi. Stabilii corespondena dintre operaiile din stnga i cele din dreapta.

1. AiAjAk. a) De la A1 la An exist un lan de


lungime n-1.
2. AiAj; AkAj.
b) De la Ak la Ai exist un lan de
3. A1A2... An-1An.
lungime 2.
4. A1=A2=... An-1=An.
c) Graful este tare conex.
d) De la Ai la Ak exist un drum de
lungime 2.

35. Refacei problema anterioar n cazul n care se consider n


numere naturale i relaia de divizibilitate. De asemenea, ncercai 1
s adugai noi situaii n care se cere corespondena.
36. Cte componente conexe i cte componente tare conexe
conine graful din figura 9.33? 3
a) 1 1; b) 1 3; c) 1 0; d) 0 0.
2

Figura 9.33.

37. n graful din figura 9.34, care este lungimea celui mai
lung lan elementar i care este lungimea celui mai lung 1
drum elementar?

a) 3 2; b) 2 2; c) 2 3; d) 1 2. 4
3

2
Figura 9.34.

Problemele de la 38 la 41 se refer la graful alturat:


5
38. Cte circuite conine? 1
a) 3; b) 2; c) 1; d) 4.
39. Cte componente tare conexe conine? 4 3
a) 4; b) 3; c) 2; d) 1.
40. Care este nodul cu grad interior maxim i care 2
este nodul cu grad exterior minim?
a) 1 1; b) 1 2; c) 2 2; d) 2 5. Figura 9.35.
Manual de informatic pentru clasa a XI-a 285

41. Care este numrul minim de arce care trebuie adugate pentru ca graful s
devin tare conex?
a) 1; b) 2; c) 3; d) 4.
42. Se d un graf orientat. Se cere s se afieze, pentru fiecare vrf n parte, gradul
interior i gradul exterior. 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.
43. Fiind date un graf orientat i o succesiune de vrfuri s se decid dac
succesiunea este drum, iar n caz afirmativ se va preciza dac este sau nu un drum
elementar. 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.
44. La fel ca mai sus, dar se cere s se determine dac succesiunea respectiv este
sau nu lan (lan elementar).
45. Se d un graf prin lista muchiilor. Programul va decide dac graful este
neorientat.
46. Se dau listele de adiacene ale unui graf orientat. Programul va afia matricea de
adiacen.
47. Se d matricea de adiacen a unui graf orientat. Programul va afia listele de
adiacene ale acestuia.
48. Se d matricea de adiacen a unui graf orientat. Se cere s se listeze toate
circuitele de lungime 3.
49. Se d matricea de adiacen a unui graf orientat. Se cere s se listeze toate
ciclurile de lungime 3.
*
50 . Algoritmul lui Lee. Se d un labirint sub forma unei matrice ptratice, L.
L(i,j) =-1 dac prin camera respectiv nu se poate trece i 0 n caz contrar. S
se afieze distanele minime de la camera de coordonate (l,c) la toate camerele
accesibile din camera iniial.
*
51 . La fel ca la problema anterioar. Se cere drumul care trece printr-un numr
minim de camere ntre o camer iniial i una final.
52 . Pe o tabl de ah de dimensiuni nxn se poate deplasa un nebun conform
*

regulilor obinuite ale ahului. n plus, pe tabl se pot afla obstacole la diferite
coordonate; nebunul nu poate trece peste aceste obstacole. S se indice dac
exist vreun drum ntre dou puncte A(X1,Y1) i B(X2,Y2) de pe tabl i, n caz
afirmativ, s se tipreasc numrul minim de mutri necesare. Se citesc: N, X1,
Y1, X2, Y2, apoi perechile de coordonate ale obstacolelor.
*
53 . Sortare n limita posibilitilor. Se consider c ntr-un vector V cu n
componente se pot inversa numai coninuturile anumitor componente dintre cele n. O
pereche de componente de indice i i j ale cror coninuturi se pot inversa este dat
de perechea i i j. Fiind date m astfel de perechi i tiind c vectorul conine
numerele 1, 2, , n ntr-o ordine oarecare, se cere ca vectorul s conin numerele
1, 2, , n sortate. Pentru sortare se inverseaz numai coninuturile componentelor
care se pot inversa (care sunt perechi dintre cele m). Dac sortarea este posibil, se
286 Capitolul 9. Introducere n teoria grafurilor

vor afia indicii componentelor care se inverseaz, iar dac sortarea nu este posibil,
se afieaz Nu. Datele de intrare se gsesc n fiierul text date.in astfel:
Linia 1 n
Linia 2 1, ..., n ntr-o ordine oarecare;
Linia 3 m
urmtoarele m linii conin fiecare cte o pereche de indici i, j.

Exemplu:
3 Programul va afia:
3 1 2 1 2
2 2 3
2 3
1 2
*
54 . Lucrare n echip. Se dorete scrierea unei aplicaii de informare a cltorilor
privind transportul n comun ntr-un ora. Se cunosc cele n staii de autobuz din oraul
respectiv. De asemenea, se tie traseul a k linii de autobuz (staiile prin care acestea
trec). Se cere ca aplicaia s furnizeze modul n care o persoan se poate deplasa cu
autobuzul ntre dou staii date, n ipotezele:
a) n numr minim de staii.
b) Prin utilizarea unui numr minim de linii de autobuz.

! Este sarcina dvs. s organizai intrrile i ieirile de date.

Rspunsuri

1. Nu. Ar rezulta un graf cu 5 noduri. Cum fiecare persoan colaboreaz cu exact 3


persoane, nseamn c fiecare nod are gradul 3. De aici, rezult c suma gradelor
este 15. Aceasta nseamn c 2m=15, deci m nu ar fi numr ntreg.
2. Dac ar fi impar, suma gradelor impare ar fi un numr impar. Cum suma gradelor
pare este un numr par, rezult c suma tuturor gradelor este un numr impar. Ori,
acesta trebuie s fie un numr par, pentru c ea este egal cu dublul numrului de
muchii. Absurd.
3. 1-d, 2-c, 3-a, 4-b.
4. c) Faptul c graful este conex, nseamn c ntre oricare dou noduri exist un
lan care le are ca extremiti. Dar din di||dj i dj||dk di||dk rezult c toate
dreptele sunt paralele ntre ele.
5. 1-d, 2-e, 3-c, 4-b, 5-a.
6. 6.1. Graful conine 3 componente conexe. Pentru fiecare component conex
este necesar un robinet. 6.2. Pentru a folosi un singur robinet, este necesar ca graful
s fie conex. Cum are 3 componente conexe, sunt suficiente 2 evi.
Manual de informatic pentru clasa a XI-a 287

7. Pentru a putea modela anumite relaii cu ajutorul unui graf neorientat trebuie ca
relaia existent ntre i i j s fie reciproc, pentru c muchia (i,j) presupune c i
este n relaie cu j i j este n relaie cu i. Dac i cunoate pe j, nu este obligatoriu
ca j s cunoasc pe i, dac i simpatizeaz pe j, nu este obligatoriu ca j s
simpatizeze pe i, dac i este eful lui j, j nu poate fi eful lui i. n concluzie,
rspunsul este d) pentru c relaia de prietenie este reciproc.
8. b).
9. 9.1. A; 9.2. F; 9.3. F; 9.4. F; 9.5. F; 9.6. A; 9.7. F; 9.8. A; 9.9. A;
9.10. A.
10. b) Dac matricea este de adiacen, atunci v putei orienta dup gradele
vrfurilor. Evident, graful reprezentat de matricea de adiacen trebuie s aib
vrfurile cu aceleai grade cu vrfurile grafului reprezentat n desen.
11. d) Desigur, putei desena graful, dar, mai uor, eliminai variantele n care
avei 1 pe doagonala principal, sau acelea n care matricea nu este simetric.
12. 8. Dac matricea este dat corect, nu este nevoie s desenai graful pentru
ca, apoi, s-i numrai muchiile. Se tie c suma gradelor tuturor nodurilor este
egal cu dublul numrului de muchii. Prin urmare, este suficient s nsumai
elementele reinute de matrice si s mprii rezultatul la 2.
13. 2.
14. a) Dac matricea are 4 linii i 4 coloane, este clar c subgraful ar rezulta prin
eliminarea unui singur nod i a muchiilor incidente lui. Dac eliminm nodul din
centru, se obin 4 noduri izolate. Oricare alt nod am elimina, rmn 1 nod cu
gradul 3 i 3 noduri cu gradul 1.
15. 2 componente conexe i 5 componente conexe.
16. n2-2m. Matricea de adiacen are n2 elemente. Am vzut faptul c suma
tuturor cifrelor de 1 (adic a gradelor vrfurilor) este 2m (unde m este numrul de
muchii).
17. 0 i 5.
18. 210.
19. 19.1. A, 19.2. A, 19.3. F, 19.4. A, 19.5. F, 19.6. F, 19.7. A,
19.8. A, 19.9. F.
20. 20.1. A, 20.2. F, 20.3. A, 20.4. F, 20.5. A, 20.6. F, 20.7. F,
20.8. A, 20.9. A, 20.10. A, 20.11. F, 20.12. A, 20.13. A, 20.14. A
(lsm un singur nod).
29., 30. Descompunerea unui graf n componente conexe.
31. Backtracking. O soluie are lungimea k.
32. Fie i<j<k<l, 4 noduri care formeaz un ciclu. Avem:
A(i,j)=1 A(i,l)=1
A(k,j)=1 A (k,l)=1
288 Capitolul 9. Introducere n teoria grafurilor

Astfel, n matricea de adiacen se formeaz un dreptunghi. Trebuie


identificate toate dreptunghiurile astfel formate.
33. 1-b, 2-a.
34. 1-d, 2-b, 3-a, 4-c.
36. b).
37. a).
38. c).
39. b).
40. c).
41. a).

50. Se poate lucra direct pe matricea L. Idee: pentru camera iniial vom avea
L(i,j)=1. Apoi, pentru toate camerele accesibile vecine cu ea vom avea
L(i,j)=2, apoi pentru toate camerele accesibile cu ele vom avea L(i,j)=3...
.a.m.d. Pentru a obine aceast marcare vom parcurge n lime graful asociat.
Putem evita memorarea acestuia. Vom introduce n coad coordonatele camerei
iniiale. Vom ncrca n coad coordonatele tuturor camerelor vecine pentru care
L(i,j)=1. Pentru fiecare astfel de camer, pentru care, iniial, L(i,j)=0, vom avea
L(i,j)=2. Se trece apoi la urmtorul element din coad cu care se procedeaz
asemntor. Se tie c, prin parcurgerea n lime, se viziteaz nodurile n
ordinea lungimii drumului, de la ele la nodul iniial. Deducem, astfel, c marcarea
este corect. Algoritmul se termin cnd coada este vid. n final, se afieaz
matricea L.

51. Se procedeaz ca la problema anterioar. Imediat ce a fost vizitat camera final,


se reface drumul de la camera iniial ctre ea. Astfel, se pleac de la camera final,
marcat cu k. Printre vecinele acestei camere se caut una care este marcat cu k-1.
Printre camerele vecine cu ea se caut una care este marcat cu k-2. Se procedeaz
n mod asemntor pn se ajunge la camera iniial marcat cu 1. Drumul se
afieaz n ordinea invers gsirii lui, de la camera final la cea iniial.

52. Algoritmul lui Lee.

53. Asociem problemei un graf neorientat. Nodurile sunt indicii elementelor vectorului,
de la 1 la n. Cnd coninuturile a dou elemente se pot inversa, nodurile
corespunztoare sunt unite printr-o muchie. Dac nodurile i1, i2, ..., ik sunt unite
printr-un drum: atunci interschimbrile (i1, i2), (i2, i3), ..., (ik-1, ik),
(ik-1, ik-2), ..., (i2, i1) inverseaz coninuturile elementelor de indice i1 i ik,
lsnd coninuturile celorlalte elemente de indici i2, ..., ik-1 nemodificate. O
parcurgere n lime determin distana minim ntre dou noduri.
289

Anexa 1
Memento

A.1. Limbajul Pascal

A.1.1. Tipuri standard

A) Tipuri ntregi

Ocup
Nume tip Semnificaie Valori admise
(bii)
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 numr natural scurt 8 de la 0 la 255
word cuvnt 16 de la 0 la 65535

B) Tipul caracter - un caracter se noteaz ntre apostrofuri.


Exemple: 'a','A','1' (caracterul '1' nu trebuie confundat cu numrul 1).

C) Tipuri reale

Ocup
Nume tip Semnificaie Valori admise (n modul)
(bii)
virgul mobil, [-1,71038, -2,910-29]
real 48
simpl precizie [2,910-29, 1,71038]

virgul mobil, [-3,41038, -1,510-45]


single 32
simpl precizie [1.510-45, 3,41038]

double virgul mobil, [-1,710308, -510-324]


64
dubl precizie [510-324, 1,710308]

virgul mobil, [-1,1104932, -3,410-4932]


extended 80
format lung [3,410-4932, 1,1104932]
comp virgul mobil 64 [-9,21018, 9,21018]

D) Tipul logic boolean poate reine doar dou valori true i false.
290 Anexa 1. Memento

A.1.2. Constante

A) Constante ntregi. Sunt alctuite dintro submulime a numerelor ntregi care


pot fi reprezentate n memoria calculatorului. Se pot reprezenta numere ntregi
cuprinse n intervalul: [2.147.483.648, 2.147.483.647].

B) Constante reale. Sunt alctuite dintro submulime a numerelor reale (mai


precis, a numerelor raionale) care pot fi reprezentate n calculator. Modulul
numerelor reale se gsete n intervalul [3.410 ,1.110 ]. n locul virgulei se
-4352 4932

folosete punctul.
Exemple: 2.34, 45.26, 512E+23, 45.1E3.

Ultimele dou numere folosesc o scriere nentlnit n matematic. Ele reprezint


numerele 51210 i 45.110 .
23 -3

C) Constante ir de caractere. Dup cum reiese i din denumire, cu ajutorul lor


se reprezint iruri de caractere. Caracterele din ir pot fi specificate enumerndu-le
ntre apostrofuri.
Exemplu: 'abc'.

D) Constantele simbolice. Acestea sunt constante care au n program un anumit


nume. Definirea constantelor simbolice precede definirea tipurilor utilizator i se
realizeaz astfel:
const identificator = expresie;
...
identificator = expresie;

Expresiile care intervin n definirea constantelor trebuie s poat fi evaluate


la momentul compilrii programului.
Exemplu:
const NrMaxValori = 30;
Dim = NrMaxValori*2-1;
Mesaj = 'Nu exista solutie'#10#13;
PI = 3.14;

A.1.3. Operatori
A.1.3.1. Prioritatea operatorilor

Iat cele 4 grupe de prioritate.

Grupa 1 (prioritate maxim):


NOT, + (operator unar), - (operator unar).
Grupa 2 (operatorii din grupa doi se mai numesc i operatori multiplicativi):
AND, *, /, DIV, MOD.
Manual de informatic pentru clasa a XI-a 291

Grupa 3 (operatorii din aceast grup se numesc i operatori aditivi):


OR, XOR, +, -.

Grupa 4 (operatori cu cea mai mic prioritate - cuprinde i operatorii relaionali):


<, <=, >, >=, =,<>.

A.1.3.2. Operatori aritmetici

Operatorii aritmetici sunt de dou feluri:

operatori unari (+ i -);


operatori binari ( +, -, *, /, DIV, MOD).

Operatorul +. Are semnificaia de adunare. Operanzii si sunt de tip ntreg


sau real. Se poate ca unul s fie de tip ntreg i cellalt de tip real. Dac cel
puin unul din operanzi este, real rezultatul este de tip real, altfel este de
tip ntreg.

Operatorul + apare i ca operator unar. De asemenea, apare ca sum de


iruri (caz pe care nu-l discutm acum).

Operatorul -. Are semnificaia de scdere. Operanzii sunt de tip ntreg sau


real. Dac cel puin un operand este real, rezultatul este de tip real, altfel
este de tip ntreg.

Operatorul *. Are semnificaia de nmulire. Operanzii sunt de tip ntreg sau


real. Dac cel puin unul din operanzi este de tip real, rezultatul este de tip
real, altfel rezultatul este de tip ntreg.

Operatorul /. Are semnificaia de mprire. Operanzii pot fi de tip ntreg sau


real dar, ntotdeauna rezultatul este de tip real.

Operatorul DIV. Are semnificaia de mprire ntreag. Operanzii sunt n


mod obligatoriu de tip ntreg. Este obligatoriu ca fiecare operand s fie
separat cu cel puin un spaiu de operator. Rezultatul este de tip ntreg.

Operatorul DIV furnizeaz rezultat corect numai dac ambele valori sunt
numere ntregi pozitive.

Rezultatul pentru operatorul DIV se obine astfel:

se face mprirea ntreag a celor dou numere care sunt considerate


pozitive (de exemplu, 13 div 4=3);

semnul ctului se stabilete dup regula semnelor (+ cu + rezultat +, +


cu - rezultat -, - cu - rezultat + i - cu + rezultat -).
292 Anexa 1. Memento

Operatorul MOD. Are semnificaia de rest al mpririi pentru numere ntregi.


Operanzii sunt n mod obligatoriu de tip ntreg, iar rezultatul va fi ntotdeauna
de tip ntreg. Operanzii trebuie separai de operator prin cel puin un spaiu.

Ca i n cazul operatorului DIV, rezultatul este corect numai dac ambii


operanzi sunt pozitivi. Fie a MOD b. Rezultatul se obine astfel:
a - a DIV b (R=D-C).

A.1.3.3. Operatori relaionali

Operatorul < (mai mic). Fiind dai doi operanzi a i b, operatorul < arat
dac este adevrat sau nu relaia a<b. Dac relaia este adevrat
rezultatul va fi TRUE, altfel rezultatul va fi FALSE.

Operatorul <= (mai mic sau egal). Fiind dai doi operanzi a i b, operatorul
<= arat dac este adevrat sau nu relaia a<=b. Dac relaia este
adevrat rezultatul va fi TRUE, altfel rezultatul va fi FALSE.

Operatorul > (mai mare). Fiind dai doi operanzi a i b, operatorul > arat
dac este adevrat sau nu relaia a>b. Dac relaia este adevrat
rezultatul va fi TRUE, altfel rezultatul va fi FALSE.

Operatorul >= (mai mare sau egal). Fiind dai doi operanzi a i b, operatorul
>= arat dac este adevrat sau nu relaia a>=b. Dac relaia este
adevrat rezultatul va fi TRUE, altfel rezultatul va fi FALSE.

Operatorul = (egal). Fiind dai doi operanzi a i b, operatorul = arat dac


este adevrat sau nu relaia a=b. Dac relaia este adevrat rezultatul va
fi TRUE, altfel rezultatul va fi FALSE.

A.1.3.4. Operatori logici

Operatorul NOT (negare). Modul de aciune se poate observa mai jos:


NOT (TRUE)=FALSE;
NOT (FALSE)=TRUE.

Operatorul AND (i). Regula de obinere a rezultatului este foarte simpl:


rezultatul este TRUE numai dac ambii operanzi au valoarea TRUE (altfel
rezultatul este FALSE).

Operatorul OR (sau). Regula este simpl: dac unul dintre operanzi este
TRUE, rezultatul este TRUE, altfel rezultatul este FALSE.

Operatorul XOR (sau exclusiv). i aici, regula este foarte simpl: dac
argumentele sunt diferite rezultatul este TRUE, contrar el este FALSE.
Manual de informatic pentru clasa a XI-a 293

Dac operanzii sunt de tip ntreg, se pleac de la reprezentarea binar a


numerelor.

Operatorul NOT (negare) este unar. Transform toi biii 1 n 0 i invers.

Operatorul AND (I) este binar. Se face I logic pentru toate perechile de
bii aflai pe aceeai poziie a celor doi operatori. Dac ambii bii sunt 1,
rezultatul este 1, iar n orice alt caz, rezultatul este 0.

Operatorul OR (SAU) este binar. Se face SAU logic pentru toate perechile de
bii aflai pe aceeai poziie a celor doi operatori. Dac cel puin un bit este 1
rezultatul este 1, altfel, rezultatul este 0.

Operatorul XOR (SAU EXCLUSIV) este binar. Se face XOR pentru toate
perechile de bii aflai pe aceeai poziie a celor doi operatori. Dac biii sunt
diferii rezultatul este 1, altfel, rezultatul este 0.

A.1.4. Instruciuni

1. Instruciunea vid. Nu se trece nimic, dar totui exist. O succesiune de


separatori ';' indic prezena mai multor instruciuni vide.

2. Instruciunea de atribuire este de forma: v:=expresie, unde v este numele


unei variabile.

Principiul de executare este urmtorul:


se evalueaz expresia;
variabila v ia aceast valoare.

Regula fundamental este urmtoarea: tipul expresiei trebuie s coincid cu


tipul variabilei v.

3. Instruciunea IF. Exist dou forme ale acestei instruciuni:

Forma 1.

IF expresie logic THEN instruciune1


ELSE instruciune2

Principiul de executare este urmtorul:

se evalueaz expresia logic;


dac aceasta ia valoarea TRUE, se execut instruciunea plasat dup
THEN, n caz contrar se execut instruciunea plasat dup ELSE.
294 Anexa 1. Memento

Forma 2.

IF expresie logic THEN instruciune.

Principiul de executare este urmtorul:

se evalueaz expresia logic;


n situaia n care aceasta are valoarea TRUE, se execut instruciunea
aflat dup THEN, n caz contrar se trece la instruciunea urmtoare.

4. Instruciunea compus. Se utilizeaz pentru a putea scrie mai multe


instruciuni care vor fi interpretate de compilator ca una singur. Instruciunile se
scriu ntre begin i end.

5. Instruciunea CASE. Corespunde structurii alternative multiple.

Forma general a instruciunii CASE este:


CASE expresie ordinal OF
c1,[c2,...,cn1]: instruciune1;
p1,[p2,...,pn2]: instruciune2;
z1,[z2,...,znp]: instruciunep
[ELSE instruciune]
END

Aici, c1,...,cn1,...,znp reprezint constante de acelai tip ca i expresia ordinal.

Principiul de executare este urmtorul:

se evalueaz expresia ordinal;


se execut acea instruciune care are n fa constanta obinut n
evaluarea expresiei;
n situaia n care nici una din instruciunile 1...p nu este precedat
de acea constant, se execut instruciunea plasat dup ELSE;
dac i clauza ELSE este absent, se trece la instruciunea urmtoare.

6. Instruciunea WHILE. Reproduce structura Ct timp... execut.

Forma general este:


WHILE expresie logic DO instruciune

Principiul de executare este urmtorul:

se evalueaz expresia logic i n caz c aceasta are valoarea TRUE


se execut instruciunea, se evalueaz din nou expresia, dac aceasta
are valoarea TRUE se execut din nou instruciunea; procedeul
continu pn cnd, la evaluarea expresiei, se obine FALSE.
Manual de informatic pentru clasa a XI-a 295

7. Instruciunea REPEAT. Aceast instruciune reproduce structura REPEAT


UNTIL i are forma general:
REPEAT
i1;
i2;
...
in
UNTIL expresie logic
Aici, i1, i2, ..., in reprezint instruciuni.

Principiul de executare este urmtorul:


se execut secvena de instruciuni;
se evalueaz expresia logic;
dac aceasta ia valoarea FALSE se execut din nou secvena de
instruciuni, contrar se trece mai departe.

8. Instruciunea FOR. Atunci cnd cunoatem de cte ori se execut o secven


este bine s se utilizeze instruciunea FOR. Ea are dou forme, prezentate n
continuare.

Forma 1.
FOR variabil := expresie1 TO expresie2 DO instruciune
unde:
variabila poate fi de orice tip ordinal (de exemplu, de tip integer, char
sau boolean, dar n nici un caz de tipul real);
expresie1, expresie2 sunt expresii de acelai tip cu variabila.

Principiul de executare este urmtorul:


Pasul 1. Se evalueaz cele dou expresii.
Pasul 2.
Pasul 2.1.
Dac valoarea obinut n urma evalurii expresiei 1 este strict mai
mare dect valoarea obinut n urma evalurii expresiei 2,
executarea FOR este ncheiat;
Dac valoarea obinut n urma evalurii expresiei 1 este egal cu
valoarea obinut n urma evalurii expresiei 2, se atribuie variabilei
de ciclare valoarea obinut n urma evalurii expresiei 1, se execut
instruciunea subordonat i executarea FOR este ncheiat;
Dac valoarea obinut n urma evalurii expresiei 1 este strict mai
mic dect valoarea obinut n urma evalurii expresiei 2, se
atribuie variabilei de ciclare valoarea obinut n urma evalurii
expresiei 1 i se trece la 2.2.
296 Anexa 1. Memento

Pasul 2.2. Se execut instruciunea subordonat.


Pasul 2.3.
Dac valoarea reinut de variabila de ciclare este strict mai mic
dect valoarea obinut n urma evalurii expresiei 2 (evaluare
efectuat la nceput) se adun 1 variabilei de ciclare i se trece
la 2.2.
Dac valoarea reinut de variabila de ciclare este egal cu valoarea
obinut n urma evalurii expresiei 2, executarea instruciunii FOR
se ncheie.

Forma 2.

FOR variabil := expresie1 DOWNTO expresie2 DO instruciune

n acest caz, variabila de ciclare scade la fiecare pas.

A.1.5. Cteva funcii utile

sinus:[-1,1]; are forma general:

function Sin(X: Real): Real;

cosinus:[-1,1]; are forma general:

function Cos(X: Real): Real; {cosinus (x)}

arctangent:(-/2,/2); are forma general:

function ArcTan(X: Real): Real; {arctangent (x)}

Atenie: argumentul trebuie exprimat n radiani. Celelalte funcii uzuale


se obin prin aplicarea formulelor trigonometrice.

Funcia exponenial f:+, unde f(x)=ex (e este un numr iraional,


e2.71) are forma general:

function Exp(X: Real): Real;

Funcia logaritmic f:+, unde f(x)=ln(x) este funcia invers funciei


exponeniale i are forma general:
function Ln(X: Real): Real;

Funcia logaritmic are urmtoarele proprieti:

pentru A, B > 0, avem : ln(A B) = ln(A) + ln(B); ln(AB ) = B ln(A).


Manual de informatic pentru clasa a XI-a 297

Din faptul c funcia logaritmic este inversa funciei exponeniale i din a


doua relaie de mai sus, deducem pentru x>0, y oarecare:

x y = eln(x ) = e yln(x) .
y

Aceasta nseamn c putem calcula xy - pentru c funcia putere nu exist


n Pascal: exp(y*ln(x)).

Funcia ptratic f:+, unde f(x)=x2:

function Sqr(X: Real): Real;

unde x este o valoare ntreag sau real.

Funcia radical f:++, unde: f(x) = x , are forma general:

function Sqrt(X: Real): Real;

Funcia parte ntreag


function Int(X: Real): Real;

returneaz partea ntreag a lui x.

Atenie: dac X este negativ, rezultatul este eronat.


Exemple: int(2.75) returneaz 2.0, int(-2.75) returneaz -2.0.

Funcia de trunchiere

function Trunc(X: Real): Longint;

returneaz valoarea trunchiat a argumentului.

Funcia parte fracionar

function Frac(X: Real): Real;

returneaz X-Int(X).

Funcia de rotunjire
function Round(X: Real): Longint;

returneaz valoarea rotunjit a lui x.

Funcia valoare absolut


function Abs(X);

returneaz x .
298 Anexa 1. Memento

A.2. Limbajul C++


A.2.1. Tipuri standard
Limbajul C++, n varianta Borland C++, admite tipurile de mai jos:

A) Tipuri ntregi

Ocup
Nume tip Semnificaie Valori admise
(bii)
unsigned
char caracter fr semn 8 de la 0 la 255

char caracter 8 de la -128 la 127


unsigned
int ntreg fr semn 16 de la 0 la 65535

int ntreg 16 de la -32768 la 32767


unsigned ntreg lung fr 32 de la 0 la 4.294.967.295
long semn

32 de la -2.147.483.648 la
long ntreg lung cu semn 2.147.483.647

B) Tipuri reale

Ocup
Nume tip Semnificaie Valori admise (n modul)
(bii)
virgul mobil,
float 32 [3.4 10-38,3.41038]
simpl precizie
virgul mobil,
double 64 [1.710-308 ,1.710+308]
dubl precizie
long virgul mobil,
dubl precizie [3.410-493 , 1.1 10+4932]
2
double 80
format lung

A.2.2. Constante
1. Constante ntregi. Acestea se clasific astfel:
zecimale (n baza 10). Exemple: 23, 1239, 56.
octale (n baza 8). O constant n baza 8 se declar precedat de un 0
nesemnificativ. Exemplu: 0123. Se reine numrul ntreg 123(8).
hexazecimale (n baza 16). Acestea sunt precedate de 0X sau 0x.
Exemplu: pentru 0X1A2 adic 1A2(16) sau 0x1a2, adic 1A2(16).
Manual de informatic pentru clasa a XI-a 299

2. Constante caracter. Acestea se trec ntre dou caractere apostrof (').

Exemple: 'A', '1', 'a'.

3. Secvene escape. O secven escape ncepe prin caracterul '\' (backslash).


S considerm o constant caracter 'a'. Codul su este 97(10)=141(8)=61(16).
Printr-o secven escape, constanta se introduce prin codul su ntr-una din bazele
8 sau 16. De exemplu, constanta 'a' poate fi scris (echivalent) astfel: '\141'
sau '\x61'. n cazul cnd se folosete codul scris n baza 16, acesta este
precedat de caracterul 'x'. Uneori, pentru anumite caractere, se pot utiliza i semne
speciale, aa cum rezult din exemplele urmtoare.
backslash: '\\','\134','\x5c';
newline: '\n','\12','\xa';
apostrof: '\'','\47','\x27';
bel: '\a','\7','\x7';
cr: '\r','\15','\xd'.

4. Caractere albe (whitespaces). Au un rol special n cadrul operaiilor de


citire/scriere. Acestea sunt:
blank (' ');
tab orizontal ('\t');
newline ('\n');
tab vertical ('\v');
cr ('\r').

5. Constante reale

Exemple: -45.66, 1., .2, 0.3, -2.5E-12, adic, 2.510-12.

6. Constante ir de caractere
Exemplu: "'acesta este un text".

Pentru a da un nume constantelor folosim const. Forma general a unei


astfel de declaraii este (construcia dintre paranteze drepte este opional):

const [tip] nume=valoare;


unde:

tip - reprezint tipul constantei (dac este absent, tipul este int);
nume - reprezint numele constantei;
valoare - reprezint valoarea constantei.
300 Anexa 1. Memento

A.2.3. Operatori

A.2.3.1. Prioritatea operatorilor (n ordine descresctoare)

Privii urmtorul tabel:

prioritate operator asociativitate

1 ()[] >::. s d
2 !~ ++ + *(typecast )sizeof newdelete d s
3 . * > * 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 , s d

A.2.3.2. Operatori aritmetici

n C++ exist urmtorii operatori aritmetici:

- minus (unar, adic acioneaz asupra unui singur operand);


+ plus (unar);
+ (binar), pentru adunare;
- (binar), pentru scdere;
* (binar), are semnificaia de nmulire;
/ (binar), pentru mprire;
% (binar), restul mpririi ntregi.
Manual de informatic pentru clasa a XI-a 301

Observaii

1. Operatorul / (mprire) acioneaz n mod diferit n funcie de operanzi:

a) dac ambii sunt de tip ntreg, rezultatul este ntreg i are semnificaia de
mprire ntreag. Cu toate acestea, rezultatul este corect (din punct de
vedere matematic) numai dac valorile care se mpart sunt pozitive.

b) dac cel puin un operand este de unul din tipurile reale, rezultatul este
real (se efectueaz mprirea obinuit).
2. Operatorul % acioneaz numai asupra operanzilor de tip ntreg. Rezultatul
obinut este corect din punct de vedere matematic numai dac ambii
operanzi sunt numere naturale.
3. n cazul n care se mpart dou valori ntregi, se procedeaz astfel:
a) se face mprirea ntreag a celor dou valori care sunt considerate n
modul;
b) semnul ctului se stabilete dup regula semnelor (+ cu + rezultat +,
+ cu -, rezultat -), etc.

A.2.3.3. Operatori relaionali

n C++ exist urmtorii operatori relaionali:

< (mai mic);


<= (mai mic sau egal);
> (mai mare);
>= (mai mare sau egal).
Rezultatul unei operaii logice este 1, n cazul n care inegalitatea este
respectat i 0, n caz contrar.

A.2.3.4. Operatori de egalitate

Acetia sunt:

== pentru egalitate;

!= pentru inegalitate.

n cazul n care relaia indicat de operator este respectat, expresia


returneaz 1, altfel returneaz 0.
302 Anexa 1. Memento

A.2.3.5. Operatori de incrementare i decrementare

Aceti operatori sunt unari i au rolul de a incrementa (adun 1) sau


decrementa (scad 1) coninutul unei variabile. Operatorii sunt:
++ pentru incrementare;
-- pentru decrementare.

Operatorii pot fi prefixai (aplicai n faa operandului) sau postfixai (aplicai dup
operand).

Dac operatorul este prefixat, variabila este incrementat (decrementat)


nainte ca valoarea reinut de ea s intre n calcul.
Dac operatorul este postfixat, variabila este incrementat (decrementat)
dup ce valoarea reinut de ea intr n calcul.

A.2.3.6. Operatori logici

Exist trei operatori logici:


! - negare logic;
&& - i logic;
|| - sau logic.
Operatorul negare logic acioneaz astfel: dac operandul este o valoare
diferit de 0, rezultatul este 0, altfel rezultatul este 1.

Operatorul i logic (binar) acioneaz astfel: dac ambii operanzi sunt diferii
de 0, rezultatul este 1, altfel el este 0.

Operatorul sau logic (binar) acioneaz astfel: dac cel puin unul din
operanzi este o valoare diferit de 0, rezultatul este 1, altfel rezultatul este 0.

A.2.3.7. Operatori logici pe bii

Limbajul C++ este dotat cu un set de operatori care permit accesul la bit.
Acetia sunt:
<<, >> operatori de deplasare;
& i pe bii;
| sau pe bii;
^ sau exclusiv pe bii;
~ negare pe bii (operator unar).

Aceti operatori acioneaz numai asupra operanzilor de tip ntreg.


Manual de informatic pentru clasa a XI-a 303

Operatorul << este binar. El are rolul de a deplasa ctre stnga coninutul
tuturor biilor operandului din stnga sa, cu un numr de poziii egal cu valoarea
reinut de al doilea operand. Poziiile rmase libere (n dreapta) vor reine valoarea 0.

Operatorul >> este binar. El are rolul de a deplasa ctre dreapta coninutul
tuturor biilor operandului din stnga cu un numr de poziii egal cu valoarea reinut
de al doilea operand. Dac operandul din stnga este de un tip ntreg fr semn,
poziiile rmase libere (n stnga) vor reine valoarea 0. Dac al doilea operand
reine valoarea m, o astfel de deplasare este echivalent cu mprirea ntreag cu
2m. n cazul n care primul operand este un ntreg cu semn, fiecare poziie din
stnga rmas liber se completeaz cu valoarea reinut de bitul de semn.
n cazul operatorilor binari & , | , ^, rezultatul se obine aplicnd pentru
fiecare pereche de bii aflai pe aceeai poziie regulile din tabelul urmtor. Atunci
cnd cei doi operanzi nu au aceeai lungime (dar numai atunci - de exemplu, dac
ambii operanzi sunt de tip char i rezultatul este de tip char), se aplic regulile de
conversie pentru expresii aritmetice.

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 bii) are rolul de a inversa coninutul biilor (dac


un bit conine 0, va conine 1 i invers).

A.2.3.8. Operatori de atribuire

n C++ atribuirea este operator. n plus, n C++ avem mai muli operatori de
atribuire. Operatorul = se folosete ntr-o expresie de forma:
v=expresie
Aici, v este o variabil.

Principiul de executare este urmtorul:

se evalueaz expresia;

variabilei v i se atribuie valoarea obinut (dac este cazul, se


efectueaz conversia respectiv).
Se pot efectua i atribuiri multiple de forma:
v=v1=v2=...=vn=expresie
unde v, v1, , vn sunt variabile.
304 Anexa 1. Memento

n acest caz, principiul de executare este urmtorul:


se evalueaz expresia;
valoarea obinut este atribuit variabilei vn (eventual convertit - dac
este cazul);
coninutul variabilei vn este atribuit variabilei vn-1 (eventual, se
efectueaz conversia necesar);
...
coninutul variabilei v1 este atribuit variabilei v (eventual, se efectueaz
conversia necesar).
Pentru atribuiri se mai pot utiliza i operatorii: *=, /=, %=, +=, -=,
<<=, >>=, &>, ^= sau |=.
O atribuire de forma: v op expresie, are acelai rezultat ca
v=v op expresie
(diferena este c, n primul caz, se genereaz un cod main eficient).

A.2.3.9. Operatorul ',' (virgul)

C++ permite programatorilor s scrie mai multe expresii separate prin


virgul, ca mai jos:
exp1,exp2, ..., expn;

ntruct, dup cum rezult din tabel, operatorul virgul se asociaz de la


stnga la dreapta, expresiile se evalueaz n ordinea exp1, exp2,..., expn. S-a
convenit ca ntreaga expresie (care cuprinde cele n expresii separate prin virgul)
s produc ca rezultat valoarea obinut n urma evalurii ultimei expresii (evident,
tipul acestei valori este i tipul expresiei).

A.2.3.10. Operatorul condiional

Se folosete n expresii de genul:


exp1?exp2:exp3
Principiul de executare este urmtorul:
se evalueaz exp1;
dac aceasta produce o valoare diferit de 0, se evalueaz exp2 i
exp3 este ignorat (nu se evalueaz);
altfel, se evalueaz exp3 i exp2 este ignorat.

n ansamblu, expresia este de tipul lui exp2 sau exp3 i produce valoarea
exp2 sau exp3 (n funcie de cea care se evalueaz).
Manual de informatic pentru clasa a XI-a 305

A.2.3.11. Operatorul sizeof

Are rolul de a returna numrul de octei utilizai pentru memorarea unei


valori. Operatorul sizeof poate fi utilizat ntr-una din cele dou forme prezentate
n continuare:
sizeof (expresie)
sizeof (tip)

A.2.3.12. Operatorul de conversie explicit

De multe ori, dorim ca unul sau mai muli operanzi s intre n calcul convertii
aa cum dorim (nu implicit). Pentru aceasta, naintea operandului se trece ntre
paranteze tipul su.
Exemplu: fie declaraia: float x= -1.9;. Atunci: (int)x=-1 (se face
conversia din float n int prin trunchiere).

A.2.4. Instruciuni
1. Instruciunea expresie este de forma:
expresie;.
La ntlnirea unei astfel de instruciuni, se evalueaz expresia. n limbajul
C++ i o atribuire este o expresie.

2. Instruciunea if se poate utiliza n urmtoarele dou forme:

Forma 1.
if (expresie) instruciune1 else instruciune2

Principiul de executare este urmtorul:


se evalueaz expresia;
dac valoarea produs de aceasta este diferit de 0, se execut
instruciune1;
dac valoarea produs este 0 se execut instruciune2.

Forma 2.
if (expresie) instruciune
Principiul de executare este urmtorul:
se evalueaz expresia;
dac valoarea produs de aceasta este diferit de 0, se execut
instruciunea subordonat.
306 Anexa 1. Memento

3. Instruciunea compus. Se utilizeaz n cazul n care se dorete ca mai multe


instruciuni s fie tratate de compilator ca o singur instruciune. Este de forma de
mai jos, unde i1, i2, ..., in sunt instruciuni:
{ i1;
i2;
:
:
in;
}

4. Instruciunea switch are forma general:

switch (expresie)
{ case exp1: secven instruciuni1; break;
case exp2: secven instruciuni2; break;
.................................
case expn: secven instruciunin; break;
[default: secven instruciunin+1];
}
unde:
expresie are semnificaia: expresie de tip ntreg;
expi sunt expresii constante de tip ntreg;
instruciunii reprezint o secven oarecare de instruciuni.

Principiul de executare:

se evalueaz expresia;
dac aceasta produce o valoare egal cu cea produs de expi, se
execut, n ordine, instruciunii i se trece la instruciunea urmtoare,
altfel se execut numai secvena instruciunin+1.

Alternativa default este facultativ. n absen, n cazul n care nu exist


coinciden de valori, se trece la instruciunea urmtoare.

5. Instruciunea while

Aceast instruciune reproduce structura de tip Ct timp ... execut.

Forma general este:


while (expresie) instruciune

Principiul de executare este urmtorul:


pasul 1: se evalueaz expresia;
pasul 2: dac valoarea produs de aceasta este diferit de 0, se
execut instruciunea subordonat, apoi se revine la pasul 1, altfel se
trece la instruciunea urmtoare.
Manual de informatic pentru clasa a XI-a 307

6. Instruciunea do while. Traduce n limbaj structura Execut...ct


timp. Forma general a acestei instruciuni este urmtoarea:
do
instruciune
while(expresie);

Principiul de executare este urmtorul:


pasul 1: se execut instruciunea subordonat;
pasul 2: se evalueaz expresia. n cazul n care valoarea produs la
evaluare este 0, execuia instruciunii do se termin, altfel se trece
la pasul 1.

7. Instruciunea for are forma general:

for (expresieiniializare; expresietest; expresieincrementare) instruciune

Dup cum se observ, ntre paranteze se gsesc trei expresii:


expresieinitializare se folosete de regul, pentru iniializarea variabilei de
ciclare. Este de remarcat faptul c n cadrul acestei expresii (cu rol special)
este posibil chiar s declarm variabila de ciclare (cu valoare iniial).
expresietest se folosete pentru a testa dac se execut instruciunea
subordonat - dac expresia produce la evaluare o valoare diferit de 0,
instruciunea subordonat for se execut.
expresieincrementare se folosete pentru incrementarea variabilei de ciclare.

Principiul de executare:

pasul 1: se evalueaz expresieiniializare (un caz special este acela n


care aceasta conine i declaraia variabilei de ciclare);
pasul 2: se evalueaz expresiatest. n cazul n care aceasta produce o
valoare diferit de 0, se execut instruciunea subordonat for; apoi se
trece la pasul 3, altfel se trece la instruciunea urmtoare (se termin
execuia instruciunii for).
pasul 3: se evalueaz expresia de incrementare i se revine la pasul 2.

A.2.5. Cteva funcii utile

Pentru a le utiliza, includei fiierul math.h: #include <math.h>.

Funcia abs are forma general: int abs(int x); Rolul ei este de a
ntoarce x (modulul lui x).
Funcia fabs are forma general double fabs(double x); are acelai
rol cu abs, numai c ntoarce valoarea unui numr real (chiar double).
308 Anexa 1. Memento

Funcia labs are forma general long int labs(long int x); i
acelai rol cu abs, numai c ntoarce valoarea unui ntreg lung.
Funcia acos are forma general: double acos(double x); i
calculeaz valoarea funciei arccos( x) : [1,1] [0, ].

Funcia asin are forma general: double asin(double x); i



calculeaz valoarea funciei arcsin( x) : [1,1] [ , ].
2 2
Funcia atan are forma general: double atan(double x); i

calculeaz valoarea funciei arctg ( x) : ( , ).
2 2
Funcia atan2 are forma general: double atan2(double y,double x)
y
i calculeaz arctg ( ). Rezultatul este n intervalul ( , ). Motivul?
x
Funcia floor are forma general double floor(double x); i
calculeaz valoarea rotunjit a lui x (rotunjirea se face n minus).
Exemple: floor (123.78)=123, floor (-23,34)=-24.
Funcia ceil are forma general double ceil(double x); i calculeaz
valoarea rotunjit a lui x (rotunjirea se face n plus).
Exemple: ceil(123.78)=124, ceil(-23,34)=-23.
Funcia cos are forma general double cos(double x); i calculeaz
valoarea funciei cos( x) : [1,1].

Funcia sin are forma general double sin(double x); i calculeaz


valoarea funciei sin( x) : [1,1].

Funcia tan are forma general double tan(double x); i calculeaz



valoarea funciei tg ( x) : k + k .
2

Funcia exp are forma general double exp(double x); i calculeaz


funcia e x : *+ .

Funcia log are forma general double log(double x); i calculeaz


funcia ln( x) : *+ , unde ln( x) = log e ( x) .

Funcia log10 are forma general double log10(double x); i


calculeaz funcia lg( x) : *+ , unde lg( x) = log 10 ( x).

Funcia pow are forma general double pow(double x, double y);


i calculeaz x y .
309

Anexa 2
Aplicaii practice ale grafurilor

Cu siguran, unii dintre voi v -ai pus o serie de ntreb


ri referitoare la
aplicabilitatea teoriei grafurilor n problemele reale:

unde pot utiliza grafurile i de ce?


exist aplicaii din alte domenii, n afar de Informatic, ce pot fi
rezolvate cu ajutorul teoriei grafurilor?
n fapt, dup cum vei vedea n continuare, grafurile sunt foarte utile ntr -o
multitudine de aplicaii din diverse domenii, iar prin utilizarea lor, se poate obine
o bun optimizare a resurselor (umane sau materiale) sau a timpului.

A.1. Reele de comunicaie

Comunicaia ntre diversele dispozitive electronice din zilele noastre reprezint


poate cea mai rspndit aplicaie practic a teoriei grafurilor. Spre exemplu, dac
ne referim la reelele de calculatoare sau la Internet i dac considerm fiecare
calculator ca fiind un nod, atunci vom avea un graf extrem de complex i foarte
diversificat din punct de vedere al structurii. n continuare, vom prezenta o schem
de principiu care descrie o reea de calculatoare, legat la Internet:

Internet

Router

Switch 1 Switch 2

Subreeaua 1 Subreeaua 2

Figura A.1. Exemplu de reea de calculatoare legat la Internet


310 Anexa 2 - Aplicaii practice ale grafurilor

Observaii

Structura anterioar este de tip arbore. Pe fiecare nivel ns, protocoalele de


comunicaie efectueaz operaii specifice pentru asigurarea transmisiei
bidirecionale ntre fiecare dispozitiv terminal (calculator).

Router-ul este un dispozitiv electronic care decide calea (drumul optim) pe


care vor fi trimise informaiile de la un calculator din Subreeaua 1, ctre un
altul din Subreeaua 2. La nivel local, Switch-ul decide la rndul su, n
funcie de adresa MAC (Media Access Control, identificator unic pe glob) a
fiecrei plci de reea, crui destinatar i este dedicat blocul de date. Pentru
a se conecta la reeaua Internet, Router-ul are o legtur cu un ISP
(Internet Service Provider).

Exist o ntreag teorie legat de reelele de calculatoare, dar ceea ce este


ns de reinut este faptul c din punct de vedere topologic, o reea de
calculatoare se poate reprezenta sub forma unui graf. Comunicaia optim
(calea cea mai scurt ntre dou noduri) este realizat cu ajutorul
protocoalelor specializate de routare, cum ar fi: IP (Internet Protocol), NAT
(Network Address Translation), RIP (Routing Information Protocol), etc.

Protocoale de routare

Un protocol de routare are rolul de a obine i de a trimite informaiile


topologice ale reelei ctre Router-e, permindu-le acestora s ia decizii la nivel
local. Fiecare Router deine o serie de liste, numite tabele de routare, n care sunt
memorate adresele (fizice i logice) tuturor nodurilor care au legtur fizic direct
cu el i drumurile optime deja cunoscute i parcurse. Aceste liste trebuie
reactualizate frecvent pentru a preveni anumite modificri topologice ale reelei.

Router-ele utilizeaz protocoalele de comunicaie care au la baz algoritmi


de optimizare ce trebuie s determine cea mai bun cale. Cnd ne referim la
drumul cel mai bun, avem n vedere numrul de hopuri (din englez, hops) pe
care trebuie s le parcurg datele pn la destinaie sau un alt punct intermediar
sau durata/viteza de trimitere a informaiilor.

Exist dou tipuri de algoritmi de routare mai importante, utilizate n funcie


de modalitatea router-ului de a reine i de a analiza informaiile structurale ale
reelei:
Algoritmi de routare globali. Fiecare router reine toate informaiile
despre celelalte router-e existente n reea i despre trafic. Cnd se
pornete un astfel de router, el trimite un mesaj ctre toate celelalte
router-e din reea, fr a cunoate n prealabil destinatarii (mesaj de tip
broadcast). Fiecare router i va rspunde cu un mesaj n care va ataa
adresa IP a sa, identificndu-se astfel. Se face apoi un test prin care se
analizeaz timpul de rspuns, trimindu-se un mesaj de tip echo
(ecou) ctre router-ele determinate anterior. Rspunsul primit de la
Manual de informatic pentru clasa a XI-a 311

fiecare este reinut pentru a fi utilizat n continuare. Algoritmul de


determinare a drumului minim ntre oricare dou noduri ale reelei (de
exemplu, se poate utiliza Dijkstra) este apoi aplicat, considerndu-se
pentru fiecare legtur un cost ce depinde de timpul de rspuns, media
traficului sau, mai simplu, numrul de noduri intermediare. Astfel,
dispozitivul obine o hart a reelei pe care o reine apoi n tabelul su
de routare. n cazul unei reele de dimensiuni foarte mari, un algoritm de
acest tip funcioneaz corect, dar poate ncetini traficul, scznd astfel
eficiena reelei.

Algoritmi de routare descentralizai. Router-ele ce au implementate


un astfel de algoritm rein informaiile doar despre nodurile legate n mod
direct (adiacente). Astfel, router-ul memoreaz costul fiecrei legturi
directe i la o anumit perioad de timp, face schimb de tabele cu
celelalte router-e, reactualizndu-i astfel informaiile. De exemplu, dac
avem trei router-e legate n serie:

L1 L2

Router 1 Router 2 Router 3

Figura A.2. Exemplu de reea

n cazul n care Router 1 trebuie s trimit date ctre Router 3,


informaiile vor trece automat prin Router 2. Cnd pachetele de date
ajung la Router 2, el verific lista sa de routare i decide cum s trimit
pachetele de date spre destinaie.
Problemele reale pe care le ntmpin reelele de calculatoare se datoreaz
numrului mare de dispozitive (noduri) din reea. Cu ct aceast valoare este mai
mare, cu att numrul de calcule efectuate la nivel de router este mai mare. Astfel,
se poate implementa virtual o ierarhizare a reelei, mprindu-se pe regiuni.
Fiecare router deine informaii doar despre toate router-ele din regiunea sa.
Legtura cu celelalte regiuni se face prin anumite router-e, ca un fel de pori de
ieire spre exterior. Astfel, un router dintr-o regiune nu reine nici o informaie
despre un altul dintr-o alt regiune, ci doar calea ctre acea regiune.

A.2. Instrumente de management economic

Proiectele i situaiile economice determinate de punerea n


practic a acestora, presupun efectuarea unor activiti interco-
nectate, care pot fi modelate prin intermediul grafurilor.
Managementul informatic al proiectelor permite gestiunea, coordonarea,
planificarea i controlul resurselor astfel nct obiectivele propuse s se ating n
mod optim i la timp.
312 Anexa 2 - Aplicaii practice ale grafurilor

O aplicaie foarte rspndit a grafurilor orientate o constituie simularea


proiectelor complexe ce presupun o multitudine de activiti distincte, efectuate n
serie sau n paralel. Teoria grafurilor vine n ajutorul oricrui analist de proiect prin
modelarea acestor activiti, prin structurarea grafic a dependenelor dintre ele i
prin determinarea timpului necesar de realizare a proiectului.
n evaluarea oricrui proiect este necesar cunoaterea timpului maxim de
execuie a ntregii lucrri. Acesta reprezint drumul cel mai lung de la faza iniial
la faza final a proiectului i este numit drum critic.

Un graf de activiti este un graf asociat unei lucrri complexe a crei


realizare presupune desfurarea mai multor aciuni (procese, activiti). Un astfel
de graf presupune dou tipuri de componente:

arcele reprezint activitile sau etapele elementare ale lucrrii, iar


lungimea asociat unui arc semnific timpul de desfurare al activitii.
Exemple: proiectarea unei componente, implementarea unui algoritm, etc. n
cadrul unui proiect, activitile se pot efectua:

- n serie: o activitate nu poate ncepe pn cnd alta nu a fost terminat;


- n paralel: mai multe activiti desfurate n acelai timp.

nodurile reprezint evenimente care pot fi interpretate ca indicnd


realizarea unor obiective pariale ale lucrrii; ele sunt un punct de verificare
al evoluiei lucrrii. Exemple: terminarea etapei de analiz, sosirea
materialelor de construcie, terminarea unor teste, etc.

Proiectul este format dintr-o serie de activiti (i evenimente), efectuate


ntr-o anumit perioad de timp (cu un nceput i un sfrit definit). La final,
rezultatul este scopul pentru care a fost dezvoltat acel proiect.

Numim drum critic al unui graf de activiti un drum de lungime maxim


care leag nodul iniial de cel final. Drumul critic reunete activiti a cror
ntrziere duce la ntrzierea realizrii ntregului proiect, de aceea trebuie
supravegheate cu mare atenie. Activitile i evenimentele ce formeaz drumul
critic poart i ele denumirea de critice.

Figura A.3. Exemplu de graf de activiti


Manual de informatic pentru clasa a XI-a 313

n figura A.3, drumul critic este format din nodurile: 1, 2, 7, 5 i 6. Timpul de


terminare al proiectului este de 21 de uniti (msura de unitate a costului). De
altfel, nu exist n mod obligatoriu un singur drum critic. Sunt cazuri n care graful
conine mai multe drumuri critice, ns cu suma ponderilor arcelor egal. n
Capitolul 9 ai studiat grafurile i algoritmul lui Roy-Floyd, metod ce permite
determinarea drumului maxim ntr-un graf. Aceast tehnic se poate implementa
cu succes pentru a detecta drumul critic ntr-un graf de activiti.

Avnd cunoscut drumul critic pentru un graf asociat unui proiect, se pot
analiza n detaliu anumite aspecte particulare ale fiecrui eveniment sau activitate.
Dorim s cunoatem cum se pot derula celelalte activiti, care nu sunt critice, n
funcie de durata drumului critic. Astfel, au fost introduse cteva noiuni teoretice,
ce vor fi prezentate n continuare.

Se consider un graf de activiti, pentru care notm cu Vi (vrfurile)


evenimentele i cu A[i,j] (arcul de la Vi la Vj) activitile. Vom defini:

ti - data ateptat a unui eveniment Vi ca fiind drumul cel mai lung de la


V1 la Vi (cea mai mare distan);

ti* - data limit a unui eveniment Vi ca fiind diferena ntre tn (data


ateptat a lui Vn) i drumul maxim de la Vi la Vn.

S revenim la exemplul din figura A.3. Pentru evenimentul 4, vom avea data
ateptat egal cu 10 (5+2+3) uniti, iar data limit, egal cu 16 (21-5) uniti.
Putem astfel considera c evenimentul 4 trebuie s fie atins dup 10 uniti
temporale, iar n cazul unei ntrzieri, atingerea sa nu poate s dureze cu mai mult
de 6 (16-10) uniti fa de data sa ateptat de terminare.

Cele dou valori asociate evenimentului Vi determin un interval de


fluctuaie, notat cu [ti, ti*], ce specific perioada de timp n care poate avea loc
evenimentul Vi, fr a schimba timpul total asociat proiectului (drumul critic).

n urma unor calcule uoare, se poate observa c pentru toate evenimentele


ce aparin drumului critic, ti = ti*.

Considernd cunoscute toate datele ateptate i cele limit pentru graf,


definim n continuare dou noiuni privitoare la arce:

ML[i,j] marginea liber a unei activiti, ca fiind tj-ti-d(A[i,j]), ce


semnific durata cu care se poate ntrzia nceperea activitii A[i,j], fr a
modifica data de ateptare a evenimentului Vj;

MT[i,j] marginea total a unei activiti, ca fiind tj*-ti-d(A[i,j]),


ce semnific durata cu care se poate ntrzia nceperea activitii A[i,j], fr a
modifica data limit a evenimentului Vj.

Arcele ce formeaz drumul critic au aceste dou valori nule (nu le este
permis nici o ntrziere).
314 Anexa 2 - Aplicaii practice ale grafurilor

Intervalul de fluctuaie permite managerului de proiect s utilizeze resursele,


echipamentele i utilajele rmase libere pentru a ajuta alte activiti i implicit
pentru a micora durata de efectuare a ntregului proiect (n cazul n care se poate
realiza acest lucru).

Grafurile de activiti sunt extrem de utile n evaluarea lucrrilor complexe,


iar reprezentarea lor permite analistului de proiect o viziune de ansamblu i
totodat, o modalitate prin care poate testa o multitudine de variante, nainte de a
o alege pe cea considerat optim. De asemenea, soft-urile specializate ce ofer
metode complexe de analiz, utilizeaz cu succes metode de optimizare ca cea
a Drumului Critic.

A.3. Chimie molecular

tiina care se ocup cu studiul moleculelor se


numete chimie molecular. Presupunndu-se cunoscute
elementele teoretice de baz , considerm c o molecul
reprezint cea mai mic particul a unei substane chimice
ce reine toate proprietile sale chimice i de compoziie. O
molecul este format din cel puin doi atomi i este neutr
din punct de vedere electric. Formula chimic i structura unei molecule reprezint
cei mai importani factori care-i determin proprietile.

se numesc
n chimie, grafurile ce descriu topologia molecular grafuri
moleculare. Dup cum era de ateptat, nodurile rep rezint atomii, iar arcele
semnific legturile dintre atomi.

Mai jos, este prezentat un exemplu de graf molecular neorientat pentru o


hidrocarbur (lipC4):

5 7

2 1

3 4

Figura A.4. Exemplu de graf molecular asociat

Pentru graful neorientat prezentat anterior, se pot asocia urmtoarele trei matrice:
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 (distana) maxime (Detour)

Observm faptul c acest graf asociat conine cicluri, lucru obinuit n


structurile moleculare. Drumurile maxime sunt totui determinate, cu
precizarea c n programele specializate se evit ciclarea algoritmului prin
utilizarea unei condiii de stop.
Dup ce au fost determinate aceste matrice, mai multe caracteristici
topologice importante pot fi obinute direct. Prezentm doar dou dintre ele:

- indicele de drum (Detour) se obine din matricea drumurilor maxime:


n n

()
1
= ij ,
2 i =1 j =1

unde () ij este un element al matricei Detour.

- indicele Weiner introdus n anul 1947 de chimistul Harry Weiner pentru


a studia structura molecular:
n n

(d )
1
W (G ) = ij ,
2 i =1 j =1

unde (d ) ij reprezint un element al matricei drumurilor minime.

Exist mai mult de 400 de astfel de indici topologici i sunt folosii n


determinarea similarit ilor structurale ntre molecule, extrem de utile n analiza
molecular.
Exemplul prezentat anterior este foarte simplu, dar imaginai-v ct de uor
poate fi pentru un chimist sanalizeze aceste date, pentru o formul chimic
complex, simulnd totul direct pe calculator Teoria grafurilor este folosit cu
succes n chimie i genetic
, iar simularea experimentelor cu aj utorul unui PC
diminueaz considerabil timpul necesar de lucru.
316

Anexa 3
Tabela codurilor ASCII
Cod Caracter Cod Caracter Cod Caracter Cod Caracter Cod Caracter Cod Caracter
000 (nul) 022 (syn) 044 , 066 B 088 X 110 n
001 (soh) 023 (etb) 045 - 067 C 089 Y 111 o
002 (stx) 024 (can) 046 . 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 (spaiu) 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 ; 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