Documente Academic
Documente Profesional
Documente Cultură
Manual 11 PDF
Manual 11 PDF
Manual 11 PDF
INFORMATICĂ
Referenţi ştiinţifici:
I. Tudor, Sorin
004(075.35)
Cuprins
Capitolul 1. Tablouri ………………………………………………………… 7
1.1. Noţiunea de tablou ………………………………………………………………….. 7
1.2. Cum citim şi cum afişăm un tablou bidimensional?……………………………... 8
1.3. Aplicaţii cu tablouri bidimensionale………………………………………………..10
Probleme propuse……………………………………………………………………….. 16
Răspunsurile la testele grilă……………………………………………………………..19
Capitolul 1
Tablouri
Intrebări posibile
d) Ştiind că, din punct de vedere economic, anul este împărţit în patru
trimestre şi că fiecare trimestru are trei luni, cum se poate calcula suma
încasărilor din trimestrul patru al anului?
e) Cum putem determina produsul din vânzarea căruia s-a încasat anual
suma maximă?
f) Care este luna cu cele mai mari încasări?
2. Tot aşa, se poate memora, sub formă de tablou, situaţia la învăţătură a celor m
elevi ai unei clase. Dacă numerotăm elevii cu 1, 2, ..., m şi materiile pe care aceştia
le studiază cu 1, 2, ..., n, atunci ai,j reprezintă media pe care o are elevul i la
materia j.
Întrebări posibile
a) Dacă în clasă sunt 30 de elevi şi aceştia studiază 8 materii, câte elemente
are matricea?
b) Care este materia la care elevii au cele mai bune rezultate?
c) Care este media generală a elevului i?
d) Care este media generală a elevilor unei clase?
Şirul exemplelor ar putea continua pentru că sunt foarte multe situaţii în care
se utilizează tablouri bidimensionale (matrice).
Î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 începând de
la 1. În aceste condiţii se pierde o linie şi o coloană, fiecare de indice 0.
Considerăm acest fapt neesenţial.
Manual de informatică pentru clasa a XI-a 9
Programul care-l utilizează va funcţiona corect dacă avem cel mult 8 linii şi
cel mult 8 coloane.
1 2 3
4 5 6
7 8 9
Elementele vor fi afişate în ordinea:
1 2 3 6 9 8 7 4 5.
* * * * *
* * * * *
* * * * *
* * * * *
* * * * *
punctajul literelor care apar într-un cuvânt 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 cuvânt (are litere vecine doar pe orizontală sau
doar pe verticală) şi se punctează obişnuit, sau face parte din două cuvinte (are litere
vecine şi pe orizontală şi pe verticală) şi atunci se punctează dublu.
Se mai poate analiza şi cazul în care o literă nu are nici un vecin pe niciuna
dintre direcţiile orizontală sau verticală (caz în care litera nu s-ar puncta deloc), însă
problema precizează că literele sunt aşezate corect conform jocului de scrabble,
deci nu pot fi amplasate litere izolate.
Aplicaţia 1.4. Sortarea fără comparaţii este o metodă de sortare care permite
sortarea a n numere naturale, fără a face nici măcar o comparaţie între ele.
Pentru memorarea numerelor care aparţin fiecărei clase vom utiliza o matrice,
denumită Mat, cu 10 coloane, în care prima linie are indicele 0. Elementele din linia 0
reţin numărul de elemente din şir care se găsesc pe coloana respectivă:
0 1 2 3 4 5 6 7 8 9
2 1 0 0 0 1 2 0 0 0
40 41 25 6
30 36
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
Observaţii
Probleme propuse
1. Se citeşte un tablou cu n linii şi n coloane, numere întregi. Un astfel de tablou, în
care numărul liniilor coincide cu numărul coloanelor, se numeşte tablou pătratic.
a) Pentru un tablou pătratic A, numim diagonală principală, elementele aflate pe
"linia" care uneşte A[1,1] cu A[n,n].
1 2 3
,
4 5 6
7 8 9
elementele sunt: 1, 5 şi 9.
Se cere:
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ă.
Cerinţe suplimentare:
3. Un teren este dat sub forma unui tablou A cu n linii şi m coloane. Elementul
A[i,j] reţine altitudinea pătrăţelului de coordonate i şi j. Să se afişeze
coordonatele "pătrăţelelor vârf" (un pătrăţel este vârf dacă toţi vecinii săi au o
altitudine strict mai mică).
Indicaţie. Atenţie! 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
soluţii.
Exemplu: m=4, n=4. Matricea iniţială şi submatricele sunt prezentate mai jos:
12. Fiind dată o matrice cu n linii şi n coloane (pătratică) cu numere naturale şi fiind
date două elemente ale matricei de coordonate (x1,y1) şi (x2,y2), care dintre
relaţiile de mai jos testează dacă elementele se găsesc pe o dreaptă paralelă cu
una dintre diagonalele matricei (principală sau secundară)?
Capitolul 2
Subprograme
1. Se consideră funcţia:
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 afişează care dintre
valorile f(a) şi f(b) este cea mai mare.
În plus, dacă într-un alt program este necesară sortarea altui vector de
numere reale, metoda clasică ne permite să alegem din secvenţa de instrucţiuni ce
formează programul pe cele ce realizează sortarea, să le copiem în noul program
şi să facem eventualele adaptări (numărul de componente şi numele vectorului pot
fi altele). Aceste operaţii sunt destul de greoaie şi necesită multă atenţie. Prin
implementarea modulară, cu ajutorul subprogramelor, ”preluarea“ se realizează
mult mai uşor.
n
1 1 1 1 1 1
E1 = 1 + + + + ; E 2 = 1 + + + + .
2 3 n 2 3 n
Funcţia are variabile proprii - adică variabile care sunt definite în cadrul ei. În
exemplu, ele sunt s şi i. Aceste variabile se numesc variabile locale.
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.
Apelul unei proceduri se face prin utilizarea numelui ei, sub forma nume;.
Mai precis, apelul unei proceduri este instrucţiune, numită instrucţiunea
de apel.
La apel, controlul programului este transferat la prima instrucţiune a
procedurii - după cum se întâmplă şi la funcţii. După executarea procedurii,
se revine în programul principal la prima instrucţiune care urmează celei de
apel - în cazul de faţă se întâlneşte o altă instrucţiune de apel.
Ca şi funcţiile, procedurile se pot plasa în cadrul programului între
declaraţiile de variabile şi instrucţiunea compusă.
Orice variabilă a programului principal este şi variabilă a procedurii (invers nu
este adevărat).
Exemplu:
function suma(x,y:integer):real;
begin
suma:=x+y;
end
begin
suma:=x+y;
end
Exemplu:
procedure suma(var s:real; x,y:real);
begin
s:=x+y;
end;
- blocul este:
begin
s:=x+y;
end;
Manual de informatică pentru clasa a XI-a 27
Deşi aparent asemănătoare, cele două noţiuni diferă. Buna înţelegere a lor
ne ajută să evităm anumite erori. Mai mult, aceste noţiuni sunt utilizate de orice
limbaj de programare, nu numai de Pascal.
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
Exemple
Exemple
procedure s1;
begin
writeln('s1')
end;
procedure s2;
begin
s1;
writeln ('s2');
end;
begin
s1;
s2;
end.
Procedura test conţine definiţia a două proceduri s1 şi s2. Nici una din
aceste proceduri nu se consideră declarată pentru programul principal.
Amândouă 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.
procedure b; forward;
procedure a;
begin
writeln('Eu sunt a');
b;
end;
procedure b;
begin
writeln('Eu sunt b')
end;
begin
a;
end.
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.
Observaţii
Cele câteva exemple date până acum fac inutilă prezentarea unui alt
exemplu. Trebuie să ştim că:
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
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}
Figura 2.5.
Sintaxa parametrilor trimişi
Manual de informatică pentru clasa a XI-a 33
În acest paragraf vom analiza modul prin care sunt memoraţi parametrii
transmişi în momentul lansării în executare a subprogramului.
a b s
La al doilea apel, parametrii sunt: 2, 3, sum.
a b s
Observaţii
Exemplu:
var n:integer;
procedure test(n:integer);
begin
n:=n+1;
writeln(n)
end;
begin
n:=1;
test(n);
writeln(n);
end.
La ieşirea din procedură, variabila n (din stivă) se pierde - adică nu mai are
spaţiu alocat. Prin urmare, valoarea 2 este pierdută.
parametrii efectivi sunt valori sau expresii, care mai întâi 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
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.
În acest caz, ne putem întreba care este mecanismul prin care, deşi pentru o
variabilă transmisă se reţine 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
Alt termen des utilizat în practica programării 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 spaţiu pe tot timpul executării programului.
durata locală - variabila are alocat spaţiu doar în timpul cât se execută un
anumit bloc.
durată dinamică - alocarea spaţiului de memorie se face în timpul executării 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
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ă greşit:
procedure er (var v:array[1..99] of real);
...
begin
...
end;
IMPLEMENTATION
[tipuri de date şi variabile locale (valabile numai pentru unitatea de
program)]
procedurile şi funcţiile utilizate de unitatea de program
[BEGIN
....... o eventuală secvenţă de program care se execută
....... în momentul declarării unităţii de program în
....... programul care o utilizează.
END.]
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
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.
#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;
}
La apel, lucrurile stau altfel: valorile acestora sunt cunoscute. Prin urmare,
aceştia se numesc parametri efectivi.
#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
Cele trei funcţii au tipul void, adică nu au valoare de retur. Ele returnează
rezultatul prin intermediul parametrilor.
tip nume.
Exemple de antete:
- int suma(int a, int b) - funcţia se numeşte suma, returnează un
rezultat de tip int şi are doi parametri formali de tip int, numiţi a şi b.
- void t(int n, float v[20]) - funcţia se numeşte 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 reţine adrese de vectori
cu elemente de tipul float).
Observaţii
test(2-3,2+3),
Segment de date
Segment de stivă
Heap
• clasa de memorare;
• vizibilitate;
• durata de viaţă;
• tipul variabilei, singurul pe care l-am studiat până în prezent.
Astfel avem:
A) Variabile globale
3. Durata de viaţă a variabilelor globale este statică. Ele au spaţiu rezervat în tot
timpul execuţiei programului.
B) Variabile locale
Acestea sunt declarate în corpul funcţiilor. Mai precis, pot fi declarate în orice
bloc (instrucţiune compusă) al acestora.
Variabila a este declarată în corpul funcţiei t(), iar variabila b este declarată
în corpul funcţiei main().
Exemplu:
void t()
{ int a=3;
}
main()
{ int b=4;
}
Exemplu:
register int b=4;
Exemplu:
void t()
{ int b=4;
{ int c=3;
cout<<b<<" "<<c;
}
}
Manual de informatică pentru clasa a XI-a 49
Observaţii
În cazul în care, într-un anumit bloc sunt vizibile (se pot accesa) mai multe
variabile, toate cu acelaşi nume, dar au domenii de vizibilitate diferite, se
accesează variabila cu vizibilitatea cea mai mică. De exemplu, dacă în
programul anterior se tipăreşte variabila a din cadrul subblocului funcţiei, se
tipăreşte 3, pentru că acesta este conţinutul variabilei cu cea mai mică
vizibilitate (cea declarată în subblocul respectiv).
Există posibilitatea ca, un ciclu for să conţină declaraţia unei variabile locale.
În secvenţa următoare se calculează suma primelor 4 numere naturale.
Variabila i este declarată (şi în consecinţă vizibilă) doar în blocul for:
3. Durata de viaţă a variabilelor locale este atât timp cât durează execuţia blocului
respectiv.
Iată, de exemplu, cum sunt memoraţi în stivă parametrii în cazul primului apel:
stiva 2 3
a b
Observaţii
Exemplu:
#include <iostream.h>
void test (int n)
{ n+=1;
cout<<n<<endl;
}
main()
{ int n=1;
test(n);
cout<<n<<endl;
}
Apelăm funcţia. La apel, se rezervă spaţiu în stivă, spaţiu care are numele
parametrului (deci tot n) şi este iniţializat cu valoarea memorată de variabila
n a programului principal. În acest moment avem două variabile n şi ambele
reţin valoarea 1.
La ieşirea din funcţie, variabila n (din stivă) se pierde - adică nu mai are
spaţiu alocat. Prin urmare, valoarea 2 este pierdută.
Exemplu:
#include <iostream.h>
void test (int n)
{ cout<<n<<endl;
}
main()
{ test(3);
test(3+4*5);
}
#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
#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;
}
Deşi aparent asemănătoare, cele două noţiuni diferă. Buna înţelegere a lor
ne ajută să evităm anumite erori. Mai mult, aceste noţiuni sunt utilizate de orice
limbaj de programare, nu numai în C++.
#include <iostream.h>
void s1()
{ cout<<"Eu sunt s1"<<endl;
}
void s2()
{ s1(); cout<<"Eu sunt s2"<<endl;
}
main()
{ s1();
s2();
}
#include <iostream.h>
void s2();
void s1()
{ s2(); cout<<"Eu sunt s1"<<endl;
}
void s2()
{ cout<<"Eu sunt s2"<<endl;
}
main()
{ s1();
}
x +1
1 + x 2 , pentru x ∈ [-1,1];
f ( x ) = x + 1, pentru x ∈ (-∞,-1); .
6
1+ x , pentru x ∈ (1, ∞ ).
Rezolvare. Cunoaşteţi 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 împărţind produsul lor la cel mai mare divizor comun al
lor. Prin urmare, scriem o funcţie cmmdc cu doi parametri formali m şi n, care
întoarce o valoare întreagă - cel mai mare divizor comun al lor.
56 Capitolul 2. Subprograme
Exerciţii
1. Găsiţi două motive pentru care parametrii funcţiei cmmdc trebuie să fie transmişi
prin valoare şi nu prin referinţă.
2. Scrieţi un subprogram cmmmc care să returneze cel mai mic multiplu comun a
două numere naturale. Stabiliţi locul unde trebuie integrat acesta în programul dat
şi modificaţi programul principal astfel încât să se utilizeze subprogramul cerut.
3. Câte 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 funcţiei 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)).
Rezolvare. Dispunem deja de o funcţie care calculează cel mai mare divizor
comun a două numere şi am testat-o în programul anterior. Pentru varietate, de
data aceasta implementăm o funcţie cu algoritmul lui Euclid pentru calculul celui
mai mare divizor comun, algoritm prin împărţiri, nu prin scăderi repetate. Cu
ajutorul ei calculăm cel mai mare divizor comun pentru valorile reţinute de primele
două componente şi memorăm această valoare în variabila c. Apoi, pentru fiecare
dintre componentele următoare, se calculează cel mai mare divizor comun între
valoarea curentă memorată în c şi cea reţinută de componenta curentă a
vectorului. Cel mai mare divizor comun va fi reţinut din nou de c.
Manual de informatică pentru clasa a XI-a 57
Exerciţiu
În locul secvenţei
c cmmdc(v[1],v[2])
pentru i=3,n execută
c cmmdc(c,v[i])
sfârşit pentru
Care este valoarea pe care trebuie să o aibă variabila c înaintea acestei secvenţe?
Exerciţii
1. După apelul tipar(m,n) din programul principal, dacă se adaugă o instruc-
ţiune care afişează valoarea lui n, ce valoare estimaţi că se va tipări?
2. Cum trebuie să arate definiţia subprogramului suc, dacă în subprogramul
tipar, în loc de nsuc(n), se utilizează instrucţiunea suc(n)?
3. Cum putem defini subprogramul suc astfel încât secvenţa de afişare din
subprogramul tipar să fie:
pentru i=1,m execută
pentru j=1,i execută
scrie suc(n)
sfârşit pentru
scrie EOLN
sfârşit pentru
Manual de informatică pentru clasa a XI-a 59
Care este avantajul scrierii fiecărei funcţii? Acesta este dat de faptul că,
atunci când scriem funcţia, ne concentrăm exclusiv asupra ei, deci posibilitatea de
a greşi este mai mică. Dacă dispunem de funcţie, algoritmul în continuare devine
mult mai simplu. În plus, este posibil ca oricare dintre funcţii să poată fi folosită şi în
alte cazuri, la rezolvarea altor probleme.
Observaţii
Exerciţii
1. Modificaţi antetul funcţiei invers, transformând parametrul n în parametru
transmis prin referinţă şi urmăriţi efectele modificării efectuate.
Junior Division
Rezolvare. Suma cuburilor cifrelor unui număr natural este calculată de funcţia
suma. Numărul n (citit) va fi memorat de prima componentă a vectorului seria.
Apoi, suma cuburilor sale, de componenta a doua, suma cuburilor numărului reţinut
de a doua componentă va fi reţinută de a treia componentă, ş.a.m.d. După ce este
memorată o valoare, ea este comparată, pe rând, cu cele obţinute anterior. Dacă
este regăsită, se tipăreşte numărul de termeni ai seriei şi seria propriu-zisă.
Funcţia suma calculează suma cuburilor cifrelor unui număr. Funcţia este verifică
dacă valoarea n există deja în şirul s format din ls componente.
Manual de informatică pentru clasa a XI-a 61
3 4
3 1 7 9
1 2 3 4
9 1 3 8
62 Capitolul 2. Subprograme
Probleme propuse
a) G(x) = sin(x)+cos(x)∗cos(2∗x);
b) H(x) = 10∗{x} (am notat prin {x} partea fracţionară a lui x).
Manual de informatică pentru clasa a XI-a 63
7. Scrieţi o funcţie care returnează prima cifră a unui număr natural. De exemplu,
dacă parametrul efectiv este 127, funcţia va returna 1.
11. Să se tipărească toate numerele prime aflate între doi întregi citiţi. Programul va
folosi o funcţie care testează dacă un număr este prim sau nu.
12. Scrieţi un program care tipăreşte numerele întregi găsite între două valori citite,
numere care se divid cu suma cifrelor lor. Programul va utiliza o funcţie care
returnează suma cifrelor unui număr întreg primit ca parametru.
13. Să se scrie o procedură care permută două linii date ale unei matrice pătratice.
14. Scrieţi o procedură care interclasează doi vectori sortaţi ştiind că aceştia sunt
ordonaţi descrescător.
17. Un număr raţional este reţinut prin utilizarea unui vector cu două componente.
Scrieţi subprogramele care adună, scad, înmulţesc şi împart două numere
raţionale. Subprogramele trebuie să returneze numărul simplificat.
Junior Division
Manual de informatică pentru clasa a XI-a 65
a) 6 6; b) 5 6; c) 6 6; d) 6 5.
a) 3; b) 4; c) 5; d) eroare de sintaxă.
22. Ştiind că valorile citite ale variabilelor a şi b sunt numere naturale nenule, care
dintre secvenţe utilizează subprogramul increment pentru a obţine produsul celor
două numere?
23. Pentru varianta de limbaj preferată, stabiliţi care dintre următoarele 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 funcţiile definite mai jos, care dintre afirmaţii este falsă?
25. Fiind dată funcţia următoare, care dintre afirmaţii sunt adevărate?
26. Fiind dată funcţia următoare, care dintre afirmaţii este falsă?
în care:
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. Dacă n=4, câte caractere ”*” se afişează în total (pe toate rândurile)?
C. Dacă n=20, câte spaţii se scriu înaintea primului caracter ”*”, pe penultimul rând?
A. Care este conţinutul 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 iniţial conţinutul 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).
a) 3; b) 5;
c) nu există o astfel de valoare; d) 2.
C. Pentru X=(1, ,5,7), Y=(1,2, ,7), stabiliţi ce valori pot fi scrise în casete
astfel încât apelul cmp(4,4,X,Y) să returneze aceeaşi 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, funcţia Cmp, apelată pentru oricare doi vectori
consecutivi în şir, returnează aceeaşi valoare?
Răspunsuri
19. d); 20. c); 21. a); 22. b); 23. c); 24. d); 25. b), c); 26. a);
29. Indicaţie: fiecare număr este scris în binar pe o linie a matricei. Se afişează
numerele zecimale obţinute din numerele în binar de pe fiecare coloană a matricei.
A. c), B. a), C. a).
30. Indicaţie: funcţia realizează compararea lexicografică a doi vectori care reţin
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. Generalităţi
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]
Mai mult, putem modifica conţinutul unui singur octet, aşa cum rezultă din
programul următor.
var t: string;
begin
t := 'Iepuras';
t[6] := 'i';
write(t);
end.
În loc de 'a', t[6] reţine 'i'. Prin urmare, programul afişează 'Iepuris'.
Definiţia 3.2. Concatenarea este operaţia prin care din două şiruri de
caractere se obţine un al treilea, format astfel: primul şir (cel aflat în
stânga 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.
Observaţii
• dacă codul primului caracter al şirului a este mai mare decât codul
primului caracter al şirului b, atunci a>b.
• dacă codul primului caracter al şirului b este mai mare decât codul
primului caracter al şirului a, atunci a<b (b>a).
Exemple:
• a='abc', b='bactr'. Atunci a<b, pentru că a[1] este a şi are codul mai
mic decât 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
decât 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).
Algoritmul de sortare este unul studiat (care?), motiv pentru care nu revenim
asupra lui. Programul este prezentat în continuare:
Exemple:
• şirul 'mama' are lungimea 4;
• şirul 'mama ' are lungimea 5 (şi blank-ul este caracter).
Exemple de utilizare
a) var a: string;
c: integer;
...
a := 'un test';
c := length(a);
2. După cum am învăţat, octetul de indice 0 reţine lungimea şirului. Pentru a-l
obţine folosim funcţia ord astfel: ord(a[0]). De ce aşa? Atunci când scriem
a[0] ne referim la un caracter. Folosind această expresie obţinem caracterul care
are codul dat de număr (în binar). Deci, pentru a obţine codul său, utilizăm
funcţia ord.
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 înţelegem un şir fără nici un
caracter. Evident, şirul vid are lungimea 0.
3.2.5. Subşiruri
Definiţia 3.4. Fiind dat un şir de caractere, prin subşir al său se înţelege
un şir de caractere consecutive care se regăsesc în şirul iniţial.
Exemple:
• Şirul 'harnic' are ca subşir şirul 'rni'. Acesta începe în poziţia a treia din
şirul iniţial (3 caractere);
• Şirul 'mama' are ca subşir şirul 'ma'. Observăm faptul că subşirul apare de
două ori - începând cu poziţiile 1 şi 3. Prin urmare, în cadrul unui şir, un subşir
poate apărea de mai multe ori.
• Şirul 'harnic' nu are ca subşir şirul 'rnit', chiar dacă primele trei
caractere (rni) se regăsesc în şirul iniţial.
Manual de informatică pentru clasa a XI-a 81
Funcţia copy are rolul de a extrage un subşir din cadrul unui şir dat:
function copy(s:string;inceput,lungime:integer):string;
unde:
• variabila s (de tip string) - conţine şirul din care se face extragerea;
• inceput - reţine poziţia de început a subşirului care se extrage;
• lungime - reţine numărul de caractere care se extrag.
Funcţia pos are rolul de a verifica dacă un şir este subşir pentru altul:
function pos(subsir,sir:string):byte;
Funcţia returnează:
Exemple: 1. Fie şirul 'abcde'. Se caută subşirul 'bcd'. Acesta este găsit, iar
funcţia returnează 2 (poziţia de început a subşirului). Dacă se caută subşirul 'cz',
funcţia returnează 0 ('cz' nu este subşir al şirului 'abcde').
82 Capitolul 3 – Şiruri de caractere
2. Programul următor citeşte două şiruri a şi b şi verifică dacă b este subşir al lui
a. În ambele cazuri se dau mesaje. Dacă b nu a fost identificat ca subşir al lui a,
sau, în caz contrar, se afişează poziţia 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.
Exemplu:
var a, b: string;
begin
write('a= '); readln(a);
write('b= '); readln(b);
insert(b, a, 3);
writeln(a);
end.
Exemplu: dacă a reţine şirul 'abcd' şi b reţine şirul '123', programul afişează
'ab123cd'.
Procedura delete are rolul de a şterge un subşir din cadrul unui şir dat.
Pentru aceasta, ea are ca parametri, în ordine:
procedure delete(var sir: string; indice, nr_car: integer);
unde:
Exemplu:
Aplicaţia 3.2. Ştergerea tuturor apariţiilor unui subşir din cadrul unui şir:
Aplicaţia 3.3. Înlocuirea tuturor apariţiilor unui subşir cu alt subşir. Analizaţi
programul următor 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.
Exemplu:
var a: string;
n: integer;
begin
write ('n= '); readln(n);
str(n, a);
writeln(a)
end.
⇒ Dacă variabila n reţine numărul 123, atunci a va reţine şirul ' 123'.
Rezultă că şirul, fără octetul de lungime, ocupă 4 octeţi. În astfel de
cazuri, şirul este completat în stânga cu numărul de blank-uri necesar.
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.
Observaţii
Dacă variabila care reţine rezultatul este de tip întreg, iar şirul conţine punctul
zecimal, conversia nu reuşeşte.
Aceste variabile se citesc începând din poziţia curentă a cursorului până este
citit numărul de caractere necesar tipului sau până s-a ajuns la sfârşitul de linie.
Programul de mai jos demonstrează acest fapt (dacă linia 1 a fişierului are 3
caractere, se vor afişa două pe un rând şi unul pe al doilea rând). După citirea unei
astfel de variabile, pointerul se află sau pe caracterul ce urmează după ultimul
caracter citit sau pe CR. În situaţia în care pointerul se află pe CR şi se forţează 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.
3.3.1. Generalităţi
c a l c u 0
Exemple:
char vect[11]="calculator".
char vect[]="calculator". În acest caz, compilatorul face calculul
numărului de octeţi necesari.
char vect[100]="calculator". Am rezervat mai mulţi octeţi decât era
necesar.
#include <iostream.h>
main()
{
char a[20];
cin>>a;
cout<<a;
}
Observaţii
Din păcate, prin metoda de mai sus nu poate fi citit un şir care conţine mai
multe cuvinte separate prin spaţii. De exemplu, dacă la rularea programului anterior
tastăm şirul " Un om", se va afişa "Un". Aceasta înseamnă că citirea se face astfel:
• Se sar toate caracterele albe. În exemplu, s-au sărit blank-urile.
Funcţia:
cin.get(vector_de_caractere, int nr, char='\n')
citeşte un şir de caractere până când este îndeplinită una dintre condiţiile de mai jos:
• au fost citite nr-1 caractere;
Observaţii
De exemplu, dacă tastăm 'mama' şi Enter se citeşte şirul "ma", care va fi afişat.
2. La fel ca mai sus, dar citirea se întrerupe la întâlnirea caracterului 'g' sau când
au fost citite 9 caractere ale şirului.
char a[10];
cin.get(a,10,'g');
cout<<a;
În C++ pot exista mai multe funcţii cu acelaşi nume, dar care diferă prin
parametrii primiţi. Astfel, există şi funcţia:
cin.get()
fără parametri. Ea are rolul de a citi un caracter (fie că este alb, fie că nu).
Observaţie. În cazul utilizării repetate a funcţiei cin.get() cu trei
parametri, apare o problemă. Analizaţi programul următor:
#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);
}
cu trei parametri, citirea se face până la întâlnirea lui. În concluzie, se citeşte şirul
vid. Utilizatorul nu mai apucă să îşi tasteze textul. Prin utilizarea funcţiei fără
parametri, acel caracter se citeşte şi noua citire se poate efectua fără probleme.
Din acest motiv, programul tipăreşte pentru a+1 şirul "asa", pentru a+2
şirul "sa", ş.a.m.d. Mai mult, vectorii astfel adresaţi pot fi accesaţi aşa cum suntem
deja obişnuiţi. Astfel, pentru exemplul anterior, (a+1)[0] reţine caracterul 'a',
(a+1)[1] reţine caracterul 's', etc.
După cum observaţi, a+1, a+2, ... sunt expresii. Orice expresie are un
anumit tip. Care este tipul lor? Tipul acestor expresii este char*. Ce semnificaţie
are? Semnificaţia este cea de adresă.
#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;
}
Funcţia 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ă către un şir).
94 Capitolul 3 – Şiruri de caractere
#include <iostream.h>
#include <string.h>
main()
{ char a[100];
cin.get(a,100);
cout<<"Sirul citit are "<<strlen (a)<<" caractere";
}
Aşa cum ştim din lucrul cu tablouri, atribuirile de forma a=b, unde a şi b sunt
vectori de caractere, nu sunt permise. Tot aşa, o atribuire de forma a="un sir"
nu este permisă. Astfel de operaţii ca şi multe altele se fac cu anumite funcţii, puse
la dispoziţie de limbaj. Pentru ca acestea să poată fi folosite, trebuie să fie inclus
fişierul antet "string.h", tot aşa cum includem fişierul "iostream.h". Ordinea
de includere nu are importanţă. În continuare, vom prezenta cele mai uzuale funcţii
şi modul în care acestea se folosesc.
şi are rolul de a copia şirul de adresă sursa la adresa dest. Copierea se termină
după ce a fost copiat caracterul nul. Se returnează adresa dest. Analizaţi
codul următor:
#include <iostream.h>
#include <string.h>
main()
{ char a[100]="un sir", b[100]="alt sir";
strcpy (a,b);
cout<<a;
}
şi rolul de a adăuga şirului de adresă dest şirul de adresă sursa. Şirul de adresă
sursa rămâne nemodificat. Această operaţie se numeşte concatenare şi nu este
comutativă. Rezultatul este adresa şirului sursa, iar şirul va avea ca lungime,
suma lungimilor celor două şiruri care au fost concatenate.
Manual de informatică pentru clasa a XI-a 95
În programul următor se caută în şirul a caracterul 't'. Acesta este găsit, iar
programul tipăreşte şirul "ta este". Evident, acesta este subşirul care începe cu
caracterul 't'.
#include <iostream.h>
#include <string.h>
main()
{
char a[20]="Acesta este";
cout<<strchr (a,'t');
}
Programul de mai jos tipăreşte indicele primei apariţii a caracterului 't', şi anume 4:
#include <iostream.h>
#include <string.h>
main()
{
char a[20]="Acesta este";
cout<<strchr (a,'t')-a;;
}
Observaţii
Fiecare adresă de subşir, care are ca prim caracter cel reţinut de c, intră în
calculul indicelui acelui caracter - din ea se scade adresa de început.
Manual de informatică pentru clasa a XI-a 97
Dar care este mecanismul prin care se compară două şiruri de caractere?
Fie m numărul de caractere al şirului s1 şi n numărul de caractere al şirului s2.
Să presupunem că primele i caractere ale lui s1 coincid cu primele i ale lui s2.
În cazul în care codul caracterului i+1 al şirului s1 este mai mare decât codul
caracterului corespunzător şirului s2, avem s1>s2.
În cazul în care codul caracterului i+1 al şirului s1 este mai mic decât codul
caracterului corespunzător şirului s2, avem s1<s2. În cazul în care şi la
această comparaţie avem egalitate, avem patru posibilităţi:
• ambele şiruri au un număr strict mai mare de caractere decât i+1, caz în
care se compară ca înainte caracterele de pe poziţia i+2;
• s1 are i+1 caractere, iar s2 are un număr de caractere mai mare decât
i+1, în acest caz s1<s2;
• s2 are i+1 caractere, iar s1 are un număr de caractere mai mare decât
i+1, în acest caz s1>s2;
Pe scurt, un şir s1 este mai mic ca altul s2, dacă în dicţionar s1 ar figura
înaintea lui s2.
Exemple: "soare">s;
"tata">mama;
98 Capitolul 3 – Şiruri de caractere
Funcţia strcmp face distincţie între literele mari şi mici ale alfabetului.
şi acelaşi rol ca strcmp. Diferenţa este că nu face distincţie între literele mari şi
mici.
Vectori de cuvinte. Există aplicaţii în care este necesar să se lucreze cu n
cuvinte - înţelegând prin cuvânt o succesiune de caractere care nu sunt albe. În
acest caz avem posibilitatea să declarăm vectori de cuvinte. Acestea sunt, de fapt,
matrice cu elemente de bază de tip char.
Fiecare linie din cele 10 ale matricei poate reţine un şir de caractere. Acesta poate
avea cel mult 25 de caractere (inclusiv caracterul nul). Cuvintele pot fi adresate prin
a[0] (primul cuvânt), a[1] cuvântul al doilea, ş.a.m.d.
do
{ gasit=0;
for (i=0;i<n-1;i++)
if (strcmp(cuvinte[i],cuvinte[i+1])>0)
{ strcpy(man,cuvinte[i]);
strcpy(cuvinte[i],cuvinte[i+1]);
strcpy(cuvinte[i+1],man);
gasit=1;
}
}
while (gasit);
for (i=0;i<n;i++) cout<<cuvinte[i]<<endl;
}
Dacă pot compara două cuvinte, atunci pot să le sortez. Am folosit sortarea
prin interschimbare. Iată că, algoritmii, cu precădere cei fundamentali, pot fi folosiţi
şi într-un context diferit de cel în care au fost prezentaţi.
3.3.8. Subşiruri
şi are rolul de a identifica dacă şirul s2 este subşir (caractere succesive) al şirului
s1. Dacă acesta este identificat, funcţia returnează adresa de început în cadrul
şirului s1, altfel returnează adresa nulă (0). Căutarea se face de la stânga la
dreapta. În cazul în care s2 apare de mai multe ori în cadrul lui s1, se returnează
adresa de început a primei apariţii.
Exemplu: fie char s1[]="xyzt", s2[]="yz", atunci strstr(s1,s2);
returnează s1+1 (adresa caracterului y în s1).
Aplicaţia 3.10. Ştergerea tuturor apariţiilor unui subşir din cadrul unui şir.
Imediat ce am identificat adresa de început a subşirului, restul şirului (fără subşir)
este copiat pe poziţia de început a subşirului.
#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;
}
Aplicaţia 3.11. Înlocuirea tuturor apariţiilor unui subşir cu alt subşir. Vă las pe
dvs. să descoperiţi modul în care programul următor 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
• şirul s1 este considerat ca fiind alcătuit din 0, 1, ..., n entităţi separate prin
unul sau mai multe caractere cu rol de separator, iar şirul s2 ca fiind alcătuit
din unul sau mai multe caractere cu rol de separator;
Exemplu: şirul s1 este " mama, tata si bunicul". Şirul s2 este:
" ,". Întrucât caracterele separatoare sunt blank-ul şi virgula, rezultă că
entităţile sunt: "mama", "tata" "si" "bunicul".
Aplicaţia 3.17. Se citeşte un text alcătuit din mai multe cuvinte. Se cere să se
calculeze suma valorilor numerice întâlnite în text. Se presupune că valorile
numerice sunt introduse corect.
Şirul se separă în entităţi. Dacă o entitate este alcătuită exclusiv din '+',
'-', '.' şi cifre, atunci ea este convertită în double şi adunată la o variabilă s,
iniţializată cu 0.
#include <iostream.h>
#include <string.h>
#include <stdlib.h>
main()
{ char sir[1000],separator[]=" ",cifre[]="0123456789.+-", *p;
double s=0;
cin.get(sir,1000);
p=strtok(sir,separator);
while (p)
{ if (strspn(p,cifre)==strlen(p)) s+=atof(p);
p=strtok(NULL, separator);
}
cout<<"suma numerelor intalnite in sir este "<<s;
}
Funcţia ecvt are rolul de a converti o valoare de tip double către un şir.
Caracterul nul este adăugat automat şirului obţinut. Forma generală este:
char* ecvt(double valoare, int poz, int* zec, int* semn);
unde:
• valoare - valoarea de convertit;
• poz - numărul de poziţii pe care trebuie să le ocupe şirul obţinut în urma
conversiei;
• zec - adresa unei variabile, de tip int, care reţine, după apel, numărul
zecimalelor pe care le are numărul;
• semn - adresa unei variabile, de tip int, care are rolul de a memora,
după apel, 1, dacă numărul este negativ, sau 0, în caz contrar.
Funcţia nu inserează în şir nici punctul zecimal, nici semnul numărului. Şirul
obţinut începe cu cifra cea mai semnificativă a numărului. Pentru ca şirul obţinut să
fie corect, este necesar să fie prelucrat, în continuare, de către programator. Dacă
numărul poziţiilor ocupate de şir (poz) este mai mare decât ocupă data, şirul este
completat în stânga cu un număr de 0 necesar. În cazul în care numărul poziţiilor
este mai mic decât cel ocupat de număr, rezultatul este rotunjit.
Exemple: ecvt(val,3,&zec,&semn);
106 Capitolul 3 – Şiruri de caractere
− dacă val reţine 1234, atunci se returnează şirul "123", zec reţine 4 şi
semn reţine 0.
− dacă val reţine 1239, atunci se returnează şirul "124", zec reţine 4 şi
semn reţine 0.
− dacă val reţine 1, atunci se returnează şirul "100", zec reţine 1 şi semn
reţine 0.
− dacă val reţine -0.001, atunci se returnează şirul "100", zec reţine -3
şi semn reţine 1.
Funcţia itoa are rolul de a converti o valoare de tip int într-un şir, a cărui
adresă este memorată în variabila sir. Valoarea baza reţine baza de numeraţie
către care să se facă conversia (de obicei baza este 10). În cazul bazei 10, şirul
obţinut reţine şi, eventual, semnul “-“. Funcţia întoarce adresa şirului obţinut.
Funcţia ltoa are acelaşi efect ca itoa, deosebirea fiind dată de faptul că se
converteşte către şir o valoare de tip long int.
Funcţia ultoa are acelaşi efect ca itoa, deosebirea fiind dată de faptul că
se converteşte către şir o valoare de tip unsigned long.
Funcţia
long strtol(const char *s, char **endptr, int baza);
are rolul de a converti un şir către long. În afara şirului care trebuie convertit,
funcţia primeşte ca parametru de intrare adresa unei variabile de tip char*. După
apel, această variabilă va reţine poziţia primului caracter din şir care nu poate fi
convertit. De exemplu, dacă şirul este "12m21", după apel, variabila a cărei adresă
a fost transmisă ca parametru reţine adresa caracterului 'm'. Dar care este rostul
existenţei acestei informaţii? Cu ajutorul ei se pot depista eventualele erori apărute
atunci când se introduc datele. Priviţi şirul dat anterior ca exemplu: e clar că
utilizatorul a dorit să introducă un număr, dar în locul unei cifre a tastat 'm'.
Funcţia
double strtod(const char *s, char **endptr);
converteşte un şir către double.
Funcţia
long double _strtold(const char *(s), char **endptr);
converteşte un şir către long double.
Funcţia
unsigned long strtoul(const char *s,char **endptr,int baza);
converteşte un şir către unsigned long.
Pentru a citi o linie a unui fişier text, se poate folosi funcţia următoare:
Funcţia citeşte un şir de caractere, până când una dintre condiţiile următoare
este îndeplinită:
Evident, în acest fel, la o citire pot fi aduse în memorie cel mult 32766
caractere (vezi limitele tipului int).
Observaţii
Programul următor citeşte linie cu linie un fişier text ale cărui linii nu au
mai mult de 500 de caractere şi afişează liniile pe monitor. Nu se
cunoaşte lungimea fiecărei linii.
#include <fstream.h>
main()
{ fstream f("f.dat",ios::in);
char Linie_citita[501];
while (f.getline(Linie_citita,501))
cout<<Linie_citita<<endl;
}
Tot aşa, se pot "citi" mai multe cuvinte dintr-un şir. Programul următor citeşte
cuvintele şi le afişează. 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
Probleme propuse
1. Se citeşte de la tastatură un text. Cuvintele se consideră separate prin virgulă,
spaţiu sau punct. Câte cuvinte are textul citit?
2. Se citeşte de la tastatură un text şi o succesiune de caractere. De câte ori
întâlnim această succesiune în cadrul textului?
3. Se citeşte un text. Două cuvinte pot fi separate printr-unul sau mai multe spaţii.
Se cere să se elimine spaţiile inutile.
4. Se citeşte un fişier text care conţine o singură propoziţie, pe o linie. Programul
rearanjează literele în fiecare propoziţie, în ordine alfabetică, păstrând locul
cuvintelor. Numai literele de la 'A' la 'Z' sunt afectate. Celelalte caractere rămân
neschimbate. Ieşirea va fi tot un fişier text. Numele fişierelor este ales de dvs.
Exemple:
Junior Division
Manual de informatică pentru clasa a XI-a 111
Capitolul 4
Structuri de date neomogene
Observaţii
Câmpurile unei variabile de acest tip pot fi citite sau afişate individual. De
asemenea, li se pot atribui valori, pot intra în calcule, tot individual.
Dacă a şi b sunt două variabile de acelaşi tip RECORD se pot face fără
probleme atribuiri de genul a:=b;.
Aşa cum a fost prezentat, modul de acces la câmpurile unei variabile de tip
RECORD este deosebit de greoi - întotdeauna punem numele variabilei în faţă. În
realitate, accesul la câmpurile unei astfel de variabile se poate face mult mai uşor,
prin utilizarea instrucţiunii WITH, cu forma generală:
with var1, var2, ..., varn do instrucţiune
În general, câmpurile care alcătuiesc un tip RECORD pot avea orice tip. O
întrebare: dar RECORD? Răspunsul este afirmativ. Analizaţi programul următor:
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 naşterii. Aceasta din urmă este de
tipul RECORD (include ziua, luna şi anul naşterii). Pentru adresarea simplificată a
câmpurilor care o alcătuiesc s-a folosit, din nou, instrucţiunea WITH. Adresarea s-ar
fi putut face şi aşa: e.data_n.zi, e.data_n.luna, e.data_n.an. Evident, o
astfel de adresare este greoaie şi nerecomandată…
Putem avea oricâte niveluri de imbricare (un tip RECORD include un alt tip
RECORD, care include un altul, ş.a.m.d.).
Observaţii
În practică, apar situaţii în care toate tipurile de date învăţate până în prezent
nu ne sunt de mare folos. Pentru a înţelege aceasta, vom porni de la un exemplu.
şi se numeşte elev.
Manual de informatică pentru clasa a XI-a 119
1. Aşa cum rezultă din forma generală, scriind la sfârşit numele variabilelor:
struct elev
{ char nume[20], prenume[20];
float nota_mate,nota_info;
int varsta;
}inr1,inr2;
Definiţia structurii poate figura atât în cadrul funcţiei main() cât şi în faţa ei,
după includerile de fişiere antet. În ce ne priveşte, vom prefera a doua variantă.
Se pune următoarea întrebare: fiind dată o variabilă de un tip struct, care
este modalitatea de acces la câmpurile ei? Pentru aceasta, se foloseşte operatorul
de selecţie directă, notat cu '.', operator cu prioritate maximă.
Între două variabile de acelaşi tip struct se poate folosi atribuirea. Astfel,
dacă inr1, inr2 sunt două variabile de tip elev, prin atribuirea inr1=inr2,
variabila inr1 ia aceeaşi valoare ca variabila inr2. În C++, o astfel de atribuire se
mai numeşte copiere bit cu bit.
Există situaţii când un tip structurat conţine în interiorul său un alt tip
structurat. Priviţi tipul următor:
struct elev
{ char nume[20], prenume[20];
struct
{ int clasa;
float note[20];
}situatie;
int varsta;
};
Pentru început, vom studia un tip aparte de dată structurată, numit union.
Analizaţi programul următor:
#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;
}
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. Citiţi o variabilă cu următoarea structură:
• nume_elev: 30 caractere;
• data_nasterii:
zi : intreg;
luna : intreg;
an : intreg;
• nota matematica – real;
• nota informatica – real;
• nota engleza – real;
• media – real;
Testaţi dacă datele au fost introduse corect. Citirea se va verifica prin afişarea
rezultatului.
2. Citiţi n înregistrări de tipul celei de mai sus şi afişaţi-le în ordinea alfabetică a
numelui.
Capitolul 5
Structuri de date
o mulţime de valori;
o regulă de codificare a acestora;
1
o mulţime de operaţii definite pe mulţimea datelor.
Prin tipul de mai sus se descrie structura unei variabile capabilă să reţină
numere raţionale. Fie A1 mulţimea valorilor care pot fi memorate prin utilizarea
tipului întreg. Fie A2=A1-{0}. Atunci, o variabilă de tip rational poate memora
valori care aparţin mulţimii A1×A2. Evident, este sarcina programatorului ca valoarea
reţinută de variabila corespunzătoare lui q să fie diferită de 0.
În unele lucrări veţi întâlni definiţii ale tipului de date care exclud regula de codificare. Aici
1
Fie B mulţimea valorilor care pot fi reţinute de tipul real. Atunci, variabila a
poate reţine la un moment dat un element al mulţimii:
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.
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
alcătuit dintr-o mulţime cu un număr variabil de noduri.
Relaţiile existente între noduri şi operaţiile care pot fi efectuate cu ele vor fi
prezentate prin exemple.
De reţinut!
În practică s-au impus anumite structuri. Acest lucru este datorat faptului că
există mulţi algoritmi care le utilizează. Ele vor fi tratate, pe scurt, în
paragrafele următoare.
126 Capitolul 5. Structuri de date
Definiţia 5.2. O listă liniară este o colecţie de n≥0 noduri, X1, X2, ..., Xn
aflate într-o relaţie de ordine. Astfel, X1 este primul nod al listei, X2
este al doilea nod al listei, ..., Xn este ultimul nod.
a) Evidenţa situaţiei şcolare a elevilor unei clase. Fie n numărul elevilor. Aici, un
nod reţine 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 transferaţi 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.
a) Accesul la oricare nod al listei se poate face cu mare uşurinţă. Dacă dorim să
adresăm nodul k, atunci scriem V[k] (presupunând că vectorul care reţine nodul
se numeşte V).
7 3 1 2 8 9 5 8 3 2 6
7 1 2 8 9 5 8 3 2 6
7 1 2 8 9 5 8 3 2 6
7 3 1 2 8 9 5 8 3 2 6
128 Capitolul 5. Structuri de date
7 3 1 2 8 9 5 8 3 2 6
7 5 3 1 2 8 9 5 8 3 2 6
Observaţii
in1, in2, ..., inn reprezintă informaţiile conţinute de noduri, de altă natură
decât cele de adresă.
fiecare nod reţine adresele predecesorului şi succesorului său, aşa cum se vede în
figura următoare:
Aşa cum am învăţat, lista liniară este alcătuită din mai multe noduri între care
există o relaţie de ordine. În cazul alocării înlănţuite, informaţia memorată de
fiecare nod va cuprinde şi un câmp de adresă -în cazul alocării simplu înlănţuită-
sau două câmpuri de adresă -în cazul alocării dublu înlănţuită. În acest paragraf
vom studia implementarea alocării simplu înlănţuită.
Iată cum se descrie informaţia dintr-un nod, în cazul listelor alocate simplu
înlănţuit, atunci când acesta reţine un număr întreg.
Pentru memorarea listei folosim un vector care are componentele de tip Nod,
descris mai jos:
Din descriere rezultă că lista poate avea cel mult 1000 de noduri. Acesta
este spaţiul disponibil.
L 7 3 5 4 1 5 4 0
1 2 3 4 5 6
130 Capitolul 5. Structuri de date
7 3 5 4 1 5 4 0
1 3 4 5
Ce observăm?
a) Fiecare nod trebuie să reţină şi adresa nodului următor. Adresa este, de fapt,
indicele componentei din vector care reţine informaţia asociată nodului următor.
Prin urmare, necesarul de memorie este mai mare.
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
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
b) Memorăm informaţia: 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
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
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
7 3 5 4 1 5 9 5 4 0
1 3 4 5 5
132 Capitolul 5. Structuri de date
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
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
Lista va deveni:
7 4 1 5 4 0
1 4 5
Exemplele sunt date pentru lista liniară simplu înlănţuită, dar bine înţelese,
ne permit să deducem singuri modul de efectuare a operaţiilor respective pentru
liste dublu înlănţuite.
Manual de informatică pentru clasa a XI-a 133
Definiţia 5.3. Stiva este o listă pentru care singurele operaţii permise
sunt:
Stivele se pot aloca secvenţial (ca vectorii). Fie ST[i] un vector. ST[1],
ST[2], ..., ST[n] pot reţine numai litere sau numai cifre. O variabilă k indică
în permanenţă vârful stivei, adică ultimul element introdus.
B
Introducem în stivă litera B, deci k va lua valoarea 2.
A
Observaţii
Exemple
x − 1, x ≥ 12
F(x) =
F(F(x + 2)),x < 12
Vom începe prin a studia modul de calcul al funcţiei 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.
12 13
10 10 11 11
8 8 8 8 8
12 13
8 11 11 12
f=11
Se poate demonstra uşor că pentru valori mai mici decât 12, funcţia ia
valoarea 11. Observaţia simplifică mult programul, dar exemplul a fost dat în
alt scop.
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 calculul acestei funcţii, folosim o stivă dublă, ST. Iniţial, valorile m şi n
se reţin la nivelul 1. Pe nivelul k al stivei se reţin valorile curente m şi n. În funcţie de
valorile acestora se procedează astfel:
pentru cazul n=0, se rămâne pe acelaşi nivel în stivă, punând în locul lui m
valoarea m-1, iar în locul lui n, valoarea 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
Funcţia lui Ackermann ia valori extrem de mari pentru valori mici ale lui m şi
n. De exemplu, nu veţi reuşi să calculaţi Ack(4,4). Încercaţi...
138 Capitolul 5. Structuri de date
Definiţia 5.4. O coadă este o listă pentru care toate inserările sunt făcute
la unul din capete, toate ştergerile (consultările, modificările) la celălalt
capăt.
1 2 3 4
2 3 4 5
3 4 5 6
Probleme propuse
1. Care dintre structurile de mai jos nu este liniară?
a) b) c) d)
Exemplu: k=3; V=(1,2,3); p=2. Numărul citit este 5. După rulare trebuie să
avem:
k=4; V=(1,5,2,3).
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 afirmaţiile de mai jos este falsă?
a) Pentru a şterge un nod aflat pe poziţia p sunt necesare k-p deplasări spre
stânga ale conţinuturilor celorlalte noduri.
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ă deplasări către stânga.
10. În jurul arbitrului sunt aşezaţi N jucători numerotaţi în sens orar. Arbitrul,
începând de la un jucător K numără până la M. Persoana la care s-a oprit
numărătoarea este eliminată din cerc. Arbitrul repetă procedeul începând cu
persoana următoare celei eliminate. Procedeul se repetă până când rămâne un
jucător L. Să se scrie un program care:
citeşte M, N, K şi-l determină pe L;
citeşte M, N, L şi-l determină pe K.
11. Urmăriţi secvenţa următoare, care se referă la o stivă, unde Push a este
operaţia prin care se pune în stivă valoarea a, iar Pop este operaţia prin care se
extrage din stivă.
Push 1; Push 2; Pop; Push 3; Push 4; Pop; Pop
Care din afirmaţiile de mai jos nu este adevărată după executarea secvenţei
de mai sus?
a) Din stivă au fost extrase, în această ordine, valorile: 2, 4, 3;
b) stiva este vidă; c) stiva conţine valoarea 1; d) stiva are un singur nivel.
15. Să se scrie un program care, pentru problema de mai sus, citeşte ca date de
intrare şirul vagoanelor aflate pe linia A şi şirul vagoanelor care trebuie obţinut pe
linia B. Se cere să se afişeze şirul mutărilor de tip Push nr_vagon şi Pop
nr_vagon (aţi recunoscut, desigur, o structură de tip stivă) prin care se poate
obţine şirul vagoanelor de pe lina B. Dacă acest şir nu se poate obţine, în momentul
în care se ajunge în situaţia unei mutări imposibile, să se afişeze mesajul
‘Imposibil’.
Răspunsuri
1. d) 4. b) 11. b) 14. d)
141
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.
function suma(n:integer):integer;
begin
suma:=0;
if n<>0 then
suma:=n+suma(n-1);
end;
begin
writeln(suma(7));
end.
După cum ştim, în C++ funcţiile pot fi de tipul void sau de un alt tip. În acest
din urmă caz, funcţiile returnează o anumită valoare. Oricare ar fi tipul funcţiei,
aceasta se poate autoapela, însă modul în care se realizează autotransferul, diferă.
#include <iostream.h>
int suma (int n)
{ if (n!=0) return n+suma(n-1);
}
main()
{ cout<<suma(7);
}
1, n=0
n! = fact(n) = cu n ∈ N
n ⋅ fact(n − 1), altfel
3!=fact(3)=3×fact(2)=3×2×fact(1)=3×2×1×fact(0)=3×2×1×1=6.
n 3
n 3
1 1 3 Referinţă către p
p val n prod
2 3 Referinţă către p
1 1 3 Referinţă către p
p val n prod
3 3 Referinţă către p
2 3 Referinţă către p
2 1 3 Referinţă către p
p val n prod
Pentru că val este 3 - 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.
Observaţii
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 fără
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ţă, atât din motive didactice cât şi pentru
a asigura independenţa subprogramului. Dezavantajul este dat de faptul că stiva
va fi încărcată suplimentar cu adresa acestei variabile.
Manual de informatică pentru clasa a XI-a 147
Observaţii
Rezolvare. Această aplicaţie a fost tratată iterativ, prin utilizarea stivei. În cazul
tratării recursive, nu facem altceva decât să transcriem definiţia recursivă a funcţiei.
n + 1, m=0
Ack(m, n) = Ack(m − 1,1), n=0
Ack(m − 1, Ack(m, n − 1)), altfel
Rezolvare. Utilizăm o definiţie 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
Aplicaţia 6.5. Să se scrie o funcţie recursivă pentru a calcula suma cifrelor unui
număr natural.
Reţinem ideea: se izolează ultima cifră, iar lui n i se atribuie câtul întreg
dintre vechea valoare şi 10. Această idee foloseşte pentru a găsi o relaţie de
recurenţă, necesară elaborării variantei recursive. Relaţiile sunt scrise prin
utilizarea operatorilor din Pascal (stânga) şi C++ (dreapta):
Aplicaţia 6.6. Se consideră şirurile definite recurent astfel: a0=a; b0=b; a,b>0:
a n−1 + b n−1
an = , b n = a n−1b n−1 .
2
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.
se citeşte un caracter;
dacă este diferit de 0, se reapelează funcţia;
se tipăreşte caracterul.
213
1
312
12 132
Figura 6.1.
Cazul în care n=3
123
0 1 1 0
0 0 0 1
A=
0 1 1 1
1 0
0 0
1 1 1 0
1 1 1 1
A=
1 1 1 1
1 0 0 0
1 1 0 0
0 0 0 1
A= ,
1 1 1 1
1 1
1 1
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 ieşirii din matrice,
aceasta este bordată cu linii şi coloane având valoarea ”0”. Algoritmul este tot cel
din problema anterioară (Fill), dar aici căutarea se face pe 8 direcţii.
În programul principal se citeşte matricea şi se caută primul element ”1”
printre elementele acesteia. Se apelează apoi funcţia compact() care are rolul de
a marca cu 0 toate elementele matricei care aparţin 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 iniţial mai multe
obiecte (altfel, fotografia conţinea un singur obiect).
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. Calculaţi recursiv suma a n numere naturale citite.
a) 1×2+2×3+...+n×(n+1);
b) 1+1/2+...+1/n;
c) 1/(2×3)+2/(3*4)+...+n/((n+1)(n+2)).
160 Capitolul 6. Introducere în recursivitate
1, k = 0;
C = n − k + 1 k −1
k
n
Cn altfel.
k
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 citeşte un număr natural n. Se cere să se scrie o funcţie recursivă care
returnează cea mai mică bază în care se poate considera n.
10. Scrieţi o funcţie recursivă care testează dacă un număr natural n>1 este prim.
11. Scrieţi o funcţie recursivă care returnează suma elementelor pare ale unui
vector citit.
Exemplu: Pentru n=4 şi V=(2,2,5,6), se returnează 10.
12. Scrieţi o funcţie recursivă prin care se testează dacă un număr natural x se
regăseşte între componentele unui vector V cu n numere naturale.
13. Scrieţi o funcţie recursivă care primeşte ca parametri două numere naturale
i<j şi un număr real x∈[i,j] şi returnează [x] (parte întreagă din x). Nu se
vor folosi funcţiile specializate ale limbajului.
Manual de informatică pentru clasa a XI-a 161
14. Scrieţi o funcţie recursivă care verifică dacă un vector cu componente numere
naturale este palindrom (afişarea componentelor de la 1 la n coincide cu afişarea
lor de la n la 1).
15. Scrieţi o funcţie recursivă care returnează numărul cifrelor pe care le are un
număr natural primit ca parametru.
16. Scrieţi un subprogram recursiv care afişează, cifră cu cifră, oglinditul unui
număr natural.
Exemplu: pentru n=123, se afişează 321.
17. Scrieţi un subprogram recursiv care returnează, oglinditul unui număr natural.
Exemplu: pentru n=123, se returnează 321.
18. Scrieţi o funcţie recursivă care testează dacă un vector cu n numere naturale
reţine numai valori distincte, caz în care funcţia returnează true, iar în caz contrar,
returnează false.
21. Scrieţi o funcţie recursivă care afişează valoarea unui polinom în punctul a.
Coeficienţii polinomului sunt daţi într-un vector. Astfel, pentru V=(1,2,3) avem
polinomul P=x2+2x+3.
0, k>n
S(n, k) = 1, k ∈ {1, n}
S(n − 1, k − 1) + kS(n − 1, k) 1 < k < n
23. Calculaţi S(n,k) nerecursiv.
26. Calculaţi iterativ şi recursiv cel mai mare divizor comun pentru două numere
naturale m şi n, utilizând algoritmul lui Euclid:
a) 12;
b) 21;
c) eroare de executare;
d) 0.
29. Pentru care dintre numerele de mai jos, care sunt parametri de intrare pentru
funcţia următoare, ultimele 2 numere afişate vor fi 0?
a) 295;
b) 1024;
c) 1000;
d) 10000.
a) 1 2 3;
b) 1 4 2 5 3 1;
c) cerinţa nu este corectă;
d) 4 5 1.
164 Capitolul 6. Introducere în recursivitate
31. Care dintre afirmaţiile de mai jos sunt corecte dacă n este 3 şi apelul este
t(n,1,1)?
32. Dacă n este 3, pentru care din datele de mai jos se afişează un număr 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).
a) 1 2 3; b) 1 2 3 4 5 6;
c) 4 5 6; d) nici o valoare.
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 afişează două
valori?
a) 4; b) 3; c) 2; d) 1.
36. Dacă n=4, pentru care valoare a lui m se afişează toate cele 4 valori ale
vectorului?
37. În funcţia de mai jos înlocuiţi linia ”...” cu una dintre instrucţiunile următoare,
astfel încât funcţia să-şi încheie execuţia fără eroare pentru orice valoare admisibilă
a argumentului:
38. Dacă funcţia este apelată prin an(4), de câte ori se autoapelează?
39. Pentru care dintre valorile de mai jos, care sunt parametri de intrare pentru
funcţia an, executarea funcţiei se termină cu eroare?
40. Pentru programul de mai jos, de câte ori se autoapelează funcţia an?
Indicaţii / Rezolvări
1. Sn=1+2+....n-1+n; Sn⇒Sn-1+n;
0, daca n = 0
Sn =
S n−1 + n, altfel
(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
11.
12.
15.
16.
27. a); 28. d); 29. b); 30. a); 31. a), b); 32. a); 33. d);
3 2
2 1 1 0
1 0
Acum, vedeţi “pe viu” motivul pentru care, în cazul unor astfel de formule de
recurenţă, în care, în expresie, intervin mai mulţi operanzi ce se calculează
recursiv, este de preferat metoda iterativă.
172
Capitolul 7
7.2. Aplicaţii
0, n = 1;
T(n) = n
2T + n, altfel.
2
Avem:
Rezolvare. Este necesară o funcţie POZ care tratează o porţiune din vector,
cuprinsă între indicii daţi de li (limita inferioară) şi ls (limita superioară). Rolul
acestei funcţii este de a poziţiona prima componentă a[li] pe o poziţie k cuprinsă
între li şi ls, astfel încât toate componentele vectorului cuprinse între li şi k-1
să fie mai mici sau egale decât a[k] şi toate componentele vectorului cuprinse
între k+1 şi ls să fie mai mari sau egale decât a[k].
• i=1, j=5;
• a[1]>a[5], deci se inversează elementele aflate pe poziţiile 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;
• funcţia se încheie, elementul aflat iniţial pe poziţia 1 se găseşte
acum pe poziţia 4, toate elementele din stânga lui fiind mai mici
decât el, totodată toate elementele din dreapta lui fiind mai mari
decât el (k=4).
După aplicarea funcţiei POZ, este evident că elementul care se află iniţial în
poziţia li va ajunge pe o poziţie k şi va rămâne pe acea poziţie în cadrul
vectorului deja sortat, fapt care reprezintă esenţa algoritmului.
• se apelează POZ;
• se apelează QUICK pentru li şi k-1;
• se apelează QUICK pentru k+1 şi ls.
Rezolvare.
Dacă n=1, se face mutarea ab, adică se mută discul de pe tija a pe tija b.
ab, n =1
H(n, a, b, c) =
H(n − 1, a, c, b), ab, H(n − 1, c, b, a), n >1
h
• xv(i),yv(i)
x,y l
Pentru a se afla în interiorul dreptunghiului, gaura trebuie să îndeplinească
simultan condiţiile:
1) xv(i)>x;
2) xv(i)<x+l;
3) yv(i)>y;
4) yv(i)<y+h.
Dacă facem o tăietură verticală prin această gaură, obţinem două dreptunghiuri:
1) x, y, xv(i)-x, h;
2) xv(i), y, l+x-xv(i), h.
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
initgraph(gdriver,gmode,‘cale’);.
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 iniţializare grafică poate eşua din diverse motive, cum ar fi: lipsa
unităţii GRAPH, calea indicată greşit, etc. Testarea se realizează cu funcţia întreagă
graphresult care returnează 0 în caz afirmativ şi o valoare diferită de 0, în
caz contrar.
Limbajul C++ (în varianta Borland), conţine o serie de funcţii care permit
realizarea unor aplicaţii grafice. Acestea sunt reunite în fişierul GRAPHICS.H, ce se
găseşte în folderul INCLUDE.
initgraph(&gdriver,&gmode,"cale");.
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ă):
Tentativa de iniţializare grafică poate eşua din diverse motive, cum ar fi: lipsa
unităţii GRAPHICS, calea indicată greşit, etc. Testarea se realizează cu funcţia
întreagă graphresult() care returnează 0 în caz afirmativ şi o valoare diferită
de 0, în caz contrar.
Odată intraţi în modul grafic, nu se mai poate scrie pe monitor ca până acum
(de exemplu, cu cout). Ieşirea din modul grafic se face prin utilizarea
procedurii closegraph().
Atenţie! Pentru a putea scrie şi rula programe C++ ce utilizează modul grafic al
limbajului, trebuie bifată următoarea opţiune, din meniu:
Aceste culori sunt cele implicite. Pentru a utiliza mai multe culori (dar nu în
acelaşi timp), se poate schimba setul (paleta) de culori. Întrucât î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
Observaţii
Operaţia de desenare
Fiecare latură a acestui poligon se transformă din nou, după aceeaşi regulă.
Să se vizualizeze figura obţinută după ls paşi (număr citit de la tastatură).
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. }
Priviţi mai jos rezultatele obţinute pentru diferite valori ale lui ls:
ls = 2 ls = 3 ls = 4
Figura 7.2. Exemple de fractali formaţi cu ajutorul curbei lui Koch (triunghi echilateral)
Manual de informatică pentru clasa a XI-a 191
Fiecare segment al liniei frânte astfel formate se transformă din nou după
aceeaşi regulă. Se cere să se vizualizeze curba după ls transformări (valoare
citită de la tastatură).
Sunt prezentate mai jos imaginile obţinute în urma rulării programului, pentru
diferite valori ale lui ls:
Figura 7.4. Exemple de fractali formaţi cu ajutorul curbei lui Koch (pătrat)
7.3.4. Arborele
Pentru diverse valori ale parametrului de intrare ls, vom obţine arborii:
ls = 3 ls = 5 ls = 7
Observaţii
Exemplele grafice prezentate au fost generate pentru valori mici ale lui ls
deoarece la tipărire, detaliile sunt greu de observat peste o anumită limită.
Generarea fractalilor reprezintă o aplicaţie a recursivităţii, 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 citeşte a≥1, număr real. Se cere să se scrie o funcţie care calculează ln(a)
cu 3 zecimale exacte. Nu este permisă utilizarea funcţiei logaritmice a limbajului.
2. Scrieţi o funcţie care calculează prin metoda DIVIDE ET IMPERA suma numerelor
reţinute dintr-un vector.
Răspunsuri
1. ln(a)=x ⇔ a=ex ⇔ ex-a=0. Dacă notăm cu f(x)=ex-a, atunci trebuie
rezolvată ecuaţia 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 rădăcină în intervalul (0,a). Cum f(x) este strict
crescătoare (ca diferenţă între funcţia strict crescătoare ex şi o constantă),
rădăcina este unică. Algoritmul pe care îl folosim se numeşte în matematică
“metoda înjumătăţirii 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, rădăcina se găseşte în
(li,m), altfel rădăcina este în [m,ls). Condiţia de terminare este ca
li − ls < 0.0001 , pentru că trebuie să avem 3 zecimale exacte.
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. }
0 n = 1;
T(n) = n
2T + 1 altfel.
2
5. Vedeţi problema 1.
6. Funcţia Poz returnează poziţia k pe care se va găsi, după rularea ei, primul
element al vectorului. În plus, toate elementele de indice mai mic decât k sunt mai
mici sau egale decât A[k] şi toate elementele de indice mai mare decât k sunt mai
mari sau egale decât A[k]. Altfel spus: elementul A[1], care se află după rularea
funcţiei pe poziţia 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 căutat are
indicele cuprins între li şi k-1 şi reluăm rularea funcţiei Poz între aceste limite, iar
dacă t>k, elementul căutat are indicele între k+1 şi ls şi reluăm rularea funcţiei
Poz între aceste limite. Datorită faptului că, la fiecare pas, se restrânge numărul
valorilor de căutare, se ajunge în situaţia în care t=k.
Secvenţa este:
Capitolul 8
Metoda BACKTRACKING
Principiul metodei
Pentru permutări, vom avea o soluţie când s-a generat o secvenţă alcătuită din n
numere distincte. Cum subprogramul este recursiv, acest fapt se întâmplă atunci
când s-a ajuns pe nivelul n+1.
De exemplu, dacă n=4, o soluţie este reprezentată în figura 8.1., a). Modul de
obţinere al soluţiei este prezentat în figurile următoare, de la b) la i):
a) b) c)
d) e) f)
g) h) i)
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.
Exerciţii
1. Desenaţi configuraţia tablei corespunzătoare vectorului sol=(3,1,4,2,5) şi
verificaţi dacă aceasta reprezintă o soluţie a problemei damelor.
Manual de informatică pentru clasa a XI-a 207
Este întotdeauna necesar să-l folosim pe acesta sau putem reţine numai
ideea şi, după caz, să scriem mai puţin?
Exerciţii
1. Testaţi subprogramul anterior pentru problema generării permutărilor.
2. Adaptaţi rezolvarea problemei permutărilor astfel încât să se afişeze numai
permutările în care oricare două numere consecutive nu sunt alăturate.
3. Observaţi că ordinea de afişare a soluţiilor depinde de ordinea în care se
consideră elementele mulţimilor A1, A2, … Ce modificări trebuie aduse
procedurii recursive back astfel încât permutarile de 4 elemente să fie afişate în
ordinea: 4321, 4312, 4231, 4213, 4132, 4123, 3421, 3412 … 1243, 1234?
4. Renunţaţi la utilizarea subprogramului valid, utilizând un vector folosit, în
care folosit[i] are valoarea 0 dacă numărul i nu este deja folosit în soluţie
şi are valoarea 1 în caz contrar. Astfel, plasarea valorii i în vectorul soluţie
(sol[k]i) trebuie însoţită de memorarea faptului că i este utilizat
(folosit[i]1), la revenirea din recursie (cand se înlătură valoarea de pe
poziţia curentă) fiind necesară memorarea faptului că i nu mai este utilizat în
soluţie (folosit[i]0). Condiţia de validare se reduce în acest caz la:
Dacă folosit[i]=0 atunci ...
5. Urmăriţi toate modalităţile diferite de a aşeza patru obiecte identificate prin
numerele 1, 2, 3, 4 pe un cerc, la distanţe egale. Vom observa că nu toate
permutările de patru obiecte sunt configuraţii distincte, datorită distribuţiei pe
cerc. Astfel permutările 1234, 2341, 3412 şi 4123 reprezintă una şi aceeaşi
configuraţie. Scrieţi un program care afişează numai permutările distincte
conform aşezării pe un cerc. Indicaţie: se va considera sol[1]=1 şi se vor
permuta doar celelalte elemente.
210 Capitolul 8. Metoda backtracking
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.
Exerciţii
1. Soluţia afişată este şi soluţia care utilizează un număr
minim de culori?
2. Dacă ţările din centrul figurii alăturate sunt numerotate
cu 1, 2, 3, 4, iar cele de la exterior cu 5 şi 6, care este
soluţia afişată de programul dat? Este acesta numărul
minim de culori necesare?
3. Câte culori sunt suficiente pentru colorarea unei hărţi
particulare în care orice ţară se învecinează cu cel Figura 8.5.
mult două ţări?
4. Daţi exemplu de particularitate pe care poate să o aibă o hartă pentru a fi
suficiente două culori pentru colorarea tuturor ţărilor?
Exemple
Observaţii
2. O altă interpretare pentru metoda backtracking: fiind date n mulţimi: A1, A2, ...,
An, produsul cartezian al lor A1×A2×...×An se mai numeşte spaţiul soluţiilor. În
acest context, metoda backtracking caută una sau toate soluţiile, care sunt
elemente ale produsului cartezian şi care îndeplinesc anumite condiţii. Astfel, se
poate justifica faptul că, în generarea produsului cartezian, nu este necesar
subprogramul valid pentru că se generează toate elementele produsului
cartezian, fără a verifica anumite condiţii.
Manual de informatică pentru clasa a XI-a 215
Deşi algoritmul este exponenţial, există aplicaţii utile, evident, atunci când
fiecare mulţime Ai poate lua numai câteva valori şi unde n este suficient de mic.
Exerciţii
1. Tabelarea anumitor funcţii. Se dă funcţia
f:A1×A2×...×An→R,
unde fiecare mulţime Ai este dată de numerele întregi din intervalul [ai,bi] şi
f=c1x1+c2x2+...+cnxn, ci∈R.
2. Scrieţi programul care generează toate ”cuvintele” cu patru litere care au prima
şi ultima literă vocale, litera a doua consoană din mulţimea {P, R, S, T}, iar a treia
literă consoană din mulţimea {B, M, R, T, V}.
3. Scrieţi programul care generează şi numără câte cuvinte de cinci litere ale
alfabetului englez se pot forma, cu condiţia să nu existe două consoane alăturate
şi nici două vocale alăturate.
1, pentru i ∈ A
V[i] =
0, pentru i ∉ A
De exemplu, dacă A={1,2,3}, pentru submulţimea {1,3} vom avea
V=(1,0,1). De aici, rezultă că problema se reduce la generarea tuturor valorilor
posibile pe care le poate reţine vectorul caracteristic.
Aceasta înseamnă că o soluţie este de forma x1,x2,...,xn, unde
xi∈{0,1}. Şi în acest caz, orice valoare ar reţine componenta i, ea nu trebuie să
îndeplinească nici o condiţie de continuare, motiv pentru care subprogramul valid
nu este necesar.
begin
write('n=');
readln(n);
back(1)
end.
Exerciţii
1. Problema nu este rezolvată în totalitate. Programul afişează numai toate valorile
pe care le poate lua vectorul caracteristic. Completaţi-l astfel încât programul să
afişeze toate submulţimile mulţimii {1,2...n}!
Observaţii
⋅ 2
2 ⋅ 2...2
=2 .
n
de n ori
n!
C pn = .
(n − p)! p!
Rezolvare. O soluţie este de forma x1,x2,...,xp, unde x1, x2, ..., xp∈A.
În plus, x1, x2, ..., xp trebuie să fie distincte. Cum la o mulţime ordinea elementelor
nu prezintă importanţă, putem genera elementele ei în ordine strict crescătoare.
Această observaţie ne ajută foarte mult în elaborarea algoritmului.
a) Pentru k>1, sol[k]>sol[k-1].
218 Capitolul 8. Metoda backtracking
sol[k+1]>n-p+k+1,
...
sol[p]>n-p+p=n.
1≤sol[1]≤n-p+1,
sol[1]<sol[2]≤n-p+2,
...
sol[n-1]<sol[n]≤n-p+p=n.
Relaţiile de mai sus simplifică mult algoritmul, pentru că ţinând cont de ele, nu mai
este necesar să se testeze nici o condiţie de continuare.
Exerciţii
1. Se dau coordonatele din plan a n puncte. Afişaţi coordonatele vârfurilor tuturor
pătratelor care au ca vârfuri puncte din mulţimea considerată.
2. Se dau n substanţe chimice. Se ştie că, în anumite condiţii, unele substanţe intră
în reacţii chimice cu altele. Fiind date p perechi de forma (i,j) cu semnificaţia că
substanţa i intră în reacţie cu substanţa j, se cer toate grupurile de s<n substanţe
astfel încât oricare două substanţe din grup nu intră în reacţie.
Exemplu: p=2, n=3. Avem: 12, 21, 13, 31, 23, 32. De exemplu, 21 este funcţia
f:A→B dată astfel: f(1)=2; f(2)=1. Avem relaţiile:
n!
A pn = = n(n − 1)...(n − p + 1) .
(n − p)!
Pe de altă parte, se poate lucra mult mai eficient. O soluţie este de forma:
x1x2...xp, unde x1, x2, ..., xp∈B. În plus, x1, x2, ..., xp trebuie să fie distincte.
Spre deosebire de algoritmul de generare a combinărilor, aici ne interesează toate
permutările unei soluţii (acestea sunt, la rândul lor, alte soluţii). Aceasta înseamnă
că nu mai putem pune în soluţie elementele în ordine crescătoare. Să recapitulăm:
Exerciţii
1. Se citesc n, p şi apoi n litere distincte. Afişaţi toate cuvintele care se pot forma
cu p dintre ele.
Rezolvare. Chiar dacă ştim să generăm toate submulţimile unei mulţimi, tot nu ne
ajută să generăm toate partiţiile.
1. Pentru a putea genera toate partiţiile, trebuie să găsim o metodă prin care să
putem reţine o partiţie. O primă idee ne conduce la folosirea unui vector, sol, astfel:
dacă sol[i]=k, atunci elementul i se găseşte în mulţimea k a partiţiei. Totuşi, nu
ştim câte mulţimi sunt în partiţia respectivă. Există o partiţie care conţine n mulţimi
atunci când fiecare element este într-o mulţime şi una care conţine toate mulţimile,
adică tocmai mulţimea A. Cu alte cuvinte, numărul mulţimilor dintr-o partiţie este
între 1 şi n.
Prin această condiţie se evită situaţia în care, de exemplu, vectorul sol reţine
(1,3,1). Aceasta ar avea semnificaţia că elementele 1 şi 3 se găsesc în
submulţimea 1 a partiţiei, iar elementul 2 se găseşte în submulţimea 3 a partiţiei. În
acest caz, lipseşte submulţimea 2 a partiţiei.
- 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}.
begin
write('n='); readln(n);
back(1);
end.
8.5.1. Generalităţi
Toate problemele pe care le-am întâlnit până acum admit soluţii care
îndeplinesc următoarele caracteristici:
soluţiile sunt sub formă de vector;
toate soluţiile unei probleme au aceeaşi lungime, unde prin lungime
înţelegem numărul de componente ale vectorului soluţie.
224 Capitolul 8. Metoda backtracking
c) Toate soluţiile sub formă de vector ale problemei generării tuturor partiţiilor
mulţimii A au lungimea n.
Ordinea numerelor din sumă este importantă. Astfel, se tipăreşte 112 dar
şi 211, 121.
sol[1]+sol[2]+...sol[k]≤n.
sol[1]+sol[2]+...sol[k]=n.
soluţie
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
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
Observaţi modul în care calculăm suma la fiecare pas. De câte ori se trece
la componenta următoare (k+1), la s se adună sol[k], de câte ori se face
pasul înapoi (se trece la componenta k-1), din s se scade sol[k].
Exerciţii
1. Cum trebuie procedat în cazul în care se cere ca soluţiile să fie afişate o singură
dată? Spre exemplu, dacă s-a afişat descompunerea 1,1,2 să nu se mai afişeze
2,1,1 sau 1,2,1?
Indicaţie: procedeul a mai fost întâlnit, de exemplu la generarea combinărilor.
Soluţiile se vor genera în ordine crescătoare. Modificaţi programul în acest sens.
2. Adaptaţi metoda de rezolvare astfel încât să se genereze numai partiţiile formate
din numere naturale distincte.
3. Adaptaţi metoda de rezolvare astfel încât să se genereze numai partiţiile formate
din cel puţin p numere naturale distincte (n şi p citite de la tastatură).
4. Adaptaţi metoda de rezolvare astfel încât să se genereze numai partiţiile formate
din numere naturale aflate în intervalul [a,b] (n, a şi b citite de la tastatură).
5. Rezolvaţi problema scrierii numărului natural n ca sumă de numere naturale
alese dintr-o mulţime formată din k valori date {v1, v2, …, vk}. Astfel, 10 se
poate scrie ca sumă de numere alese din mulţimea {2,3,6} în felul următor:
10=2+2+2+2+2, 10=2+2+3+3, 10=2+2+6.
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;
Ce observăm?
Manual de informatică pentru clasa a XI-a 227
1. Există componente ale vectorului sol care reţin 0. Această situaţie corespunde
cazului în care moneda respectivă nu este luată în calcul. Din acest motiv, fiecare
componentă a vectorului sol va fi iniţializată cu o valoare aflată înaintea tuturor celor
posibile, adică cu -1.
Fie l(i,j) un element al matricei. Acesta poate lua valori între 0 şi 15. Se
consideră ieşirile spre nord, est, sud şi vest, luate în această ordine. Pentru fiecare
direcţie cu ieşire se reţine 1, iar în caz contrar, se reţine 0. Un şir de patru cifre 1
sau 0 formează un număr în baza 2. Acest număr este convertit în baza 10 şi
reţinut în l(i,j). De exemplu, pentru o cameră care are ieşire în nord şi vest,
avem 1001(2)=9(10).
Rezolvare
1. O cameră vizitată se reţine prin coordonatele ei: lin (linia) şi col(colana). Din
acest motiv, pentru a reţine un traseu vom utiliza o matrice cu două coloane şi mai
multe linii: sol. De exemplu, dacă camera iniţială este cea de coordonate (2,2)
o soluţie este (2,2), (2,3), (1,3).
Manual de informatică pentru clasa a XI-a 229
Exerciţii
1. Adaptaţi rezolvarea pentru un labirint în care fiecare căsuţă reţine valoarea 1
sau 0 (1 semnificând căsuţă plină, prin care nu se poate trece, iar 0 căsuţă liberă,
pe unde se poate trece). Ca şi în problema prezentată, deplasarea se poate face
dintr-o căsuţă în orice altă căsuţă alăturată, orizontal sau vertical, cu condiţia ca ea
să existe şi să fie liberă. Validaţi poziţia iniţială a omului (lin, col), astfel încât
aceasta să corespundă unei căsuţe libere. Estimaţi spaţiul de memorie utilizat
în această variantă.
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
10. Fiind dat un număr natural pozitiv n, se cere să se producă la ieşire 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 câmpuri
sunt “arse“, poziţiile lor fiind cunoscute. Calul nu poate călca pe câmpuri “arse“,
iar orice mişcare a calului face ca respectivul câmp să devină “ars“. Să se afle
dacă există o succesiune de mutări permise (cu restricţiile de mai sus), prin
care calul să poată ajunge la rege şi să revină la poziţia iniţială. Poziţia iniţială
a calului, precum şi poziţia regelui sunt considerate “nearse“.
12. Se dau n puncte în plan prin coordonatele lor. Se cer toate soluţiile de unire a
acestor puncte prin exact p drepte, astfel încât mulţimea punctelor de
intersecţie ale acestor drepte să fie inclusă în mulţimea celor n puncte.
17. Se dau N puncte albe şi N puncte negre în plan, de coordonate întregi. Fiecare
punct alb se uneşte cu câte un punct negru, astfel încât din fiecare punct, fie el
alb sau negru, pleacă exact un segment. Să se determine o astfel de
configuraţie de segmente astfel încât oricare două segmente să nu se
intersecteze. Se citesc N perechi de coordonate corespunzând punctelor albe
şi N perechi de coordonate corespunzând punctelor negre.
19. O trupă cu N actori îşi propune să joace o piesă cu A acte astfel încât:
24. Fiind date numele a n soldaţi, ce algoritm vom utiliza pentru a lista toate
grupele de câte k soldaţi? Se ştie că într-o grupă, ordinea prezintă importanţă.
25. Fiind date n numere naturale, ce algoritm vom utiliza pentru a determina
eficient o submulţime maximală de numere naturale distincte?
Indicaţii
23. b)
24. a)
25. d)
Explicaţie: primele două variante prezintă soluţii exponenţiale, a treia este în
O(n2). Dar dacă sortăm numerele, atunci le putem afişa pe cele distincte dintr-o
singură parcurgere. Sortarea se poate efectua în O(n×log(n)), iar parcurgerea
în O(n). Prin urmare, complexitatea este O(n×log(n)).
26. d)
Explicaţie: pe fiecare nivel al stivei se caută succesorii în ordinea n, n-1, ..., 1.
239
Capitolul 9
9.1.1. Introducere
1. Se dau n oraşe. Unele dintre ele sunt unite prin şosele directe (care nu mai
trec prin alt oraş).
2. Se cunosc relaţiile de prietenie dintre n persoane.
3. Se dau n ţări şi se cunoaşte relaţia de vecinătate între ele.
4. Se dau n triunghiuri, iar unele dintre ele sunt asemenea.
Observaţii
Două noduri distincte pot fi unite prin cel mult o muchie. În exemplul de
mai sus, (1,2) este muchia care uneşte nodul 1 cu nodul 2. Dacă scriem
(2,1), ne referim la aceeaşi muchie (perechea este neordonată).
Definiţia este restrictivă, în unele lucrări veţi întâlni definiţii mai puţin restrictive, de
1
Definiţia 9.3. Într-un graf neorientat, prin gradul unui nod v se înţelege
numărul muchiilor incidente cu nodul v şi se notează cu d(v). Un nod
cu gradul 0 se numeşte nod izolat, iar unul cu gradul 1 se numeşte nod
terminal.
O relaţie utilă: fie un graf neorientat cu n noduri şi m muchii. Dacă notăm cu d1,
d2, ..., dn gradele celor n noduri, atunci avem relaţia:
d 1 + d 2 + d 3 + ...d n = 2m.
- fie afirmaţia: nodul i este izolat. Pentru exemplul 1., înseamnă că nu există nici
o şosea care leagă oraşul 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.
Exerciţiu
Daţi un exemplu inspirat din viaţa reală, pentru care să găsiţi graful asociat.
Astfel, veţi răspunde la întrebările: ce semnificaţie au nodurile sau muchiile şi ce
înseamnă gradul unui nod.
242 Capitolul 9. Introducere în teoria grafurilor
În acest paragraf, prezentăm principalele structuri de date prin care grafurile pot
fi memorate în vederea prelucrării lor. De la început, precizăm faptul că vom alege o
2
structură sau alta în funcţie de :
a) algoritmul care prelucrează datele referitoare la graf;
Pentru fiecare structură de date pe care o vom folosi, vom avea câte o
procedură (funcţie) care citeşte datele respective. Toate aceste subprograme se
găsesc grupate în unitatea de program grafuri.pas (pentru Pascal) şi în
grafuri.cpp (pentru C++). Vom fi astfel scutiţi ca, pentru fiecare program pe care îl
realizăm, să fim nevoiţi să adăugăm liniile de cod necesare citirii şi ne permite să ne
concentrăm exclusiv asupra algoritmului.
6
Fişierul 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
2
Modul de alegere a structurii îl veţi înţelege pe parcursul studiului acestui capitol.
Manual de informatică pentru clasa a XI-a 243
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
Observaţii
1. Întrucât, din modul în care a fost definit graful, rezultă că nu există muchii de la un
nod la el însuşi, rezultă că elementele de pe diagonala principală reţin 0:
ai ,i = 0, ∀i ∈ {1,2,..., n} .
ai , j = a j ,i , ∀i, j ∈ {1,2,..., n} .
6. Dacă graful citit are un număr mic de muchii, atunci matricea de adiacenţă este o
formă ineficientă de memorare a lui, pentru că ea va reţine o mulţime de 0.
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
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
Mai jos, puteţi observa cum se descrie un vector (V) care reţine muchiile
unui graf:
Să considerăm mulţimea elevilor unei clase. Teoretic, oricare doi elevi din
clasă se cunosc. Pentru a transpune în limbaj specific teoriei grafurilor această
situaţie, 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 obţinut un graf aparte, pe care-l vom numi graf complet.
Definiţia 9.4. Prin graf complet vom înţelege un graf neorientat în care
oricare două noduri sunt adiacente. Vom nota un graf complet prin Kn, unde
n este numărul de noduri ale grafului.
248 Capitolul 9. Introducere în teoria grafurilor
2 3
Figura 9.5. 4
Exemplu de graf complet
Relaţii utile:
1. Într-un graf complet, gradul oricărui nod este n-1. Evident, din fiecare nod,
pleacă (sosesc) n-1 muchii.
n(n − 1)
2. Într-un graf complet, avem relaţia: m= , unde m este numărul de
2
muchii, iar n, numărul de noduri.
n(n − 1)
2
şi corespunde unui graf complet. Se ştie că, fiind dată o mulţime A cu n elemente,
avem 2n submulţimi disjuncte ale acesteia (aici este inclusă şi submulţimea vidă
şi A). Prin urmare, avem:
n(n−1)
2 2
Definiţia 9.5. Un graf parţial al unui graf neorientat dat G=(V,E) este un
graf G1=(V,E1), unde E1⊆E.
Manual de informatică pentru clasa a XI-a 249
Un graf parţial al unui graf dat, este el însuşi sau se obţine din G prin
suprimarea anumitor muchii. Priviţi exemplul de mai jos:
Figura 9.6.
Obţinerea unui 1 1
graf parţial
2 3 3
rezultă 2
4 4
G=(V,E) G1=(V,E1)
1. Fiind date n persoane, între unele dintre ele există o relaţie de prietenie. Asociem
acestei situaţii 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 obţine un graf
parţial G1.
2. Fiind date n oraşe, unele dintre ele sunt unite printr-o şosea directă (care nu mai
trece prin alte oraşe). Asociem situaţiei date un graf G. Datorită precipitaţiilor, anumite
şosele se inundă şi nu mai pot fi utilizate. Aceasta înseamnă că în G se suprimă
anumite muchii şi se obţine un graf parţial G'.
Un subgraf al unui graf G este el însuşi sau se obţine din G prin suprimarea
anumitor noduri şi a tuturor muchiilor incidente cu acestea. Priviţi exemplul de
mai jos:
Figura 9.7. 1
Obţinerea unui 1
subgraf
2 3 3
rezultă
4 4
G=(V,E) G1=(V1,E1)
250 Capitolul 9. Introducere în teoria grafurilor
1. Se dau n firme. Între unele din acestea se stabilesc relaţii de colaborare. Asociem
situaţiei date un graf G. Între timp, anumite firme se desfiinţează. Aceasta înseamnă
că în G vom elimina anumite noduri şi muchiile incidente lor, obţinând un subgraf al
lui G, G1.
2. Mai multe calculatoare (n) sunt legate în reţea cu ajutorul unor cabluri. Asociem
situaţiei date un graf G. Între timp, anumite calculatoare se defectează. Astfel, se
obţine un subgraf al lui G, G1.
...
Parcurgerea continuă în acest mod până când au fost vizitate toate nodurile
accesibile.
3
În acest paragraf vom exemplifica parcurgerile doar în cazul grafurilor conexe. Cum
noţiunea nu a fost prezentată până în acest moment, precizăm 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
1
Pentru graful alăturat avem:
2 6 3 Nod pornire 1: 1 3 6 2 7 5 4
Nod pornire 3: 3 7 6 1 2 5 4
Nod pornire 6: 6 3 1 7 2 5 4
4 5 7
Figura 9.8.
1
Pentru graful alăturat, 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
9.1.7. Lanţuri
Definiţia 9.8. Se numeşte lanţ elementar un lanţ care conţine numai noduri
distincte.
1
Exemple: pentru graful din figura alăturată:
1. [1,2,5] este un lanţ elementar cu lungime 2,
între nodurile 1 şi 5. 2 4 3
Problema 9.1. Fiind dat un graf şi două noduri ale sale a şi b, să se scrie un
program care decide dacă între ele există un lanţ sau nu, iar în caz că acest lanţ
există, se cere să se afişeze lanţul.
a) pentru exemplul 1, întrebarea este: cum putem afla, pentru fiecare oraş în parte,
oraşele în care putem ajunge cu maşina?
b) pentru exemplul 4, întrebarea este: cum putem afla, pentru fiecare triunghi în
parte, care sunt triunghiurile asemenea cu el?
Problema 9.2. Fiind dat un graf G, cum putem obţine matricea lanţurilor?
Răspunsul este uşor de dat. Parcurgem graful începând cu nodul 1. Pentru toate
nodurile j vizitate, vom avea L(1,j)=1, completând astfel prima linie a matricei.
Apoi, vom parcurge din nou, graful, pornind de la nodul 2. Pentru toate nodurile j,
vizitate, vom avea L(2,j)=1, apoi parcurgem graful începând cu nodul 3.... ş.a.m.d.
O anumită îmbunătăţire a algoritmului se obţine dacă ţinem cont de faptul că matricea
lanţurilor este simetrică (de ce?). Lăsăm ca exerciţiu scrierea acestui program.
Întrebare: care este complexitatea acestui algoritm?
Definiţia 9.9. Un graf neorientat G=(V,E) este conex, dacă pentru orice
pereche de noduri x,y∈V, există un lanţ în care extremitatea iniţială este x
şi extremitatea finală este y.
2 3 5
Un graf cu un singur nod este, prin definiţie, conex. Aceasta pentru că nu există
două noduri diferite pentru care să se pună problema existenţei unui lanţ.
260 Capitolul 9. Introducere în teoria grafurilor
Problema 9.3. Fiind dat un graf G=(V,E), să se scrie un program care să decidă
dacă graful dat este sau nu conex.
Rezolvare. Ţinând cont de cele învăţate, problema nu este grea. Putem utiliza
una din metodele de parcurgere învăţate, DF sau BF. Ideea este următoarea: dacă,
pornind de la un nod, printr-una din metodele de parcurgere, ajungem să vizităm toate
celelalte noduri, atunci graful dat este conex. Cum putem şti dacă am vizitat toate
nodurile? Simplu, după parcurgere, toate componentele vectorului s reţin 1. Puteţi
scrie acest program?
Observaţii
Rezolvare. După cum uşor vă puteţi da seama, o parcurgere a grafului (DF sau
BF) pornind de la un anumit nod, vizitează toate nodurile componentei conexe care
îl conţine. Pentru fiecare nod vizitat, s[i] reţine 1. Dacă, după o parcurgere, mai
rămân noduri nevizitate, parcurgerea se reia începând de la primul nod nevizitat.
Evident, numărul componentelor conexe este egal cu numărul de parcurgeri
necesare pentru a fi vizitate toate nodurile.
9.1.10. Cicluri
Problema 9.5. Fiind dat un graf conex, G=(V,E), să se scrie un program care
decide dacă graful conţine cel puţin un ciclu.
Rezolvare. Începem prin a observa că dacă graful nu este conex, putem rezolva
problema verificând dacă există un ciclu într-o componentă conexă a sa. Pentru
simplitate, am preferat să considerăm că graful este conex. Şi aici, problema se poate
rezolva pornind de la o parcurgere DF. Graful conţine cel puţin un ciclu dacă, în
timpul parcurgerii, algoritmul va ajunge în situaţia de a vizita un nod de două ori
(tentativă oricum respinsă, pentru că algoritmul testează acest lucru, vedeţi rolul
vectorului s). Vom da un exemplu, cu graful de mai jos, pe care îl parcurgem DF.
Observaţie
unde m este numărul de muchii, iar n este numărul de noduri. Dacă relaţia este
verificată, înseamnă că graful nu conţine cicluri, altfel, dacă m>n-1 înseamnă că
graful conţine cel puţin un ciclu, iar dacă m<n-1 înseamnă că nu este conex, şi ar
contrazice cerinţa.
9.1.11. Arbori
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. }
Demonstraţie
⇒ Fie G un arbore (graf neorientat, conex şi fără cicluri). Trebuie să demonstrăm
că are n-1 muchii. Vom demonstra prin inducţie. Dacă n=1, numărul muchiilor
este 0 (se verifică, are n-1 muchii). Vom presupune proprietatea adevărată
pentru arbori cu n noduri (adică au n-1 muchii). Fie un arbore cu n+1 noduri.
Există cel puţin un nod terminal (nod care are o singură muchie incidentă).
Dacă nu ar exista un astfel de nod, să considerăm un lanţ care porneşte
dintr-un nod oarecare. La fiecare pas, vom selecta o muchie. Până la urmă,
pentru că mulţimea nodurilor este finită şi pentru că nu există nod terminal,
lanţul va trece de două ori printr-un acelaşi nod. Asta înseamnă că arborele ar
conţine cicluri (absurd, se contrazice definiţia). Eliminăm nodul terminal şi
muchia care îi este incidentă. Obţinem un arbore cu n noduri. Conform ipotezei
făcute, acesta va avea n-1 muchii. Înseamnă că arborele cu n+1 noduri va
avea n muchii (n-1+1).
Observaţii
Definiţia 9.15. Într-un graf orientat, prin gradul exterior al unui vârf v vom
înţelege numărul arcelor incidente spre exterior cu v. Gradul exterior al
unui nod va fi notat cu d+(v).
Definiţia 9.16. Într-un graf orientat, prin gradul interior al unui nod v vom
înţelege numărul arcelor incidente spre interior cu v.
Gradul interior al unui nod va fi notat cu d-(v).
d+(i)=3 şi
d-(i)=2. Figura 9.21.
Manual de informatică pentru clasa a XI-a 269
Demonstraţie. Relaţia este adevărată, pentru că fiecare arc este incident spre
exterior cu un vârf şi fiecare arc este incident spre interior cu un vârf.
Demonstraţia se face prin inducţie. 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 adevărată, adică dacă sunt n vârfuri, avem
n ( n −1)
2
4
grafuri orientate. Trebuie să demonstrăm că dacă sunt n+1 vârfuri, avem
n ( n +1)
2
4
grafuri orientate. Adăugăm vârful n+1. Acest vârf poate fi adiacent cu fiecare
dintre celelalte n vârfuri în exact 3 moduri (vedeţi adiacenţa) sau poate să nu fie
adiacent. Atunci, numărul de grafuri orientate cu n+1 noduri este
n ( n −1) n ( n −1) n ( n +1)
+n
4 2 ×4 =
n
4 2 = 4 2 .
Definiţia 9.17. Un graf orientat este complet dacă oricare două vârfuri i
şi j (i≠j) sunt adiacente.
270 Capitolul 9. Introducere în teoria grafurilor
n ( n −1)
2
O relaţie utilă: avem 3 grafuri complete. Demonstraţia se face prin inducţie!
Exerciţiu!
Pentru fiecare structură de date pe care o vom folosi, vom avea câte o
procedură (funcţie) care citeşte datele respective. Toate aceste subprograme se
găsesc grupate în unitatea de program grafuri.pas (pentru Pascal) şi în
grafuri.cpp (pentru C++).
Toate subprogramele pe care le utilizăm citesc datele dintr-un fişier text, în care
pe prima linie vom scrie numărul de noduri (n), iar pe următoarele linii câte o muchie
(i, j) ca în exemplul următor, în care este prezentat un graf şi liniile fişierului text
care este citit pentru el:
Fişierul 6
1 text: 1 2
6
1 3
1 5
2 2 3
5 3 4
3 4 5
4
Figura 9.22.
Definiţia 9.18. Un graf parţial al unui graf orientat dat G=(V,A) este un
graf G1=(V,A1), unde A1⊆A.
Un graf parţial al unui graf dat, este el însuşi, sau se obţine din G prin
suprimarea anumitor arce.
1 1
2 3 3
2
rezultă
4 4
Figura 9.23.
Obţinerea unui
graf parţial 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 parţial al grafului iniţial.
Un subgraf al unui graf G este graful G sau se obţine din G prin suprimarea
anumitor vârfuri şi a tuturor arcelor incidente cu acestea.
1 1
2 3 3
rezultă
4 4
Figura 9.24.
Obţinerea unui G1=(V1,A1)
subgraf G=(V,A)
Definiţia 9.20. Graful orientat G=(V,A) este tare conex dacă ∀x,y∈V,
∃ drum de la x la y şi drum de la y la x.
Figura 9.26.
Problema 9.8. Fie un graf orientat G=(V,A), memorat prin intermediul matricei de
adiacenţă. Se cere să se determine vârfurile fiecărei componente tare conexă.
Rezolvare
a) Vom numi succesori ai vârfului i, toate nodurile j, pentru care există drum de la
i la j, la care se adaugă i. De exemplu, pentru graful dat., succesorii vârfului 1 sunt
vârfurile 1, 2, 3 şi 4. Pentru a determina toţi succesorii vârfului i, vom efectua o
parcurgere DF a grafului pornind de la acest vârf. Succesorii nodului i vor fi reţinuţi în
vectorul suc.
276 Capitolul 9. Introducere în teoria grafurilor
c) Dacă un vârf este simultan succesor şi predecesor al lui i, atunci el va face parte
din componenta tare conexă a vârfului i. Mulţimea nodurilor cu această proprietate va
fi o componentă tare conexă a grafului. De ce? Pentru că între două vârfuri k şi l,
există atât drum de la k la l (de la k la i şi de la i la l) cât şi drum de la l la k (de la
l la i şi de la i la k). Mulţimea nodurilor cu această proprietate este maximală în
raport cu relaţia de incluziune. Dacă, prin absurd, ar mai exista un vârf cu această
proprietate, care nu aparţine acestei mulţimi, atunci ar trebui să existe drum de la i la
el, şi de la el la i, caz în care acesta ar fi fost găsit prin procedeul dat.
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
Probleme propuse
1. O cunoştinţă mi-a zis: la mine în birou suntem 5 persoane. Fiecare dintre noi
colaborează cu exact 3 persoane. A zis adevărul?
2. Demonstraţi că într-un graf neorientat numărul nodurilor de grad impar este par.
3. Fiind date n persoane şi m relaţii de prietenie între ele de forma: persoana i este
prietenă cu persoana j, se cere să se stabilească corespondenţele între afirmaţiile
din stânga şi cele din dreapta.
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 numărul minim de
robinete care asigură umplerea tuturor bazinelor.
6.2. Care este numărul minim de ţevi prin care se pot uni două bazine, astfel încât să
se poată umple toate bazinele cu un singur robinet. Daţi exemple de bazine unite care
asigură cerinţa problemei.
7. Fiind dat un grup de n persoane, în care dintre situaţiile de mai jos se poate
folosi pentru modelare un graf neorientat?
a) Unele persoane din grup cunosc alte persoane din grup;
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.
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 adevăr a afirmaţiilor de mai jos (A, adevărat, iar F, fals):
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
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 numărul minim şi care este numărul maxim de componente conexe
pe care le poate avea un graf neorientat cu 8 noduri şi 6 muchii?
16. Care este numărul de cifre 0 pe care îl reţine matricea de adiacenţă a unui graf
neorientat cu n noduri şi m muchii?
17. Care este numărul minim şi numărul maxim de noduri izolate pe care îl poate
avea un graf neorientat cu 10 noduri şi 10 muchii?
18. Care este numărul de grafuri neorientate cu 5 noduri?
19. Precizaţi care dintre afirmaţiile urmă-
toare sunt adevărate şi care sunt false. 1 3
Toate afirmaţiile se referă la graful din 2
figura 9.32.
19.1. "6 1 2 3 5 4 7" reprezintă o 5
parcurgere în adâncime a grafului. 4
6
19.2. "3 2 5 1 4 6 7" reprezintă o 7
parcurgere în lăţime a grafului. Figura 9.32.
19.3. Există două noduri din graf pentru care nu există un lanţ care le uneşte.
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. Numărul minim de muchii care trebuie eliminate pentru a obţine un arbore
parţial este 2.
19.7. Numărul maxim de muchii care pot fi eliminate astfel încât graful să rămână
conex este 3.
19.8. Numărul minim de muchii care pot fi eliminate pentru ca graful să nu conţină
cicluri este 3.
19.9. Un arbore parţial al grafului dat are 7 muchii.
282 Capitolul 9. Introducere în teoria grafurilor
20. Precizaţi dacă afirmaţiile de mai jos sunt adevărate sau false.
20.1. Cu ajutorul parcurgerii în adâncime se poate determina dacă un graf neorientat
are cel puţin un ciclu.
20.2. Orice graf neorientat are un graf parţial care este arbore.
20.3. Cu ajutorul parcurgerii în lăţime se poate determina dacă un graf este conex.
20.4. Orice graf neorientat cu 10 noduri şi 9 muchii este un arbore.
20.12. Orice graf complet este alcătuit dintr-o singură componentă conexă.
20.13. Din orice graf complet, prin eliminarea anumitor muchii se poate obţine 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 afişeze
toate nodurile care au gradul maxim.
22. Să se scrie un subprogram care transformă matricea de adiacenţă a unui graf în
liste de adiacenţe.
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 conţine sau nu cicluri.
26. Se dă un graf memorat prin matricea de adiacenţă şi un nod al său, v. Se cere
să se parcurgă graful în lăţime, pornind de la nodul v. Algoritmul va utiliza coada
creată ca listă liniară simplu înlănţuită implementată static.
Manual de informatică pentru clasa a XI-a 283
34. Se dau n mulţimi de numere naturale: A1, A2...An. Acestor mulţimi li se asociază
un graf orientat astfel: dacă mulţimea Ai este inclusă în mulţimea Aj, în graful asociat
vom avea arcul (Ai,Aj). Nu vom considera cazul de incluziune a unei mulţimi în ea
însăşi. Stabiliţi corespondenţa dintre operaţiile din stânga şi cele din dreapta.
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.
41. Care este numărul minim de arce care trebuie adăugate 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 afişeze, pentru fiecare vârf î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 vârfuri 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 adiacenţe ale unui graf orientat. Programul va afişa matricea de
adiacenţă.
47. Se dă matricea de adiacenţă a unui graf orientat. Programul va afişa listele de
adiacenţe 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 pătratice, L.
L(i,j) =-1 dacă prin camera respectivă nu se poate trece şi 0 în caz contrar. Să
se afişeze distanţele minime de la camera de coordonate (l,c) la toate camerele
accesibile din camera iniţială.
*
51 . La fel ca la problema anterioară. Se cere drumul care trece printr-un număr
minim de camere între o cameră iniţială şi una finală.
52 . Pe o tablă de şah de dimensiuni nxn se poate deplasa un nebun conform
*
regulilor obişnuite 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 tipărească numărul minim de mutări necesare. Se citesc: N, X1,
Y1, X2, Y2, apoi perechile de coordonate ale obstacolelor.
*
53 . Sortare în limita posibilităţilor. Se consideră că într-un vector V cu n
componente se pot inversa numai conţinuturile anumitor componente dintre cele n. O
pereche de componente de indice i şi j ale căror conţinuturi se pot inversa este dată
de perechea i şi j. Fiind date m astfel de perechi şi ştiind că vectorul conţine
numerele 1, 2, …, n într-o ordine oarecare, se cere ca vectorul să conţină numerele
1, 2, …, n sortate. Pentru sortare se inversează numai conţinuturile componentelor
care se pot inversa (care sunt perechi dintre cele m). Dacă sortarea este posibilă, se
286 Capitolul 9. Introducere în teoria grafurilor
vor afişa indicii componentelor care se inversează, iar dacă sortarea nu este posibilă,
se afişează Nu. Datele de intrare se găsesc în fişierul text date.in astfel:
Linia 1 n
Linia 2 1, ..., n într-o ordine oarecare;
Linia 3 m
următoarele m linii conţin fiecare câte o pereche de indici i, j.
Exemplu:
3 Programul va afişa:
3 1 2 1 2
2 2 3
2 3
1 2
*
54 . Lucrare în echipă. Se doreşte scrierea unei aplicaţii de informare a călătorilor
privind transportul în comun într-un oraş. Se cunosc cele n staţii de autobuz din oraşul
respectiv. De asemenea, se ştie traseul a k linii de autobuz (staţiile prin care acestea
trec). Se cere ca aplicaţia să furnizeze modul în care o persoană se poate deplasa cu
autobuzul între două staţii date, în ipotezele:
a) În număr minim de staţii.
b) Prin utilizarea unui număr minim de linii de autobuz.
Răspunsuri
7. Pentru a putea modela anumite relaţii cu ajutorul unui graf neorientat trebuie ca
relaţia existentă între i şi j să fie reciprocă, pentru că muchia (i,j) presupune că i
este în relaţie cu j şi j este în relaţie cu i. Dacă i cunoaşte 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,
răspunsul este d) pentru că relaţia 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ă puteţi orienta după gradele
vârfurilor. Evident, graful reprezentat de matricea de adiacenţă trebuie să aibă
vârfurile cu aceleaşi grade cu vârfurile grafului reprezentat în desen.
11. d) Desigur, puteţi desena graful, dar, mai uşor, eliminaţi variantele în care
aveţi 1 pe doagonala principală, sau acelea în care matricea nu este simetrică.
12. 8. Dacă matricea este dată corect, nu este nevoie să desenaţi graful pentru
ca, apoi, să-i număraţi muchiile. Se ştie că suma gradelor tuturor nodurilor este
egală cu dublul numărului de muchii. Prin urmare, este suficient să însumaţi
elementele reţinute de matrice si să împărţiţi 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ă eliminăm nodul din
centru, se obţin 4 noduri izolate. Oricare alt nod am elimina, rămân 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 văzut faptul că suma
tuturor cifrelor de 1 (adică a gradelor vârfurilor) este 2m (unde m este numărul 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
(lăsăm un singur nod).
29., 30. Descompunerea unui graf în componente conexe.
31. Backtracking. O soluţie 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
50. Se poate lucra direct pe matricea L. Idee: pentru camera iniţială 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 obţine această marcare vom parcurge în lăţime graful asociat.
Putem evita memorarea acestuia. Vom introduce în coadă coordonatele camerei
iniţiale. Vom încărca în coadă coordonatele tuturor camerelor vecine pentru care
L(i,j)=1. Pentru fiecare astfel de cameră, pentru care, iniţial, L(i,j)=0, vom avea
L(i,j)=2. Se trece apoi la următorul element din coadă cu care se procedează
asemănător. Se ştie că, prin parcurgerea în lăţime, se vizitează nodurile în
ordinea lungimii drumului, de la ele la nodul iniţial. Deducem, astfel, că marcarea
este corectă. Algoritmul se termină când coada este vidă. În final, se afişează
matricea L.
53. Asociem problemei un graf neorientat. Nodurile sunt indicii elementelor vectorului,
de la 1 la n. Când conţinuturile a două elemente se pot inversa, nodurile
corespunzătoare sunt unite printr-o muchie. Dacă nodurile i1, i2, ..., ik sunt unite
printr-un drum: atunci interschimbările (i1, i2), (i2, i3), ..., (ik-1, ik),
(ik-1, ik-2), ..., (i2, i1) inversează conţinuturile elementelor de indice i1 şi ik,
lăsând conţinuturile celorlalte elemente de indici i2, ..., ik-1 nemodificate. O
parcurgere în lăţime determină distanţa minimă între două noduri.
289
Anexa 1
Memento
A) Tipuri întregi
Ocupă
Nume tip Semnificaţie Valori admise
(biţi)
shortint întreg scurt 8 de la -128 la 127
integer întreg 16 de la -32768 la 32767
longint 32 de la -2147483648 la
întreg lung
2147483647
byte număr natural scurt 8 de la 0 la 255
word cuvânt 16 de la 0 la 65535
C) Tipuri reale
Ocupă
Nume tip Semnificaţie Valori admise (în modul)
(biţi)
virgulă mobilă, [-1,7×1038, -2,9×10-29] ∪
real 48
simplă precizie [2,9×10-29, 1,7×1038]
D) Tipul logic – boolean – poate reţine doar două valori true şi false.
290 Anexa 1. Memento
A.1.2. Constante
foloseşte punctul.
Exemple: 2.34, −45.26, 512E+23, −45.1E−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 enumerându-le
între apostrofuri.
Exemplu: 'abc'.
A.1.3. Operatori
A.1.3.1. Prioritatea operatorilor
Operatorul DIV furnizează rezultat corect numai dacă ambele valori sunt
numere întregi pozitive.
Operatorul < (mai mic). Fiind daţi doi operanzi a şi b, operatorul < arată
dacă este adevărată sau nu relaţia a<b. Dacă relaţia este adevărată
rezultatul va fi TRUE, altfel rezultatul va fi FALSE.
Operatorul <= (mai mic sau egal). Fiind daţi doi operanzi a şi b, operatorul
<= arată dacă este adevărată sau nu relaţia a<=b. Dacă relaţia este
adevărată rezultatul va fi TRUE, altfel rezultatul va fi FALSE.
Operatorul > (mai mare). Fiind daţi doi operanzi a şi b, operatorul > arată
dacă este adevărată sau nu relaţia a>b. Dacă relaţia este adevărată
rezultatul va fi TRUE, altfel rezultatul va fi FALSE.
Operatorul >= (mai mare sau egal). Fiind daţi doi operanzi a şi b, operatorul
>= arată dacă este adevărată sau nu relaţia a>=b. Dacă relaţia este
adevărată rezultatul va fi TRUE, altfel rezultatul va fi 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
Operatorul AND (ŞI) este binar. Se face ŞI logic pentru toate perechile de
biţi aflaţi pe aceeaşi poziţie a celor doi operatori. Dacă ambii biţi 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
biţi aflaţi pe aceeaşi poziţie a celor doi operatori. Dacă cel puţin un bit este 1
rezultatul este 1, altfel, rezultatul este 0.
Operatorul XOR (SAU EXCLUSIV) este binar. Se face XOR pentru toate
perechile de biţi aflaţi pe aceeaşi poziţie a celor doi operatori. Dacă biţii sunt
diferiţi rezultatul este 1, altfel, rezultatul este 0.
A.1.4. Instrucţiuni
Forma 1.
Forma 2.
Forma 1.
FOR variabilă := expresie1 TO expresie2 DO instrucţiune
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 acelaşi tip cu variabila.
Forma 2.
x y = eln(x ) = e y⋅ln(x) .
y
Funcţia de trunchiere
returnează X-Int(X).
Funcţia de rotunjire
function Round(X: Real): Longint;
returnează x .
298 Anexa 1. Memento
A) Tipuri întregi
Ocupă
Nume tip Semnificaţie Valori admise
(biţi)
unsigned
char caracter fără semn 8 de la 0 la 255
32 de la -2.147.483.648 la
long întreg lung cu semn 2.147.483.647
B) Tipuri reale
Ocupă
Nume tip Semnificaţie Valori admise (în modul)
(biţi)
virgulă mobilă,
float 32 [3.4 ×10-38,3.4×1038]
simplă precizie
virgulă mobilă,
double 64 [1.7×10-308 ,1.7×10+308]
dublă precizie
long virgulă mobilă,
dublă precizie [3.4×10-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 reţine numărul î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
5. Constante reale
• 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
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
Observaţii
a) dacă ambii sunt de tip întreg, rezultatul este întreg şi are semnificaţia de
împărţire întreagă. Cu toate acestea, rezultatul este corect (din punct de
vedere matematic) numai dacă valorile care se împart sunt pozitive.
b) dacă cel puţin un operand este de unul din tipurile reale, rezultatul este
real (se efectuează împărţirea obişnuită).
2. Operatorul ”%” acţionează numai asupra operanzilor de tip întreg. Rezultatul
obţinut 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 împărţirea întreagă a celor două valori care sunt considerate în
modul;
b) semnul câtului se stabileşte după regula semnelor (+ cu + rezultat +,
+ cu -, rezultat -), etc.
Aceştia sunt:
• == pentru egalitate;
• != pentru inegalitate.
Operatorii pot fi prefixaţi (aplicaţi în faţa operandului) sau postfixaţi (aplicaţi după
operand).
Operatorul şi logic (binar) acţionează astfel: dacă ambii operanzi sunt diferiţi
de 0, rezultatul este 1, altfel el este 0.
Operatorul sau logic (binar) acţionează astfel: dacă cel puţin unul din
operanzi este o valoare diferită de 0, rezultatul este 1, altfel rezultatul este 0.
Limbajul C++ este dotat cu un set de operatori care permit accesul la bit.
Aceştia sunt:
• <<, >> operatori de deplasare;
• & şi pe biţi;
• | sau pe biţi;
• ^ sau exclusiv pe biţi;
• ~ negare pe biţi (operator unar).
Operatorul ”<<” este binar. El are rolul de a deplasa către stânga conţinutul
tuturor biţilor operandului din stânga sa, cu un număr de poziţii egal cu valoarea
reţinută de al doilea operand. Poziţiile rămase libere (în dreapta) vor reţine valoarea 0.
Operatorul ”>> ” este binar. El are rolul de a deplasa către dreapta conţinutul
tuturor biţilor operandului din stânga cu un număr de poziţii egal cu valoarea reţinută
de al doilea operand. Dacă operandul din stânga este de un tip întreg fără semn,
poziţiile rămase libere (în stânga) vor reţine valoarea 0. Dacă al doilea operand
reţine valoarea m, o astfel de deplasare este echivalentă cu împărţirea întreagă cu
2m. În cazul în care primul operand este un întreg cu semn, fiecare poziţie din
stânga rămasă liberă se completează cu valoarea reţinută de bitul de semn.
În cazul operatorilor binari ”& ”, ”|” , ”^”, rezultatul se obţine aplicând pentru
fiecare pereche de biţi aflaţi pe aceeaşi poziţie regulile din tabelul următor. Atunci
când cei doi operanzi nu au aceeaşi 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.
Operatorul ”~” (negare pe biţi) are rolul de a inversa conţinutul biţilor (dacă
un bit conţine 0, va conţine 1 şi invers).
În C++ atribuirea este operator. În plus, în C++ avem mai mulţi operatori de
atribuire. Operatorul ”=” se foloseşte într-o expresie de forma:
v=expresie
Aici, v este o variabilă.
• se evaluează expresia;
În ansamblu, expresia este de tipul lui exp2 sau exp3 şi produce valoarea
exp2 sau exp3 (în funcţie de cea care se evaluează).
Manual de informatică pentru clasa a XI-a 305
De multe ori, dorim ca unul sau mai mulţi operanzi să intre în calcul convertiţi
aşa cum dorim (nu implicit). Pentru aceasta, înaintea operandului se trece între
paranteze tipul său.
Exemplu: fie declaraţia: float x= -1.9;. Atunci: (int)x=-1 (se face
conversia din float în int prin trunchiere).
A.2.4. Instrucţiuni
1. Instrucţiunea expresie este de forma:
expresie;.
La întâlnirea unei astfel de instrucţiuni, se evaluează expresia. În limbajul
C++ şi o atribuire este o expresie.
Forma 1.
if (expresie) instrucţiune1 else instrucţiune2
Forma 2.
if (expresie) instrucţiune
Principiul de executare este următorul:
• se evaluează expresia;
• dacă valoarea produsă de aceasta este diferită de 0, se execută
instrucţiunea subordonată.
306 Anexa 1. Memento
switch (expresie)
{ case exp1: secvenţă instrucţiuni1; break;
case exp2: secvenţă instrucţiuni2; break;
.................................
case expn: secvenţă instrucţiunin; break;
[default: secvenţă instrucţiunin+1];
}
unde:
− expresie are semnificaţia: expresie de tip întreg;
− expi sunt expresii constante de tip întreg;
− instrucţiunii reprezintă o secvenţă oarecare de instrucţiuni.
Principiul de executare:
• se evaluează expresia;
• dacă aceasta produce o valoare egală cu cea produsă de expi, se
execută, în ordine, instrucţiunii şi se trece la instrucţiunea următoare,
altfel se execută numai secvenţa instrucţiunin+1.
5. Instrucţiunea ”while”
Principiul de executare:
Funcţia abs are forma generală: int abs(int x); Rolul ei este de a
întoarce x (modulul lui x).
Funcţia fabs are forma generală double fabs(double x); are acelaşi
rol cu abs, numai că întoarce valoarea unui număr real (chiar double).
308 Anexa 1. Memento
Funcţia labs are forma generală long int labs(long int x); şi
acelaşi rol cu abs, numai că întoarce valoarea unui întreg lung.
Funcţia acos are forma generală: double acos(double x); şi
calculează valoarea funcţiei arccos( x) : [−1,1] → [0, π ].
Anexa 2
Aplicaţii practice ale grafurilor
Internet
Router
Switch 1 Switch 2
Subreţeaua 1 Subreţeaua 2
Observaţii
Protocoale de routare
L1 L2
Având cunoscut drumul critic pentru un graf asociat unui proiect, se pot
analiza în detaliu anumite aspecte particulare ale fiecărui eveniment sau activitate.
Dorim să cunoaştem cum se pot derula celelalte activităţi, care nu sunt critice, în
funcţie de durata drumului critic. Astfel, au fost introduse câteva noţiuni teoretice,
ce vor fi prezentate în continuare.
Să revenim la exemplul din figura A.3. Pentru evenimentul 4, vom avea data
aşteptată egală cu 10 (5+2+3) unităţi, iar data limită, egală cu 16 (21-5) unităţi.
Putem astfel considera că evenimentul 4 trebuie să fie atins după 10 unităţi
temporale, iar în cazul unei întârzieri, atingerea sa nu poate să dureze cu mai mult
de 6 (16-10) unităţi faţă de data sa aşteptată de terminare.
Arcele ce formează drumul critic au aceste două valori nule (nu le este
permisă nici o întârziere).
314 Anexa 2 - Aplicaţii practice ale grafurilor
ǎ se numesc
În chimie, grafurile ce descriu topologia molecular grafuri
moleculare. Dupǎ cum era de aşteptat, nodurile rep rezintǎ atomii, iar arcele
semnificǎ legǎturile dintre atomi.
5 7
2 1
3 4
Pentru graful neorientat prezentat anterior, se pot asocia urmǎtoarele 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
∑∑ (∆)
1
ω= ⋅ ij ,
2 i =1 j =1
∑∑ (d )
1
W (G ) = ⋅ ij ,
2 i =1 j =1
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 (spaţiu) 054 6 076 L 098 b 120 x
011 ♂ (vt) 033 ! 055 7 077 M 099 c 121 y
012 ♀ (np) 034 " 056 8 078 N 100 d 122 z
013 ♪ (cr) 035 # 057 9 079 O 101 e 123 {
014 ♫ (so) 036 $ 058 : 080 P 102 f 124 |
015 ☼ (si) 037 % 059 ; 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