Sunteți pe pagina 1din 61

See discussions, stats, and author profiles for this publication at: https://www.researchgate.

net/publication/320518711

Metode si tehnici de programare. Indrumar de laborator

Book · February 2003

CITATION READS

1 110

1 author:

Adrian Runceanu
University of Craiova
26 PUBLICATIONS   22 CITATIONS   

SEE PROFILE

All content following this page was uploaded by Adrian Runceanu on 20 October 2017.

The user has requested enhancement of the downloaded file.


Nici o parte din această carte nu poate fi reprodusă
sub nici o formă sau metodă fără acordul scris al
autorului.
Aveţi drepturi de utiliza acestă carte doar pentru
propria persoană.
Această carte digitală nu poate fi revândută sau
dată către alte persoane.
Dacă doriţi să folosiți părți din această carte sunteți
liber să o faceți doar cu citarea corectă a cărții și
precizarea linkului de unde ați accesat-o.
Cuvânt înainte

Scopul acestui îndrumar de laborator este să


prezinte într-o manieră accesibilă studentilor câteva
metode de programare utile în rezolvarea unor
probleme de teoria grafurilor, arborilor cât şi a altor
problemele.
In conceperea acestui îndrumar s-au avut în
vedere atât aspectele teoretice, care sunt
prezentate succint, cât şi, aspectele practice.Am
realizat implementarea metodelor de programare
prin rezolvarea cât mai multor probleme, fiecare
lucrare de laborator, continând atât probleme
rezolvate cât si propuse.
Indrumarul este împărţit în 11 lucrări de
laborator şi este util în primul rând studenţilor de la
secţia de AUTOMATICA, care urmează cursul de
TEHNICI DE PROGRAMARE, cât şi tuturor
studenţilor care vor să-şi îmbunătăţească
cunoştinţele de programare. Structurarea
îndrumarului este următoarea:

LUCRAREA NR. 1: ALGORITMI RECURSIVI


LUCRAREA NR. 2: METODE DE SORTARE
LUCRAREA NR. 3: IMPLEMENTAREA STATICĂ A STIVELOR
ŞI COZILOR
LUCRAREA NR. 4: LISTE LINIARE SIMPLU ÎNLĂNŢUITE
LUCRAREA NR. 5: LISTE LINIARE DUBLU ÎNLĂNŢUITE
LUCRAREA NR. 6: ELEMENTE DE TEORIA GRAFURILOR
LUCRAREA NR. 7: ARBORI BINARI
LUCRAREA NR. 8: METODA DIVIDE ET IMPERA
LUCRAREA NR. 9: METODA GREEDY
LUCRAREA NR. 10: METODA BACKTRACKING
LUCRAREA NR. 11: METODA PROGRAMĂRII DINAMICE
Toate programele sursă prezentate în acest
îndrumar sunt realizate în limbajul de programare
Borland C++ şi sunt verificate pe date de test
variate, autorul garantând buna funcţionare a lor.
Indrumarul se doreste a fi un mic ghid de
programare care doar deschide interesul
studenţilor, problematica prezentată putând fi
largită şi completată în cadrul unei apariţii
ulterioare.

2003
Autorul
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

LUCRAREA nr. 1

ALGORITMI RECURSIVI

A. Consideraţii teoretice

Numim program recursiv un program care conţine cel putin un


subprogram recursiv. Un subprogram se numeşte recursiv dacă în
cadrul definiţiei sale se autoapelează.
Din matematică, multe definiţii utilizează construirea unor concepte
prin această metodă specială numită recursie. Astfel se pot defini
numerele naturale sau unele funcţii:
 1 este un număr natural
 succesorul unui număr natural este tot un număr natural (bineînţeles
mai trebuie definită apoi noţiunea de succesor)
 funcţia lui ACKERMANN:
| n+1 dacă m=0;
ack(m,n)= | ack(m-1,1) dacă n=0;
| ack(m-1,ack(m,n-1)) altfel;

B. Exemple de programe

Prezentăm în continuare cateva programe simple cu soluţii recursive


pentru :
1. Calculul celui mai mare divizor comun. (algoritmul lui EUCLID).
#include<iostream.h>
#include<conio.h>
#include<ctype.h>
int cmmdc(int a,int b)
{
if(a==b) return a;
else if(a>b) cmmdc(a-b,b);
else cmmdc(a,b-a);
}
void main(void)
{
int a,b;
char c;
do
{
clrscr();
cout<<"Introduceti a= ";cin>>a;
cout<<"Introduceti b= ";cin>>b;
cout<<"C.m.m.d.c. "<<a<<","<<b<<" este "<<cmmdc(a,b)<<endl;

1
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

cout<<"Mai doriti sa calculati pentru alte valori? (d/n) ";


cin>>c;
}while(toupper(c)!='N');
}

2. Calculul lui n!. (n factorial)

#include<iostream.h>
#include<conio.h>
long int factorial(long int n)
{
if(n==0) return 1;
else factorial(n-1)*n;
}
void main(void)
{
long int n;
clrscr();
cout<<"Introduceti n= ";cin>>n;
cout<<"n! = "<<factorial(n)<<endl;
}

3. Să se calculeze suma primelor n numere naturale.


#include<iostream.h>
#include<conio.h>
long int suma(long int i)
{
if(i!=1) suma(i-1)+i;
else return 1;
}
void main(void)
{
long int n;
clrscr();
cout<<"Introduceti n= ";cin>>n;
cout<<"Suma primelor "<<n<<" numere este "<<suma(n)<<endl;
}

4. Să se genereze partiţiile unui număr natural n, doua partiţii diferind fie


prin valorile elementelor din partiţie, fie prin ordinea acestora.

Solutie: Generarea partiţiilor presupune alegerea primului termen din partitie -


pi, si apoi generarea tuturor partiţiilor numărului n-pi.

#include<iostream.h>
#include<conio.h>
int n,nn,p[20];
void partitie(int i,int n)

2
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

{
int j,k;
for(j=1;j<=n;j++)
{
p[i]=j;
if(n-j>0) partitie(i+11,n-j);
else if(i>1)
{
cout<<nn<<" = "<<p[1];
for(k=2;k<=i;k++)
if(p[k]!=0) cout<<"+"<<p[k];
cout<<endl;
}
}
}
void main(void)
{

clrscr();
cout<<"Introduceti n= ";cin>>n;
nn=n;
partitie(1,n);
}

5. Suma puterilor rădăcinilor

Fie ecuaţia x2 - Sx + P = 0 cu S, P  R si x1, x2 rădăcinile ecuaţiei. Să


se calculeze Sn= x1 + x2, n  N.

Căutăm relaţia de recurenţă pentru Sn, ştiind că x1, respectiv x2 sunt rădăcinile
ecuaţiei date şi deci îndeplinesc relaţiile :
x12  Sx1  P  0
x22  Sx2  P  0
Înmulţim aceste relaţii cu x1n 2 şi x 2n 2 şi adunăm relaţiile obţinute şi rezultă:
Sn = x1n  x2n = S * ( x1n1  x2n1 ) – P * ( x1n2  x2n2 ) = S*Sn-1 - P*Sn-2.

Astfel am obţinut o relaţie de recurenţă :

S0 = x0 + x0 = 2,
S1 = x1 + x2 = S,
Sn = S*Sn-1 - P*Sn-2, pentru n2.

#include<iostream.h>
#include<conio.h>
int n;
float s,p,r;
float suma(int n)

3
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

{
if(n==0) return 2;
else if(n==1) return s;
else return(s*suma(n-1)-p*suma(n-2));
}
void main(void)
{

clrscr();
cout<<"Introduceti valorile ecuatiei de gradul II "<<endl;
cout<<"Dati s = ";cin>>s;
cout<<"Dati p = ";cin>>p;
cout<<" N = ";cin>>n;
r=suma(n);
cout<<"Valoarea lui S("<<n<<") este "<<r;
}

6. Să se afle elementul maxim dintr-un vector dat.

Soluţia este dată de relaţia recurenţă : maxim(a1, a2 , . . . ,an) = maxim(an,


maxim(a1, a2, . . . , an-1))

#include<iostream.h>
#include<conio.h>
int a[100],n,i;
int max(int a,int b)
{
if(a>b) return a;
else return b;
}
int maxim(int a[],int n)
{
if(n==1) return a[0];
else return max(a[n],maxim(a,n-1));
}
void main(void)
{

clrscr();
cout<<"Introduceti dimensiunea sirului n = ";cin>>n;
for(i=0;i<n;i++)
{
cout<<"a["<<i<<"]=";
cin>>a[i];
}
cout<<"Elementul maxim din vector este = "<<maxim(a,n);
}

4
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

7. Sa se genereze toate submultimile unei multimi.

Se fol. un vector a care indica selectia elementelor multimii:


a[i]=1 daca elem. b[i] face parte din submultime
a[i]=0,in caz contrar
Pentru generarea submultimilor vom defini functia recursiva submultime, care are
un parametru i ce reprezinta pozitia elementului care se va genera.
In cazul in care s-au generat toate elementele(i=n) se tipareste submultimea
generata, in caz contrar, se trece la generarea elem. urmator(aflat pe pozitia i+1).

#include<iostream.h>
int a[40],b[40],n,i;
void tipar()
{
for(i=1;i<=n;i++)
if(a[i]==1) cout<<b[i]<<" ";
cout<<endl;
}
void submultime(int i)
{
int j;
for(j=0;j<=1;j++)
{
a[i]=j;
if(i==n) tipar();
else submultime(i+1);
}
}
void main(void)
{
cout<<"Dati numarul de elemente n = ";cin>>n;
cout<<"Dati elementele multimii "<<endl;
for(i=1;i<=n;i++) cin>>b[i];
submultime(1);
}

8. Sa se transforme un numar n, dat in baza 10, intr-o alta baza b


(2<=b<=10).
#include<iostream.h>
#include<conio.h>
int n,b;
void baza(int n)
{
if(n<b) cout<<n;
else
{
baza(n/b);
cout<<n%b;

5
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

}
}
void main(void)
{
clrscr();
cout<<"Dati numarul in baza 10, n = ";cin>>n;
cout<<"Dati baza in care vreti sa se transforme "; cin>>b;
cout<<n<<" in baza "<<b<<" este ";
baza(n);
}

9. Se citeste un numar intreg cu cel mult 255 cifre. Sa se afiseze


numarul cu cifrele in ordine inversa.

#include<iostream.h>
#include<conio.h>
#include<string.h>
char n[255],i,l;
void invers(int i)
{
if(i<l) invers(i+1);
cout<<n[i];
}
void main(void)
{
clrscr();
cout<<"Dati numarul in n = ";cin>>n;
l=strlen(n);
cout<<"Numarul rasturnat este ";
invers(0);
}

10. Se dau doi vectori x si y cu n componente de tip cifra. Se cere sa se


calculeze suma x[0]^y[0] + x[1]^y[1] + . . . + x[n-1]^y[n-1].
Se vor utiliza doua functii recursive pentru calculul unui numar a la puterea b
si respectiv suma componentelor unui vector.

#include<iostream.h>
#include<conio.h>
int x[50],y[50],z[50],i,n;
long int putere(int a,int b)
{
long int i,p=1;
for(i=1;i<=b;i++) p*=a;
return p;
}
int suma(int t[50],int i)
{

6
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

if(i<n) return(t[i]+suma(t,i+1));
else return 0;
}
void main(void)
{
clrscr();
cout<<"Dati numarul de elemente n = ";cin>>n;
cout<<"Dati elementele primului vector X "<<endl;
for(i=0;i<n;i++) cin>>x[i];
cout<<"Dati elementele celui de-al doilea vector Y "<<endl;
for(i=0;i<n;i++) cin>>y[i];
for(i=0;i<n;i++) z[i]=z[i]+putere(x[i],y[i]);
cout<<" suma este "<<suma(z,1);
}

C. Exerciţii şi teme

1. Să se ruleze cele zece programe, urmărind apelurile şi valorile parametrilor de


apel.
2. Să se scrie un program care să calculeze al n-lea termen din şirul lui Fibonacci,
care este definit recursiv astfel:
fib[1]=0; fib[2]=1; fib[n]=fib[n-1]+fib[n-2]
3. Să se caute o soluţie nerecursivă pentru şirul lui Fibonacci.
4. Să se calculeze funcţia Manna-Pnueli, dată de relaţia :
| x-1, dacă x>=12
f(x)=|
| f(f(x+2)), dacă x<12
5. Să se scrie un program recursiv care să compare două şiruri de caractere.
6. Să scrie un program recursiv care să verifice dacă două şiruri de caractere sunt
anagrame. Două cuvinte sunt anagrame dacă conţin aceleaşi litere dar în ordine
diferită.
Exemplu: şirul ‘DARIAN’ este anagrama pentru şirul ‘ADRIAN’.

7
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

LUCRAREA nr. 2

IMPLEMENTAREA STATICĂ A
STIVELOR ŞI A COZILOR

A. Consideraţii teoretice

Numim stivă ( din engleza “STACK” ) o structură omogenă, unidimensională,


care funcţionează pe principiul LIFO (Last In, First Out – Ultimul Intrat, Primul Ieşit).
Adică toate prelucrările se efectuează la un singur capăt numit vârful stivei.
Implementarea statică se face cu ajutorul tablourilor unidimensionale. Astfel putem
construi următoarele operaţii pentru lucrul cu stivele:

1. Stiva_Init - iniţializează stiva (care devine vidă)


2. Stiva_Vârf – returnează elementul aflat în vârful stivei
3. Stiva_Adaug (E) – adaugă elementul E în stivă
4. Stiva_Sterg (E) – şterge (scoate) elementul E din stivă
5. Stiva_Vidă – verifică dacă stiva este vidă sau nu.

Numim coadă (din engleza QUEUE) o structură de date omogenă,


unidimensională, care funcţionează pe principiul FIFO (First In, First Out – Primul
Intrat, Primul Ieşit). Adică elementele sunt adăugate la un capăt (numit spate) şi
sunt scoase pe la celălalt capăt numit faţă. Operaţiile cu coadă sunt asemănătoare cu
cele de la stivă:

1. Coada_Init – iniţializează coada


2. Coada_Prim - returnează primul element din coadă
3. Coada_Adaug (E) – adaugă elementul E în coadă
4. Coada_Scot - scoate primul element din coadă
5. Coada_Vidă – verifică dacă coada este vidă sau nu.

B. Exemple de implementare

1. // programul cu stiva
#include<iostream.h>
#include<conio.h>
int n,i,nr,k;
int st[50];
char c;
int stiva_vida(int k)
{
if(k==0) return 1;
else return 0;
}
int stiva_plina(int k)
{

8
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

if(k==n) return 1;
else return 0;
}
void stiva_init(int k)
{
st[k]=0;
return;
}
void stiva_adaug(int e)
{
if(stiva_plina(k)==0)
{
k++;
st[k]=e;
}
else cout<<"Nu se mai poate adauga nici un element!!";
}
int stiva_sterg(void)
{
int e;
if(stiva_vida(k)==0)
{
e=st[k];
k--;
return e;
}
else cout<<"Nu se mai poate sterge nici un element";
}
void tipar(int k)
{
int i;
for(i=1;i<=k;i++) cout<<st[i]<<" ";
cout<<endl;
}

void main(void)
{
clrscr();
cout<<"Dati dimensiunea stivei n = ";cin>>n;
k=0;
stiva_init(k);
do{
cout<<endl;
cout<<"Alegeti operatia dorita"<<endl;
cout<<"(A)daugare"<<endl;
cout<<"(S)coatere"<<endl;
cout<<"verificare stiva (V)ida"<<endl;
cout<<"verificare stiva (P)lina"<<endl;
cout<<"(T)iparire"<<endl;

9
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

cout<<"(I)esire"<<endl;
cin>>c;
switch(c)
{
case 'A','a' :{cout<<"Dati numarul care se adauga ";cin>>nr;
stiva_adaug(nr);break;}
case 'S','s' :{nr=stiva_sterg();
cout<<"S-a eliminat numarul "<<nr;break;}
case 'V','v' :{if(stiva_vida(k)==1) cout<<"Stiva este VIDA!";
else cout<<"Stiva nu este vida ";break;}
case 'P','p' :{if(stiva_plina(k)==1) cout<<"Stiva este PLINA!";
else cout<<"Stiva nu este plina ";break;}
case 'T','t' :{cout<<"continutul stivei este "; tipar(k); break;}
}
}while( (c!='i') && (c!='I'));
}

2. Prezentam in continuare o problema a carei rezolvare presupune cunoasterea


functionarii si gestionarii unei cozi:
Se considera un joc de carti astfel: Se dau 13 carti numerotate de la 1 la 13,
asezate in cerc, in ordine crescatoare. Se considera un numar de carte – notat c - de
unde se incepe numaratoarea si un numar – notat n – de pozitii care se numara si se
cere sa elimine fiecare a n-a carte, numarand succesiv cate c carti. Sa se afiseze
cartea ramasa.
#include<iostream.h>
#include<conio.h>
int m,n,i,j,k,c;
int v[50];
void main(void)
{
clrscr();
cout<<"Dati numarul care se numara n = ";cin>>n;
cout<<"Dati pozitia cartii de unde incepe eliminarea "; cin>>c;
for(i=1;i<=13;i++) v[i]=i+1;
for(i=1;i<=13;i++) cout<<v[i]<<" ";
cout<<endl;
i=c+n;m=13;
do{
i++;
if(i>=13) i=i%m;
for(j=i-1;j<=m;j++) v[j]=v[j+1];
i=i+n;
m--;
for(k=1;k<=m;k++) cout<<v[k]<<" ";
cout<<endl;
}while(m!=1);
cout<<"Cartea ramasa este "<<v[m];
}

10
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

C. Exerciţii şi teme

1. Se consideră următoarea structură de date:


int x[10];
Cu ajutorul acestui tablou să se creeze o stivă care, citind un număr n de la
tastatura în baza 10, să reprezinte cifrele numărului în baza 2 şi să le afişeze.

3. Se consideră următoarea structură de date: char x[20]. In acest tablou se reţine


un text de la tastatură format din cuvinte scrise cu litere mici, alcătuit din una sau
maxim 20 de linii, care se termină cu caracterul ‘.’. Cu ajutorul unei stive, să se
indice care dintre cuvinte sunt palindroame (cuvinte care citite de la dreapta la
stânga se obţine cuvântul iniţial; exemple: cojoc, lupul, capac, rotor, etc).

11
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

LUCRAREA nr. 3

LISTE LINIARE SIMPLU ÎNLANŢUITE

A. Consideraţii teoretice

În anumite limbaje cum ar fi cele de nivel superior (exemplu PASCAL, ALGOL,


etc) se pot construi programe în care gestiunea memoriei să se facă dinamic. Astfel
se utilizează alocarea dinamică a memoriei. Aceasta se realizează prin două zone
diferite de memorie pusă la dispoziţie de limbaj, şi anume stiva programului, unde
se memorează informaţiile asociate apelurilor de subprograme şi o zonă numită
heap, de unde pot fi "luate" sau unde pot fi "aruncate" zone utilizate de
programator, acesta prelucrând la un moment dat numai necesarul de memorie de
care are nevoie. Zonele din heap au nume simbolic asociat în program ele fiind
accesate prin adresa de memorie a începutului (prima locaţie).

Definiţie: Se numeşte listă liniară simplu înlănţuită o multime de


elemente de forma:

INF ADR
unde INF reprezinta informaţia pentru care se constuieşte lista, iar ADR
reprezintă adresa de memorie unde se află următorul element din listă.

Astfel o listă liniară simplu înlănţuită se poate reprezenta grafic astfel:

inf1 inf2 infn


adr1 adr2 NIL
prim ultim

B. Exemple de program

1. Vom da un exemplu de utilizare a unei structuri de tip listă simplu înlănţuită


împreună cu operaţiile ce se pot efectua asupra ei.

#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
typedef struct lista
{
int inf;
lista *adr;
}lista;
lista *p,*prim,*ultim;

12
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

void creare()
{
int n;
lista *p;
cout<<"dati inf. <> 0 ";cin>>n;
p=NULL;
while(n!=0)
{
if(prim==NULL)
{
prim=new lista;
prim->inf=n;
prim->adr=NULL;
ultim=prim;
}
else
{
p=new lista;
p->inf=n;
ultim->adr=p;
ultim=p;
}
cout<<"dati inf. <> 0 ";cin>>n;
}
ultim->adr=NULL;
}
void afisare(void)
{
lista *p;
p=prim;
while(p!=NULL)
{
cout<<p->inf<<" ";
p=p->adr;
}
}

void adaug_la_sf(void)
{
int n;
cout<<"Dati informatia care se adauga ";cin>>n;
p=new lista;
p->inf=n;
ultim->adr=p;
p->adr=NULL;
ultim=p;
}

void adaug_la_inceput(void)

13
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

{
int n;
cout<<"Dati informatia care se adauga ";cin>>n;
p=new lista;
p->inf=n;
p->adr=prim;
prim=p;
}
void adaug_dupa_o_informatie_data(void)
{
int n1,n2;
lista *q;
cout<<"Dati informatia dupa care se adauga ";cin>>n1;
cout<<"Dati informatia care se adauga ";cin>>n2;
p=prim;
while( (p!=NULL) && (p->inf!=n1) ) p=p->adr;
if(p == NULL) cout<<"Informatia nu EXISTA in lista!";
else
{
q=new lista;
q->inf=n2;
q->adr=p->adr;
p->adr=q;
}
}

void stergere(void)
{
int n;
lista *q;
p=prim;
cout<<"informatia care se sterge ";cin>>n;
if(prim->inf!=n)
{
q=p->adr;
while(q->inf!=n)
{
p=q;q=q->adr;
}
p->adr=q->adr;
}
else
{
prim=p->adr;
}
}
void main()
{
clrscr();

14
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

cout<<"Dati elementele pentru crearea listei "<<endl;


creare();
cout<<"Apasa o tasta pentru continuare"<<endl;
getch();
cout<<"Parcurgerea listei : "<<endl;
afisare();
cout<<endl;
adaug_la_sf();
cout<<"Apasa o tasta pentru continuare"<<endl;;
getch();
cout<<"Parcurgerea listei : "<<endl;
afisare();
cout<<endl;
adaug_la_inceput();
cout<<"Apasa o tasta pentru continuare"<<endl;
getch();
cout<<"Parcurgerea listei : "<<endl;
afisare();
cout<<endl;
adaug_dupa_o_informatie_data();
cout<<"Apasa o tasta pentru continuare"<<endl;
getch();
cout<<"Parcurgerea listei : "<<endl;
afisare();
cout<<endl;
stergere();
cout<<"Apasa o tasta pentru continuare"<<endl;
getch();
cout<<"Parcurgerea listei : "<<endl;
afisare();
getch();
}

2. Se considera o lista liniara simplu inlantuita de numere intregi si se cere sa


se inverseze legaturile intre elemente. Se va afisa lista inainte si dupa modificare.

#include<iostream.h>
#include<conio.h>
#include<alloc.h>

typedef struct lista {


int inf;
struct lista *adr;
} lista;

lista *prim,*ultim;

void crearelista()
{

15
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

int y,i;
lista *p;
cout<<"Creati lista pina la citirea elementului cu informatia 0"<<endl;
cout<<"dati informatia : ";cin>>y;
prim=new lista;
prim->inf=y;
prim->adr=NULL;
ultim=prim;
cout<<"dati informatia : ";cin>>y;
while(y!=0)
{
p=new lista;
p->inf=y;
ultim->adr=p;
ultim=p;
cout<<"dati informatia : ";cin>>y;
}
ultim->adr=NULL;
}
void listarelista()
{
lista *p;
p=prim;
while(p!=NULL)
{
cout<<p->inf<<" ";
p=p->adr;
}
}
lista *inversare(lista *prim)
{
lista *p1,*p2,*p3;
if ( !prim || !prim->adr )
return prim;
else
{
p1=prim;
p2=prim->adr;
prim->adr=NULL;
while (p2 != NULL)
{
p3=p2->adr;
p2->adr=p1;
p1=p2;
p2=p3;
}
return p1;
}
}

16
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

void main()
{
clrscr();
cout<<"Creare lista : "<<endl;
crearelista();
cout<<"Afisare lista initiala : "<<endl;
listarelista();
prim=inversare(prim);
cout<<endl;
cout<<"Afisare lista inversata : "<<endl;
listarelista();
getch();
}

3. Sa se creeze o lista liniara simplu inlantuita in care informatiile (nr. intregi) sa fie
ordonate in ordine crescatoare.
Indicatie: Se va folosi sortarea prin insertie.

SORTAREA PRIN INSERTIE consta in :


Se citesc numere distincte. Sa se sorteze crescator,
utilizand lista liniara simplu inlantuita, dupa urmatorul algoritm :
* initial lista va contine primul numar;
* daca al doilea numar este mai mare decat primul, se va pune in coada listei,
altfel va fi primul element din lista;
* orice numar se aseaza in lista inaintea celui mai mic numar mai mare decat el
(daca exista un asemenea numar), sau la sfarsitul listei, in caz contrar.
In final se tipareste continutul listei.

#include<iostream.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

typedef struct lista


{
int inf;
struct lista *adr;
}lista;
lista *p,*prim,*ultim,*a;

void creare()
{
int y;
lista *p,*a;
p=new lista;
cout<<"Introduceti informatia primului element : "; cin>>y;
p->inf=y;
prim=p;
ultim=p;

17
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

while(y!=100)
{
cout<<"Introduceti informatia dorita dar <>100 : "; cin>>y;
p=new lista;
if (y<prim->inf)
{
p->inf=y;
p->adr=prim;
prim=p;
}
else
if (y>ultim->inf)
{
p->inf=y;
ultim->adr=p;
ultim=p;
}
else {
p->inf=y;
a=prim;
while((y>=a->adr->inf)) a=a->adr;
p->adr=a->adr;
a->adr=p;
}
}
ultim->adr=NULL;
}
void afisare()
{
lista *p;
p=prim;
while(p->adr!=NULL)
{
cout<<p->inf<<" ";
p=p->adr;
}
}

void main()
{
clrscr();
cout<<" Vom crea o lista simplu inlantuia pana la introducerea"<<endl;
cout<<" unui element egal cu 100."<<endl;
creare();
cout<<"\n";
afisare();
getch();
}

18
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

4. Sa se scrie un program care sa creeze o lista simplu inlantuita si apoi sa


stearga elementele multiple de 5.
#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
typedef struct lista
{
int inf;
lista *adr;
}lista;
lista *p,*prim,*ultim;
void creare()
{
int y;
lista *p;
cout<<"dati inf. <> 100 ";
cin>>y;
p=new lista;
p->inf=y;
prim=p;
ultim=p;
while(y!=100)
{
cout<<"dati inf. <> 100 ";cin>>y;
p=new lista;
p->inf=y;
ultim->adr=p;
ultim=p;
}
ultim->adr=NULL;
}
void sterge(int a)
{
lista *p,*q;
if(a==prim->inf)
{
p=prim;
prim=prim->adr;
}
else
{
p=prim;
while(p->inf!=a)
{
q=p;
p=p->adr;
}
q->adr=p->adr;
}

19
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

}
void scoate()
{
int y;
lista *p;
p=prim;
while(p->adr!=NULL)
{
y=p->inf;
if(y%5==0) sterge(y);
p=p->adr;
}
}
void afisare()
{
lista *p;
p=prim;
while(p->adr!=NULL)
{
cout<<p->inf<<" ";
p=p->adr;
}
}
void main()
{
clrscr();
creare();
cout<<"lista initiala "<<endl;
afisare();
cout<<"\n";
scoate();
cout<<"lista dupa stergere "<<endl;
afisare();
}
5. Sa se creeze o lista liniara simplu inlantuita folosind tehnica recursivitatii.

#include<iostream.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
typedef struct lista
{
int inf;
struct lista *adr;
}lista;
lista *prim;

lista *creare(void)
{

20
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

int n;
lista *p;
cout<<"Dati informatia <>0 ";cin>>n;
if(n!=0)
{
p=new lista;
p->inf=n;
p->adr=creare();
return p;
}
else return NULL;
}
void afisare()
{
lista *p;
p=prim;
while(p!=NULL)
{
cout<<p->inf<<" ";
p=p->adr;
}
}
void main()
{
clrscr();
cout<<" Vom crea o lista simplu inlantuita pana la introducerea unui
element egal cu 0."<<endl;
prim=creare();
cout<<"\n";
afisare();
getch();
}
C. Exerciţii şi teme

1. Să se ruleze programele prezentate, urmărind fiecare operaţie în parte.


2. Să se constuiască o listă simplu înlănţuită care să conţină informaţii despre
studenţii unei grupe şi să afişeze numele şi media acelora care au media >=5.00. Se
va utiliza o structură de tip listă:
typedef struct listă
{ char nume[30];
int grupa;
float media;
struct lista *adr;
}lista;
3. Să se construiască o listă liniară simplu înlănţuită cu numere întregi şi să afiseze
numărul aflat în mijlocul listei.

21
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

LUCRAREA nr. 4

LISTE LINIARE DUBLU ÎNLĂNŢUITE

A. Consideraţii teoretice

În unele probleme este necesară prelucrarea informaţiilor aflate într-o listă


înlănţuită şi de la primul element spre ultimul şi de la ultimul spre primul. Acest lucru
nu se poate face prin constuirea unei liste simplu înlănţuite ci prin construierea unei
liste dublu înlănţuite.
Definitie. Se numeşte listă liniară dublu înlănţuită o mulţime de elemente
de forma:

AS INF AD

unde INF reprezintă informaţia pentru care se constuieşte lista, iar AD


reprezintă adresa de memorie unde se află următorul element din listă (elementul
aflat în dreapta celui curent), iar AS reprezintă adresa elementului anterior celui
curent (elementul aflat în stânga celui curent).

Astfel o listă liniară dublu înlănţuită se poate reprezenta grafic astfel:

NULL adr0 inf1 adrn-1 infn NULL


inf1 adr2
prim
adr1 ultim

Într-o astfel de listă fiecare element este de forma:

typedef struct lista


{ Tip_Element inf;
struct lista *ad,*as;
}lista;

unde:
- inf reprezintă informaţia utilă ce urmează a fi prelucrată;
- as reprezintă adresa stânga a elementului aflat înaintea celui curent
- ad reprezintă adresa dreapta a elementului aflat după cel curent
În plus se pot construi şi liste liniare înlănţuite simplu sau dublu înlănţuite
circulare, adică liste în care ultimul element să fie legat de primul element.

B. Exemple de programe

1. Vom da un exemplu de utilizare a unei structuri de tip listă dublu înlănţuită


împreună cu operaţiile ce se pot efectua asupra ei. Aplicaţia prezentată va afişa
un meniu principal din care se poate selecta consecutiv operaţia dorită:

22
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

#include<iostream.h>
#include<conio.h>
typedef struct lista
{
int inf;
lista *as,*ad;
};
lista *p,*prim,*ultim,*q;
int opt;
void creare(void)
{
int n;
prim=new lista;
ultim=new lista;
prim->ad=ultim;
prim->as=NULL;
ultim->as=prim;
cout<<"Dati informatia !=0 ";cin>>n;
while(n!=0)
{
p=ultim;
p->inf=n;
ultim=new lista;
p->ad=ultim;
ultim->as=p;
cout<<"Dati informatia !=0 ";cin>>n;
}
ultim->ad=NULL;
cout<<"Apasa o tasta pentru continuare"<<endl;
getch();
}
void afisare_stanga_dreapta(void)
{
cout<<"parcurgerea listei de la stanga la dreapta"<<endl;
p=prim->ad;
if(p==ultim) cout<<"Lista este VIDA!!";
else
{
do{
cout<<p->inf<<" ";
p=p->ad;
}while(p!=ultim);
}
cout<<"Apasa o tasta pentru continuare"<<endl;
getch();
}
void afisare_dreapta_stanga(void)
{

23
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

cout<<"parcurgerea listei de la dreapta la stanga"<<endl;


p=ultim->as;
if(p==prim) cout<<"Lista este VIDA!!";
else
{
do{
cout<<p->inf<<" ";
p=p->as;
}while(p!=prim);
}
cout<<"Apasa o tasta pentru continuare"<<endl;
getch();
}
void adaug_la_inceput(void)
{
int n;
cout<<"Dati informatia care se adauga ";cin>>n;
p=new lista;
prim->inf=n;
prim->as=p;
p->ad=prim;
prim=p;
prim->as=NULL;
cout<<"Apasa o tasta pentru continuare"<<endl;
getch();
}
void adaug_la_sfarsit()
{
int n;
cout<<"Dati informatia care se adauga ";cin>>n;
p=new lista;
ultim->inf=n;
ultim->ad=p;
p->as=ultim;
ultim=p;
ultim->ad=NULL;
cout<<"Apasa o tasta pentru continuare"<<endl;
getch();
}
void stergere(void)
{
int n;
p=prim;
cout<<"Dati informatia care se va sterge ";cin>>n;
if(p->inf!=n)
{
q=p->ad;
while(q->inf!=n)
{

24
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

p=q;
q=q->ad;
}
q->ad->as=p;
p->ad=q->ad;
}
else
{
prim=p->ad;
prim->as=NULL;
}
cout<<"Apasa o tasta pentru continuare"<<endl;
getch();
}

void main(void)
{
do{
clrscr();
cout<<endl;
cout<<" 1. Crearea listei"<<endl;
cout<<" 2. Adaugare la inceputul listei"<<endl;
cout<<" 3. Adaugare la sfarsitul listei"<<endl;
cout<<" 4. Parcurgerea de la stanga la dreapta"<<endl;
cout<<" 5. Parcurgerea de la dreapta la stanga"<<endl;
cout<<" 6. Stergerea unei informatii date"<<endl;
cout<<" 7. Terminare program"<<endl;
cout<<endl;
cout<<"Dati optiunea ";cin>>opt;
switch (opt){
case 1:{creare();break;}
case 2:{adaug_la_inceput();break;}
case 3:{adaug_la_sfarsit();break;}
case 4:{afisare_stanga_dreapta();break;}
case 5:{afisare_dreapta_stanga();break;}
case 6:{stergere();break;}
}
}while(opt!=7);
}

2. Sa se construiasca o lista care sa poata fi parcursa in ambele sensuri, apoi


sa se elimine din lista primul si ultimul element egal cu un numar a dat.

Vom folosi variabilele prim - adresa primului elem. din lista


ultim - adresa ultimului elem. din lista
p - pointer curent (de lucru)

#include<iostream.h>
#include<conio.h>

25
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

typedef struct lista


{
int inf;
lista *as,*ad;
};
lista *p,*prim,*ultim,*q;
int a;
void creare(void)
{
int n;
prim=new lista;
ultim=new lista;
prim->ad=ultim;
prim->as=NULL;
ultim->as=prim;
cout<<"Dati informatia !=0 ";cin>>n;
while(n!=0)
{
p=ultim;
p->inf=n;
ultim=new lista;
p->ad=ultim;
ultim->as=p;
cout<<"Dati informatia !=0 ";cin>>n;
}
ultim->ad=NULL;
cout<<"Apasa o tasta pentru continuare"<<endl;
getch();
}
void cautare_stanga_dreapta(void)
{
p=prim;
q=p->ad;
while(p!=NULL)
{
while(q->inf!=a)
{
p=q;
q=q->ad;
}
if(q->inf==a)
{
q->ad->as=p;
p->ad=q->ad;
break;
}
p=p->ad;
}
cout<<"Apasa o tasta pt. continuare"<<endl;

26
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

getch();
}
void cautare_dreapta_stanga(void)
{
p=ultim;
q=p->as;
while(p!=NULL)
{
while(q->inf!=a)
{
p=q;
q=q->as;
}
if(q->inf==a)
{
q->as->ad=p;
p->as=q->as;
break;
}
p=p->as;
}
cout<<"Apasa o tasta pt. continuare"<<endl;
getch();

}
void afisare_stanga_dreapta(void)
{
cout<<"parcurgerea listei de la stanga la dreapta"<<endl;
p=prim->ad;
if(p==ultim) cout<<"Lista este VIDA!!";
else
{
do{
cout<<p->inf<<" ";
p=p->ad;
}while(p!=ultim);
}
cout<<"Apasa o tasta pentru continuare"<<endl;
getch();
}

void main(void)
{
clrscr();
cout<<endl;
creare();
afisare_stanga_dreapta();
cout<<endl;
cout<<" Dati numarul care va fi sters ";cin>>a;

27
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

cautare_stanga_dreapta();
afisare_stanga_dreapta();
cout<<endl;
cautare_dreapta_stanga();
afisare_stanga_dreapta();
}

3. Sa se construiasca o lista care sa poata fi parcursa in ambele sensuri, apoi


sa se elimine din lista elementele care se repeta.

Vom folosi variabilele prim - adresa primului elem. din lista


ultim - adresa ultimului elem. din lista
p - pointer curent (de lucru)

#include<iostream.h>
#include<conio.h>
typedef struct lista
{
int inf;
lista *as,*ad;
};
lista *p,*prim,*ultim;
void creare(void)
{
int n;
prim=NULL;
cout<<"Dati informatia !=0 ";cin>>n;
while(n!=0)
{
if(prim==NULL)
{
prim=new lista;
prim->inf=n;
prim->as=NULL;
prim->ad=NULL;
ultim=prim;
}
else
{
p=new lista;
p->inf=n;
p->ad=NULL;
p->as=ultim;
ultim->ad=p;
ultim=p;
}
cout<<"Dati informatia !=0 ";cin>>n;
}
cout<<"Apasa o tasta pentru continuare"<<endl;

28
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

getch();
}
void afisare_stanga_dreapta(void)
{
cout<<"parcurgerea listei de la stanga la dreapta"<<endl;
p=prim;
do{
cout<<p->inf<<" ";
p=p->ad;
}while(p!=NULL);
cout<<"Apasa o tasta pentru continuare"<<endl;
getch();
}
void eliminare(void)
{
lista *p,*q,*t;
int k;
p=prim;
while(p!=NULL)
{
k=0;
q=p->ad;t=q->as;
while( (q!=NULL) && (k==0) )
{
if(q->inf==p->inf) k=1;
else {t=q;q=q->ad;}
}
if(k==1)
{
t->ad=q->ad;
q->ad->as=t;
}
p=p->ad;
}
}
void main(void)
{
clrscr();
cout<<endl;
creare();
afisare_stanga_dreapta();
cout<<endl;
cout<<endl;
eliminare();
afisare_stanga_dreapta();
}

29
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

C. Exercitii si teme

1. Să se ruleze programele prezentate anterior pe mai multe date de test pentru a


vedea toate operaţiile ce se executa.
2. Există numere prime mai mici de 100.000 care au suma cifrelor egală cu 13 ?
Dacă da, atunci să se formeze o listă circulară cu aceste numere, în ordinea găsirii
lor.
3. Într-o listă dublu înlănţuită de n numere reale deja construită, pentru un element
x al listei, să se adauge în faţa lui media aritmetică a elementelor aflate înaintea
lui, iar după el să se adauge media geometrică a elementelor aflate după el în
lista iniţială.

30
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

LUCRAREA nr. 5

ELEMENTE DE TEORIA GRAFURILOR


(grafuri neorientate)

A. Consideraţii teoretice

Definiţie. Numim graf o pereche de mulţimi G=(V, E), unde V - reprezintă mulţimea
vârfurilor din graf, iar E - reprezintă mulţimea muchiilor din graf.
La curs s-au prezentat modalităţile de reprezentare a unui graf neorientat.
Acum prezentam două modalităţi de parcurgere.

1. Parcurgerea în lăţime, care constă în a “vizita” vârful iniţial i, apoi vecinii


acestuia, apoi vecinii nevizitaţi ai acestora ş.a.m.d.
Pentru construcţia algoritmului, atunci când avem de ales dintre toţi vecinii
unui vârf, pe acela nevizitat încă trebuie să folosim un vector numit VIZ care are n
elemente astfel:
VIZ[i]=1 dacă vârful i a fost vizitat
VIZ[i]=0 dacă vârful i nu a fost vizitat
Iniţial se consideră că nici un nod nu este vizitat, încă. Rezolvarea problemei
se bazează pe folosirea unei cozi C, în care prelucrarea unui vârf v aflat la un capat al
cozii constă în introducerea în celălalt capăt al ei a tuturor vârfurilor i vecine cu v,
nevizitate încă.

Exemplu: Pentru graful din figura următoare, ordinea de parcurgere BF a vârfurilor,


plecând de la nodul 1 este : 1, 2, 3, 4, 5, 6, 7, 8.

2 3 4

5 6

7 8
2. Parcugerea în adâncime, constă în a “vizita” vârful iniţial i şi a continua cu
vecinii săi nevizitaţi j. Tot timpul mergem în adâncime, cât este posibil, cât nu ne
întoarcem şi plecăm, dacă este posibil, spre vecinii nevizitaţi încă.

Pentru implementarea acestui algoritm se foloseşte vectorul VIZ cu aceeaşi


semnificaţie ca la parcurgerea în lăţime şi se înlocuieşte coada C cu o stivă S, care ne
dă posibilitatea să parcurgem în fiecare moment de la vârful curent spre primul

31
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

dintre vecinii săi nevizitaţi, acesta din urmă fiind plasat în vârful stivei; cu acest nod
se continuă la fel. Varianta aleasă este cea recursivă.
Exemplu: Pentru graful din figura următoare, ordinea de parcurgere DF a
vârfurilor, plecând de la nodul 1 este : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10.

10
3 4 6 7

5 9
8
B. Exemple de programe

În continuare prezentăm programele care permit implementarea metodelor de


parcurgere a unui graf neorientat şi anume parcurgerea în lăţime – BF (Breath
First) şi parcurgerea în adâncime – DF (Deep First). Ultimul program este
determinarea componentelor conexe ale unui graf neorientat.

1. Parcurgerea în lăţime – BF:

#include<iostream.h>
#include<conio.h>
int viz[30],n,m,i,j,u,v,p,a[20][20],c[30];
void main(void)
{
clrscr();
cout<<"Dati numarul de varfuri n = ";cin>>n;
cout<<"Dati numarul de muchii m = ";cin>>m;
for(i=1;i<=n-1;i++)
for(j=i+1;j<=n;j++)
{
cout<<"a["<<i<<","<<j<<"]= ";cin>>a[i][j];
a[j][i]=a[i][j];
}
cout<<"Dati varful de plecare ";cin>>i;
for(j=1;j<=n;j++) viz[j]=0;
c[1]=i;p=1;u=1;viz[i]=1;
while(p<=u)
{
v=c[p];
for(j=1;j<=n;j++)
{
if( (a[v][j]==1) && (viz[j]==0) )
{
u++;

32
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

c[u]=j;
viz[j]=1;
}
}
p++;
}
cout<<"Lista varfurilor in parcugerea in latime : "<<endl;
cout<<i<<" ";
for(j=2;j<=u;j++) cout<<c[j]<<" ";
getch();
}

2. Parcurgerea in adancime – DF:

#include<iostream.h>
#include<conio.h>
int s[30],n,m,i,j,p,a[20][20];
void df(int k)
{
int l;
cout<<k<<" ";
s[k]=1;
for(l=1;l<=n;l++)
if( (a[k][l]==1) && (s[l]==0) )
df(l);
return;
}
void main(void)
{
clrscr();
cout<<"Dati numarul de varfuri n = ";cin>>n;
for(i=1;i<=n-1;i++)
for(j=i+1;j<=n;j++)
{
cout<<"a["<<i<<","<<j<<"]= ";cin>>a[i][j];
a[j][i]=a[i][j];
}
cout<<"Dati varful de plecare ";cin>>p;
df(p);
getch();
}

3. Afisarea componentelor conexe ale unui graf neorientat:

#include<iostream.h>
#include<conio.h>
int x[30],k,n,m,i,j,p,
int a[20][20]; // matricea de adiacenţă a grafului

33
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

int viz[30]; // arată dacă un nod a fost sau nu vizitat


int g,t,u,kk,r,tt;
int c[5][5]; // matrice ce conţine componentele conexe //care sunt reprezentate sub
forma de mulţimi
void main(void)
{
clrscr();
cout<<"Dati numarul de varfuri n = ";cin>>n;
cout<<"Matricea de adiacenta "<<endl;
for(i=1;i<=n-1;i++)
for(j=i+1;j<=n;j++)
{
cout<<"a["<<i<<","<<j<<"]= ";cin>>a[i][j];
a[j][i]=a[i][j];
}
k=0;kk=0;
for(i=1;i<=n;i++) viz[i]=0;
g=1; // g ramane 1 atata timp cat mai sunt
//componente conexe de gasit in graf
while(g!=0)
{
j=1;t=1;
while( (t==1) && (j<=n) )
if(viz[j]==0) t=0;
else j++;
if(t==1) g=0;
else
{
k++;//s-a gasit o noua componenta conexa
kk++;
r=1;//numar cate elem. are o componenta conexa
viz[j]=k;
c[kk][r++]=j;
p=1;u=1;// folosesc parcurgerea in latime
x[p]=j;
while(p<=u)
{
for(i=1;i<=n;i++)
if( (viz[i]==0) && (a[i][x[p]]==1) )
{
u++;
x[u]=i;
viz[i]=k;
c[kk][r++]=i;
}
p++;
}
tt=r;
}

34
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

}
if(k==1) cout<<"Graful este conex";
else
{
cout<<"S-au gasit urmat. componente conexe" <<endl;
for(i=1;i<=kk;i++)
{
cout<<"Componenta conexa : "<<i<<" = { ";
for(j=1;j<=tt;j++)
cout<<c[i][j]<<",";
cout<<"}"<<endl;
}
}
getch();
}

C. Exerciţii şi teme

1. Să se ruleze programele prezentate mai sus şi să se verifice folosind mai multe


grafuri ca exemplu.
2. Să se scrie un program PASCAL care să parcurgă în lăţime un graf neorientat,
reprezentat printr-o listă de adiacenţă simplu înlănţuită.

35
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

LUCRAREA nr. 6

ARBORI BINARI

B. Consideraţii teoretice

Definiţie: Numim arbore un graf conex şi fără cicluri.


Un arbore este determinat de câteva proprietăţi:
 Un nod special numit rădăcină în care nu intra nici un arc.
 Din fiecare nod pot iesi zero, unul sau mai multe arce, numite fii.
 Din fiecare nod (mai puţin rădăcina) poate intra un singur arc, dintr-un nod numit
părinte.
 Nodurile sunt organizate pe nivele, primul nivel este ocupat numai de rădăcină,
iar ultimul nivel conţine numai noduri din care nu iese nici un arc, numite noduri
terminale, sau frunze.
 Fiecare nod poate conţine o informaţie numită informaţie utilă sau cheie a
arborelui.
Nivel 1
1
Nivel 2
2 3
4
7
5
Nivel 3 6

Nivel 4 8 9

În acest exemplu avem un arbore cu 9 noduri, în care nodul 1 este rădăcina şi


care se află pe nivelul 1, are trei fii – nodurile 2, 3 şi 4 de pe nivelul 2, nodul are doi
fii – nodurile 5 şi 6, nodul 3 este un nod terminal, iar nodul 4 are un fiu – nodul 7 –
toate se află pe nivelul 3, iar pe nivelul 4 se află fii nodului 6 – nodurile 8 şi 9, pentru
că nodurile 5 şi 7 sunt noduri terminale.

Definitie: Numim arbore binar un arbore în care fiecare nod are zero, unul sau cel
mult doi fii (descendenţi).
Definitie: Un arbore binar cu proprietatea că fiecare nod cu excepţia frunzelor
(nodurilor terminale) are exact doi descendenţi se numeşte arbore binar complet.

2 3

4 5 6 7

8 9

Reprezentarea arborilor binari

36
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

1. Se specifică rădăcina RAD şi pentru fiecare nod se precizează descendentul stâng


şi descendentul drept, în cazul în care aceştia există. Aceşti descendenţi de reţin
cu ajutorul unor vectori numiţi chiar ST şi DR, astfel pentru un nod I, ST[i]
reprezintă descendentul stâng, DR[i] descendentul drept, iar INF[i] reprezintă
informaţia ataşată arborelui. Atunci când un descendent lipseşte se specifică
valoarea zero.
Exemplu: Pentru arborele binar următor:
1

2 3
2 1
1
2 5 4
1
6 7
Avem n = 7 noduri, RAD = 1 si ST = { 2, 0, 5, 0, 6, 0, 0 } şi DR = { 3, 0, 4, 0, 7,
0, 0 }.
2. Se folosesc vectorii TATĂ care precizează pentru fiecare nod i, nodul părintelui
său – TATĂ[i] şi DESC, vector care reţine valorile –1 sau 1 în funcţie de nodul i
care este fie descendent stâng, fie descendent drept.
Pentru acelasi exemplu, vectori consideraţi mai sus au următoarele valori:
TATĂ = { 0, 1, 1, 3, 3, 5, 5 } şi DESC = { 0, -1, 1, 1, -1, -1, 1 }.
3. Se foloseşte alocarea dinamică a memoriei astfel încât fiecare nod este compus
din un câmp de informaţie şi două câmpuri de adrese stânga şi dreapta. Definirea
unui astfel de nod se face astfel:
typedef struct pnod
{
int inf;
struct *as,*ad;
}
Un exemplu de arbore binar reprezentat prin alocarea dinamică memoriei este
următorul:

2 3

NIL NIL

NIL
4 5

NIL NIL NIL NIL

37
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

Parcurgerea arborilor binari

Pentru a putea prelucra informaţiile dintr-un arbore, trebuie să parcurgem


nodurile arborelui. Există trei modalităţi de parcurgere a arborilor de binari: nodurile
pot fi vizitate în preordine, inordine şi postordine. Aceste metode sunt definite
recursiv, astfel dacă arborele este vid atunci el este traversat fără a se face nimic;
altfel traversarea se face în trei etape.

1) Travesarea în preordine
- se viziteaza rădăcina
- se traversează subarborele stang
- se traversează subarborele drept
2) Travesarea în inordine
- se traversează subarborele stang
- se vizitează rădăcina
- se traversează subarborele drept
3) Travesarea în postordine
- se traversează subarborele stang
- se traversează subarborele drept
- se vizitează rădăcina

C. Exemple de programe

1. Prezentăm în continuare programul în limbajul Borland C++ care


implementează cele trei tipuri de parcurgere de mai sus, în reprezentarea arborilor
static folosind vectorii ST şi DR.

#include<iostream.h>
#include<conio.h>
int st[20],dr[20],i,n,rad;
void inordine(int k)
{
if(st[k]!=0) inordine(st[k]);
cout<<k<<" ";
if(dr[k]!=0) inordine(dr[k]);
}

void preordine(int k)
{
cout<<k<<" ";
if(st[k]!=0) preordine(st[k]);
if(dr[k]!=0) preordine(dr[k]);
}
void postordine(int k)
{
if(st[k]!=0) postordine(st[k]);
if(dr[k]!=0) postordine(dr[k]);
cout<<k<<" ";

38
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

void main(void)
{
clrscr();
cout<<"Dati numarul de noduri ale arborelui ";
cin>>n;
cout<<"Dati radacina arborelui ";cin>>rad;
cout<<"Dati elementele pentru vectorii ST si DR "
<<endl;
for(i=1;i<=n;i++)
{
cout<<"st["<<i<<"]= ";cin>>st[i];
cout<<"dr["<<i<<"]= ";cin>>dr[i];
}
cout<<"Parcurgere Stanga Varf Dreapta - INORDINE : ";
inordine(rad);
cout<<endl;
cout<<"Parcurgere Varf Stanga Dreapta - PREORDINE : ";
preordine(rad);
cout<<endl;
cout<<"Parcurgere Stanga Dreapta Varf - POSTORDINE : ";
postordine(rad);
cout<endl;
getch();
}

2. Prezentăm în continuare programul în limbajul Borland C++ care


implementează cele trei tipuri de parcurgere de mai sus, în reprezentarea arborilor
dinamic folosind pointeri. Vom construi arborele binar recursiv:

#include<iostream.h>
#include<conio.h>
typedef struct arbore
{
int inf;
struct arbore *st,*dr;
}arbore;
arbore *rad;
arbore *creare(void)
{
int n;
arbore *c;
cout<<" Dati n = ";cin>>n;
if(n!=0)
{
c=new arbore;
c->inf=n;
c->st=creare();

39
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

c->dr=creare();
return c;
}
else return(NULL);
}
void INORDINE(arbore *c)
{
if(c!=NULL)
{
INORDINE(c->st);
cout<<c->inf<<" ";
INORDINE(c->dr);
}
return;
}
void PREORDINE(arbore *c)
{
if(c!=NULL)
{
cout<<c->inf<<" ";
PREORDINE(c->st);
PREORDINE(c->dr);
}
return;
}
void POSTORDINE(arbore *c)
{
if(c!=NULL)
{
POSTORDINE(c->st);
POSTORDINE(c->dr);
cout<<c->inf<<" ";
}
return;
}

void main(void)
{
clrscr();
rad=creare();
cout<<"Parcurgere Stanga Varf Dreapta - INORDINE : ";
INORDINE(rad);
cout<<endl;
cout<<"Parcurgere Varf Stanga Dreapta - PREORDINE : ";
PREORDINE(rad);
cout<<endl;
cout<<"Parcurgere Stanga Dreapta Varf - POSTORDINE : ";
POSTORDINE(rad);
cout<endl;

40
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

getch();
}

3. Să se creeze un arbore binar cu informaţii numere întregi, apoi să se tipărească


suma elementelor pare din arbore.

#include<iostream.h>
#include<conio.h>
typedef struct arbore
{
int inf;
struct arbore *st,*dr;
}arbore;
arbore *rad;
arbore *creare(void)
{
int n;
arbore *c;
cout<<" Dati n = ";cin>>n;
if(n!=0)
{
c=new arbore;
c->inf=n;
c->st=creare();
c->dr=creare();
return c;
}
else return(NULL);
}
void PREORDINE(arbore *c)
{
if(c!=NULL)
{
cout<<c->inf<<" ";
PREORDINE(c->st);
PREORDINE(c->dr);
}
return;
}
int suma(arbore *p)
{
if(p==NULL) return 0;
else
if(p->inf%2==0) return (p->inf+suma(p->st)+suma(p->dr));
else return (suma(p->st)+suma(p->dr));
}
void main(void)
{

41
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

clrscr();
rad=creare();
cout<<"Suma cheilor pare este “<<
suma(rad)<<endl;
cout<<"Parcurgere Varf Stanga Dreapta - PREORDINE : ";
PREORDINE(rad);
cout<<endl;
getch();
}

4. Sa se construiasca un arbore binar si apoi sa se caute o valoare data,


specificandu-se daca este sau in arbore.

#include<iostream.h>
#include<conio.h>
typedef struct arbore
{
int inf;
struct arbore *st,*dr;
}arbore;
arbore *rad,*q;
int val;
arbore *creare(void)
{
int n;
arbore *c;
cout<<" Dati n = ";cin>>n;
if(n!=0)
{
c=new arbore;
c->inf=n;
c->st=creare();
c->dr=creare();
return c;
}
else return(NULL);
}
void INORDINE(arbore *c)
{
if(c!=NULL)
{
INORDINE(c->st);
cout<<c->inf<<" ";
INORDINE(c->dr);
}
return;
}
arbore *cautare_recursiva(arbore *p,int val)

42
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

{
if( (p==NULL) || (p->inf==val) ) return p;
else
if(val < p->inf)
return (cautare_recursiva(p->st,val));
else return (cautare_recursiva(p->dr,val));
}
void main(void)
{
clrscr();
rad=creare();
cout<<"Parcurgere INORDINE (stanga-varf-dreapta) ";
cout<<endl;
INORDINE(rad);
cout<<"Dati informatia pe care o cautati ";
cin>>val;
q=cautare_recursiva(rad,val);
if(q==NULL) cout<<"Valoarea "<<val<<" NU exista in arbore";
cout<<"EXISTA!!";
getch();
}

D. Exerciţii şi teme

1. Să se ruleze programele prezentate mai sus.


2. Să se scrie o funcţie care returnează numărul de nivele ale unui arbore binar (
înălţimea arborelui ). Funcţia va primi ca parametru un pointer p către rădăcina
arborelui.
3. Să se scrie o procedură care tipăreste informaţiile din nodurile aflate pe un
anumit nivel dat. Procedura va primi ca parametri numărul nivelului, precum şi un
pointer către rădăcina arborelui.

43
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

LUCRAREA nr. 7

METODA DIVIDE ET IMPERA

a. Consideraţii teoretice

O abordare obişnuită de rezolvare a unei probleme este împărţirea acesteia în mai


multe părţi mai mici la care soluţiile se pot determina uşor, şi din ele să se obţină
soluţia problemei iniţiale. Aceasta este de fapt principiul pe baza căruia funcţionează
metoda DIVIDE ET IMPERA. Printr-un procedeu recursiv, se împarte problema în
două jumătăţi care se soluţionează separat şi din combinarea soluţiilor se obţine
soluţia problemei date.
Presupunem un vector A cu n elemente asupra căruia vrem să aplicăm
această metodă de prelucrare. Presupunem că pentru orice p, q  N, 1  p < q  n,
() m din mulţimea { p, . . . , q-1 } astfel încât prelucrarea secvenţei { ap, . . . , aq },
se face prelucrând secvenţele vecine următoare { ap, . . . , am }, { am+1, . . . , aq } şi
apoi combinând rezultatele pentru a obţine prelucrarea întregii secvenţe { ap, . . . , aq
}. Următoarea procedură exemplifică metoda Divide et Impera:
procedure DIVIDE_IMPERA(p, q, a)
begin
if q-p then
SOL(p, q, a)
else
begin
DIVIDE(p, q, m)
DIVIDE_IMPERA(p, m, b)
DIVIDE_IMPERA(m+1, q, c)
COMB(b, c, a)
end
end
Dacă dimensiunea problemei iniţiale sau a subproblemelor aparute este mai mică
decât , atunci problema se rezolvă direct prin procedura SOL soluţia fiind în
vectorul a.
Soluţia se pune în vectorul a, iar soluţiile parţiale se pun în vectorii b respectiv c.
Procedura DIVIDE împarte secvenţa în două subsecvenţe { ap, . . . , am } şi { am+1, . .
. , aq }, obţinând rezultatul în m. Prin cele două autoapelări ale procedurii
DIVIDE_IMPERA se rezolvă subproblemele punând rezultatele prelucrărilor în b şi
respectiv în c. Procedura COMB obţine din soluţiile subproblemelor, soluţia problemei
date. La început procedura DIVIDE_IMPERA se apelează cu p=1 şi q=n.

Exemple de programe

INTERCLASAREA UNUI VECTOR

1. În continuare prezentăm programul C++ care implementează interclasarea


elementelor unui vector:

44
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

#include<iostream.h>
#include<conio.h>
int n,i,p,q,a[100];
void sort(int p,int q,int a[100])
{
int t;
if(a[p]>a[q])
{
t=a[p];a[p]=a[q];a[q]=t;
}
return;
}
void interclasare(int p,int q,int m, int a[100])
{
int i,j,k,b[100];
i=p;j=m+1;k=1;
while( (i<=m) && (j<=q) )
if(a[i]<=a[j])
{
b[k]=a[i];i++;k++;
}
else
{
b[k]=a[j];j++;k++;
}
if(i<=m)
for(j=i;j<=m;j++)
{
b[k]=a[j];
k++;
}
else
for(i=j;i<=q;i++)
{
b[k]=a[i];
k++;
}
k=1;
for(i=p;i<=q;i++)
{
a[i]=b[k];
k++;
}
return;
}
void divide_impera(int p,int q, int a[100])
{
int m;

45
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

if(q-p<=2) sort(p,q,a);
else
{
m=(p+q)/2;
divide_impera(p,m,a);
divide_impera(m+1,q,a);
interclasare(p,q,m,a);
}
return;
}
void main(void)
{
clrscr();
cout<<"Dati numarul de elemente ";cin>>n;
cout<<endl<<"Dati elementele vectorului "<<endl;
for(i=1;i<=n;i++)
{
cout<<"a["<<i<<"]= ";
cin>>a[i];
}
divide_impera(1,n,a);
cout<<"vectoarul sortat este ";
for(i=1;i<=n;i++) cout<<a[i]<<" ";
getch();
}

CAUTAREA BINARĂ

2. Fie un vector v cu n elemente ordonate crescător şi un număr nr. Să se


caute acest număr în vector şi în caz că este găsit să se afiseze indicele pe care se
găseşte.

#include<iostream.h>
#include<conio.h>
int n,i,nr,v[100];
int caut(int i,int j)
{
if(nr==v[(i+j)/2]) return ((i+j)/2) ;
else
if(i<j)
if(nr<v[(i+j)/2]) return(caut(i,(i+j)/2 - 1));
else return(caut((i+j)/2 + 1,j));
}
void main(void)
{
clrscr();
cout<<"Dati numarul de elemente ";cin>>n;
cout<<endl<<"Dati elementele vectorului "<<endl;

46
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

for(i=1;i<=n;i++)
{
cout<<"v["<<i<<"]= ";
cin>>v[i];
}
cout<<"Dati numarul care se cauta ";cin>>nr;
cout<<"Am gasit elementul pe pozitia "<<caut(1,n);
getch();
}

SORTAREA RAPIDĂ (QUICKSORT)

3. Se dă un vector A cu n elemente, şi se cere să se ordoneze crescător.

#include<iostream.h>
#include<conio.h>
int n,i,k,a[100];
int poz(int li,int ls,int a[100])
{
int i,j,c,i1,j1;
i1=0;j1=-1;i=li;j=ls;
while(i<j)
{
if(a[i]>a[j])
{
c=a[i];a[i]=a[j];a[j]=c;
c=i1;i1=-j1;j1=-c;
}
i=i+i1;
j=j+j1;
}
return i;
}
void quick(int li,int ls)
{
if(li<=ls)
{
k=poz(li,ls,a);
quick(li,k-1);
quick(k+1,ls);
}
return;
}
void main(void)
{
clrscr();
cout<<"Dati numarul de elemente ";cin>>n;
cout<<endl<<"Dati elementele vectorului "<<endl;

47
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

for(i=1;i<=n;i++)
{
cout<<"a["<<i<<"]= ";
cin>>a[i];
}
quick(1,n);
cout<<"vectorul sortat este ";
for(i=1;i<=n;i++) cout<<a[i]<<" ";
getch();
}

Exerciţii şi teme

1. Să se ruleze programele prezentate mai sus.


2. TURNURILE DIN HANOI

Se dau trei tije numerotate cu 1, 2, 3 şi n discuri perforate având diametre


diferite. Iniţial toate discurile sunt asezate pe tija 1, în ordinea crescătoare a
diametrelor lor, considerând sensul de la vârful tijei la baza ei.
Să se mute toate discurile pe tija 2 în aceeaşi ordine, folosind tija 3 şi
respectând următoarele reguli:
 la fiecare pas se mută un singur disc
 în permanenţă pe fiecare tijă deasupra fiecărui disc pot apare numai
discuri de diametre mai mici.

3. Ghicirea unui număr ascuns


Fie următorul joc: avem un număr x natural între 0 şi 3000 care este ascuns
de o persoană. O altă persoană va trebui să găsească numărul ascuns prin
încercări cât mai puţine, pe baza informaţiilor date de persoana care a ascuns
numărul, aceasta precizând dacă numă ascuns este mai mare sau mai mic decât
numărul presupus.

4. Fiind dat un sir ordonat, sa se scrie un program recursiv care realizeaza cautarea
binara, prin impartirea multimii initiale in doua multimi care contin 1/3 respectiv
2/3 din elementele multimii initiale.

48
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

LUCRAREA nr. 8

METODA GREEDY

a. Consideraţii teoretice

Pentru a exemplifica această metodă considerăm o mulţime A cu n elemente.


Şi problema care ar trebui rezolvată constă din determinarea unei submulţimi B a lui
A. Aceasta trebuie să îndeplinească anumite condiţii pentru a fi acceptată ca soluţie.
Dintre toate submulţimile acceptate (numite soluţii posibile ), se va alege una singură
numită soluţie optimă.
Dintre soluţiile posibile trebuie aleasă cea optimă tinând cont de proprietatea
următoare: Dacă B este soluţie posibilă şi CB atunci şi C este soluţie posibilă.
Multimea  este întotdeauna soluţie posibilă.
Iniţial se porneşte de la mulţimea vidă. Se alege într-un anumit fel un element din
A, neales la paşii precedenţi. Dacă acestă adăugare la soluţia parţial construită
conduce la o soluţie posibilă atunci construim noua soluţie posibilă prin adăugarea
elementului – procedura Greedy1.

procedure Greedy1(A, n, B)
Begin
B<-
for i=1 to n do
begin
ALEGE(A, i, x)
POSIBIL(B, x, v)
if v=1 then ADAUG(B, x)
end
end

Procedura ALEGE selectează un element x = aj { aI, . . ., an } şi efectuează


interschimbarea aiaj. Procedura POSIBIL verifică dacă B  {x} este soluţie posibilă,
caz în care variabila booleană v va lua valoarea 1, altfel ea va lua valoarea 0.
Procedura ADAUG înlocuieşte pe B cu B  {x}. Procedura ALEGE nu precizează deloc
cum se selectează un element x, de aceea trebuie stabilită o procedură de prelucrare
( PREL), care va preciza ordinea în care vor fi introduse elementele lui A în soluţie -
procedura Greedy2 .

procedure Greedy2(A, n, B)
begin
PREL
B<-
for i=1 to n do
begin
POSIBIL(B, ai, x )
if v=1 then ADAUG(B, ai)
end

49
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

end

1. Suma maximă

Se dă o mulţime X= { x1, x2, . . ., xn } cu elemente reale. Să se determine o


submulţime a lui X astfel încât suma elementelor submulţimii să fie maximă.

Aplicăm metoda Greedy selectând din mulţime toate elementele pozitive. Să


presupunem că rezultatul îl construim în vectorul S = { s1, s2, . . . , sk } unde kn şi
există j{ 1, 2, . . .,n } astfel încât si=xj pentru orice i=1, … , k.

program submultime(X, n, S, k)
begin
k<-0
for i=1 to n do
if xI>0 then
begin
k<-k+1
sk<-xi
end
end

2. K divizori naturali

Fiind dat numărul natural k > 1, se cere să se determine cel mai mic număr
natural n având exact k divizori naturali proprii (diferiţi de 1 şi n).

procedure VERFI(n, k, v)
begin
i<=0
for j=2 to n-1 do
if n mod j = 0 then i<=i+1
if i=k then v<-true
else v<=false
end
program divizori(k,n)
begin
read k
n<-k+2
s<-0
while s=0 do
begin
VERIF(n,k,v)
If v=true then
begin
write n
s<-1
end
n<-n+1

50
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

end
end.

b. Exemple de programe

1. SUMA MAXIMĂ

Se dă o mulţime X= { x1, x2, . . ., xn } cu elemente reale. Să se determine o


submulţime a lui X astfel încât suma elementelor submulţimii să fie maximă.
(Ioan Odagescu, Felix Furtuna –
METODE SI TEHNICI DE PROGRAMARE)

#include<iostream.h>
#include<conio.h>
int n,i,k;
float s[20],x[20];
void main(void)
{
clrscr();
cout<<"Dati numarul de elemente n = ";cin>>n;
cout<<endl<<"Dati elementele matricei "<<endl;
for(i=1;i<=n;i++)
{
cout<<"x["<<i<<"]= ";
cin>>x[i];
}
k=0;
for(i=1;i<=n;i++)
if(x[i]>0)
{
k++;s[k]=x[i];
}
cout<<"Submultimea ceruta este formata din : ";
for(i=1;i<=k;i++) cout<<s[i]<<" ";
getch();
}

2. K DIVIZORI NATURALI

Fiind dat numărul natural k > 1, se cere să se determine cel mai mic numar
natural n având exact k divizori naturali proprii (diferiti de 1 şi n).
(Ioan Odagescu, Felix Furtuna –
METODE SI TEHNICI DE PROGRAMARE)

#include<iostream.h>
#include<conio.h>
int n,i,k,s,v;
51
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

int verif(int n,int k)


{
int i=0,j;
for(j=2;j<=n-1;j++)
if(n%j==0) i++;
if(i==k) v=1;
else v=0;
return v;
}
void main(void)
{
clrscr();
cout<<"Dati numarul de divizori k > 1 ";cin>>k;
cout<<endl<<"Cel mai mic numar care are exact "<<k<<" divizori este
"<<endl;
n=k+2;
s=0;
while(s==0)
{
v=verif(n,k);
if(v==1){ cout<<n;s=1; }
n++;
}
getch();
}

3. PROBLEMA COMIS-VOIAJORULUI

Se consideră un graf G neorientat în care oricare două vârfuri distincte sunt


unite între ele. Un comis-voiajor pleacă dintr-un oraş (reprezentat de nodurile
grafului), şi trebuie să viziteze toate orasele astfel încât costul deplasării să fie
minim.(fiecare muchie dintre două noduri are asociat un cost reprezentând distanţa
dintre cele două oraşe).

Soluţia dată este generalizată astfel încât se reiau pe rând calculele pentru
fiecare nod de pornire {1,2,...,n}.

4. PROBLEMA CONTINUA A RUCSACULUI

O persoana are un rucsac cu care poate transporta o greutate maxima G.


Persoana are la dispozitie n obiecte si cunoaste pentru fiecare greutatea si castigul
care se obtine in urma transportului sau la destinatie. Se cere sa se precizeze ce
obiecte trebuie sa transporte persoana a.i. castigul sa fie maxim.
Precizare : obiectele pot fi taiate.
Astfel se obtine o incarcare mai eficienta a rucsacului.

#include<iostream.h>
#include<conio.h>
float c[20],g[20],ef[20];

52
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

//c - vectorul in care se trec castigurile pt. fiecare obiect in parte


// g - vectorul in care se trec greutatile fiecarui obiect
// ef - vectorul eficientei transportului fiecarui obiect
// de fapt, eficienta este castigul impartit la greutate (
// castigul obtinut prin transportul unitatii de greutate )
int ordine[20];
// ordine - vector folosit la sortarea in ordine descrescatoare a
// eficientei de transport
int i,n,aux1,invers;
// n - numarul de obiecte
// i - contor de lucru
// aux1 - variabila de interschimbare
// invers - variabila folosita la sortarea vectorului ordine
float gr,aux,castig;

void main(void)
{
clrscr();
cout<<"Greutatea ce poate fi transportata ";
cin>>gr;
cout<<endl<<" Dati numarul de obiecte ";cin>>n;
for(i=1;i<=n;i++)
{
cout<<endl<<"cost["<<i<<"]= ";cin>>c[i];
cout<<"greutate["<<i<<"]= ";cin>>g[i];
ordine[i]=i;
ef[i]=c[i]/g[i];
cout<<"eficienta pt. obiectul "<<i<<" este "<<ef[i];
}
do{
invers=0;
for(i=1;i<=n-1;i++)
if(ef[i]<ef[i+1])
{
//sortam vectorul ef
aux=ef[i];ef[i]=ef[i+1];ef[i+1]=aux;
//sortam vectorul c
aux=c[i];c[i]=c[i+1];c[i+1]=aux;
//sortam vectorul g
aux=g[i];g[i]=g[i+1];g[i+1]=aux;
invers=1;
//sortam vectorul ordine aux1=ordine[i];
ordine[i]=ordine[i+1];
ordine[i+1]=aux1;
}
}while(invers!=0);
castig=0;i=1;
cout<<endl;
cout<<"Posibilitatea de incarcare eficienta a rucsacului este : "<<endl;

53
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

cout<<"Apasa ENTER pentru afisarea rezultatului"<<endl;


getch();
while( (gr>0) && (i<=n) )
{
if(gr>g[i])
{
cout<<"se incarca obiectul "<<ordine[i]<<" : "<<1<<endl;
gr=gr-g[i];
castig=castig+c[i];
}
else
{
cout<<"se incarca obiectul "<<ordine[i]<<" :
"<<gr/g[i]<<endl;
gr=0;
castig=castig+c[i]*gr/g[i];
}
i++;
}
cout<<"Castig total = "<<castig;
getch();
}

5. Alg. lui Dijkstra

#include<iostream.h>
#include<conio.h>
const nmax=15;
const inf=15000;
int c[nmax][nmax],k,i,j,n,m,x,y,xp,z,arc,g;
int s[nmax],d[nmax],prec[nmax];
int min(void)
{
m=inf*2;
for(i=1;i<=n;i++)
if( (s[i]==0) && (d[i]<m) )
{
m=d[i];k=i;
}
return k;
}
void drum(int i)
{
if(i!=0)
{
drum(prec[i]);
cout<<i;
}
else cout<<endl;

54
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

return;
}

void main(void)
{
clrscr();
cout<<endl<<" Dati numarul n = ";cin>>n;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++) c[i][j]=inf;
for(i=1;i<=n;i++) c[i][i]=0;
cout<<"Arc = ";cin>>arc;
for(i=1;i<=n;i++)
{
cout<<"Dati arcul "<<i<<" : "<<endl;
cout<<"x = ";cin>>x;
cout<<"y = ";cin>>y;
cout<<"z = ";cin>>z;
c[x][y]=z;
}
cout<<"xp = ";cin>>xp;
for(i=1;i<=n;i++)
{
d[i]=c[xp][i];
s[i]=0;
if(c[xp][i]<inf) prec[i]=xp;
else prec[i]=0;
}
s[xp]=1;
prec[xp]=0;
g=1;
x=0;
do{
k=min();
x++;
if( (d[k]==inf) || (x==n) ) g=0;
else
{
s[k]=1;
for(j=1;j<=n;j++)
if( (s[j]==0) && (d[j]>d[k]+c[k][j]) )
{
d[j]=d[k]+c[k][j];
prec[j]=k;
}
}
}while(g!=0);
for(i=1;i<=n;i++)
if(i!=xp)
if(d[i]==inf)

55
METODE SI TEHNICI DE PROGRAMARE – Limbajul C++
ADRIAN RUNCEANU

cout<<"Nu exista drum de la "<<xp<<" la "<<i<<endl;


else
{
cout<<"Drum minim de la "<<xp<<" la "<<i<<" : "<<d[i]<<endl;
drum(i);
cout<<endl;
}
getch();
}

c. Exerciţii şi teme

1. Să se ruleze programele prezentate anterior, executând mai multe exemple de


test pentru a vedea valorile obţinute în situaţii diferite.
2. Se dau n orase şi costul conectării anumitor perechi de oraşe. Se cere să se
aleagă acele muchii care asigură existenţa unui drum între oricare două oraşe
astfel încât costul total să fie minim. (se poate folosi algoritmul lui Prim)
3. Problema spectacolelor. Într-o sală de spectatcole, într-o zi, trebuie planificate
n spectacole. Pentru fiecare spectacol se cunoaşte intervalul în care se desfaşoară
: [st,sf). Se cere să se planifice un număr maxim de spectacole astfel încât să nu
se suprapună.

56

View publication stats

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