Documente Academic
Documente Profesional
Documente Cultură
Sintaxa limbajului este data de totalitatea regulilor de scriere corecta.Dar un program bun din punct de
vedere sntactic nu este automat un program bun.Corectitudinea sintactica este numai o cerinta a
programelor, tot asa cum pentru a fi campion mondial la alergari este necesar sa nu ai picioarele
amputate.Cel mai greu este ca programul sa execute intocmai ce si-a propus cel ce l-a realizat.
Prin semantica unui limbaj se intelege semnificatia constructiilor sintactice corecte.
Sintaxa este formalizta perfect din punct de vedere matematic dar nu acelasi lucru se intampla si cu
semantica.Sintaxa poate fi descrisa cu ajutorul diagramelor de sintaxa sau a notatiei BNF.
In esenta, o diagrama de sintaxa este constituita din urmatoarele simboluri grafice:
Cercuri—incadreaza anumite simboluri speciale;
Elipse—incadreaza cuvinte rezervate;
Dreptunghiuri—incadreaza elemente definite prin alte diagrame de sintaxa;
Sageti—indica sensul de parcurgere al diagramei.
Orice drum de la intrare la iesirea din diagrama reprezinta o constructie sintactica corecta.
VOCABULARUL LIMBAJULUI
CITIRI, SCRIERI
TIPURI INTREGI
TIPURI REALE
CONSTANTE
Exista mai multe tipuri de constante:
zecimale;
octale;
hexazecimale;
EXPRESII
Se numeste expresie o succesiune de operatori si operanzi legati intre ei, dupa reguli specifice
limbajului, in scopul efectuarii unor operatii.Operanzii pot fi: constante, variabile, functii.
Esential este sa intelegem modul in care se evaluaza o expresie.Pentru aceasta prezentam cateva notiuni
fundamentale.
Prioritatea operatorilor.Cu aceasta notiune suntem obisnuiti.Ea indica ordinea in care se
efectueaza operatiile.In c++ avem 16 niveluri de prioritate.
Asociativitatea operatorilor.Notiunea este noua.Asociativitatea este de doua feluri: de la
stanga la dreapta si de la dreapta la stanga.De la inceput, precizam faptul ca operatorii cu
aceeasi prioritate au aceeasi asociativitate.
Regula conversiilor implicite.Fiind dat un operator binar(care leaga doi operanzi din care unul
este de un tip iar altul de alt tip) se cere sa se pecizeze tipul rezultatului .Tipul acestuia este dat
de regula conversiilor implicite, regula prezentata in cadrul acestui capitol.
OPERATORI C++
Operatorii constituie unul din conceptele cele mai importante si mai interesante care stau la baza unui
limbaj de programare. Limbajul C este vestit pentru marea varietate de operatori pe care-I pune la
dispozitia programatorului rezultand si o diversitate de expresii ce se pot forma pe baza acestora. Dupa
cum se stie, o expresie este formata din variabile, constante, functii si operatori. In acest paragraf voi
aminti cateva din categoriile de operatori ai limbajului C:
Operatorii aritmetici:
+ adunare, - scadere, * produs, / impartire, % - restul impartirii intregi
++, -- incrementare si decrementare
Operatori relationali:
<,>
= = egal
<= mai mic sau egal
>= mai mare sau egal
!= diferit
Operatori logici:
&& - si logic
|| -sau logic
! - negatie logica
Operatorii logici si relationali sunt implicati in formarea expresiilor cu valoare logica. Spre exemplu:
a<2, 7/5+2<s-1, (X>-3) &&(x<3), !(a==b)
sunt expresii cu valoare logica.
Operatori de atribuire.
In sectiunea precedenta am amintit, vorbind despre reprezentarea algoritmilor prin scheme logice, de
operatia de atribuire. In limbajul C se considera ca atribuirea este un operator. Atribuirea are
urmatoarea sintaxa:
variabila = expresie;
Modul de functionare este urmatorul:
se evalueaza expresia;
valoarea obtinuta este stocata in zona de memorie a variabilei, realizindu-se eventuale conversii de tip.
valoarea rezultata in urma atribuirii este valoarea atribuita variabilei.
OBS: Atribuirea fiind un operator, se considera ca operanzii sunt variabila din partea stanga, respectiv
expresia din partea dreapta. Orice expresie trebuie sa aiba, in urma evaluarii sale, o valoare (un
rezultat). Valoarea expresiei de atribuire este valoarea obtinuta prin evaluarea expresiei din partea
dreapta a atribuirii.
Daca valoarea obtinuta prin evaluarea expresiei din dreapta atribuirii este de alt tip decat tipul variabilei
din stanga atunci se incearca conversia tipului valorii la tipul variabilei. Nu intotdeauna conversia este
posibila, caz in care se va afisa un mesaj de eroare in urma compilarii.
Exemplul 1 : Fie urmatoarea secventa de program:
void main(void)
{
int a;
float c;
a=3./2.+9./4.;
cout<<”a=”<<a<<”\n”;
c=3./2.+9./4.;
cout<<”c=”<<c<<”\n”
/* aceeasi expresie are valori diferite, in functie de tipul variabilei
din stanga atribuirii */
}
Obs: Executand programul de mai sus se vor obtine valori diferite pentru variabilele a si c, desi
expresia care apare in dreapta ambelor atribuiri este aceeasi. Pentru a se obtine valoarea 3, iar pentru c
se obtine 3,75. Explicatia sta in modul in care se realizeaza conversiile de tip in cadrul atribuirilor.
Valoarea expresiei 3./2.+9./4. este 3,75 dar variabilei a i se va atribui partea intreaga a acestei valori,
adica 3. Variabila c este de tip float (numar real), deci nu va mai fi nevoie de conversia rezultatului
evaluarii expresiei la tipul variabilei.
Exemplul 2:
void main(void)
{
float a;
a=5/2;
cout<<a;
}
OBS: Executand acest program se va afisa valoarea 2 si nu valoarea 2,5 asa cum ar fi fost de asteptat.
Explicatia este urmatoarea: operatorul / realizeaza, atunci cand operenzii sunt intregi, impartirea
intreaga, deci vom obtine catul impartirii intregi dintre 5 si 2. Exista doua modalitati de rezolvare a
problemei: fie inlocuim expresia cu 5./2. (5.=5.0) fie utilizam operatorul de conversie explicita ( ).
Acest nou operator ( numit cast), se utilizeaza astfel:
( tip_de_date) expresie;
Semnificatia este urmatoarea: Se cere convertirea valorii rezultate din evaluarea expresiei la o valoare
de tipul specificat intre paranteze. In exemplul de mai sus vom inlocui atribuirea cu:
a=(float) 5/2;
(rezultatul expresiei va fi de tip float, deci variabilei a i se va atribui valoarea 2.5).
Exercitiu: Fie urmatorul program, care calculeaza media aritmetica a trei numere a,b,c , primele doua
introduse de la tastatura.:
void main(void)
{
int a,b,c;
float s;
cin>>a;
cin>>b;
c=a/2+b/3;
s=(a+b+c)/3;
cout<<”Media aritmetica este=”<<s;
}
Executati acest program si identificati greselile. Corectati greselile gasite.
operatori de atribuire compusa: +=, -=, *=, /=, %=
Operatorii de atribuire compusa au fost introdusi pentru a fi utilizati in locul atribuirilor de tipul:
v=v+expresie;
v=v-expresie;
v=v*expresie; e.t.c
unde: v-variabila
expresie-orice expresie corecta in limbajul C
In locul acestor expresii se vor folosi:
v+=expresie;
v-=expresie; e.t.c
Folosirea operatorilor de atribuire compusa sporeste lizibilitatea programului si viteza de executie .
Daca atribuirea este de forma:
v=v+1;
sau
v=v-1;
se pot utiliza operatorii de incrementare si decrementare ++ si --. Expresiile de mai sus se vor putea
scrie:
v++;
respectiv
v--;
Exemplul 3: Se da urmatorul program:
void main(void)
{
int a,b,i=3;
a=++i; //preincrementare a lui i
cout<<a;
b=a--; //postdecrementare
cout<<”a=”<<a<<”\n”;
cout<<”b=”<<b<<”\n”;
a+=++b; //atribuire compusa
cout<<”a=”<<a<<”\n”;
cout<<”b=”<<b<<”\n”;
}
Obs: Cand apar expresii de tipul :
++variabila
sau
--variabila;
spunem ca se realizeaza o preincrementare, respectiv predecrementare. In cazul preincrementarii,
variabila va primi ca valoare 1+valoarea initiala. Daca preincrementarea apare intr-o expresie se
realizeaza intai aceasta operatie si abia apoi celelalte operatii care mai apar in expresie
(preincrementarea are prioritate mai mare). In atribuirea a=++I se realizeaza intai preincrementarea, in
urma careia i va avea valoarea 4, si abia apoi se realizeaza atribuirea (a va avea valoarea 4).
In expresia b=a—se realizeaza o postdecrementare, adica se modifica valoarea variabilei a doar dupa ce
s-au efectuat celelalte operatii din expresie. In cazul nostru, b va promi valoarea lui a (4, in momentul
acela) si abia apoi se va micsora valoarea lui a cu o unitate.
Exercitiu: Executati programul de mai sus si urmariti rezultatele care se obtin.
Mai exista operatori de atribuire compusa, folosindu-se operatorii logici ce actioneaza la nivel de bit,
dar despre acestia vom vorbi intr-o sectiune viitoare. Tot atunci vom discuta si despre atribuirea
multipla.
Instructiuni iterative
Toate problemele prezentate pana acum au putut fi rezolvate folosind numai functii de citire/scriere,
atribuiri si instructiunea de decizie (IF). Cele mai multe dintre probleme vor necesita structuri de date
mai complexe precum si folosirea unor noi instructiuni, care sa permita repetarea de un numar oarecare
de ori a unor parti din algoritm.
Sa luam ca exemplu algoritmul de calcul al sumei a 2 numere introduse de la tastatura. El consta in
citirea valorilor pentru cele doua numere si afisarea sumei acestora. Nu era nevoie decat de doua
variabile, cate una pentru fiecare numar. Acest exemplu este doar unul didactic, in practica
neintalnindu-se prea des cazuri in care sa fie nevoie de a suma doua numere. Generalizarea problemei
pentru n numere va modifica substantial algoritmul nostru. Nu vom putea folosi cate o variabila pentru
fiecare numar introdus deoarece nu cunoastem exact cate numere avem (n este introdus de utilizatorul
programului, altfel algoritmul nu ar mai fi general). Chiar daca s-ar sti ca avem, sa zicem, 2500 de
numere ar fi cam dificil sa utilizam 2500 de variabile distincte.
Problema noastra se poate rezolva simplu daca utilizam o instructiune iterativa (ciclica), care sa ne
permita repetarea de n ori a urmatoarei secvente de operatii:
citim o valoare folosind variabila “a”;
adunam valoarea citita la rezultatul partial, memorat in variabila “s”;
Instructiunile ce descriu aceste prelucrari ciclice sunt compuse din doua parti:
corpul ciclului, format din prelucrarile ce se doresc a fi realizate de mai multe ori;
conditia, pe baza careia se stabileste daca se vor mai executa prelucrarile din ciclu (este obligatorie
executarea prelucrarilor din corpul instructiunii de un numar finit de ori);
Corpul ciclului sau conditia trebuie sa contina acele operatii care, dupa efectuarea de un numar de ori a
corpului instructiunii, sa determine schimbarea valorii de adevar a conditiei, permitand incheierea
executarii instructiunii ciclice si trecerea la urmatoarele instructiuni ale algoritmului. In cazul in care
este neglijat acest aspect, programul se va executa la infinit.
Conditia terbuie sa fie o expresie cu valoare logica. Reamintesc faptul ca in limbajul C++ nu exista un
tip de date logic, dar se utilizeaza conventia ca orice valoare nenula sa fie considerata ca adevar iar
valoarea zero ca valoarea fals. In consecinta, orice expresie cu valoare de tip intreg va putea fi utilizata
drept conditie intr-o instructiune iterativa.
In finalul paragrafului o chestiune de terminologie: o executie a corpului instructiunii poarta numele se
iteratie.
Instructiunile iterative pot fi clasificate, in functie de momentul evaluarii conditiei, astfel:
cicluri cu test initial (While si For)-evaluarea conditiei se face inaintea fiecarei iteratii;
cicluri cu test final (Do..While)- evaluarea conditiei se face la sfarsitul fiecarei iteratii;
2. Instructiunea While
Instructiunea While (“atat timp cat”) este o instructiune iterativa cu test initial si are urmatoarea
sintaxa:
while ( <conditie>) instructiune;
unde:
<conditie> -orice expresie cu valoare intreaga;
instructiune- orice instructiune valida a limbajului;
Mod de functionare:
daca expresia este adevarata se executa prelucrarile din ciclu;
altfel, se trece la urmatoarea instructiune de dupa while;
Cu alte cuvinte, prelucrarile din ciclu se executa atat timp cat conditia este adevarata. Daca cexpresia
este falsa de la inceput corpul ciclului nu se va executa deloc.
Problema propusa in paragraful precedent se poate rezolva astfel:
Exemplul 1 Suma a n numere introduse de utilizator
#include <iostream.h>
void main()
{
float a;
int i,n;
float suma=0;
i=1;
cout<<"\nNumarul de elemente=";
cin>>n;
while(i<=n)
{
cout<<"Elementul "<<i<<"este: ";
cin>>a;
suma=suma+a; //se mai poate scrie suma+=a
i++;
}
cout<<"Suma este= "<<suma;
}
Exemplul 2: Suma primelor n numere naturale.
#include <iostream.h>
void main()
{
int n;
int i;
int suma=0;
cout<<"n=";
cin>>n;
i=1;
while(i<=n)
{
suma=suma+i;
i++;
}
cout<<"Suma este: "<<suma;
}
Observatie: Se poate utiliza o scriere mai compacta, care elimina si necesitatea utilizarii variabilei i:
while (n--) suma+=n;
Modul de executie este urmatorul:
valoarea conditiei este valoarea variabilei n, valoare care scade cu o unitate dupa fiecare iteratie
(postdecrementare)
conditia devine falsa atunci cand valoarea lui n devine zero;
3. Instructiunea do..while
Instructiunea do..while este o instructiune iterativa cu test final si are urmatoarea sintaxa:
do
instructiune;
while (<conditie>)
Mod de functionare:
se executa corpul instructiunii;
se evalueaza conditia: daca aceasta este adevarata se reia executia, altfel se trece la urmatoarea
instructiune din program;
Deosebirea esentiala fata de instructiunea while este aceea ca expresia se evalueaza dupa iteratie.
In cazul in care conditia este falsa de la inceput, corpul instructiunii se executa o singura data.
Utilizarea instructiunii do..while este mai putin frecventa (se foloseste pentru acele prelucrari care
trebuiesc executate cel putin o data).
Daca rescriem exemplul 1 utilizand do..while, obtinem:
………………………….
do
{
cin>>a;
suma+=a;
i++;
} while(i<=n);
4. Instructiunea for
Una dintre cele mai puternice instructiuni iterative ale limbajului C (si nu numai) este instructiunea for,
care are urmatoarea sintaxa:
for (expresie1; expresie2; expresie3) [instructiune];
unde:
expresie1, expresie2, expresie3 -expresii valide in C++;
instructiune - orice instructiune a limbajului C++;
Parantezele patrate semnifica faptul ca instructiunea este optionala;
Mod de executie:
expresiile au urmatoarea semnificatie generala:
expresie1 - expresie de initializare;
expresie2 - conditia de continuare a executiei ciclului;
expresie3 - expresie de actualizare;
atat timp cat expresie2 este adevarata se executa corpul ciclului;
de fiecare data se evalueaza expresia de actualizare, care are rolul esential de a determina ca, dupa un
numar de iteratii, expresie2 sa devina falsa;
expresie1 se evalueaza o singura data;
Exemplul 3: Reluam exemplul 1 folosind instructiunea for:
#include <iostream.h>
void main()
{
int i,n;
float a, suma=0;
cin>>n;
for(i=1;i<=n;i++)
{
cin>>a;
suma+=a;
}
cout<<”suma =”<<suma;
}
Observatii:
Variabila i este utilizata pe post de “contor” al instructiunii for, numarand la a cata iteratie s-a ajuns.
Executia instructiunii for se incheie atunci cand numarul de iteratii devine egal cu n. Initializarea lui i
cu 1 se realizeaza o singura data, la inceput; i<=n este conditia de continuare a executiei; i++ se
efectueaza dupa fiecare executie a ciclului (postincrementare).
Aceasta forma a instructiunii for este cea mai utilizata. Un alt mod de a descrie executia acesteia este
urmatorul: pentru i de la 1 la n se executa corpul instructiunii.
Nu este obligatoriu ca valoarea lui i sa se mareasca de fiecare data cu o unitate.
Nu este obligatorie utilizarea tuturor celor trei expresii din for, dar este obligatorie scrierea
separatorului “;” . Exemplul de mai sus se poate rescrie astfel:
……………………………
for( ;n--; )
{
cin>>a;
suma+=a;
}
Exemplul 1. Programul urmator interschimba continutul a doua variabile care au fost citite.La
sfarsit, se afiseaza noul continut al variabilelor.
#include <iostream.h>
main ( )
{
int a,b,c;
cout<<”a=”; cin>>a;
cout<<”b=”; cin>>b;
c=a; a=b; b=c;
cout<<”a=”<<a<<endl;
cout<<”b=”<<b<<endl;
}
Exemplul 2. Se citesc doua valori intregi a si b.Se cere sa se afiseze media lor aritmetica.
#include <iostream.h>
main ( )
{
int a,b;
float medie;
cout<<”a=”; cin>>a;
cout<<”b=”; cin>>b;
medie=float (a+b)/2;
cout<<”media este “<<medie;
}
Exemplul 3. Se citesc 3 numere naturale.Se cere sa se afiseze primul numar, suma dintre
primul si al doilea, suma primelor 3 numere.
Exemplu : daca se citeste 2,5 si 7, se va tipari 2, 7, 14.
#include <iostream.h>
main ( )
{
int s=0,nr;
cout<<”dati numarul “; cin>>nr;
s+=nr;
cout<<s<<endl;
cout<<”dati numarul “; cin>>nr;
s+=nr;
cout<<s<<endl;
cout<<”dati numarul “; cin>>nr;
s+=nr;
cout<<s<<endl;
}
2.Instructiunea IF
Exemplu 1. Programul care urmeaza calculeaza maximul dintre doua numere citite este:
#include <iostream.h>
main ( )
{
int a,b,max;
cout<<”a=”; cin>>a;
cout<<”b=”; cin>>b;
if (a>b) max=a;
else max=b;
cout<<”maximul este”<<max;
}
Obs.: Se pune “ ; “ inainte de ELSE.
Exemplul 2. Se citesc trei variabile reale a, b si c.Sa se calculeze
a+b, c>0
e= a*b, c=0
a-b, c<0
#include <iostream.h>
main ( )
{
double a,b,c,e;
cout<<”a=”; cin>>a;
cout<<”b=”; cin>>b;
cout<<”c=”; cin>>c;
if (c>0) e=a+b;
else
if (c==0) e=a*b;
else e=a-b;
cout<<e;
}
Exemplul 3. Se citeste o valoare intreaga.In cazul in care aceasta este para (se imparte exact la
2) se va afisa mesajul “am citit un numar par”.Altfel, programul nu va da mesaj.
#include <iostream.h>
main ( )
{
int nr;
cin>>nr;
if (nr%2==0) cout<<”am citit valoare para”;
}
3.Instructiunea compusa
Pentru a putea scrie mai multe instructiuni care sa fie interpretate de compilator ca una
singura se foloseste instructiunea compusa.Aceasta are forma urmatoare:
{
i1;
i2;
:
:
In ;
}
Exemplul 1. Sa se scrie un program care rezolva ecuatia algebrica de gradul 1 cu o
necunoscuta, cu coeficienti reali (a*x+b=0).Modul de rezolvare a acestei ecuatii a fost discutat pe larg
atunci cand am exemplificat elaborarea algoritmilor prin utilizarea schemelor logice si a limbajului de
tip pseudocod.Aici prezentam programul C++ care rezolva aceasta problema.Se observa folosirea unei
instructiuni compuse la calculul radacinii si tiparirea ei.
#include <iostream.h>
main ( )
{
float a,b,x;
cout<<”a=”; cin>>a;
cout<<”b=”; cin>>b;
if (a)
{
x= -b/a;
cout<<x;
}
else
if (b==0) cout<<”infinitate de solutii”;
else cout<<”nu are solutie”;
}
4.Instructiunea switch
5.Instructiunea while
Exemplul 1. Se citeste n, numar natural.Sa se calculeze suma cifrelor sale (pentru n=213, se va
tipari 6).
#include <iostream.h>
main ( )
{
int n,s=0;
cout<<”n=”; cin>>n;
while (n)
{
s=s+n%10;
n=n/10;
}
cout<<s;
}
Exemplul 2. Se citeste n, numar natural.Sa se afiseze numarul obtinut prin inversarea cifrelor
sale (pentru n=412,se va tipari214).
#include <iostream.h>
main ( )
{
int n,ninv=0;
cout<<”n=”; cin>>n;
while (n)
{
ninv=ninv*10+n%10;
n=n/10;
}
cout<<”numarul inversat ”<<ninv;
}
In programul de mai jos s-au folosit mai multe expresii separate prin virgula:
#include <iostream.h>
main ( )
{
int n,ninv=0;
cout<<”n=”; cin>>n;
while (ninv=ninv*10+n%10,n/=10);
cout<<ninv;
}
Programul mai poate fi scris si asa:
#include <iostream.h>
main ( )
{
int n,ninv=0;
cout<<”n=”; cin>>n;
while (ninv*=10,ninv+=n%10,n/=10);
cout<<ninv;
}
6.Instructiunea DO WHILE
7.Instructiunea FOR
Obs.: Toate expresiile pot fi vide.In concluzie, expresiile de mai sus au rolul precizat in mod normal –
dar nu obligatoriu si nici restrictiv.De exemplu, daca expresietest este vida, se executa un ciclu infinit :
main ( )
{
for(; ;);
}
Exemplul 1. Programul urmator listeaza numerele 5, 4, 3, 2, 1.
#include <iostream.h>
main ( )
{
int i;
for (i=5;i>=1;i--) cout<<i<<” “;
}
Exemplul 2. Sa se listeze alfabetul in ordine inversa.
#include <iostream.h>
main ( )
{
char car;
for (car=’z’;car<=’a’;car--) cout<<car<<endl;
}
#include<iostream.h>
main( )
{
int i,s=0,p=1,n;
cout<<”n=”; cin>>n;
for (i=1;i<=n;i++)
{
p*=i; / / p=p*i;
s+=p; / / s=s+p;
}
cout<<s;
}
Exemplul 6. Se citesc n numere intregi.Se cere sa se afiseze cel mai mare numar
citit.Exemplu: daca avem n=4, iar numerele sunt –7, 9, 2, 3, se va afisa 9.
#include<iostream.h>
main( )
{
int i,max,n,nr;
cout<<”n=”; cin>>n;
cout<<”nr=”; cin>>nr;
max=nr;
for (i=2;i<=n;i++)
{
cout<<”nr=”; cin>>nr;
if (nr>max) max=nr;
}
cout<<”maximul este “<<max;
}
Exemplul 7. Se citeste un numar natural.Sa se afiseze numarul obtinut prin inversarea cifrelor
sale.
#include <iostream.h>
main ( )
{
int i,s,n,ninv;
cout<<”n=”; cin>>n;
for (ninv=0;n>0;)
{
ninv=ninv*10+n%10;
n=n/10;
}
cout<<ninv;
}
Se poate si asa :
#include <iostream.h>
main ( )
{
int i,s,n,ninv;
cout<<”n=”; cin>>n;
for (ninv=0;n>0;n/=10) ninv=ninv*10+n%10;
cout<<ninv;
}
9.Functii matematice
Fie Ani ={1,2,…,ni} multimea primelor ni numere naturale.Fie M=An1 X An2 X … XAnk
produsul cartezian a k astfel de multimi.
Se numeste tablou o functie f:MT, unde T este o multime oarecare.Numarul k este
dimensiunea tabloului.Daca k=1 tabloul se mai numeste si vector.Vectorul are n1 componente.Daca k=2
tabloul se mai numeste si matrice.Matricea are n1X n2 elemente.
Exemple:
1. Vector cu 5 componente numere naturale
V = (1,3,6,2,4 )
Aici k=1,n1 =5,T=N, elementele vectorului sunt v1=1,v2=3,…,v5=4.
2.Vector cu n componente reale
Z=(z1, z2,…,zn), zi real, i=1,n
Aici k=1,n1 =n,T=R.
Adresarea unei componente se face prin indice, care se trece intre paranteze drepte, iar
componentele au indici intre 0 si 99.
Exemple:
1. int v[100] - am declarat un vector cu 100 de componente de tip intreg.
2. float a[50],b[50] – am declarat doi vectori, a si b, care au componente de tip float.
3. int a[10][9] – am declarat o matrice a care are 10 linii si 9 coloane;liniile sunt de la 0 la 9, iar
coloanele de la 0 la 8, iar fiecare element al ei este de tip int.
Exemplul 2. Programul de mai jos utilizeaza doi vectori, cu componente de tip float.Se citeste
vectorul a, dupa care se face atribuirea pe componente.La sfarsit, se tipareste vectorul b.
#include <iostream.h>
main ( )
{
int n,i;
float a[50], b[50];
cout<<”numar de componente”; cin>>n;
for (i=0;i<n;i++)
{
cout<<”a[“<<i+1<<”]=”;
cin>>a[i];
}
for (i=0;i<n;i++) b[i]=a[i];
for (i=0;i<n;i++) cout<<b[i]<<endl;
}
Exemplul 3. In programul urmator se citeste si se tipareste un tablou.Initial se citesc numarul
de linii si de coloane ale tabloului (m si n).
#include <iostream.h>
main ( )
{
int m,n,i,j,a[9][9];
cout<<”m =”; cin>>m;
cout<<”n =”; cin>>n;
for (i=0;i<m;i++)
for (j=0;j<n;j++)
{
cout<<”a[“<<i+1<<’,’<<j+1<<”]=”;
cin>>a[i][j];
}
for (i=0;i<m;i++)
{
for (j=0;j<n;j++) cout<<a[i] [j]<<’ ‘;
cout<<endl;
}
}
Observatie: In memorie, tablourile sunt memorate pe linii.
Se citeste un vector cu n componente numere intregi.Se cere sa se afiseze cel mai mare numar
intreg gasit.Exemplu: n=4 si se citesc valorile 2, -4, 3, 5.Se va afisa 5.
#include <iostream.h>
int v[9],n,i,max;
main ( )
{
cout<<”n =”; cin>>n;
for (i=0;i<n;i++)
{
cout<<”v[“<<i+1<<”]=”;
cin>>v[i];
}
max=v[0];
for (i=0;i<n;i++)
if (v[i]>max) max=v[i];
cout<<”valoarea maxima citita este “<<max;
}
1.3.3. Multimi
La matematica am invatat faptul ca multimile nu se pot defini, dar pot fi descrise ca fiind date
de mai multe elemente de acelasi tip.In cadrul unei multimi, un element apare o singura data.
A citi o multime inseamna a introduce elementele care o alcatuiesc.Acestea sunt memorate
intr-o variabila da tip vector.
#include <iostream.h>
int multa[9], multb[9],multc[9],n,m,i,j,k,gasit;
main ( )
{
cout<<”numarul de elemente al multimii A “; cin>>n;
for (i=0;i<n;i++)
{
cout<<”mult[“<<i+1<<”]=”; cin>>multa[i];
}
cout<<”numarul de elemente al multimii B “; cin>>m;
for (i=0;i<m;i++)
{
cout<<”mult[“<<i+1<<”]=”; cin>>multb[i];
}
k=0;
for (i=0;i<n;i++)
{
gasit=0;
for (j=0;j<=m && !gasit;j++)
if (multa[i]==multb[j]) gasit=1;
if (!gasit) multc[k++]=multa[i];
}
cout<<”A reunit cu B”<<endl;
for (i=0;i<m;i++) cout<<multb[i]<<endl;
for (i=0;i<k;i++) cout<<multc[i]<<endl;
}
d. Intersectia a doua multimi. Se citesc doua multimi de numere intregi A si B.Se cere sa se
afiseze multimea multimea rezultata prin intersectia celor doua multimi.
#include <iostream.h>
int multa[9], multb[9],multc[9],n,m,i,j,k,gasit;
main ( )
{
cout<<”numarul de elemente al multimii A “; cin>>n;
for (i=0;i<n;i++)
{
cout<<”mult[“<<i+1<<”]=”; cin>>multa[i];
}
cout<<”numarul de elemente al multimii B “; cin>>n;
for (i=0;i<m;i++)
{
cout<<”mult[“<<i+1<<”]=”; cin>>multb[i];
}
k=0;
for (i=0;i<n;i++)
{
gasit=0;
for (j=0;j<=m && !gasit;j++)
if (multa[i]==multb[j]) gasit=1;
if (gasit) multc[k++]=multa[i];
}
cout<<”A intersectat cu B”<<endl;
for (i=0;i<k;i++) cout<<multc[i]<<endl;
}
e. Produsul cartezian dintre doua multimi. Fie multimile A={1,2,…,n} si B={1,2,…,m} (m si
n se citesc).Se cere sa se afiseze multimea C=AxB.
#include <iostream.h>
main ( )
{
int n,m,i,j;
cout<<”numarul de elemente al multimii A ”; cin>>n;
cout<<”numarul de elemente al multimii B ”; cin>>m;
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
cout<<i<<” “<<j<<endl;
}
Problema a fost rezolvata pentru cazul particular in care multimile sunt formate din numere
naturale.
Pentru cazul general, in care A este o multime formata din n caractere, iar B este o multime
formata din m caractere, rezolvarea este redata de programul de mai jos:
#include <iostream.h>
char multa[9],multb[9];
int n,m,i,j;
main ( )
{
cout<<”numarul de elemente al multimii A “; cin>>n;
for (i=0;i<n;i++)
{
cout<<”mult[“<<i+1<<”]=”; cin>>multa[i];
}
cout<<”numarul de elemente al multimii B “; cin>>m;
for (i=0;i<m;i++)
{
cout<<”mult[“<<i+1<<”]=”; cin>>multb[i];
}
for (i=0;i<=n;i++)
for (j=0;j<=m;j++)
cout<<multa[i]<<” “<<multb[j]<<endl;
}
f. Submultimi. Se citeste n, numar natural.Se cere sa se afiseze toate submultimile multimii
{1,2,…,n}.
#include <iostream.h>
int multa[9],n,i,s;
main ( )
{
cout<<”numarul de elemente al multimii A ”; cin>>n;
for (i=0;i<n;) multa[i++]
do
{
multa[n-1]++;
for (i=n-1;i>=n;i- -)
if (multa[i]>1)
{
multa[i]-=2;
multa[i-1]+=1;
}
s=0;
for (i=0;i<n;i++) s+=multa[i];
for (i=0;i<n;i++)
if (multa[i]) cout<<i+1<<’ ‘;
cout<<endl;
} while (s<n) ;
cout<<”multimea vida “;
}
#include <iostream.h>
int a[9],n,i,j,k,man,min;
main ( )
{
cout<<”n=”; cin>>n;
for (i=0;i<n;i++)
{
cout<<”a[“<<i+1<<”]=”; cin>a[i];
};
for (i=0;i<n-1;i++)
{
min=a[i];k=i;
for (j=i+1;j<n;j++)
if (a[j]<min)
{
min=a[j];
k=j;
}
man=a[k];
a[k]=a[i];
a[i]=man;
}
for (i=0;i<n;i++) cout<<a[i]<<endl;
}
#include <iostream.h>
int a[9],n,i,j,k,man,gasit;
main ( )
{
cout<<”n=”; cin>>n;
for (i=0;i<n;i++)
{
cout<<”a[“<<i+1<<”]=”; cin>a[i];
};
do
{
gasit=0;
for (i=0;i<n-1;i++)
if (a[i]>a[i+1])
{
main=a[i]; a[i]=a[i+1]; a[i+1]=man;
gasit=1;
}
} while (gasit);
for (i=0;i<n;i++) cout<<a[i]<<endl;
}
#include <iostream.h>
int a[9], b[9], n,i,j,k,gasit;
main ( )
{
cout<<”n=”; cin>>n;
for (i=0;i<n;i++)
{
cout<<”a[“<<i+1<<”]=”; cin>a[i];
};
b[0]=a[0];
for (i=1;i<n;i++)
{
j=i-1;
while (j>=0 && a[i]<b[i]) j- -;
for (k=i-1;k>=j+1;k- -) b[k+1]=b[k];
b[j+1]=a[i];
}
for (i=0;i<n;i++) cout<<b[i]<<endl;
}
1.3.5 Interclasare
#include <iostream.h>
main ( )
{
int a[100],b[100],c[200],m,n,i,j,k;
cout<<”m=”; cin>>m;
for (i=0;i<m;i++)
{
cout<<”a[“<<i+1<<”]=”; cin>a[i];
};
cout<<”n=”; cin>>n;
for (i=0;i<n;i++)
{
cout<<”b[“<<i+1<<”]=”; cin>b[i];
};
i=j=k=0;
while (i<m && j<n)
if (a[i]<b[j]) c[k++]=a[i++];
else c[k++]=b[j++];
if (i<m)
for (j=i;j<m;j++) c[k++]=a[j];
else
for (i=j;i<n;i++) c[k++]=b[i];
for (i=0;i<k;i++) cout<<c[i]<<endl;
}
Se citesc n numere intregi sortate crescator.De asemenea, se citeste un numar intreg nr.Sa se
decida daca nr se gaseste in sirul celor n numere citite.
#include <iostream.h>
main ( )
{
int a[100],n,i,li,ls,k,nr,gasit;
cout<<”n=”; cin>>n;
for (i=0;i<n;i++)
{
cout<<”a[“<<i+1<<”]=”; cin>a[i];
};
cout<”nr=”; cin>>nr;
li=0;ls=n-1;gasit=0;
do
{
k=(li+ls)/2;
if (a[k]==nr)
{
cout<<”gasit pe pozitia “<<k+1;
gasit=1;
}
else
if (a[k]<nr) li=k+1;
else ls=k-1;
} while (li<=ls && !gasit);
if (li>ls) cout<<”negasit”;
}
#include <iostream.h>
main ( )
{
int mat[10][10],m,n,i,j,x,y,man;
cout<<”m=”; cin>>m;
cout<<”n=”; cin>>n;
for (i=0;i<m;i++)
for (j=0;i<n;j++)
{
cout<<”mat[“<<i+1<<’,’<<j+1<<”]=”;
cin>>mat[i][j];
};
cout<<”x=”; cin>>x;
cout<<”y=”; cin>>y;
cout<<endl;
for (i=0;i<m;i++)
{
for (j=0;i<n;j++) cout<<mat[i][j]<<’ ‘;
cout<<endl;
}
for (j=0;i<n;j++)
{
man=mat[x-1][j];
mat[x-1][j]=mat[y-1][j];
mat[y-1][j]=man;
}
cout<<endl;
for (i=0;i<m;i++)
{
for (j=0;i<n;j++) cout<<mat[i][j]<<’ ‘;
cout<<endl;
}
}
2. Spirala. Se citeste un tablou cu n linii si n coloane.Se cere sa se afiseze elementele tabloului
in ordinea rezultata prin parcurgerea acestuia in spirala, incepand cu primul element din linia 1 in
sensul acelor de ceas.
#include <iostream.h>
main ( )
{
int mat[10][10],n,i,j,k;
cout<<”n=”; cin>>n;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
{
cout<<”mat[“<<i+1<<’,’<<j+1<<”]=”;
cin>>mat[i][j];
}
for (k=1;k<=n/2;k++)
{
for (i=k;i<=n-k+1;i++) cout<<mat[k][i]<<endl;
for (i=k+1;i<=n-k+1;i++) cout<<mat[i][n-k+1]<<endl;
for (i=n-k;i>=k;i- -) cout<<mat[n-k+1][i]<<endl;
for (i=n-k;i>=k+1;i- -) cout<<mat[i][k]<<endl;
}
}
2. Siruri de caractere
2.1. Memorarea si declararea vectorilor de caractere
In memoria interna, o constanta de tip sir este retinuta sub forma unui vector de
caractere.Conventia este ca ultimul octet sa retina 0 (caracterul nul).Pentru fiecare caracter este retinut
codul ASCII.
Vectorii de caractere pot fi initializati la declarare, caracterul nul fiind memorat automat.
Exemple:
char vect[11]=”calculator” - initializari la declarare
char vect[ ]=”calculator” – in acest caz, calculatorul face calculul pentru octetii necesari
char vect[100]=”calculator” – s-au rezervat mai multi octeti decat era necesar.
#include <iostream.h>
main ( )
{
char a[20];
int i;
for (i=0;i<10;i++) cin>>a[i];
a[10]=0;
for (i=0;i<10;i++) cout<<a[i];
}
Limbajul C++ permite simplificarea lucrului cu siruri de caractere.Programul anterior poate fi
scris si astfel:
#include <iostream.h>
main ( )
{ char a[20];
cin>>a; cout<<a;
}
Observatie: Un vector poate fi adresat pe componente.De exemplu a[0]=’c’,a[1]=’a’
s.a.m.d.Din pacate, utilizand vectorii, spatiile nu se pot citi, ceea ce constituie un dezavantaj.
Functia cin.get(vector_de_caractere, int nr, char=’\n’) , care citeste un sir de caractere,
memorat sub forma unui vector cu nr-1 componente, pana la intalnirea caracterului dat.Al treilea
parametru este optional.Daca nu este trecut, se presupune ca este ‘\n’.
Exemple:
1.Se citeste un sir de maxim 2 caractere.
char a[10];
cin.get(a,3);
cout<<a;
2.La fel ca mai sus, dar citirea se intrerupe la intalnirea caracterului g, sau cand s-au citit 9 caractere
ale sirului.
char a[10];
cin.get(a,10,’g’);
cout<<a;
Functia cin.get( ) citeste un caracter, alb sau nu.
Observatie: In cazul utilizarii repetate a functiei cin.get( ) cu trei parametri, este necesara
introducerea unui caracter gol pentru a continua citirea, ca in programul urmator:
#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);
}
Limbajul C++ permite ca un vector de caractere sa fie adresat incepand de la un anumit octet
al sau.De aceea, in urma executiei programului urmator, se tipareste: asa sa a.
#include <iostream.h>
main ( )
{
char a[ ]=’masa’;
cout<<a+1<<” “<<a+2<<” “<<a+3;
}
Observatii:
Octetii memoriei interne sunt numerotati incepand cu 0.
Numarul de ordine al unui octet in memoria interna se numeste adresa octetului respectiv.
Prin definitie, adresa unui vector de caractere este ardesa primului sau octet.
O variabila de tipul char* poate retine adresa unui vector de caractere.
In C++ numele unui vector de caractere este o adresa constanta de vector si poate fi atribuit unei
variabile de tip char*.
Exemplu:
#include <iostream.h>
main ( )
{ char a[ ]=”Exemplu”, *p;
p=a; cout<<p<<endl;
p++; cout<<p<<endl; { ne deplasam pe urmatorul octet}
p++; cout<<p<<endl;
cout<<p[1]<<endl; { se tipareste numai acest octet}
cout<<p-a;
}
Fiind dati doi vectori de caractere a si b, nu sunt permise atribuiri de forma a=b sau o atribuire
de forma a=”un sir”.Aceste operatii , ca si multe altele se fac cu anumite functii specifice
limbajului.Pentru folosirea acestor functii, trebuie sa fie inclus fisierul antet string.h.Cele mai uzuale
functii sunt:
Functia strlen are rolul de a returna lungimea efectiva a unui sir (in calculul lungimii nu intra
caracterul nul).Forma generala este:
size_t strlen (char*);
size_t este un tip intreg, utilizat in adresarea memoriei (il putem privi ca pe tipul unsigned int)
argumentul este de tip char* - un vector de caractere
Programul de mai jos citeste un sir si afiseaza numarul de caractere pe care le are sirul citit
(exclusiv caracterul nul).
#include <iostream.h>
#include <string.h>
main ( )
{ char a[100];
cin.get(a,100);
cout<<”Sirul citit are “<<strlen (a)<<”caractere”;
}
Functia strcpy are forma generala:
char *strcpy (char* dest,char* sursa);
si are rolul de a copia sirul de adresa sursa la adresa dest.
#include <iostream.h>
#include <string.h>
main ( )
{ char a[100]=”un sir”,b[100]=”alt sir”;
strcpy (a,b);
cout<<a;
}
Functia standard strcat are forma generala:
char *strcat (char* dest, char* sursa);
si rolul de a adauga sirului de adresa dest sirul de adresa sursa.Sirul de adresa sursa
ramane nemodificat.Aceasta operatie se numeste concatenare si nu este comutativa.
#include <iostream.h>
#include <string.h>
main ( )
{
char a[20]=”mama”,b[100]=”merge”;
strcat (a,b);
cout<<a;
}
Programul de mai sus tipareste “mama merge”.
Functia strncat are forma generala:
char *strncat (char *dest, const char *sursa, size_t nr);
si acelasi rol ca strcat cu deosebirea ca adauga sirului destinatie primii nr octeti ai sirului
sursa.Adaugarea se face inaintea caracterului nul.
Functia strchr are forma generala:
char *strchr (char *s, int c);
si rolul de a cauta caracterul c in sirul s.Cautarea se face de la stanga la dreapta.In cazul in
care caracterul este gasit, functia
intoarce adresa subsirului care incepe cu prima aparitie a caracterului citit si se termina cu
caracterul nul al sirului in care se face cautarea.
Exemplu. In programul urmator se cauta in sirul a caracterul t.Acesta este gasit, iar programul
tipareste sirul “ta este”.
#include <iostream.h>
#include <string.h>
main ( )
{
char a[20]=”Aceasta este”;
cout<<strchr (a,’t’);
}
Aplicatia 1. Indicele in cadrul vectorului a unui caracter cautat se obtine ca diferenta intre
adresa vectorului in care se face cautarea si adresa returnata de functie.Programul de mai jos tipareste
indicele primei aparitii a caracterului ‘t’, si anume 4. #include <iostream.h>
#include <string.h>
main ( )
{
char a[20]=”Aceasta este”;
cout<<strchr (a,’t’)-a;
}
Aplicatia 2. In programul urmator se citeste un sir si un caracter.Daca acesta este gasit in sir se
tipareste indicele primei aparitii a caracterului in sirul solicitat, altfel programul semnaleaza ca acest
caracter nu se afla in sir.
#include <iostream.h>
#include <string.h>
main ( )
{
char a[100], *t,c;
cout<<”Introduceti sirul “; cin.get(a,100);
cout<<”caracterul cautat “; cin>>c;
t=strchr(a,c);
if (t) cout<<”Indicele este “<<t-a;
else cout<<”Sirul nu contine acest caracter “;
}
Aplicatia 3. In programul urmator se listeaza indicii tuturor aparitiilor caracterului citit in sir.
#include <iostream.h>
#include <string.h>
main ( )
{
char a[100], *t,c;
cout<<”Introduceti sirul “; cin.get(a,100);
cout<<”caracterul cautat “; cin>>c;
t=a-1;
do
{
t++;
t=strchr(t,c);
if (t) cout<<”Indicele este “<<t-a<<endl;
} while (t);
}
Vectori de cuvinte
Aplicatia 10. Se citeste un sir de caractere care nu contine caractere albe.Sa se verifice daca
sirul este alcatuit exclusiv din caractere numerice.
In vectorul cifre se retin toate caracterele numerice de la 0 la 9.
#include <iostream.h>
#include <string.h>
main ( )
{ char cuvant[100],cifre[ ]=”0123456789”;
cout<<”Introduceti cuvantul “; cin>>cuvant;
if (strspn(cuvant,cifre)==strlen(cuvant))
cout<<”numeric”;
else cout<<”nenumeric”;
}
Aplicatia 11. Se citeste un sir de caractere care nu contine caractere albe.Sa se verifice daca
sirul e alcatuit exclusiv din caractere nenumerice.
#include <iostream.h>
#include <string.h>
main ( )
{ char cuvant[100],cifre[ ]=”0123456789”;
cout<<”Introduceti cuvantul “; cin>>cuvant;
if (strcspn(cifre,cuvant)==10) cout<<”corect”;
else cout<<”incorect”;
}
Functia strlwr are forma generala:
char *strlwr(char *s);
si converteste toate literele mari in litere mici.Restul literelor raman neschimbate, iar functia
intoarce adresa s.
Functia strupr are forma generala:
char *strupr(char *s);
si converteste toate literele mici in litere mari.Restul literelor raman neschimbate, iar functia
intoarce adresa s.
In programul urmator se citeste un cuvant, care apoi se tipareste cu litere mari.
#include <iostream.h>
#include <string.h>
main ( )
{
char a[20];
cout<<”Introduceti cuvantul”; cin>>a;
cout<<strupr(a);
}
#include <iostream.h>
#include <string.h>
#include <stdlib.h>
main (void)
{
char numar[20], *adresa;
long v;
cin>>numar;
v=strtol(numar,&adresa,10);
if (adresa-numar!=strlen(numar))
cout<<”Data contine caractere nenumerice”;
else
if (v<10 | | v>20)
cout<<”Data numerica in afara limitei “;
else cout<<v<<endl;
}
Functia :
double strtod(const char *s, char **endptr);
converteste un sir catre double.
Functia :
long double _strtold(const char *(s), char **endptr);
converteste un sir catre long double.
Functia :
unsigned long strtoul(const char *s, char **endptr, int radix);
converteste un sir catre unsigned long.
In cazul in care numarul nu poate fi memorat, fiind in afara tipului, ultimele patru functii
returneaza cea mai mare sau cea mai mica valoare care poate fi memorata de tipul in care se face
conversia, dupa cum valoarea este pozitiva sau negativa.
Fisiere
1.Notiunea de fisier
Definitie: Se numeste fisier o colectie de date omogene (adica de acelasi tip),
stocate pe suport extern si accesata printr-un nume, care reprezinta numele fisierului.
Exemple:
1.Programele sub forma executabila sunt memorate pe suport sub forma de
fisiere.Acestea se recunosc dupa extensia numelui, care este .exe sau .com.
2.Programele in format sursa (adica textul introdus de programator) sunt
stocate si ele sub forma de fisiere.Acestea pot fi recunoscute dupa extensia lor .pas
pentru surse Pascal si .cpp pentru surse C++.
3.Informatiile de natura economica sunt memorate, de asemenea, sub forma
de fisier.Informatiile referitoare la un material constituie o inregistrare, iar ansamblul
inregistrarilor constituie fisierul propriu-zis.
In C++ se lucreaza cu doua tipuri de fisiere:
a) Fisiere text;
b) Fisiere binare.
2.Fisiere text
2.1 Notiunea de fisier text
Fisierele text se caracterizeaza prin urmatoarele:
M a r ii i n 1 H EOF
\n
pointer
char a;
cin>>a;
cout<<a;
daca tastam ‘a’ se afiseaza ‘a’;
daca tastam “ a” se afiseaza ‘a’ , pentru ca, la citire, au fost sarite
caracterele albe.
int a;
cin>>a;
cout<<a;
In acest caz, fie ca se tasteaza cifre, litere sau blank-uri, se afiseaza doar
caracterele care reprezinta cifre.
2) Fie secventa:
int a;double b;
cin>>a>>b;
cout<<a<<” “<<b;
In acest caz:
daca se tasteaza bb17bb18.00, se afiseaza 17bbbb18
daca se tasteaza 112.55, se afiseaza 112 si 0.55.
Exemplu:
char a[10];
cin>>a;
cout<<a;
In cazul acestei secvente, daca se tasteaza un text , se afiseaza un.
Exemple de manipulatori:
Observatii:
1. De regula, utilizarea manipulatorilor are efect numai asupra primei citiri / scrieri,
chiar daca operatia recpectiva este scrisa in cadrul aceleiasi expresii.Una dintre
exceptiile de la aceasta regula este saltul peste caracterele albe.
2. In cazul in care latimea nu este suficienta, ea este ignorata.
3. Daca numarul de zecimale nu este precizat prin manipulator, atunci se tiparesc cel
mult 6 zecimale (daca valoarea respectiva are 6 zecimale).
4. In cazul in care nu a fost specificat caracterul de umplere, acesta este
blank-ul.
5. Conversia bazelor 8, 10, 16 se poate realiza simplificat prin utilizarea
manipulatorilor oct dec hex.
Observatii:
Exemplu:
char nume[20];
cout<<”numele fisierului “;cin>>nume;
fstream f(nume,ios: :out);
#include <fstream.h>
main ( )
{
fstream f(“c:\\fis.txt”,ios: :out);
char ch;
while (cin>>ch) f<<ch;
f.close( );
}
2.La fel ca mai sus, numai ca nu sunt sarite caracterele albe citite de la
tastatura.
#include <fstream.h>
#include <iomanip.h>
main ( )
{
fstream f(“c:\\fis.txt”,ios: :out);
char ch;
while (cin>>resetiosflags(ios: :skipws)>>ch) f<<ch;
f.close( );
}
#include <fstream.h>
main ( )
{
fstream f(“c:\\fis.txt”,ios: :in);
char ch;
while (f>>ch) cout<<ch;
f.close( );
}
#include <fstream.h>
#include <iomanip.h>
main ( )
{
fstream f(“c:\\fis.txt”,ios: :in);
char ch;
while (f>>resetiosflags(ios: :skipws)>>ch) cout<<ch;
f.close( );
}
#include <fstream.h>
#include <iomanip.h>
main ( )
{
fstream f(“c:\\fis.txt”,ios: :app);
char ch;
while (cin>>resetiosflags(ios: :skipws)>>ch) f<<ch;
f.close( );
}
#include <fstream.h>
#include <iomanip.h>
main ( )
{
fstream f(“c:\\fis.txt”,ios: :in),g(“c:\\fis1.txt”,ios: :out);
char ch;
while (f>>resetiosflags(ios: :skipws)>>ch) g<<ch;
f.close( );
}
#include <fstream.h>
#include <iomanip.h>
main ( )
{
fstream f(“c:\\numere.in”,ios: :out);
int i;
for (i=1;i<=100;i++)
f<<setw(5)<<hex<<i<<endl;
f.close( );
}
#include <fstream.h>
#include <iomanip.h>
main ( )
{
fstream f(“c:\\numere.in”,ios: :in);
int i;
while (f>>hex>>i)cout<<dec<<i<<endl;
f.close( );
}
#include <fstream.h>
#include <iomanip.h>
main ( )
{
fstream f(“c:\\numere.in”,ios: :in);
char cuvant[20];
while (!cin.eof( ))
{
cin>>cuvant;
cout<<cuvant<<endl;
}
}
10.In cazul in care se deschide un fisier care exista pentru creare, vechiul fisier
este distrus.In acest fel, din neatentie, se pot pierde date importante.Pentru a evita
acest lucru, se recurge la un artificiu, ca in programul urmator:
#include <fstream.h>
#include <iomanip.h>
main ( )
{
fstream f(“c:\\fis.txt”,ios: :in),g(“c:\\fis1.txt”,ios: :out);
char ch;
if (f)
{
cout<<”fisierul exista”;
f.close( );
return 0;
}
while (cin>>resetiosflags(ios: :skipws)>>ch) g<<ch;
f.close( );
}
11.In programul urmator se citeste primul cuvant al unui text memorat intr-un
fisier text.Acesta este afisat, apoi, pointerul este pozitionat la inceputul fisierului.In
fisier se suprascriu primele 4 caractere ale fisierului cu sirul “tata”.
#include <fstream.h>
#include <string.h>
main ( );
{
fstream f(“c:\\fis.txt”,ios: :in | ios: :out);
char cuvant[10];
f>>cuvant;
cout<<cuvant;
f.seekp(0,ios: :beg);
strcpy(cuvant, “tata”);
f<<cuvant;
f.close( );
}
12.Programul urmator citeste un fisier text alcatuit din mai multe linii – unele
pot fi si vide.El afiseaza liniile fisierului citit.Observati ca si in cazul fisierelor putem
folosi fara probleme functia get.
#include <fstream.h>
main ( )
{
fstream f(“c:\\fis.txt”,ios: :in);
char linie [100];
while (f.get(linie,100))
{
cout<<linie<<endl;
f.get( );
}
}
Exista mai multe functii cu ajutorul carora programatorul poate actiona asupra
pointerului dintr-un fisier de intrare:
Functia long tellp( ) returneaza pozitia pointerului la un moment dat.Pozitia este
relativa la inceputul fisierului.
Functia seekp(long, seek_dir) pozitioneaza pointerul.Primul parametru
reprezinta pozitia, iar al doilea reperul in raport de care este calculata pozitia.
In acest sens, au fost definite 3 constante.Pentru accesul la ele se foloseste si de
aceasta data ios:
beg – inceput de fisier;
cur – pozitia curenta in fisier;
end – sfarsit de fisier.
Cand stabilim o pozitie aflata in stanga reperului, primul parametru este
negativ, iar in dreapta reperului, parametrul este pozitiv.
13.Fiind dat un fisier text fis.txt si doua numere naturale i<j, se cere sa se
listeze caracterele fisierului text dintre i si j (inclusiv) in alt fisier text numit fis1.txt.
#include <fstream.h>
main ( )
{
fstream f(“c:\\fis.txt”,ios: :in);
int i,j;
char ch;
cout<<”i=”;cin>>i;
cout<<”j=”;cin>>j;
/ / pozitionez pointerul direct pe octetul i
f.seekp(i,ios: :beg);
/ / atat timp cat pointerul este mai mic sau egal cu j
while (f.tellp( )<=j)
{
f>>ch;
cout<<ch;
}
f.close( );
}
1.Se da un fisier text alcatuit din mai multe cuvinte.se cere sa se afiseze toate
pozitiile de inceput ale tuturor aparitiilor unui anumit cuvant citit de la tastatura.
Rezolvare.Algoritmul consta in a citi fisierul, cuvant cu cuvant, pana la
sfarsitul sau.In cazul in care acesta coincide cu cuvantul citit de la tastatura, este
afisata pozitia in care se gaseste.Tinand cont ca dupa citirea unui cuvant, pointerul
este plasat pe primul caracter alb care ii urmeaza, pozitia se poate afla cu tellp( ), iar
daca din aceasta valoare scadem lungimea cuvantului, aflam pozitia de inceput a
acestuia.
#include <fstream.h>
#include <string.h>
main( )
{
fstream f(“c:\\fis.txt”,ios: :in);
char cuvant[20],cuvant1[20];
cout<<”dati cuvantul “;cin>>cuvant;
while (f>>cuvant1 && !f.eof( ))
if (!strcmp(cuvant,cuvant1))
cout<<f.tellp( )-strlen(cuvant)<<endl;
}
#include <fstream.h>
#include <string.h>
#include <iomanip.h>
main( )
{
fstream f(“c:\\fis.txt”,ios: :in);
g(“c:\\fis1.txt”,ios: :out);
char cuvant[20],cuvant1[20],ch;
long pv=0,pn,lung,semnal=0;
cout<<”dati cuvantul “;cin>>cuvant;
lung=strlen(cuvant);
while (!f.eof( ))
{
f>>setiosflags(ios: :skipws)>>cuvant1;
pn=f.tellp( );
f.seekp(pv,ios: :beg);
if (f.eof( )) { semnal=1;f.clear( ); };
if (strcmp(cuvant,cuvant1))
while (f.tellp( )<pn)
{
f>>resetiosflags(ios: :skipws)>>ch;
g<<ch;
}
else
{ while (f.tellp( )<pn-lung)
{
f>>resetiosflags(ios: :skipws)>>ch;
g<<ch;
}
f.seekp(pn,ios: :beg);
}
pv=pn;
if (semnal) f.clear(1);
}
f.close( );
g.close( );
}
3.Se da un fisier text si doua cuvinte (suucesiune de caractere care sa nu
contina caractere albe).Se cere sa se creeze un alt fisier text care sa cuprinda
informatiile din primul fisier, dar in care toate aparitiile primului cuvant sunt inlocuite
cu al doilea cuvant.
#include <fstream.h>
#include <string.h>
#include <iomanip.h>
main( )
{
fstream f(“c:\\fis.txt”,ios: :in);
g(“c:\\fis1.txt”,ios: :out);
char cuvant[20],cuvant1[20],cuvanta[20],ch;
long pv=0,pn,lung,semnal=0;
cout<<”dati cuvantul care se sterge“;cin>>cuvant;
cout<<”dati cuvantul care se adauga“;cin>>cuvanta;
lung=strlen(cuvant);
while (!f.eof( ))
{
f>>setiosflags(ios: :skipws)>>cuvant1;
pn=f.tellp( );
f.seekp(pv,ios: :beg);
if (f.eof( )) { semnal=1;f.clear( ); };
if (strcmp(cuvant,cuvant1))
while (f.tellp( )<pn)
{
f>>resetiosflags(ios: :skipws)>>ch;
g<<ch;
}
else
{ while (f.tellp( )<pn-lung)
{
f>>resetiosflags(ios: :skipws)>>ch;
g<<ch;
}
g<<cuvanta;
f.seekp(pn,ios: :beg);
}
pv=pn;
if (semnal) f.clear(1);
}
f.close( );
g.close( );
}
#include <fstream.h>
#include <stdlib.h>
main( )
{
char nume[20];
cout<<”numele fisierului “;cin>>nume;
fstream f(nume,ios: :out);
long n,i;
cout<<”n=”;cin>>n;
f<<n<<endl;
randomize( );
for(i=0;i<n;i++) f<<rand( )<<’ ‘;
f.close( );
}
5.Sa se sorteze crescator numerele din fisierul text anterior creat.Datele sortate
vor fi scrise in alt fisier text.
#include <fstream.h>
int v[20000],n,i,man,gasit;
main( )
{
char nume1[20],nume2[20];
cout<<”numele fisierului de intrare “;cin>>nume1;
cout<<”numele fisierului de iesire “;cin>>nume2;
fstream f(nume1,ios: :in);
g(nume2,ios: :out);
/ /citesc datele
f>>n;
for(i=0;i<n;i++) f>>v[i];
/ /sortez
do
{
gasit=0;
for(i=0;i<n-1;i++)
if (v[i]>v[i+1])
{
man=v[i];
v[i]=v[i+1];
v[i+1]=man;
gasit=1;
}
} while (gasit);
/ /tiparesc
for(i=0;i<n;i++) g<<v[i]<<’ ‘;
}
#include <fstream.h>
main( )
{
char int1[20],int2[20],ies[20];
long a,b,i,j,m,n,flag;
cout<<”primul fisier “;cin>>int1;
cout<<”al doilea fisier “;cin>>int2;
cout<<”iesirea “;cin>>ies;
fstream f(int1,ios: :in);
g(int2,ios: :in);
h(ies,ios: :out);
i=j=1;
f>>m>>a; g>>n>>b;
h<<n+m<<endl;
#include <fstream.h>
main( )
{
char intr[20],ies1[20],ies2[20];
long m,n,i,j,man;
cout<<”fisierul de intrare “;cin>>intr;
cout<<”prima iesire “;cin>>ies1;
cout<<”a doua iesire “;cin>>ies2;
fstream f(intr,ios: :in);
g(ies1,ios: :out);
h(ies2,ios: :out);
f>>n;
m=n/2;
n=-m;
g<<m<<endl;
for(i=0;i<m;i++)
{
f>>man;
g<<man<<’ ‘;
}
h<<n<<endl;
for(i=0;i<n;i++)
{
f>>man;
h<<man<<’ ‘;
}
f.close( );
g.close( );
h.close( );
}
3.Fisiere binare
Fisierele binare sunt alcatuite din mai multe inregistrari de acelasi tip.
Din acest motiv, aceste fisiere se mai numesc si cu tip.
Exemple:
inregistrarile sunt de tip int;
inregistrarile sunt de un tip descris cu struct.
Datele sunt memorate in format intern.De exemplu, daca inregistrarea este de tip
int, datele sunt memorate in cod complementar.
Fisierele binare se termina cu EOF, ca si cele text.
Observatie:Tot ce s-a studiat la fisierele text ramane valabil si pentru fisierele binare!
#include <fstream.h>
struct persoana
{
char nume[30];
int varsta;
};
main( )
{
fstream f(“pers.dat”,ios: :out | ios: :binary);
persoana p,adr_p=&p;
char x;
int n,i;
cout<<”Care este numarul de persoane “;cin>>n;
cin.get( );
for(i=0;i<n;i++)
{
cout<<”Numele “;cin.get(p.nume,30);cin.get(x);
cout<<”Varsta “;cin>>p.varsta;cin.get(x);
f.write((char*)adr_p,sizeof p);
}
f.close( );
}
Observatii:
#include <fstream.h>
struct persoana
{
char nume[30];
int varsta;
};
main( )
{
persoana p, *adr_p=&p;
fstream g(“pers.dat”,ios: :in | ios: :binary);
while (g.read((char*)adr_p,sizeof(p)))
cout<<p.nume<<” “<<p.varsta<<endl;
g.close( );
}
3.Sa se scrie un program care adauga la sfarsitul fisierului creat la exemplul 1,
alte n inregistrari.
#include <fstream.h>
struct persoana
{
char nume[30];
int varsta;
};
main( )
{
fstream f(“pers.dat”,ios: :app | ios: :binary);
persoana p,adr_p=&p;
char x;
int n,i;
cout<<”Care este numarul de persoane “;cin>>n;
cin.get( );
for(i=0;i<n;i++)
{
cout<<”Numele “;cin.get(p.nume,30);cin.get(x);
cout<<”Varsta “;cin>>p.varsta;cin.get(x);
f.write((char*)adr_p,sizeof p);
}
f.close( );
}
4.Sa se scrie un program care modifica varsta unei persoane din fisierul creat
la exemplul 1.
Rezolvare.Mai intai deschidem fisierul ca pe unul de intrare / iesire.Se citeste
indicele inregistrarii (intre 1 si n), apoi pozitionam pointerul cu seekp( ) pe
inregistrarea de indice dat, tinand cont ca ele sunt numerotate de la 0 la n-1.
Citim inregistrarea, citim noua varsta de la tastatura, repozitionam pointerul pentru ca
prin citirea inregistrarii acesta se pozitioneaza pe urmatoarea inregistrare, apoi
rescriem inregistrarea modificata.
#include <fstream.h>
struct persoana
{
char nume[30];
int varsta;
};
main( )
{
int i;
cout<<”care inregistrare o modific (1..n) ?”;cin>>i;
persoana p,*adr_p=&p;
fstream g(“pers.dat”,ios::in | ios::out | ios: :binary);
g.seekp((i-1)*sizeof(p),ios::beg);
g.read((char*)adr_p,sizeof(p));
cout<<”varsta “;cin>>p.varsta;
g.seekp((i-1)*sizeof(p),ios: :beg);
g.write((char*)adr_p,sizeof(p));
g.close( );
}
Creare – initial fisierul nu exista, dar dupa executia programului, acesta se va gasi
pe suport.Intrarile sunt de la tastatura sau din alte fisiere.
Exploatare – utilizarea fisierului pentru a extrage informatii din el.In urma
exploatarii, fisierul ramane nemodificat.
Actualizare – aducerea la zi a fisierului.Aceasta se face in trei feluri:
CLASA a-X-a
Capitolul I
Pointeri
Observaţii:
Exemple:
Float* adresa;
Variabile de tip pointer către variabile de tip elev, care la timpul lor
sunt de tipul struct. Variabilele a şi b, pot reţine adrese ale
variabilelor de tip elev.
Struct elev
{
char nume [20], prenume [20];
float nota_mate, nota_info;
int vârsta;
};
Caracterul “*” poate fi plasat în mai multe feluri, după cum se observă:
int* adr1;
int * adr1;
int *adr1;
Pentru a declara mai multe variabile de acest tip, caracterul “*” se trece
de fiecare dată: int *adr1, *adr2, *adr3;
Exemplu:
Adr:=&număr Variabilei adr1 i se atribuie adresa variabilei număr.
Exemple:
#include <iostream.h>
main ( )
{ int a=7, *adr=&a, **adradra=&adr;
cout <<*adr<<” “<<**adradra;
}
Adresa Adresa
7 Variabilei Variabilei
a adra
a adra adradra
# include <iostream.h>
include <string.h>
struct elev
{
char nume [20] , prenume [20];
};
main ( )
{
elev a, *adra=&a;
strcpy(a.nume, “Bojian”);
strcpy(a.prenume, “Andronache”);
cout<<(*adra):nume<<” “<<(*adra).prenume<<end;
cout<<adra->nume<<” “<<adra->prenume;
}
# include <iostream.h>
main ( )
{ int a=3, *adr=&a;
++*adr;
cout<<*adr<<” “<<a;
}
Observaţii:
Exemplu:
# include <iostream.h>
main ( )
{ int a=3, *adr_int=&a;
char adr_char=(char*)adr_int;
cout<<(int)*adr_char;
}
{ int a=3;
char adr_char=(char*)&a;
cout<<(int)*adr_char;
}
În condiţiile:
# include <iostream.h>
main ( )
{
int a=5, j;
char adr_byte=(char*)&a, masca=128;
for (j=0; j<8; j++)
{
if (*adr_byte & masca)
cout<<1;
else cout<<0;
*adr_byte= *adr_byte<<1; // sau *adr_byte<<=1;
}
}
Exemplu: dacă a este o variabilă de tip int şi tipul int ocupă doi octeţi,
atunci a+3 reprezintă adresa octetului care se obţine ca sumă între adresa
primului octet al variabilei a şi 6.
# include <iostream.h>
main ( )
{
int a=257, i , j;
char *adr_byte=(char *)&a, masca;
for (i=0; i<sizeof(a); i++)
{
masca=128;
for (j=0; j<8; j++)
{ if (*adr_byte & masca) cout<<1;
else cout<<0;
masca>>=1;
}
adr_byte++;
}
}
1. Vectori
cout<<*(a+7)<<end1;
cout<<*(7+a)<<end2;
Dacă, în cazul vectorului, adresările a[7] şi *(a+7), *(7+a) sunt
echivalente, înseamnă că parantezele drepte reprezintă, de fapt, un
operator, numit operator de tip array, care face suma între un pointer
şi o constantă. Astfel, în cazul vectorilor avem:
a[n]=*(a+n)=*(n+a)=n[a]
2. Tablouri p-dimensionale
Exemple:
■ float A[7] [4] [2]; am declarat un tablou cu 3 dimensiuni, unde tipul de
bază este float.
este interpretată ca masiv cu [7] [8] [5] componente de tip float*. Deci
este masiv care are componente de tip pointer şi nu pointer către masive.
Atenţie! Aici ce fac multe confuzii!
Exemplu:
#include <iostream.h>
main( )
{
float G[7] [8] [3], (*A) [8] [3], (*B)[3], *C;
G[2] [3] [1] = 15;
// A este un pointer către masive cu [8] [3] componente de tip float
// şi poate reţine pointerul G
nume [exp₁]…[expp];
Exemplu: Fie declaraţia float M [2] [7] [9] [6]; Presupunem că efectuăm
atribuirea: M [1] [5] [3] [2]=9.5. Numele masivului este M şi reprezintă
un pointer către masive cu [7] [9] [6] elemente de tip float, mai precis de
tipul: float (*) [7] [9] [6].
#include <iostream.h>
main ( )
{ int a=7, *b=&a;
cout <<b[0]<<end1;
}
*(*(*(*(M+1)+5)+3)+2)
2. Fie declaraţiile: float (*a) [3], c[3], d[7] [3]; a este pointer, float (*a)
[3] este pointer către vector cu 3 componente de tip float. Atunci
expresiile următoare sunt corecte:
■ a [1] [1]=&c[0]; - c[0] este de tip float, &c[0] este pointer către float;
■ a [1] [1]=&d [6] [2]; d [6] [2] este de tip float; &d [6] [2] este de tip
pointer către float.
4. Fie declaraţiile: int (*a[3] ) [2] [5], b [2] [5], c[9] [2] [5]; a[3] este
vector, *a[3] este vector care au componente de tip pointer, (*a[3] ) [2]
[5] este vector care au componente de tip pointer către masive cu [2] [5]
componente de tip int. Atunci expresiile următoare sunt corecte:
5. Fie declaraţiile: int *(*a) [3], *b[9] [3]; *a este pointer, (*a) [3] este
pointer către vectori cu 3 componente de tip *int. Atunci expresiile
următoare sunt corecte:
#include <iostream.h>
main ( )
{ int a=7; int&b=a;
cout<<”variabila a are conţinutul “<<a<<” şi adresa “<<&a<<end1;
cout<<”variabila b are conţinutul “<<b<<” şi adresa “<<&b<<end1;
}
Subprograme
E1=1+1/2+1/3+1/4+…+1/n
E2=(1+1/2+1/3+1/4+…+1/n)ⁿ
E₂=E₁ⁿ
Cerinţa este de a scrie programe separate. Cum procedăm? Prin
utilizarea cunoştinţelor dobândite până în prezent, scriem cele două
programe separat, iar în al doilea program, secvenţa care calculează prima
expresie, se copiază din primul.
E1=1+1/2+1/3+1/4+…+1/n
E2=(1+1/2+1/3+1/4+…+1/n)ⁿ
#include <iostream.h>
double subp(int n)
{
double s=0; int i;
for (i=1; i<=n; i++) s+=1./i;
return s;
}
main ( )
{ int n;
cout<<”n=”; cin>>n;
cout<<subp(n);
}
#include <iostream.h>
double subp(int n)
{
double s=0; int i;
for (i=1; i<=n; i++) s+=1./i; // sau s=s=1. /i
return s;
}
main ( )
{
int n,i;
double rez,prod=1;
cout<<”n=”; cin>>n;
rez=subp(n);
for (i=1; i<=n; i++) prod*=rez;
cout<<prod;
}
var n:integer;
function subp(ninteger):real;
var i:integer; s:real;
begin
s:=0;
for i:=1 to n do
s:=s+1/i;
subp:=s;
end;
begin
read(n);
write*subp(n));
end.
var n,i:integer;
prod,rez:real;
function subp(ninteger):real;
var i:integer; s:real;
begin
s:=0;
for i:=1 to n do
s:=s+1/i;
subp:=s;
end;
begin
read(n);
prod:=1;
rez:=subp(n);
for i:=1 to n do
prod:=prod*rez;
write(prod);
end.
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;
}
#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];
}
main ( )
{
int v[10], n;
cout<<”n=”; cin>>n;
citesc(v,n);
sortez(v,n);
scriu(v,n);
}
2.3.1 Generalităţi
parametru₁, parametru₂,…,parametrun
tip nume
#include <iostream.h>
struct mat
{
float matrice [6] [8];
};
{
mat a;
int i,j;
for (i=0; i<m; i++)
for(j=0; j<n; j++) cit>>a.matrice [i] [j];
return a;
};
main ( );
{ int i,j;
mat mtr=cit(3,2);
for (i=0; i<3; i++)
for (j=0; j<2; j++) cout<<mtr.matrice [i] [j] <<” “;
}
- void t(int n, float v[20]) – funcţia se numeşte t, este de tip void (nu
returnează rezultat prin nume), are doi parametrii formali, primul
numit n, de tip int, al doilea numit v, de tip float*.
- char* sir(int n, char a[200]) – funcţia se numeşte şir, întoarce un
pointer către un şir de caractere şi are doi parametrii formali, unul de
tip int, numit n şi altul de tip char*, numit a.
- elev mana(int n, char a[200]) – funcţie de tip elev, unde elev este
de tip struct;
struct elev
{
char nume[20];
int varsta;
};
return expresie;
Segment de date
Segment de stivă
Heap
De asemenea, există posibilitatea ca variabilele să fie memorate
într-un anumit registru al microprocesorului. În acest caz timpul de acces
la astfel de variabile este foarte mic, deci se pot obţine programe
optimizate.
1. Clasa de memorare.
2. Vizibilitate.
3. Durata de viaţă.
4. Tipul variabilei, singurul pe care l-am studiat până în prezent.
1. Clasa de memorare – precizează locul unde este memorată variabila
respectivă. O variabilă poate fi memorată în segmentul de date, în cel
de stivă, în heap sau într-un registru al microprocesorului.
#include <iostream.h>
int a;
int t( )
{
a=3;
cout<<a;
}
int b;
main ( )
{
b=4;
cout<<a<<end1;
t( );
}
void t( )
{ int a=3;}
main( )
{ int b=4;}
void t( )
{
int b=4;
{ int c=3;
cout<<b<<” “<<c;
}
}
#include <iostream.h>
int a;
voit t( )
{ int a=4;
{ int a=3;
cout<<a<<end1;
}
cout<<a<<end1;
}
main ( )
{
a=5; t( );
cout<<a;
}
var a:integer;
procedure t;
var a:integer;
begin
a:=4;
write(a);
end;
begin
a:=3;
write(a);
t;
end.
În cazul în care, într-un anumit bloc sunt vizibile (se pot accesa) mai
multe variabile, toate au 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).
3. Durata de viaţă a variabilelor locale este atât timp cât durează execuţia
blocului respectiv.
- suma (c,d) – parametrii formali sunt expresii de tip întreg (dat de tipul
variabilelor).
- suma (1.9, 3.3) – parametrii formali sunt expresii constante de tip real.
În acest caz ele sunt convertite către int la fel ca în cazul atribuirii (se
trunchiază zecimala). Prin urmare, suma calculată este: 4=1+3.
stiva 2 3
a b
Observaţii:
#include <iostream.h>
main( )
{ int n=1;
test (n);
cout<<n<<end1;
}
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ă.
#include <iostream.h>
void test(int n)
{ cout<<n<<end1;}
main( )
{ test(3);
test(3+4*5);
}
procedure test(n:integer);
begin
write(n);
end;
begin
test(3);
test(3+4*5);
end.
#include <iostream.h>
void intersc (int *x, int *y)
{ int man;
man=*x; *x=*y; *y=man;
}
main( )
{ int a=2; b=3;
intersc(&a,&b);
cout<<a<<” “<<b;
}
#include <iostream.h>
main( )
{
int a[10];
vector(a);
for (int i=0; i<10; i++) cout<<a[i]<<” “;
}
type vect=array[1..10] of integer;
var a:vect;
i:integer;
procedure vector(x:vect);
var i:integer;
begin
for i:=1 to n do
x[i]:=i;
end;
begin
vector(a);
for i:=1 to 10 do
writeln(a[i]);
end.
În acest caz ne putem întreba care este mecanismul prin care, deşi
pentru o variabilă transmisă se reţine adresa ei, în subprogram putem
adresa variabila normal (nu indirect)? La compilare, orice referinţă la
variabila respectivă, este “tradusă” ca adresare indirectă.
#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;
}
var x,y:integer;
procedure intersc(var a,b:integer);
var man:integer;
begin
man:=a;
a:=b;
b:=man;
end;
begin
x:=2;
y:=3;
intersc(x,y);
writeln(x,y);
end.
void s1( )
{ cout<<”Eu sunt s1”<<end1;}
void s2( )
{ s1( )
cout<<”Eu sunt s2”<<end1;
}
main( )
{ s1( ); s2( );}
procedure s1;
begin
writeln(‘1’);
end;
procedure s2;
begin
writeln(‘2’);
end;
begin
s1;
s2;
end.
#include <iostream.h>
void s2( );
void s1( )
{ s2( ); cout<<”Eu sunt s1”<<end1;}
void s2( )
{ cout<<”Eu sunt s2”<<end1;}
main( )
{ s1( );}
procedure s2;
forward;
procedure s1;
begin
s2;
writeln(‘1’);
end;
procedure s2;
begin
writeln(‘2’);
end;
begin
s1;
end.
#include <iostream.h>
int cmmdc(int m, int n)
{ while (m!=n)
if (m>n) m-=n;
else n-=m;
return m;
}
main( )
{ int cm, m, n;
cout<<”m=”; cin>>m;
cout<<”n=”; cin>>n;
cm=cmmdc(m,n);
cout<<cm<<” “<<m*n/cm;
}
#include <iostream.h>
int v[9], n;
Care este avantajul scrierii funcţiei? 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 minimă. Dacă dispunem de funcţie, algoritmul
este extrem de simplu: for de la m la n. În plus, este posibil ca această
funcţie să poată fi folosită şi în alte cazuri.
#include <iostream.h>
int palin(int i)
{
int isalv=i, iinv=0;
while (i)
{
iinv=iinv*10+i%10;
i=i/10;
}
return isalv==iinv;
}
main( )
{ int m, n, i;
cout<<”m=”; cin>>m;
cout<<”n=”; cin>>n;
for (i=m; i<n; i++)
if (palin(i)) cout<<i<<end1;
}
var m, n, i:integer;
function palin(i:integer):integer;
var isalv, iinv:integer;
begin
isalv:=i;
iinv:=0;
while (i<>0) do
begin
iinv:=iinv*10+i mod 10;
i:=i div 10;
end;
palin:=(isalv=iinv);
end;
begin
read(m,n);
for i:=m to n do
if palin(i) then
cout(i);
end.
Junior Division
#include <iostream.h>
long palin(long i)
{
long isalv=i, iinv=0;
while (i)
{
iinv=iinv*10+i%10;
i=i/10;
}
return isalv==iinv;
}
main( )
{ long m, n, i;
cout<<”m=”; cin>>m;
cout<<”n=”; cin>>n;
for (i=m; i<n; i++)
if (palin(i))
if (palin(i*i)) cout<<i<<” “<<end1;
}
var m, n, i:integer;
function palin(i:integer):integer;
var isalv, iinv:integer;
begin
isalv:=i;
iinv:=0;
while (i<>0) do
begin
iinv:=iinv*10+i mod 10;
i:=i div 10;
end;
palin:=(isalv=iinv);
end;
begin
read(m,n);
for i:=m to n do
if palin(i) then
if palin(i*i) then
writeln(i, i*i);
end.
#include <iostream.h>
int seria[1000], n, ind, i, gasit;
int suma(int n)
{ int s=0, c;
while (n)
{ c=n%10; s+=c*c*c; n/=10;
return s;
}
main( )
{
cout<<”n=”; cin>>n;
ind=1; seria[ind]=n;
do
{
seria[++ind]=suma(seria[ind-1]);
for (i=1; i<ind; i++)
if (seria[i]==seria[ind]) gasit=1;
} while (!gasit);
cout<<”lungimea este “<<ind<<end1;
for (i=1; i<=ind; i++) cout<<seria[i]<<end1;
}
var i, n, ind:integer;
gasit:boolean;
seria:array[1..100] of integer;
function suma(n:integer):integer;
var s, c:integer;
begin
s:=0;
while (n<>0) do
begin
c:=n mod 10;
s:=c*c*c;
n:=n div 10;
end;
suma:=s;
end;
begin
read(n);
ind:=1;
suma[ind]:=n;
gasit:=true;
repeat
seria[ind+1]:=suma(seria[ind-1]);
for i:=1 to ind do
if suma[i]=suma[ind] then
gasit:=true;
until not gasit;
writeln(ind);
for i:=1 to ind do
write(seria[i]);
end.
1. m=6, n=1
1
2 3
4 5 6
7 8 9 1
2 3 4 5 6
7 8 9 1 2 3
2. m=7, n=9
9
1 2
3 4 5
6 7 8 9
1 2 3 4 5
6 7 8 9 1 2
3 4 5 6 7 8 9
Cum procedăm? Mai întâi, avem m linii, deci vom folosi for cu i
de la 1 la m. Apoi, în prima linie trebuie tipărit un număr, în linia a doua
două numere, ş.a.m.d. Vom folosi un alt for cu j de la 1 la i. Când i este
1, j este 1, deci se tipăreşte o valoare. Când i este 2, j ia valorile 1 şi 2,
deci se tipăresc două valori ş.a.m.d. algoritmul este conţinut de funcţia
tipar, care are ca parametrii formali pe m şi n, transmişi prin valoare.
Valorile lor sunt citite în programul principal.
#include <iostream.h>
int succ(int k)
{ if (k==9) return 1;
else return k+1;
}
void tipar(int m, int n)
{ int i, j, s=n;
for (i=1; i<=m; i++)
{ for (j=1; j<=i; j++)
{ cout<<s;
s=succ(s);
}
cout<<end1;
}
}
main( )
{ int m, n;
cout<<”m=”; cin>>m;
cout<<”n=”; cin>>n;
tipar (m,n);
}
var m, n:integer;
function succ(k:integer):integer;
begin
if (k=9) then succ:=1
else succ:=k+1;
end;
procedure tipar(m, n:integer);
var i, j, n, s:integer;
begin
s:=n;
for i:=1 to m do
for j:=1 to i do
begin
write(s);
s:=succ(s);
end;
end;
begin
read(m,n);
tipar(m,n);
end.
#include <iostream.h>
main( )
{ int n, i, s=0;
cout<<”n=”; cin>>n;
for (i=1; i<=n; i++)
if (valabil(i)) s++;
cout<<s;
}
var n, s, i:integer;
function valabil(nr:integer):boolean;
var zero, unu:integer;
begin
while (nr<>0) do
begin
if nr mod 2 = 0 then
unu:=unu+1
else
zero:=zero+1;
nr:=nr div 2;
end;
valabil:=(zero=unu);
end;
begin
s:=0;
read(n);
for i:=1 to n do
if valabil(i) then s:=s+1;
write(s);
end.
□ Statistică. Se citeşte un fişier text, alcătuit din mai multe linii. Se cere
să se listeze numărul apariţiilor şi frecvenţa fiecărei litere mari a
alfabetului în fişier, ca mai jos:
A 29 12.24
B 26 10.97
Junior Division
#include <fstream.h>
int frecv[26], suma;
main( )
{ fstream f(“c: \\ borlandc \\ bin \\ fis.txt”, ios::in);
char ch;
while (f>>ch) numara(ch);
tipar( );
}
3 4
3 1 7 9
1 2 3 4
9 1 3 8
#include <iostream.h>
void citmat(int &m, int&n, int mat [10] [10] )
{
fstream f(“c:\\borlandc\\bin\\fis.txt”, ios::in);
f>>m>>n;
for (int i=1; i<=m; i++)
for (int j=1; j<=n; j++) f>>mat [i] [j];
}
main( )
{ int m, n, mat [10] [10];
citmat(m, n, mat);
cout<<m<<” “<<n<<end1;
for (int i=1; i<=m; i++)
{ for (int j=1; j<=n; j++) cout<<mat[i] [j]<<” “;
cout<<end1;
}
}
#include <iostream.h>
int suma(int n,…)
{ int suma =0;
for (int i=1; i<=n; i++) suma+=(&n+i);
return suma;
}
main( )
{ cout<<suma(3, 1, 7, 9)<<end1;
cout<<suma(5, 1, 2, 0, 9, 7);
}
#include <iostream.h>
void functia(int n)
{ cout<<”Eu sunt functia cu un parametru de tip int”<<end1;
}
void functia(int* n)
{ cout<<”Eu sunt functia cu un parametru de tip int*”<<end1;
}
main( )
{ functia (2); functia (3,5);
int n; functia (&n);
}
void functia(int n)
{ cout<<”Eu sunt functia cu un parametru de tip int”<<end1;
}
void functia(float n)
{ cout<<”Eu sunt functia cu un parametru de tip float”<<end1;
}
Capitolul III
Structuri de date
■ o mulţime de valori;
■ o regulă de codificare a acestora;
■ o mulţime de operaţii definite pe mulţimea datelor.
■ simple – descriu date care aparţin unor mulţimi care nu sunt rezultate
ca produs cartezian al altor mulţimi. Exemplu: int.
■ structurate – descriu date care aparţin unor mulţimi rezultate ca produs
cartezian al altor mulţimi.
Exemple:
1. struct rational
{
int p, q;
};
2. double a[100];
B x B x …x B x B
de n ori
Fie lista:
3 adr₂ 9 nil
7 adr₃
Etapele sunt:
7 adr₃
9 nil
3 adr₂
adr₁ adr₂ adrn
adrt
3 adr₂
7 adr₃
9 nil
5 adr₂
adrt
5 adr₂
adrt
7 adr₃ 9 nil
3 adr₂
adr₁ adr₂ adrn
7 adr₃ 9 nil
3 adr₂
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ţuite –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ţuite.
nod L[1000];
L
3 5 4 1 5 4 0
7
1 2 3 4 5 6
Ocupat
0 1 1 1 0
1
1 2 3 4 5 6
int Există_spaţiu( )
{ return nr_elem<1000; }
□ Alocarea propriu-zisă se face prin funcţia următoare:
void Aloca(adresa& x)
{
adresa i=1;
while (ocupat[i] ) i++;
x=i; ocupat[i]=1; nr_elem++;
}
void Eliberează(adresa x)
{
ocupat[x]=0; nr_elem --;
}
Observaţii:
#include <iostream.h>
struct complex
{ int m, n; };
main( )
{ complex* adr;
adr=new complex; adr->m=7; adr->n=5;
cout<<adr->m<<” “<<adr->n<<end1;
delete adr;
}
□ Următorul program creează o listă liniară simplu înlănţuită cu 3 noduri
care reţin 1 2 3. Exemplul este pur didactic.
#include <iostream.h>
struct Nod
{
int info;
Nod* adr_urm;
};
main( );
{
Nod *c, *d, *v;
v=new Nod; v->info=1;
c=new Nod; c->info=2; v->adr_urm=c;
d=new Nod; d->info=3; d->adr_urm=0; c->adr_urm=d;
c=v;
while ( c );
{ cout<<c->info<<” “;
c=c->adr_urm;
}
}
v
1 v=new Nod; v->info=1;
c
2
v
1 v->adr_urm=c;
c
2
v
d=new Nod; d->info=3;
d-> adr_urm=0;
c->adr_urm=d;
1
c
2
Observaţii:
Stivele se pot aloca secvenţial (ca vectori). 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
A Introducem în stivă litera B, deci k va lua valoarea 2.
Observaţii:
Exemple:
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
#include <iostream.h>
int st[100], n, k;
main( )
{ cout<<”n=”; cin>>n;
k=1; st[1]=n;
while (k>0)
if st([k]<12)
{ k++;
st[k]=st[k-1]+2;
}
else
{ k--;
if (k>0) st[k]=st[k+1]-1;
}
cout<<”n=”<<st[1]-1;
}
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
10 01
20 11 11 11
21 21 21 21 21
10
11 11
02 13 12 12
21 13 12 13 13
01
11 02
12 12 03
13 13 13 04
ack(2,1)=5.
else
{
k--;
if (k>0)
{ st[k] [0] =st[k[] [0] – 1;
st[k] [1] =st[k+1] [1] +1;
}
}
cout<<”ac(“<<m<<’, ‘<<n<<”) = ”<<st[1] [1] +1;
}
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).
Stivele se pot aloca şi înlănţuit. În continuare, vom prezenta alocarea
dinamică (în HEAP).
Alăturat
observaţi
modul de
memorare
a unei
stive.
Nodurile
reţin
numere
întregi.
Ultimul
nod
introdus
este cel
care reţine
Analizaţi programul următor, care creează o listă liniară simplu
înlănţuită. Adăugarea unui element în stivă se face cu funcţia PUSH,
eliminarea cu funcţia POP. Vârful stivei este reţinut de variabila v.
#include <iostream.h>
struct Nod
{
int info;
Nod* adr_inap;
};
Nod* v;
int n;
void Pop(Nod*& v)
{
Nod* c;
If (!v) cout<<”stiva este vida”;
Else
{
c=v;
cout<<”am scos”<< c->info<<end1;
v=v->adr_inap;
delete c;
}
}
main( )
128
{ Push(v,1); Push(v,2); Push(v,3);
Pop(v); Pop(v); Pop(v); Pop(v);
}
129
While (v)
If (v->info<12)
Push(v, v->info+2);
Else
{ Man=v->info;
Pop(v);
If (v) v->info=Man-1;
}
cout<<”f=”<<Man-1;
}
3.4 Structura de tip coadă
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
130
Alocarea dinamică înlănţuită a cozii. O variabilă v va reţine adresa
elementului care urmează a fi scos (servit). O alta, numită sf, va reţine
adresa elementului introdus în coadă. Figura următoare prezintă o coadă
în care primul element care urmează a fi scos are adresa în v, iar ultimul
introdus are adresa sf.
v sf
3 5 2
#include <iostream.h>
struct Nod
{
int info;
Nod* adr_urm;
}
Nod* v, *sf;
int n;
131
void Scoate(Nod*& v)
{ Nod* c;
if (!v) cout<<”coada este vida”<<end1;
else
{ cout<<”Am scos”<<v->info<<end1;
c=v; v=v->adr_urm;
delete c;
}
}
void Listare(Nod* v)
{ Nod* c=v;
while ( c )
{ cout<<c->info<<” “;
c=c->adr_urm;
}
cout<<end1;
}
main( )
{ Pune(v, sf, 1); Pune(v, sf, 2); Pune(v, sf, 3); Listare(v);
Scoate(v); Listare(v);
Scoate(v); Listare(v);
Scoate(v); Listare(v);
Scoate(v); Listare(v);
}
132
Capitolul 4
Introducere in recursivitate
n !=fact(n)= 1, n=0
n€N
n fact(n-1), astfel
3 !=fact(3)=3xfact(2)=3x2xfact(1)=3x2x1xfact(0)=3x2x1x1=6.
#include <iostream.h>
int fact(int n)
133
{ if (!n) return 1;
else return n* fact(n-1);
}
main( )
{ int n;
cout<<”n=”; cin>>n;
cout<<fact(n) ;
}
134
n→3 La primul apel al functiei fact se creeaza in segmental de
stiva o
variabila numita n, care retine 3- valoarea parametrului
formal.
n→2
n→3 Functia se autoapeleaza. De aceasta data parametrul n ia
valoarea 2
.In stiva se creeaza un nou nivel, care retine n cu valoarea
2.
135
#include <iostream.h >
void fact(int val, int n,int&prod)
{ if (val<=n)
{ prod*=val;
fact(val+1,n,prod);
}
}
main( )
{ int val,n,p=1;
cout<<”n=”; cin>>n;
fact(1,n,p) ;
}
1 3 Referinta catre p
1 3 3 Referinta catre p
2 3 Referinta catre p
p val n 1 3 Referinta catre p prod
Pentru ca val este mai mic sau egal ca n se
efectueaza : prod :=prod*val ;Intrucat variabila p a fost transmisa prin referinta,
programul lucreaza cu aceasta variabila, in loc de prod.In concluzie, se efectueaza
p :=p*val ;.Apoi, functia se autoapeleaza :
2 3 Referinta catre p
1 1 3 Referinta catre p
p val n prod
2
p val n prod
136
Pentru ca val este 3 –mai mic sau egal cu 3 – se efectueaza prod :=prod*val, deci p
ia valoarea 6, dupa care se revine pe nivelul 2, apoi 1, apoi in programul principal.
Observatii :
▫Dupa cum rezulta din aceste exemple, subprogramul lucreaza cu datele aflate pe un
anumit nivel al stivei pentru variabilele transmise prin valoare si variabilele locale
ale sale, dar si cu variabilele functiei main( ) , daca acesteav sunt transmise prin
referinta.
▫In cazul unui numar mare de autoapelari , exista posibilitatea ca segmentul de stiva
sa se ocupe total, in caz in care programul se va termina cu eroare.
▫Recursivitatea presupune mai multa memorie(ma refer la stiva), aceasta este oricum
rezervata.
1.Wirth.O camera de luat vederi are in obiectiv un televizor care transmite imaginile
primite de la camera.Evident, in televizor se va vedea un televizor, iar in acesta
un televizor...s.a.m.d
Pe scurt :
▪ in orice televizor, se vede un televizor.
137
3.Constatare. Intr-o tara cu nivel de trai scazut, orice partid care ajunge la putere
afirma:de vina nu suntem noi, sunt cei de la care am preluat puterea!.
4.Logica. Orice militar aflat in ciclu 2 de instruire afirma :cand eram in ciclu 1, cei
din ciclu 2 ‘m-au chinuit inutil’.Asa fac si eu cu cei din ciclu 1.
Observatii
▪Pentru orice algoritm recursiv exista unul iterativ care rezolva aceeasi problema.
x-1, x>=12
F(x)=
F(F(x+2)),x<12
Aceasta aplicatie a fost tratata iterativ, prin utilizarea stivei.In cazul tratarii recursive ,
nu faceam altceva decat sa transcriem definitia recursiva a functiei.
138
#include <iostream.h>
int x ;
int manna (int x)
{if (x>=12) return x-1;
else return manna(manna(x+2));
}
main( )
{ cout<<”x=”;cin>>x;
cout<<manna(x) ;
}
n+1, m=0
Ack(m,n)= Ack(m-1,1), n=0
Ack(m-1,Ack(m,n-1)),astfel
Si aceasta aplicatie a fost tratata iterative, prin utilizarea stivei. In cazul tratarii
recursive, nu facem altceva decat sa transcriem definitia recursive a functiei.
Algoritmul recursiv nu necesita comentarii.
#include <iostream.h>
int m,n;
0, n=0
139
Un= 1, n=1
Un+1+Un-2 astfel
#include <iostream.h>
int n ;
int fib (int n)
{ if (!n) return 0;
else if (n= =) return 1;
else return fib(n-1)+fib(n-2);
}
main( )
{ cout<<”n=”;cin>>n;
cout<<fib(n) ;
}
#include <iostream.h>
main( )
{ int n, f0=0,f1=1,f2;
cout<<”n=”;cin>>n;
if (!n) cout<<f0;
else
if (n= =1) cout<<f1;
else
{ for (int i=2; i<=n; i++)
{f2=f0+f1; f0=f1; f1=f2;}
cout<<f2;
}
}
140
□ Se dau doua numere naturale a si b.Se cere sa se calculeze cel mai mare divizor
comun al lor.
Pentru rezolvare, utilizam o definitie recursiva a celui mai mare divizor comun pentru
doua numere naturale a si b.
a, a=b
cmmdc(a,b)= cmmdc(a-b,b), a>b
cmmdc(a,b-a), a<b
#include <iostream.h>
int a,b;
main( )
{ cout<<”a=”;cin>>a;
cout<<”b=”;cin>>b;
cout<<cmmdc(a,b);
}
#include <iostream.h>
main( )
{ int a,b ;
cout<<”a=”; cin>>a;
cout<<”b=”;cin>>b;
while (a!=b)
if (a>b) a=a-b;
else b=b-a;
cout<<”cmmdc=”<<a;
}
141
□Sa se scrie o functie recursive pentru a calcula suma cifrelor unui numar natural.
#include <iostream.h>
main( )
{ int n,s=0 ;
cout<<”n=”;cin>>n;
while (n)
{s+=n%10; n/=10;}
cout<<”s=”<<s;
}
Retinem idea:se izoleaza ultima cifra, iar lui n i se atribuie catul intreg dintre vechea
valoare si 10.Aceasta idee foloseste pentru a gasi o relatie de recurenta, necesara
elaborarii variantei recursive :
0, n=0
S(n)=
n%10+S(n/10), astfel
#include <iostream.h>
int n ;
int s(int n)
{ if (!n) return 0;
else return n%10 +s(n/10);
}
main( )
{ cout<<”n=”; cin>>n;
cout<<s(n) ;
}
▪ se citeste un caracter ;
▪ daca este diferit de 0, se reapeleaza functia ;
▪ se tipareste caracterul ;
#include <iostream.h>
void inv( )
142
{ char a; cin>>a;
if (a!=’0’) inv( );
cout<<a;
}
main( )
{ inv( );
cout<<endl;
}
#include <iostream.h>
int n,b ;
void transform (int n,int b)
{int rest=n%b;
if (n>=b) transform(n/b,b);
cout<<rest;
}
main( )
{ cout<<”n=”;cin>>n;
cout<<”baza=”;cin>>b;
transform(n,b);
}
#include <iostream.h>
#include <math.h>
double a,b;
143
int n;
double bn (int n)
{ if (!n) return b;
else return sqrt(an(n-1)*bn(n-1));
}
main( )
{ cout<<”a=”;cin>>a;
cout<<”b=”;cin>>b;
cout<<”n=”;cin>>n;
cout<<an(n)<<’ ‘<<bn(n) ;
}
▪ Daca n este 1, avem o singura multime A1.In acest caz programul afiseaza : {1},{2},
…,{n1}.
Pentru fiecare element al produsului cartezian al primelor p+1 multimi apelam din
nou functia (din cadrul ei, deci recursiv).Cand s-a ajuns la nivelul n+1, se tipareste
rezultatul.
#include <iostream.h>
int p[9],pc[9],n ;
void tipar( )
144
{ for (int i=1; i<=n;i++) cout<<pc[i];
cout<<endl;
}
{n,a2,a3,…,an-1,a1};
{a1,n,a3,…,an-1,a2};
{a1,a2,n,…,an-1,a3};
…
{a1,a2,a3,…,an-1,an}.
#include <iostream.h>
int p[10],n;
void tipar( )
{ for (int i=1;i<=n,i++) cout<<p[i];
145
cout<<endl;
}
main( )
{ cout<<”n=”;cin>>n;
permut(1,n,p) ;
}
#include <iostream.h>
int c[10],n,k ;
void tipar( )
{
for (int i=1;i<=k,i++) cout<<c[i];
146
cout<<endl;
}
{
m=c[pas]; c[pas]=c[j]; c[j]=m;
aranj(c,k,pas+1) ;
m=c[pas];c[pas]=c[j]; c[j]=m;
}
}
}
}
main ( )
{ cout<<”n=”,cin>>n;
cout<<”k=”;cin>>k;
aranj (c,k,1);
Intrucat elementele unei submultimi sunt distincte,le vom retine intr-un vector in
ordine crescatoare.
Presupunem ca am generat o submultime cu p elemente (p<k)a multimii
A.Aceasta este {i1,i2,..,ip},i1<i2<…ip.Utilizand aceasta submultime,se pot
genera urmatoarele submultimi cu p+1 elemente ale multimii A(componenta
147
p+1 va lua valorile de la ip+1 pana la n,pentru ca,pentru a evita repetitia,o
solutie contine numere strict crescatoare):
{i1,i2,…,ip,ip+1}
{i1,i2,…,ip,ip+2}
…
{i1,i2,…,ip,in}.
#include <iostream.h>
int c[10],n,k;
void tipar( )
{ for (int i=1;i<=k;i++) cout<<c[i[;
cout<<endl;
}
148
n=1 {1};
n=2 {1,2};
{1},{2};
n=3 {1,2,3};
{1,2},{3};
{1,3},{2};
{1},{2,3};
{1},{2},{3}.
O partitie se reprezinta utilizand un vector p cu n componente.P(i)=k semnifica
faptul ca elemental I al multimii {1,2,…,n} se gaseste in submultimea k a partitiei.
Este neceasr ca submultimile sa fie numeroatte utilizand numere
consecutive.
Utilizand acesata conventie sa generam toate partitiile pentru n=3;
n=1 1 {1};
n=2 11 {1,2};
12 {1},{2};
n=3 111 {1,2,3};
112 {1,2},{3};
121 {1,3},{2};
122 {1},{2,3};
123 {1},{2 ,{3}.
→ Fiind data o partitie a multimii {1,2,…k-1} sub forma de vector,pentru a obtine
partitiile corespunzatoare ei ale multimii {1,2..,k},componenta k a vectorului ia valori
intre 1 si maximul dintre componentele 1,2,…k-1, la care se aduga 1.
Aceasta este si idea de realizare a programului.
#include <iostream.h>
int p[10],n;
void tipar( )
{
int i,j,max;
cout<<”-----Partitie------“<<endl;
max=1;
for (i=2;i<=n;i++)
if (max<p[i])max=p[i];
for (i=1;i<=max);i++
{
cout<<”multimea”<<i<< ‘ ‘;
for (j=1;j<=n;j++)
if (p[j]==i)cout<<j;
cout<<endl;
}
}
void part(int k,int n,int p[10])
{
int max,i;
if (k==n+1) tipar( );
149
else
{ max=0;
for (i=1;i<=k-1;i++)
if (max<p[i]) max=p[i];
for (i=1;i<=max+1;i++)
{p[k]=I;
part(k+1,n,p);
}
}
}
main( )
{
cout<<”n=”;cin>>n;
part(1,n,p);
}
#include<iostream.h>
int v[10],n;
150
{ cout<<”n=”;cin>>n;
for (int i=1;i<=n;i++)
{cout<<”v[“<<i<<”]=”;cin>>v[i];}
cout<<”max=”<<max(1,n);
}
151
Conform strategiei generale Divide et impera,problema este descompusa in alte doua
subprobleme de acelasi tip si,dupa rezolvarea lor,rezultatele se combina(in particular
se interclaseaza).Descompunerea unui vector in alti doi vectori care urmeaza a fi
sortati are loc pana cand avem de sortat vetori de una sau doua componente.
In aplicatie,functia sort sorteaza un vector cu maximum doua elemente;interc
interclaseaza rezultatele ;divimp implementeaza strategia generala a metodei studiate.
#include <iostraem.h>
int a[10],n;
152
for (i=p;i<=q;i++)
{
a[i]=b[k];
k=k+1;
}
}
void divimp (int p,int q,int a[10])
{
int m;
if ((q-p)<=1) sort(p,q,a);
else
{ m=(p+q)/2;
divimp(p,m,a);
divimp(m+1,q,a);
interc(p,q,m,a);
}
}
main( )
{ int i;
cout<<”n=”;cin>>n;
for (i=1;i<=n;i++)
{
cout<<”a[“<<i<<”]=”;cin>>a[i];}
divimp(1,n,a);
for (i=1;i<=n;i++)
cout<<a[i]<<” “;
}
Pentru rezolvare este necesara o functie poz care trateaza o portiune din vector
cuprinsa intre indicii dati de li (limita inferioara) si ls(limita superioara).Rolul acestei
functii este de a pozitiona prima componenta a[li] pe o pozitie k cuprinsa intre li si
ls,astfel incat toate componentele vectorului cuprinse intre li si k-1 sa fie mai mici sau
egale decat a[k] si toate componentele vectorului cuprinse intre k+1 si ls sa fie mai
mari sau egale decat a[k].
In aceasta functie exista doua moduri de lucru:
a) i ramane constant,j scade cu 1;
b) i creste cu 1,j ramane constant.
Functia este conceputa astfel:
initial,i va lua valoarea li,iar j va lua valoarea ls (elemental care initial
se afla pe pozitia li se va gasi mereu pe o pozitie data de i sau de j);
se trece in modul de lucru a);
atata timp cat i<j,se executa:
-daca a[i] este strict mai amre decat a[j],atunci se inverseaza celee
doua numere si se schimba modul de lucru;
153
-i si j se modifica corespunzator modului de lucru in care se afla
programul;
-k ia vlaoraea comuna a lui i si j.
Dupa aplicarea functiei poz,este evident ca elementul care se afla initial
in pozitia li va ajunge pe o pozitie k si va ramane pe acea pozitie in
cadrul vectorului deja sortat,fapt care reprezinta esenta algoritmului.
Functoa quick are parametrii li si ls(limita inferioara si limita superioara).In cadrul ei
se utilizeaza metoda Divide et impera,dupa cum urmeaza:
-se apeleaza poz;
-se apeleaza quick pentru li si k-1;
-se apeleaza quick pentru k+1 si ls.
#include<iostream.h>
int a[100],n,k;
154
o Turnurile din Hanoi.Se dau 3 tije simbolizate prin a,b,c.Pe tija a se gasesc
discuri de diameter diferite,asezate in orine descrescatoare a diametrelor
private de jos in sus.Se cere sa se mute discurile de pe tija a pe tija
b,utilizand ca tija intermediara tija c,respectand urmatoarele reguli:
La fiecare pas se muta un singur disc;
Nu este permis sa se aseze un disc cu diametrul mai mare peste un disc cu
diametrul mai mic.
Rezolvare:
Daca n=1 se face mutarea ab,adica se muta discul de pe tija a pe tija b.
Daca n=2 se fac mutarile ac,ab,cb.
In cazul in care n>2 problema se complica.Notam cu H(n,a,b,c) sirul mutarilor celor n
discuri de pe tija a pe tija b,utilizand ca tija intermediara,toja c.
#include <iostream.h>
char a,b,c;
int n;
155
a=’a’;b=’b’;c=’c’;
han(n,a,b,c);
}
Mentionam ca dreptunghiul de arie maxima fara gauri este retinut prin aceiasi
parametric ca si dreptunghiul cu gauri,in zonele XF,YF,LF,HF
#include <iostream.h>
int 1,h,I,n,xf,yf,lf,hf,xv[10],yv[10];
void dimp(int x,int y,int l,int h,int& xf,int&yf,int& lf,int& hf,int xv[10],int yv[10])
{ int gasit=0,i=1;
while (i<=n && !gasit)
156
if (xv[i]>x && xv[i]<l && yv[i]>y && yv[i]<y+h)
gasit=1;
else i++;
if (gasit)
{ dimp(x,y,xv[i]-x,h,xf,yf,lf,hf,xv,yv);
dimp(xv[i],y,l+x-xv[i],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],xf,yf,lf,hf,xv,yv);
}
else
if (l*h>lf*hf)
{ xf=x;yf=y;
lf=l;hf=h;
}
}
main ( )
{ cout<<”n=”;cin>>n;
for(int i=1;i<=n;i++)
{ cout<<”x[“<<i<<”]=”;cin>>xv[i];
cout<<”y[“<<i<<”]=”;cin>>yv[i];
}
cout<<”l=”;cin>>l;cout<<”h=”;cin>>h;
dimp(0,0,l,h,xf,yf,lf,hf,xv,yv);
cout<<”x=”<<xf<<”y=”<<yf<<”l=”<<”h=”<<hf;
}
Tehnica backtracking
157
metodei,realizeaza acelasi lucru.Sarcina rezolvitorului este sa scrie
explicit,pentru fiecare problema in parte,functiile apelate de rutina
backtracking.
Evident o astfel de abordare conduce la programe lungi.Nimeni nun e opreste
ca,dupa intelegerea metodei,sa scriem programe scurte,specifice fiecarei
probleme in parte(de exemplu,scurtam substantial textul doar daca renuntam
utilizarea unor functii,scriind instructiunile lor chiar in corpul rutinei).
De exemplu,pentru generarea permutarilor multimii {1,2,3…,n},orice nivel al stivei
va lua valori de la 1 la n.Initializarea unui nivel(oarecare) se face cu valoarea
0.Functia de initializare se va numi init( ).
→ Gasirea urmatorului element al multimii Ak+1,element netestat,se face cu ajutorul
functiei int Am_Succesor( ).Daca exista successor,acesta este pus in stiva si functia
returneaza 1,altfel functia returneaza 0.
→ Testul daca s-a ajuns sau nu la solutia finala se face cu ajutorul functiei int
Solutie( )
→ Solutia se tipareste cu ajutorul functiei Tipar( ).
→Testarea conditiilor de continuare(adica daca avem sansa sau nu ca prin valoarea
aflata pe nivelul k+1 sa ajungem la solutie) se face cu functia int E_Vakid( )
careintoarce 1 daca conditiile sunt indeplinite,sau 0 in caz contrar.
Prezentam rutina backtracking:
void back( )
{int AS;
k=1;Init( );
while (k>0)
{
do { } while ((AS=AM_Succesor( )) && !E_Valid( ));
if (AS)
if (Solutie()))Tipar();
else {k++;Init( );}
else k--;
}
}
1 2 2 2 2
1 1 1 1 1 1
158
1 2 3
3 3 3 3 1
1 1 1 1 2 2
1 2 3 1
1 1 1 2 3 3
2 2 2 2 2 2
#include <iostream.h>
int st[10],n,k;
void Init( )
{ st[k]=0;}
int Am_Succesor( )
{ if st[k]<n)
{ st[k]++;
return 1;
}
else return 0;
}
int E_Valid( )
{ for (int i=1;i<k;i++)
if (st[i]==st[k]) return 0;
return 1;
}
int Solutie ( )
{ return k==n;}
void Tipar( )
159
{
for (int i=1;i<=n;i++) cout<<st[i];
cout<<endl;
}
void back( )
{int AS;
k=1;Init( );
while (k>0)
{ do {}while ((AS=Am_Succesor( )) && !E_Valid( ));
if (AS)
if (Solutie( )) Tipar( );
else (k++;Init( ));}
else k--;
}
}
main ( )
{ cout<<””n=;cin>>n;
back( );
}
o Problema celor n dame.Fiind data o tabla de sah nxn,se cer toate solutiile de
aranjare a n dame,astfel incat san u se afle doua dame pe aceeasi linie,coloana
sau diagonala(damele san u se atace reciproc).
Exemplu:Presupunand ca dispunem de o tabla de dimensiune 4X4,o solutie ar fi
urmatoarea:
D
D
D
D
160
D
D
D
D
D
D
D
161
Dama a treia se aseaza in prima coloana.
D
D
D
Acum este posibil sa plasam a patra dama in coloana a-3-a si astfel am obtinut o
solutie a problemei.
D
D
D
D
Algoritmul continua in acest mod pana cand trebuie scoasa de pe tabla prima dama.
ST(4)
ST(3)
ST(2)
ST(1)
3 sau situatia:
1
4 D st(1) = 3 i = 1
2 st(3) = 1 j=3
D | st(i) – st(j) l|= | 3-1 | = 2
| i-j | = |1-3 | = 2
162
Intrucat doua dame nu se pot gasi in aceeasi coloana, rezulta ca o solutie este sub
forma de permutare. O prima idee ne conduce la generarea tuturor permutarilor si la
extragerea solutiilor pentru problema ( ca doua dame sa nu fie plasate in aceeasi
diagonala).A proceda astfel, inseamna ca nu lucram conform strategiei
backtracking.Aceasta presupune ca imediat ce am gasit doua dame care se ataca, sa
reluam cautarea in alte conditii fata de programul de generare a permutarilor,
programul de generare a tuturor solutiilor problemei celor n dame, are o singura
conditie suplimentara, in functia E_valid.
#include <iostream.h>
#include <math.h>
int st[100],n,k;
void init( )
{ st[k]=0; }
int Am_Succesor ( )
{ if (st[k]<n)
{ st[k]++;
return 1;
}
else return 0;
}
int E_Valid ( )
{ for (int i=1; i<k; i++)
if (st[k]= =st[i] l l abs(st[k] – st[i])= =abs(k-1) ) return 0;
return 1;
}
int Solutie( )
{ return k= =n;}
void Tipar( )
{ for (int i=1; i<=n; i++) cout<<st[i];
cout<<endl;
}
void back( )
{ int AS; k=1;
Init( );
While (k>0)
{
do { } while ((AS=Am_Succesor( )) && !E_Valid( ));
if (AS)
if (Solutie( )) Tipar ( );
else {k++; Init( );}
else k- - ;
163
}
}
main( )
{ cout<<”n=”; cin>>n;
back( );
}
A1={1,2,…,k1}
A2={1,2,…,k2}
……………….
An={1,2,…,kn}.
a) orice element aflat pe nivelul k al stivei este valid, motiv pentru care functia
E_Valid nu face decat sa returneze 1.
b) limita superioara pe nivelul k al stivei este data de a[k].
1
1 2 3 2
1 1 1 2 1
1 1 1 1 1
3 3
2 3 1 2
1 3
2 2 3 3 1
1 1 1 1
……………………………......
Observatii:
164
▪ Algoritmul prezentat aici este de tip backtracking? Intrebarea are sens pentru ca
este absent mecanismul de intoarcere. Vom admite ca si acesta este backtracking, dar
„degenerat”.
#include <iostream.h>
int st[10],a[10],n,k;
void init( )
{ st[k]=0; }
int Am_Succesor ( )
{ if (st[k]<a[k])
{ st[k]++; return 1;}
else return 0;
}
int E_Valid ( )
{ return 1;}
int Solutie( )
{ return k= =n;}
void Tipar( )
{ for (int i=1; i<=n;i++) cout<<st[i];
cout<<endl;
}
void back( )
{int AS;
k=1; Init( );
while (k>0)
{ do { } while ((AS=Am_Succesor( )) && !E_valid( ));
if (Solutie( )) Tipar( );
else {k++; Init( );}
else k- -;
}
}
main( )
{ cout<<”numar total de multimi=”; cin >>n;
for (int i=1; i<=n; i++)
{ cout<<”a[“<<i<<”]=”; cin>>a[i];}
back( );
}
165
▪ stiva are inaltimea p;
▪ fiecare nivel ia valori intre 1 si n;
▪ elementele plasate pe diverse niveluri trebuie sa fie distincte.
#include <iostream.h>
int st[10],n,k,p;
void Init ( )
{ st[k]=0; }
int Am_Succesor( )
{ if (st[k]<n)
{ st[k]++;
return 1;
}
else return 0;
}
int E_Valid( )
{
for (int i=1; i<k; i++)
if (st[k]= =st[i]) return 0;
return 1;
}
int Solutie( )
{ return k= =p;)
void Tipar( )
{
for (int i=1; i<=p;i++) cout<<st[i];
cout<<endl;
}
void back( )
{intAS;
k=1; Init( );
while (k>0)
{
do { } while ((AS=Am_Succesor( )) && !E_valid( ));
if (AS)
if (Solutie( )) Tipar( );
else {k++; Init( );}
else k- -;
}
}
166
main ( )
{ cout<<”n=”; cin>>n;
cout<<”p=”; cin>>p;
back( );
}
#include <iostream.h>
int st[10],n,k,p;
void Init ( )
{ if (k>1) st[k]=st[k-1];
else st[k]=0;
}
int Am_Succesor ( )
{ if (st[k]<n-p+k)
{ st[k]++;
return 1;
}
else return 0;
}
int Solutie ( )
{ return k= =p;}
void Tipar ( )
{ for ( int i=1; i<=p;i++) cout <<st[i];
cout<<endl;
}
void back ( )
{int AS;
k=1; Init( );
while (k>0)
{
do { } while ((Am_Succesor( )) && !E_valid( ));
if(AS)
if (Solutie( )) Tipar( );
167
else {k++;Init( );}
else k- -;
}
}
main ( )
{ cout<<”n=”; cin>>n;
cout<<”p=”;cin>>p;
back( );
}
□ Problema colorarii hartilor fiind data o harta cu n tari, se cer toate solutiile de
colorare a hartii, utilizand cel mult 4 culori, astfel incat doua tari cu frontiera comuna
sa fie colorate diferit. Este demonstrat faptul ca sunt suficiente numai 4 culori pentru
ca orice harta sa poata fi colorata.
Pentru exemplificare, vom considera urmatoarea harta unde tarile sunt numerotate cu
cifre cuprinse intre 1 si 5.
● tara 1 – culoarea 1;
● tara 2 - culoarea 2;
● tara 3 - culoarea 1;
● tara 4 - culoarea 3;
● tara 5 - culoarea 4.
void Init( )
{ st[k]=0; }
168
int Am_Succesor ( )
{ if (st[k]<4)
{st[k]++;
return 11;
}
else return 0;
}
int E_valid ( )
{
for (int i=1; i<=k-1; i++)
if (st[i]= =st[k] && a[i] [k] = =1) return 0;
return1;
}
int Solutie( )
{ return k= =n;}
void Tipar( )
{ cout<<”varianta”<<endl;
for (int i=1;i<’n; i++) cout”tara”<<i<<”culoarea”
<<st[i]<<endl;
cout<<endl;
}
void back ( )
{int AS;
k=1; Init( );
while (k>0)
{
do { } while (( AS=Am_Succesor( )) && !E_valid( ));
if (AS)
if (Solutie( )) Tipar( );
else {k++; Init( );}
else k--;
}
}
main ( )
{ cout<<”numar de tari”; cin>>n;
for (int i=1; i<=n; i++)
for (int j=1; j<=i-1;j++)
{ cout<<”a[„<<i<<’,’<<j<<”]=”; cin>>a[i] [j]
a[j] [i]=a[i] [j];
{
back( )
}
169
□ Problema comis-voiajorului.Un comis-voiajor trebuie sa viziteze un numar n de
orase. Initial, acesta se afla intr-unul dintre ele, notat 1.
Comis-voiajorului doreste sa nu treaca de doua ori prin acelasi oras, iar la intoarcere
sa revina in orasul 1. Cunoscand legaturile existente intre orase, se cere sa se
tipareasca toate drumurile posibile pe care le poate efectua comis-voiajorul.
● 1,2,3,4,5,6,1;
● 1,2,5,4,3,6,1;
● 1,6,3,4,5,2,1;
● 1,6,5,4,3,2,1.
Legaturile existente intre orase sunt date in matricea An,n. Elementele matricei A pot
fi 0 sau 1 ( matricea este binara).
1, daca exista drum intre orasele I si j
A(i,j)=
0, astfel.
Pentru rezolvarea problemei folosim o stiva st .La baza stivei (nivelul1) se incarca
numarul 1. Prezentam in continuare modulul de rezolvare a problemei:
170
Algoritmul continua in acest mod pana se ajunge din nou la nivelul 1, caz in care
algoritmul se incheie.
Un succesor, intre 2 si n, aflat pe nivelul k al stivei, este considerat valid daca sunt
ineplinite simultan urmatoarele conditii :
● nu s-a mai trecut prin orasul simbolizat de succesor, deci acesta nu se regaseste in
stiva ;
● exista drum intre orasul aflat pe nivelul k-1 si cela flat pe nivelul k ;
● daca succesorul se gaseste pe nivelul n, sa existe drum de le el le orasul 1.
BACKTRAKING RECURSIV
Problemele incluse in acest capitol sunt rezolvate prin metoda backtracking insa
caestea ca idee de cautare a solutiilor,nu ca rutina standardizata.
*Problema celor n dame.Fiind data o tabla de sah n*n,se cer toate solutiile de
aranjare a n dame astfel incat sa nu se afle doua dame pe aceeas linie,coloana sau
diagonala (damele sa nu se atace reciproc).
#include <iostream.h>
#include <math.h>
int t[20],n;
void tipar ( )
{for (int i:=1;i<=n;i++) cout<<t[i];
cout<<end1;}
void dame(int k)
{int i,j,corect;
if (k==n=1) tipar( );
else
{for (i=t[k]+1;i<=n;i++)}
t[k]=i;
corect=1;
for (i=t[k]+1;i<=n;i++)}
t[k]:=i;
corect=1;
for (j=1;j<=n;i==)
{t[k]:=i;
corect=1;
for (j=1;j<=k-1;j++)
if (t[j]==t[k] // abs (t[k]-t[j]==(k-j))
corect=0;
if (corect) dame (k+1);}}t[k]=0;}main( )}
cout<<"n=";cin>>n;
dame(1);
Generarea partitiilor unui numar natural :se citeste un numar natural n.Se cere sa
se tipareasca toate modurile de descompunere a lui ca suma de numere naturale .
Exemplu:n=4.Avem4,31,212,211,13,121,112,1111.
Ordinea numerelor din suma este importanta.Astfel,se tipareste 112 dar si 211,121.
Functia part are doi parametri:componenta la care s-a ajuns (k) si o valoare
v.Initial,aceasta este apelata pentru nivelul 1 si valoarea n.
Imediat ce este apelta functias va apela o alta pentru a tipari vectorul.Din valoarea
care se gaseste pe un nivel (s[k]) se scad,pe rand,valorile 1,2,s[k-1], valori cu care es
apeleaza functia pentru nivelul urmator.La revenire se reface valoarea existenta.In
concluzie putem apela:
171
-nivelul 1 la valoarea n;
-fiind dat un nivel oarecare se tipareste vectorul apoi se scad,pe rand ,toate valorile
posibile ,valori cu care functia este reapelata ,avand grija ca la revenire sa refacem
valoarea (pentru a se executa corect scaderea urmatoare ).
#include<iostream.h>
int s[20],n;
void tipar (int k)
{ for (int i=1;i<=k;i++) cout<<s[i];
cout<<end1;}void part (int k,int v)
{int i;
s[k]=v;
tipar(k);
for (i=1;i<=s[k]-1;i++)
{s[k]=s[k]-i;
part(k+1,i);
s[k]=s[k]+1;}}.
main( )
{cout<<"n=";cin>>n;
part(1,n);}
-Plata unei sume cu bacnote de valori date s .Se dau suma s si n tipuri de
monedeavand valori de a1,a2,.......,an lei,Se cer toate modalitatile de plata a sumei s
utilizand aceste monede.
Valorile celor n monede se retin in vectorul a.Se presupunem ca dispunem de atatea
monede din fiecare tip cate sunt necesare (acest numar se obtine in ipoteza ca toata
suma s este platita numai cu monede de ace;ls tip si se retine in vectorul b).O solutie
este sub forma unui vector cu n componente (sol) in componenta i retine numarul de
monedede tipul i care se folosesc pentru plata sumei s.Evident acest numar poate fi
si 0.Toate compo=nentele vectorului so1 sunt initializate cu -1(valoare aflata inaitea
valorilor posibile).Initializarea unei componente se face si atunci cand se revine la
componenta de indice imediat inferior.\
Functia plata ( ) are doi parametri :indicele componentei din so1 care urmeaza a fi
completata (k) si suma platita pana in acel moment (so0).Apelul sau (din cadrul
programului prinvcipalse face pentru prima componenta din suma 0.
Find dat un nivel oarecare (k) se procedeaza astfel:P
-atata timp cat este posibil sa utilizam in plata inca o moneda de tipul respectiv(nu se
depaseste suma care trebuie platita) se efectueaza urmatarele:
-se incrementeaza nuvelul(se foloseste o noua moneda de tipul k);
-suma platita se majoreaza cu valoarea ace;lei monede;
-in cazxul cand a fost obtinuta o solutie ,acesta se tipareste contrar se apeleaza
functia pentru nivelul urmator,cu noua valoare s0.
#include <iostream.h>
int sol[10],a[10],b[10],n,i,s;
172
s0+=a[k];
if (s0==s) tipar(k);
else if (k<n) plata (k+1,s0);}
sol[k]=-1;}
main( )
{cout<<"cate tipuri de bacnote avem?";cin>>n;
cout<<"suma=";
cin>>s;
for (i=1;i<=n;i++)
{cout<<"valoarea monedei de tipul"<<i<<' ';
cin>>a[i];
b[i]=s/a[i];
sol[i]:=-1;
}
plata{1,0);}
173
int i;
cout <<"-----------"<<end1;
for (i]1;i<=k;i++;)
cout<<"1="<<d[0] [i]<<' '>>"c="<<d[1] [i] <<end1;
}
void ies (int x,int y,int& k,int 1[10] [10],int d[2] [100])
{
int i,gasit;
if (1[x] [y]==16) tipar(k,d);
else
{
k++;
d[0] [k]=x;d[1] [k]=y;
gasit=0;
for (i=1;i<=k-1;i++)
if (d[0] [i]==d[0] [k] && d[1] [i]==d[1] [k] gasit=1;
if (!gasit
for (i=1;i<=4;i++)
switch (i)
{
case 1: {if (1[x] [y] & (int)8) ies(x-1,y,k,1,d);break;}
case 2;{if (1 [x] [y] & (int)4) ies(x,y+1,k,l,d);break;}
case 3: {if (1[x] [y] & (int)2) ies(x+1,y,k,l,d);break;}
case 4: if (1[x] [y] & (int)1) ies(x,y-1,k,l,d);
k--;
}
}
main( )
{
cout<<"N=';cin>>n;
cout<<"M=";cin>>m;
for (i=1;;i<=m;i++)
for (j=1;j<=n;j++)
{
cout<<"1"["<<i<<','<<j<<"]=";
cin>>1[i] [j];
}
cout<<"X="{;cin>>X;
cout<<"Y=";cin>>Y;
for (i=1;i<=n;i++)
{1[0] [i]=16;
l[m+1 [i]=16;
}
k=0;
ies(x,y,k,l,d);
}
174
Ex, Fie matricea:
0 1 1 0
0 0 0 1
A= 0 1 1 1
1 0 0 0
Suprafata inchisa este data de elementele
A(1,1),A(2,1),A(2,2),A(2,3),A(3,1).Consideram coordonatele (2,3) ale unui punct
situat in interiorul acestei suprafete.
Dupa executia programului matricea va arata astfel:
1 1 1 0
1 1 1 1
A= 1 1 1 1
1 0 0 0
Algoritmul se dovedeste extrem de util in colorarea unei suprafete inchise atunci
cand sunt cunoscute coordonatele unui punct situat in interiorul ei.Acest algoritm
este cunoscut si sub denumirea de algoritmul FILL.
175
a[0] [i]=1;
a[m+1][i]=1
}
for (i=i;i<=m;i++);
{
a[i] [0]=1;a[i] [n+1]=1;
{
cout<<"X=";
cin>>x;
cout<<"Y=";
cin>>y;
for (i=1;i<=m;i++)
{
for (j=1;j<=n;j+=)
cout<,a[i]][i];
cout<< end1;
}
scriu(x,y,a);
cout<<end1<<end1;
for (i=1;i<=m;i++)
{
for (j=1;j<=n;j++)
cout<<a[i] [j];
cout<<end1;
}
})
Problema fotografiei (aplicatie FILL).O fotografie alb-negru este prezentata sub
forma unei matrice binare.Ea infatiseaza unul sau mai multe obiecte.Portiunile
corespunzatoare obiectului(sau obiectelor) in matrice au valoarea unu.Se cere sa se
determine daca fotografia reprezinta unul sau mai multe obiecte.
Exemple:
In matricea urmatoare sunt reprezentate doua obicte.
1 1 0 0
0 0 0 1
A= 1 1 1 1
1 1 1 1
In matricea de mai jos este reprezentat un singur obiect
0 1 1 0
0 0 0 1
A=0 1 1 1
1 0 0 0
Ca si in problemele anterioare spre a evita testul iesiri din matrice, aceasta este
bordata cu linii si coloane avand valoarea 0.
Algoritmul este tot cel din problema anteriara (FILL),dar aici cautarea se face pe opt
directii.In programul principal se citeste matricea si se cauta primul element 1 printre
elementele acesteia.Se apeleaza apoi functia compact( )care are rolul de a marca
cu 0 toate elemntele matricei care apartin acestui [prim obiect identificat.La
revenire ,se testeaza daca mai exista elemente cu valoarea 1in matrice .In caz
afirmativ se poate trage concluzia ca in fotografie aveam initial mai multe
obiecte;astfel,fotografia continea un singur obiect
#include <iostream.h>
int a[10][10],i,j,m,n,x,y,gasit;
176
{
if (a[x][y])
{
a[x][y]=0;
compact (x-1,y,a);
compact (x-1,y+1,a);
compact (x,y+1,a);
compact (x+1,y+1,a);
compact(x+1,y,a);
compact(x+1,y-1,a);
compact(x,y-1,a);
compact(x-1,y-1,a);
}
}
main( )
{cout <<"M=";cin>>m;
cout<<"N=";cin>>n;
for (i=1;i<=m i++)
for(j=1;j<=n;j++)
{cout<<"a["<<i<<','<<j<<"]=";
cin>>a[i][j];
}
for (i=1;i<n;i++)
{a[0][i]=0;
a[m+1][i]=0;
for (i=1;i<=m;i++)
{a[i][0]=0;a[i][n+1]=0;
}
x=0;
do
{x++;
y=0;
do
{y++;}
while (y!=n &&a[x][y]!=1);
}while((x!=m)&&a[x][y]!=1);
compact(x,y,a);
gasit=0;
for (i=1;i<=m;i++)
for(j=1;j<=n;j++)
if (a[i][j]==1) gasit=1;
if(gadsit0cout<<"mai multe obiecte";else cout<<"un obiect";
}
GREEDY
177
Sa considaram o multime A cu n elemente.Se cere o submultime a sa, eventual
m<=n elemente, astfel incat sa fie indeplinite anumite conditii(acestea difera de la o
problema la alta).
Se considera o multime de n enumere reale..Se cere o submultime a sa astfel incat
suma elemetelor ei sa fie maxima.Pentru rezolvare vom alege un prim element al
multimii de numere reale.Daca este posibil acesta va fi adaugat solutiei,initial
vide.Posibilitatea ca acesta sa fie edagat este data de semnul numarului(acesta
trebuie sa fie mai mare ca 0).Se alege un al doilea numar cu care se procedeaza in
mod asemanator.
Algoritmul se incheire cand au fost alese si, 4eventual ,adaugate toate elementele
multimi.
Pentru a rezolva o problema cu Greedy ,solutiia se construieste ,dupa algoritmul de
mai jos.
Pentru fiecare element care urmeaza sa fie adaugat solutiei finale, se efectueaza
oalegere a sa dintre elementele multimi A(dupa un mecanism specific fiecarei
probleme in parte),iar daca este posibil,acesta este adaugat.Alghoritmul se
termina,fie cand a fost gasita solutia ceruta ,fie cand afost gasita solutia ceruta fie
cand s-a constatat inexistenta acesteia.
Intuitiv,alegem un element,al doilea,.... pana cand obtinem ce dorim sau pana cand
au fost testate toate elementele multimii.De aici provine si numele
metodei(greedy=lacom.)
Cel care elaboreaza un algoritm greedy trebuie sa stie faptul ca, procedand in modul
ales de el, se ajunge la rezultatul dorit.Pentru fiecare problema in parte,dupa ce se
identifica un algoritm,este onbligatoriu sa se demonstreze ca acesta conduce la
solutia optima.
In general,numarul de operatii de baza efectuate de un algoritm greedy este o
expresie polinomiala -algoritmi sunt performanti
De multe ori este necesar ca elementele multimii A sa fie sortate,pentru ca apoi sa
slegem din acestea.
Intr-o zi trebuie planificate n spectacole.Pentru fiecare spectacol se cunoaste
intervalul in care se desfasoara:[st,sf(.Se cere sa se planifice un numar maxim de
spectacole astfel incat sa nu se suprapuna.
Vom construi o solutie dupa urmatorul algoritm:
P1 Sortam spectacolele dupa ora terminarii lor;
P2 Primul spectacol programat este celo care se termina cel ma devreme;
P3 Alegem primul spectacol dintre cele care urmeaza in sir ultimului spectacol
programat care indeplineste conditia ca incepe dupa ce s-a terminat ultimul
spectacol programat;
P4 Daca tentativa de mai sus a esuat (nu am gasit un astfel de spectacol) algoritmul
se termina,astfel se progreameaza spectacolul gasit si algoritmul se reia de la P3
Demonstratia se poate gasi in "Tehnici de programare"
#include <iostream.h>
int s[2][10],o[10],n,i,h1,m1,h2,m2,ora;
void sortare( )
{
int gata,m,i;
do{ gata=1;
for (i=1;i<=n-1;i++);
if (s[i][o[i]] s[1][o[i+1]])
{m=o[i];o[i]=o[i+1];
o[i+1]=m;
178
gata=0;
}
}
while (!gata)
}
main( )
{ cout<<"n=";cin"n;
for (i=1;i<=n;i+=)
{o[i]=i;
cot<<"ora de inceput pentru spectacolul"<<i<<"( h h) m m )=";
cin >>h1>m1;
s[0][i]=h1*60+m1;
cout <<"oradesfarsit pentru spectacolul "<<i<,"(hh mm)=";
cin>>h2>>m2;
s[1][i]=h2*60+m2;
}
sortare( );
cout,<<ordinea spectacolelor este"<<end1<<0[1]<<end1;
ora=s[1][o][1]];
for (i=2;i<=n;i++)
{ if (s[0][o[i]]>=ora{cout<<o[i]<<end1;
ora=s[1][o[i]];}
}
}
persoana are un rucsac cu ajutorul caruia poate transporta o
greutate maxima G.Persoana are la dispozitie n obiecte si
cunoaste pentru fiecare obiect greutatea si castigul care se obtine
in urma transportului sau la destinatie.Se cere sa se precizeze ce
obiecte trebuie sa transporte persoana in asa fel incat castigul sa
fie maxim.
O precizare in plus trnsforma adceasta problema in alte doua probleme
distincte.Aceasta precizare serefera la faptul ca obiectele pot fi sau taiate pentru
transportul la destinatie.In prima situatie,problema poarta nyumele de problema
continua a rucsacului ,iar in a doua avem problema discreta a rucsacului.Aceste
doua probleme se rezova diferit,mpotiv pentru care ele sunt prezentate
separat .Varianta continua a probelmei rucsacului este tratata in acest
paragraf.Algoritmul este urmatorul:
-se calculeaza,pentru fiecare obiect in parte,eficienta de transport rezultata prin
impartirea castigului la greutate(de fapt,acesta reprezinta castigul optinut din
transportul unitatii de greutate);
-obiectele se sorteaza in prdine descrescatoare a eficintei de transport si se preiau in
calcul in aceasta ordine4;
-castigul initial va fi 0 iar greutatea ramasa deincarcat va fi G
-atat timp cat nu a fost completata greuataea maxima a rucsacului si nu a fodst luate
un considerare toate obiectele se procedeaza astfel:
-dintre obiectele neincarcate se selecteaza acela cu cea mai mica eficienta de
transport si avem doua posibilitati:
-acesta incape in totalitate in rucsac.deci se scade din greutatea ramasa de incarcat
greutatea obictulyui,la castig se cumul3aza castugul datorat transportului acestui
obict;
-se tipareste 1 in sensul ca intregul obict a fost incarcat;
179
-obiectul nu incape in totalitate in rucsac,caz in care se calculeaza ce patre din el
poate fi transportata cse cumuleaza castigul obtinut cu transportul aceste plati din
obiect iar greutatea ramasa de incarcat devine 0.
#include<iostream.h>
double c[9],g[9],ef[9],gv,man,castig;
int n,i,man1,inv,ordine[9];
main( )
{cout<<''Greutatea ce poate fi transportata='';cin>>gv;
cout<<''Numar de obiecte='';cin>>n;
for(i=1;i<=n;i++)
{
cout<<''c[''<<i<<'']='';cin>>c[i];
cout<<''g[''<<i<<''];cin>>g[i];
ordine[i]=i; ef[i]=c[i] /g[i];
}
do
{
inv=0;
for(i=1;i<=n-1;i++)
if(ef[i]<ef[i+1])
{ man=ef[i];ef[i]=ef[i=1];ef[i=1]=man;
man=c[i]; c[i]=c[i=1]; c[i=1]=man;
man=g[i];g[i]=g[i+1]=man;
inv=1;
man1=ordine[i];ordine[i]=ordine[i+1];ordine[i+1]=man1;
}
}
while (inv);
i=1;
while(gv>o && i<=n)
{
if (gv>g[i])
{ cout <<''Obieclul ''<<ordine[i]<< ' '<<1<<endl;
gv-=g[i];castig+=+c[i];
}
else
{cout<<''Obiectul ''<<ordine[i]<<' ' <<gv /g[i]<<endl;
castig+c[i]*gv /g[i];
gv=0;
}
i++;
}
cout<<''Castig total=''<<castig;
}
PROGRAMARE DINAMICA
Alaturi de Greedy,programarea dinamica este o tehnica ce conduce, de
cele mai multe ori, la un timp de calcul polinomial.Mai mult, ea furnizeaza in
totdeauna solutia optima .Din nefericire, programarea dinamica nu se poate
aplica tuturor problemelor, ci numai care indeplinesc anumite conditii.
Se considera o problema in care rezultatul se obtine ca urmare a unui sir de
decizii D1, D2,......Dn. In urma decizei D1 sistemul evolueaza din starea S0 in
starea S1,in urma decizei D2 sistemul evolueaza din starea S1 in starea
S2,....,in urma decizei Dn sistemul evolueaza din starea Sn-1 in stareaSn.
180
Daca D1, D2,....Dn este un sir de decizii care comduce sistemul in mod optim
din S0 in Sn,atunci trebuie indeplinita una din conditiile urmatoare (principiul
de optimalitate):
1)Dk...Dn este un sir de decizii ce conduce optim sistemul din starea Sk-1 in
stareaSn,Ak,1<=k<=n;
2)D1....Dk este un sir de decizii ce conduce optim sistemul din starea S0 in
stareaDk,Ak,1<=K<=;
3)Dk+1...Dn,D1...Dk sunt siruri de decizii care conduc optim sistemul starea Sk
in starea Sn, respectiv din starea D0 in starea Sk,Ak,1<=k<=n.
=>Daca principiulde optimalitate se verifica in forma 1,spunem ca se aplica
programarea dinamica metoda inainte.
=>Daca principul de oplimalitate se verifica in forma 2, spunem ca se aplica
programarea dinamica inapoi.
=>Daca primcipiul de optimalitate se verifica in forma 3, spunem ca se aplica
programarea dinamica metoda mixta.
Programarea dinamica se poate aplica problemelor la care optimul general implica
optimul partial.
Faptulca optimul general determina optimul partial, nu inseamna ca optimul partial
determina optimul general.
cu toate acestea, faptul ca potimul general impune optimul partial ne este de mare
ajutor:cautam optimul general,intre optimele partiale, pe care le retinem la fiecare
pas.Oricum,cautarea se reduce considerabil.
Problema triunghiului.Se considera un triunghi de numere naturale format din n
linii.Prima linie contine un numar,a doua doua numere,........ultims n numere
naturale.Cu ajutorul acestuoi triunghi se pot forma sume ne numer naturale in felul
urmator:
-se porneste cu numarul din linia unu;
-succesorul unui numar se afla pe linia urmatoare plasat sub el(acees coloana) sau
pe diagonala la dreapta(coloana creste cu 1).
Care este cea mai mare suma care se poate forma astfel si care sunt numerele care
o alcatuiesc:
Exemplu:n=4;
2
3 5
6 3 4
5 6 1 4
Se pot forma mai multe sume:
S1=2+3+6+5=16;
S2=2+5+4+1=12;
Sk=2+3+6+6=17 (care sete[i suma maxim').
Saq observam ca se pot forma 2 la puterea n-1 sume de acest fel.A le lua in
considerare pe toate pentru a gasi valoarea maxima nu este eficient.
Pentru etapa i se trateza linia i a triunghiului.Fie un sir de n numere care respecta
conditile problemei si care formeaza suma maxima.In acest sir,consideram numarul
care a fost preluat de pe linia i.Numerele intre i+1 si n,formeaza o suma maxima in
raport cu sumele care se pot forma incepand cu nu,marul preluat de pe linia
i,contrar,se contrazice ipoteza.In aceasta situatie se poate aplica programarea
dinamica,metoda inainte.
Vom forma un triunghi,de la baza catre varf,cu sumele maxime care se pot forma cu
fiecare numar.Daca am citit triunghiul de numere intr-o matrice T si calculam sumele
intr-o matrice C vom avea relatiile urmatoare:
C[n][1]:=T[n][1];
181
C[n][2]:=T[n][2];
C[n][n]:=T[n][n];
Pentru linia i (i<n), cele i sume maxime care se obtin aqstfel:
C[i][j]=max{T[i][j]+C[i+1][j],T[i][j]+C[i+1][j+1]},i apatine multimii {1,2,....,n-1) iar j
apartine multimii {1,.....,i}.
Sa rezolvam problema propusa ca exemplu:
Linia 4 a matricei C va fi linia n a matricei T:5 6 1 4;
Linia 3 se calculeaza astfel:
C[3][1]=max{6+5,6+6}=12;
C[3][2]=max{3+6,3+1}=9;
C[3][3]=max{4+1,4+4}=8;
Linia 2:
C[2][1]=max{3+12,3+9}=15;
C[2][3]=max{5+9,5+8}=14;
Linia 1
C[1][1]=max{2+15,2+14}=17.
Aceasta este si cea mai mare suma care se poate forma.
Pentru a tipari numerele luate in calcul se foloseste o matrice numita DRUM in care
pentru fiecare i apartinand multimii mai sus mentionate si j la fel apartinand multimii
mentionate mai sus se retine coloana in care se gaseste succesorul lui T[i][j].
#include<iostream.h>
int t[50][50],c[50][50],drum[50][50].n,i,j;
main()
{ cout<<"n=";cin>>n;
for (i=1;i<=n;i++)
for (j=1;j<=n;j+=)
{ cout <<"t["<<i<<','<<j<<"]=";
cin>>t[i][j];}
for (j=1;j<=n;j++) c[n][j]=t[n][j];
for (i=n-1;i>=1;i--)
{for (j=1;j<=i;i++
{ for (j=1;j<=n;j++)
if (c[i+1][j]<c[i+1][j+1])
{ c[i][j]=t[i][j]+c[i+1][j+1];drum[i][j]=J+1;
}
else
{ c[i][j]=t[i][j]+c[i+1][j];
drum[i][j]=j;}
}
cout<<"suma maxima="<<c[1][1]<<end1;
i=1;j=1;
while (i<=n)
{ cout<<t[i][j]<<end1;
j=drum[i][j];
i++;
}
}
182
cu el.In final este selectat elementul din vector cu care se poate forma cel mai lung
subsir crescator si acesta este listat,
L(k)={1+max L{i}/V{i}>=V(k). i apartine multimii {k+1,...,n} iar k apatrtine multimii
{1,2,....,n}.
In practica,folosim un vector L cu n componente,unde L(k) are semnificatia
explicata.Pentru examenul nostru vom avea:
L=(3,3,2,2,1).
Componentele vectorului L au fost calculate astfel:
-cel mai lung subsir care se poate forma cu elementul 7,aflat pe ultima pozitie,are
lungimea 1;
-cel mai lung subsir care se poate forma cu elementul 6 aflat pe pozitia 4 are
lungimea (1+L(5)), pentru ca pe pozitia 5se gaseste elementul 7 care este mai mare
decat 6;
-cel mai lung subsir care se poate forma cu elementul aflat pe pozitia 3 are
lungimea2(1+L(5)) pentru ca 7 este egal cu 7;
-algoritmul continua in acest mod pana se completeazaL(1).
Dupa aceasta se calculeaza maximul dintre componentele luiL,iar cel mai lung
subsir crescator format din elementele vectoruluiV vaavea lungimea data pe acest
maxim. Pentru a lista efectiv acel subsir de lungime maximala se procedeaza astfel:
-se cauta mazimul din vectorulL precum si indicele t,la care se gaseste acest maxim;
-se afiseazaV(t);
-se gaseste si se listeaza primul element care este mai mare sau egel cu V(t) si are
lungimea mai mica cu 1(max-1), se actualizeaza valoarea max cu max-1;
-algoritmul continua pana cand se epuizaza toate elementele subsirului.
#include<iostream.h>
int v[20],1[20],n,i,k,max,t;
main()
{
cout<<''n=''; cin>>n; for(i=1;i<=n;i++)
{cout<<''v[''<<i<<'']='';cin>>v[i];}
1[n]=1;
for(k=n-1;k>=1;k--)
{max=0;
for(i=k+1;i<=n;i++)
if (v[i]>=v[k]&& 1 [i]>max)
max=1[i];
1[k]=1+max;
}
max=i[1]; t=1;
for(k=1; k<=n;k++)
if (i[k]>max)
{max=i[k]; t=k;}
cout<<''lungimea maxima:''<<max<<end1<<v[t]<<end1;
for(i=t+1;i<=n;i++)
if (v[i]>v[t]&& 1 [i] ==max-1)
{cout<<v[i] << end1;
max--;
}
}
-Inmultirea optima a unui sir de matrice
Presupunem ca avem inmultiti doua matrice:An,p cu Bp,m.In mod evident, rezultatul
va fi o matrice Cn,m.Se pune problema de a afla cate inmultiri au fost facute pentru
a oobtine matricea C. Prin inmultirea liniei 1 cu coloana 1 se fac p inmultiri, intrucat
183
au p elemente. Dar linia 1 se inmulteste cu toate cele m coloane, deci se fac m*p
inmultiri.In mod analog se procedeaza cu toate cele n linii ale matricei A,deci se fac
n*m*p inmultiri.Retinem acest rezultat.
184
~Aceasta observatie este o consecinta directra a programarii dinamice si anume ca
produsul efectuat optim intre matricele prezentate se reduce in ultima istanta la a
efectua un produs intre 2 matrice cu conditia ca acestea sa fie calculate
optim(produsul lor sa aiba un numar minim de inmultit).
Se pune problema cum putem efectua acest calcul utilizand relatiile
prezentate.Pentru exemplificare vom utiliza exemplul dat la inceputul acestui
capitol.Datorita relatiei 1, diagonala pricipala a maricei A( cu 4 lini si 4 coloane) va fi
alcatuita numai din elemente avand valoarea 0. s
Initial se pot calcula numai elemente A(i,i+1),adica A(1,2),A(2,3),A(3,4)-elemente
situate pe o paralela la digonala principala a matricei A.Este cazul sa observam ca
portiunea din matrice situata sub diagonala principala este neeutilizata.In
concluzie,avem A(1,2)=100,A(2,3)=10,A(3,4)=100.Matricea A va arata astfel:
0 100 x x
x 0 10 x
A= x x 0 100
x x x 0
In continuare calculam:
A(1,3)=min{A(1,k)
+A(k+1,3)+DIM(1)*DIM(k+1)*DIM(4)}=min{0+10+10*1*1,100+0+10*10*1}=20;
A(2,4)=min{A(2,k)
+A(k+1,4)+DIM(2)*DIM(k+1)*DIM(5)}=min{0+100+1*10*10,10+0+1*1*10}=20;
A(1,4)=min{A(1,k)
+A(k+1,4)+DIM(1)*DIM(K+1)*DIM(5)}=min{0+20+10*1*10,100+100+10*10,20+0+10*
1*10}=120;
.
0 100 20 120
x 0 10 20
A= x x 0 100
x x x 0
In concluzie,pt. exemplul nostru, se fac minim 120 de inmultiri,rezultat luat din
matricea A si anume A(1,4).
#include<iostream.h>
int i,n,dim[10]
long a[10] [10];
185
}
}
}
cout<<"cost optim:"<<a[1] [n]<<end1;
}
main()
{cout<<"n=";cin>>n;
for (i=1;i<=n+1;i++)
{ cout<<"d=";cin>>dim[i];}
costopt(n,dim,a);
}
186
main()
{cout<<pi<<end1;
#undef pi
#define pi 3.1414
cout<<pi;
}
programul tipareste la inceput 3.14,apoi 3.1414.
2."Rebotezarea" tipurilor.Exista posibilitatea ca pentru anumite tipuri sa folosim alte
nume.
#include <iostream.h>
#define intreg long
main()
{intreg a=43; cout<<a<<end1;}
3.macro-uri generalizate au rolul de a permite scrierea unor secvente asemanatoare
functiilor.
#include <iostream.h>
#define patrat (x) x*x
main()
{
int a=10;
cout<<patrat(a);
}
Linia cout <<patrat(a); se transforma in cout<<cout<<a*a;.Mecanismul de inlocuire
ete urmatorul:initial linia se transforma in cout<<a*a; apoi fiecare aparitie a lui x cu a.
Diferenta esentiala fata de folosirea unei functii este data de faptul ca ,prin inlocuirea
in text a expresiei este evitat saltul catre codul functiei.Acest lucru este util prin faptul
ca se castiga timp,desi,in cazul in care exista mai multe apelari,codul programului
executabil este mai mare.Sa nu uitam faptul ca ,datorita progreselor imense
inregistrate in tehica de calcul memoria ocupata de program nu mai constituie o
problema,dar timpul de executie da.Mai ales atunci cand se raealizeaza programe de
o mare complexitate.
Macro-ul prezentat are o deficienta majora. Despre ce este vorba?Fie apelul
patrat(a+1).El este inlocuit in text cu a+1*a+1. daca a este 10 rezultatul este 21, in
loc de 121. Pt. a evita astfel de erori rescrise macroul ca in exemplul urmator:
#include <istream.h>
#define patrat(x) ((x)*(x))
main()
{ int a=10;
cout<<patrat(a+1);
}
Linia:cout<<patrat(a+1) se inlocuieste cu:cout<(a=10)*I(a+1). Prima urmare ,
rezultatul va fi corect. Oare in acest caz efectul apelului este identic cu cel al unei
functii care realizeaza acelas lucru?Raspunsul este negativ. De exemplu, in cazul
apelului patrat (++a); se obtine(++a)*(++a); rezultat , evident, gresit. In cazul
existentei unei functii variabila a este incrementata o singura data,inainte de apel.In
concluzie,trebuie sa fim atenti la modul in care scriem apelul.
Ne putem intreba ,care este motivul pt. care intreaga expresie a fost inclusa intr-o
pereche de paranteze rotunde?Am fi putut sa o scriem sa asa:#define patrat(x)
(x)*(x)..Procedand ca anterior,avem avantajul ca intreg rezultatul este inlocuit ca un
singur operand.In cazul existentei parantezelor,expresia cout<<1./patrat(a+10;
afiseaza rezultatul corect,iar in absenta lor,tinand cont de ordinea de efectuare a
operatiilor,rezultatul este...1(se imparte la a+1 si se inmulteste cu a+1).
187
Putem avea macro-uri cu mai multi parametri.Astfel ,macro-ul suma are rolul de a
calcula suma celor doi parametri.
#include <iostream.h>
#define suma(x,y) (x+y)
main()
{ int a=10,b=5;
cout<<suma(a,b);
}
Uneori este util ca lista parametrilor sa fie procedata de o secventa de caractere si
acest lucru este posibil.In exemplul urmator este cout<<.
#include <iostream.h>
#define tip(x,y) cout<<(x+y)
main()
{int a=10,b=5;
tip(a,b);
}
Macro-urile pot folosi un operator propriu "##".El are rolul de a concatena doi
parametri .In exemplul urmator linia cout<<concat(x,1); este transformata de
preprocesor in cout<<x1,deci se tipareste 10.
#include <iostream.h>
#define concat(x,y) (x##y)
main()
{int x1=10;
cout<<concat(x,1);
}
Tot asa, putem folosi un alt operator specific macro-urile si anume "#".El are rolul de
a converti parametrul primit in sir si de a returna un pointer catre acesta .In
exemplu ,constanta intreaga 23 este convertita in sirul "23".
#include <iostream.h>
#define sir(x) (#x)
main()
{ char*sir=sir(23);
cout<<sir;
}
Macro-urile pot fi folosite in mod asemanator cu functiile.Mai jos este prezentat un
macro ,numit modul care are rolul de a calcula modulul unei valori numerice.
-Pentru ca un macro sa poata ocupa mai multe linii ,este necesar ca dupa fiecare
linie ,cu exceptia ultimei ,sa punem caracterul'\'
-Datorita mecanismului de inlocuire ,parametri unui macro pot avea orice tip .De
exemplu macro-ul de mai jos poate fi utilizat si pentru a calcu;la modulul unui numar
de tip real ca si pentru a calcula modulul unui numar intreg.
-Tot asa ,spre deosebire de functii,unde apelul presupune un salt la codul
ei,utilizarea unui macro exclude saltul,pentru ca nu se efectueaza decat o
inlocuire.In schimb in cazul folosirii repetat,se utilizeaza mai multa memorie.
#include <iostream.h>
#define modul(mod,x)\
if (X>=0) mod=x;\
else mod=-x;
main()
{float a=-10.27,mod;
modul(mod,a);
cout<<mod;
}
188
O problema interesanta apare atunci cand macro-ul trebuie sa utilizeze variabile
auxiliare.De exemplu,dorim sa scriem un macro care interschimba cintinutul a doua
variabile.Daca ea nu este declarata ,apare eroare de sintaxa.Daca este declarata ,o
eventuala refolosire a macro-ului,in cadrul aceluias bloc,o redeclara,deci apare
eroare de sintaxa.Din acest motiv,preferam ca si variabila de manevra utilizata sa fie
declarata ca un parametru ,asa cum se vede mai jos.
#define schimb(x,y,manevra)\
manevra=x;=y;y=manevra;
main()
{float a=-10.27,b=9.12,man;
schimb(a,b,man);
cout<<a<<" "<<b<<end1;
schimb(a,b,man);
cout<<a<<" "<<b;
DIRECTIVA #INCLUDE
Directiva include a fost des folosita pana in prezent.Dupa cum stim ,rolul sau
este acela de a include in fisierul sursa(cel care contine programul pe care l-am
scris) a unui fisier antet(extensie.h)sau al altui fisier.Rolul acestui paragraf este
de a prezenta cateva notiuni suplimentare.
Directiva include are formele:
1.#include<nume_fisier>
2.#include "nume_fisier"
Pentru a intelege modul de utilizare al directivei,precizam faptul ca meniul mediului
integrat C++ are optiunea OPTION care la randul ei contine un meniu cu optiunea
DIRECTORIES.Daca utilizam aceasta optiune putem trece directoarele in care se
vor cauta fisierele.De exemplu am putea avea:
c:\tcwin\bin;c:\tcwin\include
Revenind la prezentarea directivei #include retinem ca:
-in prima forma fisierul ce trebuie inclus este cautat in subdirectoarele prezentate in
optiunea DIRECTORIES.
-in a doua forma fisierul este cautat mai intai la directorul curent ,iar apoi este cautat
ca in prima forma.
Exemplu:
Un program foloseste mai multe functii.Desigur,le putem include in textul
sursa.Exista si posibilitatea sa scriem functiile in alt fisier sursa.De exemplu ,functiile
urmatoare se gasesc in fisierul functii.cpp:
float adun(float x,floaty)
{return x+y;}
float scad(float x,floaty)
{return x-y;}
189
Observatii:
-directiva #include se poate utiliza pentru includerea oricarui fisier ,nu numai a
fisierelor antet.
-dupa includere,textul sursa ramane neschimbat.Acesta inseamna ca includerea este
numai logica - se considera ca noul fisier este format din:liniile fisierulyi sursa pana la
intalnirea fisierului,fisierul care a fost inclus ,liniile fisierului aflate dupa directiva pana
la sfarsitul sau.
190
In exemplul urmator daca param a fost definit (chiar are valoarea 0) se executa prima
secventa ,astfel se executa a doua secventa .
#define param 0
#include <iostream.h>
#ifdef param
#include "p1.cpp"
#else
#include "p2.cpp"
#endif
main() {p();} \
Daca doriti sa fie inclus fisierul p2.cpp,excludeti linia #define param 0 sau introduceti
directiva #undef param care are rolul de a anula definirea parametrului respectiv.
* Numarul directivelor preprocesor este cu mult mai mare,dar studiul lor nu face
obiectul acestui curs.
PROIECTE
NOTIUNI GENERALE
Pentru programele scrise de noi am utilizat un singur fisier.In practica,programele
sunt cu mult mai mari,motiv pentru care este dificil ca acestea sa fie scrise prin
utilizarea unui singur fisier.Mai mult la scrierea acestor programe participa mai multi
programatori si este foarte geru ca acestia sa utilizeze un singur fisier sursa.Pentru
aceasta se folosesc proiectele.
-Proiectul include mai multe texte sursa-numite module-,dar si alte componente cum
ar fi bibliotecile sau fisierele de tip resursa care contin descrierea anumitor obiecte
grafice,necesare in programarea vizual.
-Fisierele proiect au extensia .prj.
-Penru a crea un proiect se utilizeaza mediul integrat optiunea Project,apoi Open
Project.Apare o fereastra in care se testeaza numele proiectului.Meniul
prezinta optiunile Add item si Delete item,care permit adaugarea sau stergerea
unor fisiere sursa sau biblioteci.
Exemplu:Mai inainte am prezentat un fisier, numit functii.cpp.Exista posibilitatea sa-l
folosim cu ajutorul unui proiect.Pentru aceasta procedam astfel:
!.Scriem programul care-l foloseste:princ.cpp.
#include <iostream.h>
#include"mate.h";
main()
{cout<<adun(10,12);}
2,Pentru a putea folosi functiile scrise ,acestea trebuie declarate ,deci vomm scrie un
fisier antet,numit mate.h.Fisierul se salveaza in directorul curent.;
float adun(float x,float y);
float scad(float x,float y);
float produs(float x,float y);
float impart(float x,float y);
-Observati ca programul care-l foloseste include fisierul respectiv (mate.h).
3.Cream proiectul respectiv,adaugand,pe rand ,la el functii.cpp, mate.h,princ.cpp.
In continuare se ruleaza proiectul ca orice program (prin run)
BIBLIOTECI
191
Exista posibilitatea,asa cum am aratat,ca anumite functii sa fie scrise separat,in asa
numitele module.Astfel de module pot fi compilate separat.Pentru aceasta selectati
din nou meniul COMPILE.In urma compilarii,pe hard vom avea fisiere cu
extensia.obj.De exemplu daca compilam fisierul functii.cpp vom avea pe hard fisierul
functii.obj.Aceasta este ,de fapt,un modul compilat.
Modulele compilate pot fi incluse in biblioteci.O biblioteca are extensia .lib si poate
contine unul sau mai multe module compilate .
Pentru creearea unei biblioteci ,se foloseste un program special,numit tlib si aflat in
c:\borlandc\bin.
De exemplu daca dorim creearea biblioteci biblio.lib care sa contina modulul
functii,dam comanda tlib biblio +functii(extensiile sunt implicite).Daca se doreste
extragerea unui modul dintr0o biblioreca se da o comanda similara ,in care semnul +
este inlocuit cu-.
-Proiectele pot include biblioteci.De exemplu ,in proiectul anterior ,stergeti modulul
functii.cpp si adaugati biblioteca biblio.lib.Veti obtine acels rezultat.
-C++ foloseste biblioteci implicite.Pentru utilizarea acestoara nu sunt necesare
utilizarea proiectelor!De exemplu toate citirile si scrierile au fost facute cu ajutorul
unor functii aflate in biblioteciile implicite ale sistemului.
Programatorul nu face altceva decat sa includa fisierul iostream.h.
Aceste funcii se obtin prin utilizarea operatorilor ">>" si "<<".
Posibilitatea ca o functie sa fie apelata prin utilizarea unor opratori specifici poarta
numele de "supraincarcarea operatorilor".Procedeul tine de programarea orientata
pe obiecte si-l veti invata in cursul anului urmator.
=Proiectele pot include si fisiere gata compilate .
192
FUNCTII INLINE
In mod normal la fiecare apel al unei functii se executa un transfer catre codul ei si
dupa ce a fost executata se revine la instructiunea care urmeaza apelului.Pentru cei
care tin cu orice pret sa economiseasca timp de executie,in C++ exista functii
inline.Ce au ele specific?
Inaintea rulari9i prograamului fiecare apel este inlocuit cu codul functiei.Inlocuirea nu
este vizibila pentru programator.Avantajul este ca se elimina timpul de transfer de la
si catre functie.Dezavantajul este dat de faptul ca programul ocupa mai multa
memorie interna.Din acest motiv se refcomanda utilizarea acestor functii doar daca
au codul masina restrans.
In programul urmator functia mes este inline.
#include <iostream.h>
193
double an(int);
double bn(int0;
void tipar (double(*) (int),int);
main()
{
int n;
cout<<"n=";cin>>n;
cout<<"Primii "<<n<<"termini ai sirului an sunt"<<end1;
tipar(an,n);
cout<<" Primii "<<n<<"termeni ai sirului bn sunt"<<end1;
tipar(bn,n);
}
double an(int n)
{return(1./n+1);;}
double bn(int n)
{return (double)(n*(n+1)/(3*n*n+2);}
void tipar(double(*sir)(int),int n)
{
for (int i=1;i<=n;i++) cout<<sir(i)<<end1;
}
194
De la inceput trebuie sa stiti ca astazi,nu se mai utilizeaza mediile de programare
(Borland Pascal 7.0, Borland C++ 3.1),pe care le studiem in liceu din considerente
exclusiv didactice.In schimb,majoritatea cunostintelor dobandite prin utilizarea
acestor medii sunt indispensabile progaramarii profresionalee.Pentru a realiza
aplicatii care se folosesc efectiv in practica este necesar sa studiati programarea
vizuala.
Pentru a in telege probleme;le de care "e loceste" un programator plecam de la
ipoteza ca sunteti angajat la o firma de profil si vi se da sarcina elaborarii unei
aolicatii.
*Sunteti solicitat sa elaborati un set de subprograme care sa permita
programatorilor accesul cu usurinta la niveleul de bit.
Ce observam?Enuntul este ambiguu,nu se dau nici un fel de amanunte .Nu se
precizeaaza ce subprograme trebuie scrise ,ce trebuie sa realizeze.
Dar asa se intampla in practica.Beneficiarul,in cele mai multe cazuri nu este
informnatician.Rareori veti intalni beneficiari care pot spunbeclar ce doresvc.Mai
mult,in timpul elaborarii aplicatiei,beneficiarul poate veni cu cerinte noi sau poate
schimba cerintele initiale.Este sarcina d-voastra sa faceti de asa natuara inacat
aplicatia sa corespunda dorintelor acestiaia .Nu trebuie sa va enervati si sa inbtrati
in conflict cu acesta.Beneficiarul este cel care plateste deci poate apela oricand la
serviciile altei firme.
Inexemplul dat ,se presupune ca beneficiarul este tot programator ,asa ca din acest
punct de vedere sarcinile pe care lke aveti sunt mai usoare.
Pentru a realiza aplicatia ceruta trebuie sa parcugem mai multe etape.
Etapa1.Analiza problemei.
Beneficiarul doreste accesul simplunla nivelel de bit.Limbajul de programare nua are
instructiuni specializate pentru acesta si nici nu contine programe "gata fabricate" pt
a le folosi in acest scop.Aceasata inseamana ca ca este sarcina dumneavoastra sa
scrieti aceste subprograne.Dar care sunt?
-Orice programator doreste sa poata vizualiza in binar,valoarea retinuta de o anumita
variabila .Desigur poate sa-si scrie propria secventa care sa se realizeze aceastta ,
dar n-ar fi mai bine daca ar dipune de un subprogram gata scris?
-Apoi,este important ca programatorul sa poata modifica valoarea memorata de un
bit al variabilei.In concluzie este necesar sa scriem un subprogrram care sa realizeze
aceasata operatie.
-Exista situatii in care programatoryl doreste sa stie ce memoreaza un anumit
bit.Insemna ca trebuie scris un subprogram care sa reyurneze aceasta valoare.
Suntt eficientre cele tri subprograme?Daca pot afisa in binar continutul unei
variabile, daca pot modifica un sungur bit al ei.daca pot afla ce valoare memoreza
un anumit bit ,se apre ca da!
Acum trbie sa "botezam"cele trei subprograme.Este important sa alegem nume
sugestive pentru ca utilizatorul lor sa le poate retina usor.Cele trei subprograme se
pot nnumi Vidaj,Mod_bit,Cit_bit.
Etapa 2.PROIECAREA PROPRIU_ZISA A APLICATIEI(Conceptia)
In etapa anteriaoara b am stabilit subprogramele de care avem nevoie .Acum este
necesar sa gandim modul in care ele vor fi utilizate .Cu alte cuvinte trebuie sa
analizan parametrii formali ai subgramele si modul in care aceste a returneaza
rezultatele.
195
care ocupa 6 octeti,deci trebuie sa fie afisate 48 valori.Cam mult!Utilizatorul va avea
dificultati in a “descifra” informatia pe care o primeste.
Asa cum am invatat,o variabila ocupa mai multi octeti consecutive.In cazul
variabilelor numerice octetii sunt ocupati invers:octetul cel mai putin semnificativ este
primul memorat,iar cel mai semnificativ ultimul.Aceasta este o informatie pe care
utilizatorul trebuie sa o cunoasca.
Iata antetul:
196
struct hexa{ char sir[100];};
hexa Vidaj(int lungime, void*adresa)
{
hexa retur;strcpy(retur.sir,” ”);
unsigned char man [10] =” ‘,*adr=(char*)adresa,i;
for (i=0;i<lungime;i++)
{
/ / convertesc bitii 4-7 in hexa
strcat(retur.sir,itoa(*adr/16,man,16));
/ / convertesc bitii 0-3 in hexa
strcat(retur.sir,itoa(*adr%16,man,16));
/ / adaug spatiu
strcat(retur.sir,” ”);
adr++;
}
return retur;
}
Presupun ca in acest moment este clar modul in care accesam un anumit octet prin
utilizarea pointerilor.Sa analizam mecanismul prin care,fiind dat un anumit octet prin
adresa sa,putem face ca un anumit bit al sau,identificat prin numar de ordine,sa ia o
anumita valoare.
1 Initial o variabila de tip char,va retine 1 pe pozitia care trebuie modificata si 0
in rest.In programare,o astfel de variabila se numeste masca.De exemplu,daca
dorim ca bitul 3 sa ia o anumita valoare masca este 00001000-bitii sunt
numerotati de la 0 la 7,bitul cel mai putin semnificativ este bitul 0.
2 Daca vrem ca bitul marcat sa retina 0,se efectueaza urmatoarele:
a) se inverseaza continutul bitilor-cei care retin 0 vor retine 1 si invers.Masca de
exemplu devine: 11110111 (operatorul !)
197
b) Se efectueaza si logic pe biti intre masca si octetul care trebuie modificat.In
acest fel bitul caruia in masca ii corespunde 0,va fi 0,iar restul raman
nemodifeicati.
1 Daca vrem ca bitul marcat sa retina 1,atunci se efectueaza sau logic intre
masca nemodificata si octetul al carui bit trebuie modificat.
→ Iata si al treilea subprogram care are rolul de a returna valoarea memorata de
un bit.
#include <iostream.h>
#include “acbiti.h”
main( )
{
198
double a=2;int I;
cout<<Vidaj(sizeof(a),&a).sir<<end1;
Mod_bit(&a,2,3,1);
cout<<Vidaj(sizeof(a),&a).sir<<end1;
for (i=0;i<8*sizeof(a);i++)
cout<<Cit_bit(&a,i/8+1,7-i%8);
}
Etapa 5.Elaborarea documentatiei
In C++ nu exista un tip de date care sa permita lucrul cu multimi,desi ar fi fost util.
199
→ void Init(Multimea A);
{
for (int i=0; i<=255;i++) A.sir[i]=0;
}
200
Calculeaza diferenta intre multimile A si B.
Multime Diferenta (Multime A,Multime B)
{
Multime C;
for (int i=0;i<=255;i++) C.sir[i]=A.sir[i] & (a.sir[i] ^ B.sir[i]);
return C;
}
De aceasta data,pentru a putea folsi apliactia,vom include fisierul care contine textul
sursa.
A) Se citesc n numere naturale mai mici decat 200.Se cere sa se tipareasca fiecare
numar o singura data.
Problema este calsaica.Se rezolva prin utilizarea tipului Multime.O multime initial
vida este reunite,pe rand,cu fiecare numar citit.Evident ca in cazul in care un numar
este citit de doua ori,in multime nu apare decat o data.In final,se listeaza multimea.
#include <iostream.h>
#include “multimi.cpp”
main( )
{
Multime a; Init(a);
init n,nr,I;
cout<<’n=’;cin>>n;
for (i=0;i<n;i++)
{
cin>>nr;
Adaug(nr,a);
}
for (i=0;i<=200;i++)
if (Apartine(I,a)) cout<<i<<” “;
}
B) La fel ca al problema anterioara,numai ca se citesc literele mici ale alfabetului.
201
unsigned char ch;
cout<<”n=”;cin>>n;
for (i=0;i<n;i++)
{
cin>>ch;
Adaug(ch,a);
}
for (ch=’a’;ch<=’z’;ch++)
if (Apartine(ch,a)) cout <<ch<<” ”;
CLASA a-XI-a
Capitolul 1
Alocarea dinamica a memoriei
202
Adresa unei variabile se obtine cu ajutorul operatorului ‘ & ‘, care trebuie sa
preceada numele variabilei:
&Nume_variabila;
Exemplu: adr1=&numar – variabilei adr1 i se atribuie adresa variabilei numar.
Exemple:
1.Variabila a este initializata cu 7, iar variabila adr este initializata cu adresa lui
a.Secventa afisaza continutul variabilei a pornund de la adresa:
2.Variabila a, de tip elev, este initializata, iar variabila adra,de tip pointer
catre variabile de tip elev este initializata cu adresa variabilei a.Secventa
tipareste continutul variabilei a:
…
struct elev
{
char nume[20], prenume[20];
};
…
elev a, *adra=&a;
strcpy(a.nume, “Bojian”);
strcpy(a.prenume, “Andronache”);
cout<<(*adra).nume<<” “<<(*adra).prenume<<endl;
Se poate folosi si operatorul de selectie indirecta ‘-> ’, cu prioritate
maxima:
cout<<adra->nume<<” “<<adra->prenume;
Intre variabilele de tip pointer sunt permise atribuiri numai in cazul in care
retin adrese catre acelasi tip de variabile.
203
p=new tip
Operatorul delete elibereaza spatiul rezervat pentru variabila a
carei adresa este retinuta in p.Dupa eliberare, continutul variabilei p
este nedefinit.
delete p
Exemple:
1.Variabile de tip pointer catre variabile de tip int.Variabila adr1 poate
retine adrese ale variabilelor de tip int:
…
int *adr1;
adr1=new int;
*adr1=7;
cout<<*adr1;
delete adr1;
float* adresa;
adresa=new float;
*adresa=7.65;
cout<<adresa;
3.Variabile de tip pointer catre variabile de tip inreg, care la randul lor
sunt de tip struct.
Variabila adr_inr poate retine adrese ale variabilelor de tipul inreg.
#include<iostream.h>
struct inreg
{ char nume[20], prenume[20];
int varsta;
};
main ( )
{ inreg* adr;
adr=new inreg;
cin>>adr->nume>>adr->prenume<<endl<<adr->varsta;
}
204
Numele tabloului de tip p-dimensional de mai sus este pointer
constant catre un tablou p-1 dimensional de forma [n2] … [np], care
are componentele de baza de acelasi tip cu cele ale tabloului.
Un pointer catre un tablou k dimensional cu [l1] [l2] …[lk] componente
de un anumit tip
se declara astfel:
tip (*nume) [l1] [l2] …[lk];
Intrucat numele unui masiv p dimensional este pointer catre un masiv
p-1 dimensional, pentru a aloca dinamic un masiv se va utiliza un
pointer catre masive p-1 dimensionale (ultimele p-1 dimensiuni).
Exemple:
1.Alocam in HEAP un vector cu 4 componente de tip int.Numele unui
astfel de vector are tipul *int.Prin urmare, variabila a are tipul int*.Dupa
alocare, ea va contine adresa primului element al vectorului din HEAP.
#include<iostream.h>
main ( )
{
int m,n,i,j,(*adr)[10];
adr=new int[10] [10];
cout<<”m=”; cin>>m;
cout<<”n=”; cin>>n;
for(i=0;i<m;i++)
for(j=0;j<n;j++)
cin>>adr[i][j];
for(i=0;i<m;i++)
205
{
for(j=0;j<n;j++)
cout<<adr[i][j]<<” “;
cout<<endl;
}
}
#include<iostream.h>
void* Cit_Mat(int m, int n)
{
int i,j,(*adr1)[10]=new int [10] [10];
for(i=0;i<m;i++)
for(j=0;j<n;j++) cin>>adr1[i][j];
return adr1;
}
void Tip_Mat(int m, int n, int(*adr1)[10])
{ int i,j;
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
cout<<adr1[i][j]<<” “;
cout<<endl;
}
}
void* Suma_Mat(int m, int n, int(*adr1)[10],int (*adr2)[10])
{ int i,j,(*adr)[10]=new int[10][10];
for(i=0;i<m;i++)
for(j=0;j<n;j++) adr[i][j]=adr1[i][j]+adr2[i][j];
return adr;
}
main ( )
{ int m,n,i,j,(*adr)[10],(*adr1)[10],int (*adr2)[10];
cout<<”m=”; cin>>m;
cout<<”n=”; cin>>n;
adr1=(int(*)[10]) Cit_Mat(m,n);
adr2=(int(*)[10]) Cit_Mat(m,n);
206
adr=(int(*)[10]) Suma_Mat(m,n,adr1,adr2);
Tip_Mat(m,n,adr);
}
Capitolul 2
207
Liste liniare
1.Definitia listelor
Crearea listelor
#include<iostream.h>
struct Nod
{
int info;
Nod* adr_urm;
};
208
Nod*v;
int nr;
void Tip(Nod* v)
{
Nod*c=v;
while(c)
{
cout<<c->info<<endl;
c=c->adr_urm;
}
}
main( )
{
cout<<”numar=”; cin>>nr;
while(nr)
{
Adaug(v,nr);
cout<<”numar=”; cin>>nr;
};
Tip(v);
}
209
#include<iostream.h>
struct Nod
{
int info;
Nod* adr_urm;
};
Nod*v;
Nod* Adaug
{
Nod* c;
int nr;
cout<<”numar ”; cin>>nr;
if(nr)
{
c=new(Nod);c->adr_urm=Adaug( );
c->info=nr; return c;
}
else
return 0;
}
void Tip(Nod* v)
{
Nod*c=v;
while(c)
{
cout<<c->info<<endl;
c=c->adr_urm;
}
}
main( )
{ v=Adaug( );
Tip(v);
}
210
void Tip_inv(Nod* v)
{
if(v)
{
Tip_inv(v->adr_urm);
cout<<v->info<<endl;
}
}
a) Lista este vida – v retine 0.Se aloca in HEAP nodul respectiv, adresa sa
va fi in v, iar adresa primului nod va fi si adresa ultimului.
b) Lista este nevida.Initial se aloca spatiu pentru nod.Campul de adresa al
ultimului nod va retine adresa nodului nou creat, dupa care si sf va retine
aceeasi valoare.
211
- spatiul ocupat de primul nod va fi eliberat
#include<iostream.h>
struct Nod
{
int info;
Nod* adr_urm;
};
212
d=new Nod; d->info=val1; d->adr_urm=v; v=d;
}
else
{
c=v;
while(c->adr_urm->info!=val) c=c->adr_urm;
d=new Nod; d->info=val1; d->adr_urm=c->adr_urm; c-
>adr_urm=d;
}
}
void Listare(Nod* v)
{
Nod* c=v;
while(c)
{
cout<<c->info<<endl;
c=c->adr_urm;
}
cout<<endl;
}
213
#include “lista_u.cpp”
Nod* v,*sf;
int i;
main ( )
{
for(i=1;i<=10;i++) Adaugare(v,sf,i);
Listare(v);
Inserare_dupa(v,sf,7,11);
Inserare_dupa(v,sf,10,12);
Inserare_dupa(v,sf,1,13);
Listare(v);
Inserare_inainte(v,13,14);
Inserare_inainte(v,1,15);
Listare(v);
Sterg(v,sf,15);
Sterg(v,sf,13);
Sterg(v,sf,12);
Listare(v);
}
#include<iostream.h>
const MaxInt=32000;
struct Nod
{
int info;
Nod* adr_urm;
};
214
int n,i;
Nod *v,*adr,*c,*c1;
main ( )
{
cout<<”n=”; cin>>n;
v=new Nod; v->info=MaxInt; v->adr_urm=0;
for(i=1;i<=n;i++)
{
adr=new Nod;
cout<<”numar=”; cin>>adr->info;
if(adr->info<=v->info)
{
adr->adr_urm=v;
v=adr;
}
else
{
c=v;
c1=v->adr_urm;
while(c1->info<adr->info)
{
c=c->adr_urm;
c1=c1->adr_urm;
}
c->adr-urm=adr;
adr->adr_urm=c1;
}
}
c=v;
while(c->info!=MaxInt)
{
cout<<c->info<<endl;
c=c->adr_urm;
}
}
215
perechi (i,j).O astfel de pereche ne spune faptul ca, in relatia de ordine
considerata, i se afla inaintea lui j.
Pentru fiecare numar intre 1 si n trebuie sa avem urmatoarele
informatii:
- numarul predecesorilor;
- lista succesorilor.
#include<iostream.h>
struct Nod
{
int succ;
Nod* urm;
};
int n,m,i,j,k,gasit;
int contor[100],c[100];
Nod* a[100];
216
d->urm=0;d->succ=j;
if (c==0)
a[i]=d;
else
{ while(c->urm) c=c->urm;
c->urm=d;
}
}
void actual(int i)
{ Nod* c=a[i];
while(c)
{ contor[c->succ]- -;
c=c->urm;
}
}
main( )
{ cout<<”n=”; cin>>n;
for(i=1;i<=n;i++)
{ contor[i]=0;
a[i]=0;
}
while(i)
{ cout<<”Tastati i, j=”; cin>>i>>j;
if (i) adaug(i,j)
}
m=n;
do
{ k=1;
gasit=0;
for(i=1;i<=n;i++)
if(contor[i]==0)
{ gasit=1;
m- -;
c[k]=i;
k++;
contor[i]=-1;
}
for(i=1;i<=k-1;i++)
{ actual(c[i]);
cout<<c[i]<<endl;
}
217
}
while(gasit && m);
if(m) cout<<”relatii contradictorii”;
else cout<<”Totul e OK”;
}
1) creare;
2) adaugare la dreapta;
3) adaugare la stanga;
4) adaugare in interiorul listei;
5) stergere din interiorul listei;
6) stergere la stanga listei;
7) stergere la dreapta listei;
8) listare de la stanga la dreapta;
9) listare de la dreapta la stanga.
1) Creare
2) Adaugare la dreapta
218
3) Adaugare la stanga
#include<iostream.h>
struct Nod
{ Nod *as,*ad;
int nr;
};
Nod *b,*s,*c;
int n,m,i;
219
s=b;
}
void Addr(Nod*& s)
{ cout<<”n=”; cin>>n;
Nod* d=new Nod;
d->nr=n;
d->as=s; d->ad=0;
s->ad=d; s=d;
}
void Listare(Nod*& b)
{ Nod* d=b;
while(d)
{ cout<<d->nr<<endl;
d=d->ad;
}
}
main ( )
{ cout<<”Creare lista cu o singura inregistrare “<<endl;
220
Creare(b,s);
cout<<”Cate inregistrari se adauga ?”; cin>>m;
for(i=1;i<=m;i++) Addr(s);
cout<<”Acum listez de la stanga la dreapta”<<endl;
Listare(b);
cout<<”Includem la dreapta o inregistrare”<<endl;
cout<<”Dupa care inregistrare se face includerea?”; cin>>m;
Includ(m,b);
cout<<”Acum listez de la stanga la dreapta”<<endl;
Listare(b);
cout<<”Acum stergem o inregistrare din interior”<<endl;
cout<<”Ce inregistrare se sterge?”;
cin>>m;
Sterg(m,b);
cout<<”Acum listez de la stanga la dreapta”<<endl;
Listare(b);
}
Capitolul 3
Elemente de teoria grafurilor
1. Grafuri orientate
1.1 Notiuni introductive
_
Definitie.Se numeste graf orientat perechea ordonata G=(x,l ).
-Daca in acest graf [x,y] apartin multimii gama, vom spune ca x si y
sunt adiacente, iar varfurile x si y sunt incidente cu muchia [x,y].
-Daca gama(g)=multimea vida, acest graf se numeste graf nul si
reprezentarea lui in plan se reduce la puncte izolate.
Definitie.Un graf partial al unui graf orientat dat este un graf G1=(x,g1)
unde g1este inclus sau = cu g.
Definitie. Un subgraf al unui graf orientat G=(x,g) este un graf
H=(y1,g1),unde y1-inclus sau = cu x, iar muchiile din g1 sunt toate
muchiile din g care au ambele extremitati in multimea y.
-Graful unui nod x(d(x)) este de doua feluri:
*graf exterior
*graf interior
Definitie.Un drum al unui graf orientat L=[x0,x1,x2,…xp] este o
succesiune de varfuri cu proprietatea ca [x0,x1]aparrtine de g, [x1,x2] la
fel, s.a.
221
-x0 si xp = extremitatile drumului;
-p=lungimea drumului;
-daca x0,x1,…,xp sunt distincte doua cate doua drumul=elementar;
-daca x0=xp,drumul=circuit
-daca toate varfurile circuitului, cu exceptia primului si ultimului sunt
distincte, circuitul=elementar;
-un circuit elementar care trece prin toate nodurile grafului se numeste
circuit hamiltonian.
222
2-metoda listelor de adiacenta:
*Functia Citire_l are rolul de aciti graful din fisierul organizat ca
anteriorul si de a-l memora sub aceasta forma.In continuare sunt
prezentate si tipurile de date utilizate.Toate sunt incluse in modulul
grafuri.
Struct Nod
{
int nd;
Nod* adr_urm;
};
223
Int coada[50,s[50],I_csf_c,I,n;
{ functia de parcurgere BF_R sau BF
main()
{ Citire_l(“Graf.txt”,L,n);
I_c=1; sf_c=1; coada[I_c]=1; s[1]=1;
Bf_r();
For (I=1;I<=sf_c;I++) cout<<coada[I]<<” “;
}
#include “grafuri.cpp”
int coada[50],s[50],A[50][50],I_c,sf_c,I,n;
void bf_r1()
{ int k;
if (I_c<=sf_c)
{ for (k=1;k<=n;k++)
if ( (A[coada[I_c]][k]==1) && (s[k]==0) )
{ sf_c++;
224
coada[sf_c]=k;
s[k]=1;
}
I_c++;
Bf_r1();
}
}
main()
[ Citire(“graf.txt”,A,n);
I_c=sf_c=1;
Coada[I_c]=s[1]=1;
Bf_r1();
For(I=1;I<=sf_c;I++)cout<<coada[I]<<” “;
}
2.Parcurgerea in adancime(DF-depth first)
-se face incapand de la un nod I
-dupa parcurgerea unui nod se parcurge primul dintre descendntii sai
neparcursii inca.
1)-memorare prin matricea de adiacenta:
#include “grafuri.cpp”
int s[50],A[50][50],n;
void df_r9int nod)
{
int k;
cout<<nod<<” “;
s[nod]=1;
for (k=1;k<=n;k++)
if ((A[nod][k]==1) && (s[k]==0))
df_r(k);
}
main()
{
Citire(‘Graf.txt”,A,n);
Df_r(1);
}
2)-memorare prin liste de adiacenta;
#include “grafuri.cpp”
int s[50],n;
Nod *L[50];
Void df_r1(int nod)
{
225
Nod* p;
Cout<<nod<<” “;p=L[nod];
S[nod]=1;
While (p)
{
if (s[p->nd]==0)
df_r1(p->nd);
p=p->adr_urm;
}
}
main()
{
Citire_l(grag.txt”,L,n);
Df_r1910;
}
3.Estimarea timpului necesar parcurgerii grafurilor
-deoarece m<n, parcurgerea grafurilor se face in timp polinomial, adica
cel mult O(n*n).
1.4Matricea drumurilor
m[I,j]= 1, daca exista drum de la I la j,
=0, daca nu exista drum de la ila j, sau I=j.
1)Obtinem o linie a matricei;
2)Apelul functiei DF, permite obtinerea matricei drumurilor.
#include “grafuri.cpp”
int s[50],n,I,j,M_dr[50][50];
Nod* L[50];
Void df(int baza,int nod)
{
Nod* p;
If (baza!=nod) M_dr[baza][nod]=1;
P=L[nod]; s[nod]=1;
While(p)
{
if (s[p->nd]==0) df(baza,p-.nd);
p=p->adr_urm;
}
}
main()
{
Citire_l(“Graf.txt”,L,n);
226
For (I=1;I<=n;I++)
{
for (j=1;j<=n;j++) s[j]=0;
df(I,I);
}
for 9I=1;I<=n;I++)
{
for (j=1;j<=n;j++) cout<<M_dr[I][j],’ ‘;
cout<<endl;
}
}
1.5Componente tare conexe
227
Suc[I]=pred[I]=I;
Df_r1(I);df_r2(I);
For (j=1;j<=n;j++)
If ( (suc[j]==pred[j])&& (suc[j]==I) )
Cout<<j<<” “;
}
228
{
cout<<:”Componenta “<<I<<endl;
for (j=1;j<=n;j++)
if (suc[j]==I) cout<<j<<” “;
cout<<endl;
}
}
1.6Drumuri in grafuri
1.Matricea ponderilor
1)Urmatorul program va citi graful si va creea matricea ponderilor ,cu
functia urmatoare(primul caz):
void Citire_cost (char Nume_fis[20],float A[50][50],int& n)
{
int I,j;
float c;
fstream f(Nume_fis,ios::in);
f>>n;
for (I=1;I<=n;I++)
for (j=1;j<=n;j++)
if (I==j) A[I][j]=0;
else A[I][j]=Pinfinit;
while (f>>I>>j>>c) A[I][j]=c;
f.close();
}
2)Urmatorul program va citi grafului prin functia urmatoare(al doilea
caz):
void Citire_cost1(char Nume_fis[20], float A[50][50], int& n)
{
int I,j;
float c;
fstream f(Nume_fis,ios::in);
f>>n;
for (I=1;I<=n;I++)
for (j=1;j<=n;j++)
if (I==j) A[I][j]=0;
else A[I][j]=Minfinit;
while (f>>I>>j>>c) A[I][j]=c;
f.close();
}
229
*Algoritmul lui ROY-FLOYD-vom aplica strategia generala Divide
et Impera(pentru lungimea minima):
#include “grafuri.cpp”
float A[50][50];
int n;
void Drum(int I, int j)
{
int k=1,gasit=0;
while ( (k<=n) && !gasit)
{
if ( (I!=) && (j!=k) && (A[I][j]==A[I][k]+A[k][I]) )
{
Drum(I,k);Drum(k,j);
Gasit=1;
K++;
}
if (!gasit) cout<<j<<” “;
}
void Scriu_drum(int Nod_Initial, int Nod_Final)
{
if (A[Nod_Initial][Nod_Final]<Pinfinit)
{
cout<<”Drumul de la “<<Nod_Initial<<” la “<<Nod_Final<<” are
lungimea “<<A[Nod_Initial][Nod_Final]<<endl;
cout<<Nod_Initial<<” “;
Drum(Nod_Initial,Nod_Final);
}
else
cout<<”Nu exista drum de la “<<Nod_Initial<<” la
“<<Nod_Final;
}
void Lungime_Drumuri()
{
int I,j,k;
for (k=1;k<=n;k++)
for(I=1;I<=n;I++)
for (j=1;j<=n;j++)
if (A[I][j]>A[I][k]+A[k][j]
A[I][j]=A[I][k]+A[k][j];
}
main()
230
{
Citire_cost(“Graf.txt”,A,n);
Lungime_Drumuri();
Scriu_drum(4,2);
}
231
if (A[I][j]=A[I][k]+A[k][j];
}
main()
{
Citire_cost1(“Graf.txt”,A,n);
Lungime_Drumuri();
Scriu_drum(1,2);
}
ALGORITMUL DIJKSTRA
*Fiind dat un graf memorat prin matricea ponderilor, se cere sa se
determine pentru orice x,y apartin de X lungimea minima a drumului de
la nodul x la nodul y.Prin lungimea unui drum intelegem suma ponderilor
arcelor care-l alcatuiesc.
Include “grafuri.cpp”
Float A[50][50],D[50],min;
Int S[50],T[50],n,I,j,r,poz;
Void drum(int I)
{
if (T[I]) drum(T[I]);
cout<<I<<” “;
}
main()
{
Citire+cost(“Graf.txt”,A,n);
Cout<<”Introduceti nodul de pornire “<<endl;
Cout<<”r=”;cin>>r;S[r]=1;
For (I=1;I<=n;I++)
{
D[I]=A[r][I];
If (I!=r)
If (D[I]<Pinfinit) T[I]=r;}
For (I=1;I<=n;I++)
{
min=Pinfinit;
for(j=1;j<=n;j++)
if (S[j]==0)
if (D[j]<min)
{
min=D[j];
poz=j;
}
232
S[poz]=1;
For (j=1;j<=n;j++)
If (s[j]==0)
If (D[j]>D[poz]+A[poz][j])
{
D[j]=D[poz]+A[poz][j];
T[j]=poz;
}
for (I=1;I<=n;I++)
if (I!=r)
if(T[I])
{
cout<<”distanta de la “<<r<<” la “<<I<<” este “<<D[I]<<endl;
drum(I);
cout<<endl;
}
else
cout<<”nu exista drum de la “<<r<<” la “<<I<<endl;
}
2.Grafuri neorientate
2.1.Notiuni introductive
233
2)Cazul 2
void Citire_l_N(char Nume_fis[20],Nod* L[50] int& n)
{
Nod* p;
Int I,j;
Fstreamf(Nume_fis,ios::in);
f>>n;
while (f>>I>>j)
{
p=new Nod; p->adr_urm=L[I];p->nd=j;L[I]=p;
p=new Nod; p->adr_urm=L[j];p->nd=I;L[j]=p;
}.close();
}
3)Cazul 3
void Citire_cost_N(char Nume_fis[20],float A[50][50],int &n)
{
int I,j;
float c;
fstream f(Nume_fis,ios::in);
f>>n;
for (I=1;I<=n;I++)A[I][I]=0;
for (I=1;I<=n-1;I++)
for (j=I+1;j<=n;j++) A[I][j]=A[j][I]=Pinfinit;
while(f>>I>>j>>c)A[I][j]=A[j][I]=c;
f.close();
}
4)Cazul 4
Fata de cazul 3 ar deosebirile:
Citire_cost_N=Citire_costN1;
Pinfinit=Minfinit.
*Determinarea componentelor conexe ale unui graf neorientat:
#include “grafuri.cpp”
int S[50],A[50][50],n,I,k;
void df_r(int nod)
{
int k;
cout<<nod<<” “; S[nod]=1;
for (k=1;k<=n;k++)
if ( (A[nod][k]==1)&& (s[k]==0) ) df_r(k);
}
main()
234
{
CitireN(“Graf.txt”,A,n);
K=1;
For (I=1;I<=n;I++)
If (s[I]==0)
{
cout<<”componenta “<<k<<endl;
df_r(I);
cout<<endl;
k++;
}
}
2.2.Grafuri hamiltoniene
235
k=0;
}
void back()
{ int AS;
k=2; Init();
while (k>1)
{ do {} while ((AS=Am_Succesor()) && !E_Valid());
if (AS)
if (Solutie()) Tipar();
else {k++;Init();}
else k--;
}
}
main () { st[1]=1;k=2;st[k]=1;
CitireN(“graf.txt”,a,n);
Back();
}
2.3.Grafuri euleriene
236
ANod_baza->adr_urm=ANod_urm;
Int adauga()
{
int I,gasit=0;
Indice=Alista;
While (Indice && !gasit)
{
for (I=1;I<=n;I++)
if (A[Indice->nd][I]==1) gasit=1;
if (!gasit) Indice=Indice->adr_urm;
}
if (Indice)
{
ciclu (Indice);
return 1;
}
else return 0;
}
int grade_pare()
{
int I=1,j,s,gasit=0;
while ( (I<=n) && ! gasit)
{
s=0;
for (j=1;j<=n;j++) s+=A[I][j];
if (s%2) gasit=1;
I++;
}
return !gasit;
}
void df(int nod)
{
int k;
S[nod]=1;
For (k=1;k<=n;k++)
If ( (A[nod][k]==1) && (S[k]==0)) df(k);
}
int conex()
{
int gasit=0,I;
df(1);
237
for (I=1;I<=n;I++)
if (S[I]==0) gasit=1;
return !gasit;
}
main()
{ CitireN(“Graf.txt”,A,n);
if (conex())
if (grade_pare())
{
Alista=new Nod;
Alista->nd=1;Alista->adr_urm=0;
While (adauga());
Indice=Alista;
While (Indice)
{ cout<<Indice->nd<<” “;
Indice=Indice->adr_urm;
}
}
else cout <<”Graful nu indeplineste conditiile de paritate”;
else cout<<”Graful nu este conex”;
}
3.Retele de transport
3.1.Ce este o retea de transport
238
Defnitie.fluxul unei retele R este o functie definit pe X*X cu valori in N
care indeplineste anumite conditii.
3.3.Determinarea fluxului de valoare maxima
239
else
if ((A[I][coada[I_c]][1]>0) && (s[I]==0) && (I!=st) )
{
s[I]=-coada[I_c];coada[++sf_c]=I;
}
I++;
}
I_c++;
}
if (coada[sf_c]==fin gasit=1;
}
void caut()
{
do
{
for (I=1;I<=n;I++)s[I]=0;
drum_in_crestere();
if (s[fin])
{
min=32000;
refac(fin);
}
} while (gasit);
}
main()
{
Citire_Cap(“Graf.txt”,A,n,st,fin);
Caut();
For (I=1;I<=n;I++)
{
for (j=1;j<=n;j++) cout <<A[I][j][1]<<” “;
cout<<endl;
}
}
240
241