Sunteți pe pagina 1din 21

TEHNICI DE

PROGRAMARE
Banu Maria Alessandra
Clasa a XI –a B
Cuprins :
• Backtracking;
• Divide et impera;
• Metoda Greedy;
• Programarea
dinamica;
TEHNICA BACKTRACKING
Tehnica backtracking este o metoda de Aplicații ale tehnicii :
generare a tuturor solutiilor unei problem.
Este o metoda de rezolvare a problemei în • Generarea permutărilor ;
care trebuie să se genereze toate soluțiile, o • Generarea aranjamentelor ;
soluție fiind dată de un vector. Aceasta
• Generarea combinărilor ;
construiește progresiv vectorul soluție
pornind de la primul element și adăugând la • Generarea produsului cartezian ;
vector următoarele elemente cu revenire la • Generarea damelor pe tabla de șah ;
elementul anterior din vector în caz de
• Generarea submulțimilor ;
insucces. Elementul care trebuie adăugat se
caută în mulțime printre elementele care • Colorarea hărților ;
respectă condițiile interne. • Generarea partițiilor unui număr ;
EXEMPLE DE IMPLEMENTARE
BACKTRACKING
1) Să se genereze toate permutările unei mulţimi de numere oarecare. Numerele se
memorează într-un vector. (Indicaţie. Se permută indicii elementelor din vectorul v, şi în
subprogramul tipar() se afişează elementele v[st[i]]).
2) Să se afişeze toate anagramele unui cuvânt citit de la tastatura.
3) Să se genereze toate matricele binare pătrate, de dimensiune n, care au un singur element
de 1 pe linie şi un singur element de 1 pe coloană. O matrice binară este o matrice ale
cărei elemente au valoarea 0 sau 1. Indicaţie. Soluţia are n elemente. Elementul soluţiei xk
reprezintă numărul coloanei de pe linia k pe care se găseşte elementul cu valoarea 1.
4) Să se genereze toate funcţiile bijective f:A➛B,unde card(A)=card(B)=n.
5) Să se genereze toate permutările mulţimii {1, 2, 3, ..., n} în care două numere vecine nu
trebuie să fie ambele pare sau ambele impare.
1) #include <iostream> int valid()
using namespace std; {for(int i=1;i<k;i++) while(as&&!ev)
typedef int stiva[100]; if(st[k]==st[i]) {as=succesor();
int n,k,as,ev; return 0; if (as)
int v[100]; return 1;} ev=valid();}
stiva st; int solutie() if(as)
void (init()) {return k==n;} if(solutie())
{ st[k]=0;} void tipar () tipar ();
int succesor() {for(int i=1;i<=n;i++) else{k++;
{ if(st[k]<n) cout<<v[st[i]]<<" "; init();}
{ st[k]+=1; cout<<endl;} else k--;}
return 1;} void bt() }
else return 0; } { k=1; int main()
init (); {cin>>n;
while (k>0) for(int j=1;j<=n;j++)
{as=1; ev=0; cin>>v[j];
bt();}
2) #include <iostream> if(st[k]==st[i]) as=succesor();
#include <cstring> return 0; if(as)
using namespace std; return 1;} ev=valid();}
typedef int stiva[100]; int solutie() if(as)
int n,k,as,ev; {return k==n;} if(solutie())
char s[256]; void tipar() tipar();
stiva st; {for(int i=1;i<=n;i++) else{k++;
void init() cout<<s[st[i] - 1]; init();}
{st[k]=0;} cout<<'\n';} else k--;}
int succesor() void bt() }
{if (st[k] < n) {k=1; int main()
{st[k]=st[k]+1; init(); {cin >> s;
return 1;} while(k>0) n = strlen(s);
else return 0;} {as=1;ev=0; bt();
int valid() while(as&&!ev){ return 0;}
{for(int i=1;i<k;i++)
3) #include <iostream> int valid() void bt()
using namespace std; {for(int i=1;i<k;i++) {k=1;
typedef int stiva[100]; if(st[k]==st[i]) init ();
int n,k,as,ev; return 0; while (k>0)
int v[100]; return 1;} {as=1; ev=0;
stiva st; int solutie() while(as&&!ev)
void init() {return k==n;} {as=succesor();
{st[k]=0;} void tipar () if (as)
int succesor() {for(int i=1;i<=n;i++){ ev=valid();}
{if(st[k]<n) for(int j = 1; j <= n; + if(as)
{st[k]+=1; +j) if(solutie())
return 1;} if(st[i] == j) cout << 1 tipar ();
else return 0;} << " "; else{k++;
else cout << 0 << " "; init();}
cout << '\n';} else k--;}}
cout<<endl;} int main()
{cin>>n;
bt();}
4) #include <iostream> int valid() void bt()
using namespace std; {for(int i=1;i<k;i++) {k=1;
typedef int stiva[100]; if(st[k]==st[i]) init ();
int n,k,as,ev; return 0; while (k>0)
int v[100]; return 1;} {as=1; ev=0;
stiva st; int solutie() while(as&&!ev)
void (init()) {return k==n;} {as=succesor();
{st[k]=0;} void tipar () if (as)
int succesor() {for(int i=1;i<=n;i++) ev=valid();}
{if(st[k]<n) if(as)
{st[k]+=1; {cout<<"("<<i<<","<<st[i]<<" if(solutie())
return 1;} ) ";} tipar ();
else return 0;} cout<<endl;} else{k++;
init();}
else k--;}}
int main()
{cin>>n;
bt();}
5) #include <iostream> int valid() void bt()
using namespace std; {if(k > 1 && (st[k - 1]%2==0 {k=1;
typedef int stiva[100]; && st[k]%2==0 )|| init();
int n,k,as,ev; (st[k-1]%2==1 && st[k] while(k>0)
stiva st; %2==1)) return 0; {as=1;ev=0;
void init() for(int i=1;i<k;i++) while(as&&!ev)
{st[k]=0;} if(st[k]==st[i]) {as=succesor();
int succesor() return 0; if(as)
{if (st[k] < n) return 1;} ev=valid();}
{st[k]=st[k]+1; int solutie() if(as)
return 1;} {return k==n;} if(solutie())
else return 0;} void tipar() tipar();
{for(int i=1;i<=n;i++) else{k++;
cout<<st[i]<<" "; init();}
cout<<'\n';} else k--;}}
int main()
{cin>>n;
bt();
return 0;}
TEHNICA DE PROGRAMARE “DIVIDE
ET IMPERA”
Divide et impera este o tehnică specială prin care se pot rezolva anumite probleme.
Constă în două mari etape:
• Divide: Problema dată este împărţită în două sau mai multe subprobleme de acelaşi tip, dar
de dimensiuni mai mici. Subproblemele se rezolvă direct, dacă dimensiunea lor permite
aceasta, sau, fiind de acelaşi tip, se rezolvă în mod recursiv, prin acelaşi procedeu.
• Impera: Se combină soluţiile subproblemelor pentru a obţine soluţia problemei iniţiale.
Tehnica Divide et Impera se poate aplica în rezolvarea unei probleme care îndeplineşte
următoarele condiţii :
- se poate descompune în subprobleme ;
- aceste subprobleme sunt independente una faţă de alta ;
- aceste subprobleme sunt similare cu problema iniţială;
- la rândul lor subproblemele se pot descompune în alte subprobleme mai simple;
Numărul problemelor care se rezolvă prin această metodă este relativ mic,
tocmai datorită restricţiilor de mai sus. Schema generală Tehnica Divide et
Impera admite o implementare recursivă, deoarece subproblemele sunt similare
problemei iniţiale, dar de dimensiuni mai mici; la un anumit nivel sunt doua
posibilităţi: s-a ajuns la o (sub)problemă simplă ce admite o rezolvare
imediată, caz în care se rezolvă (sub)problema şi se revine din apel ; s-a ajuns
la o (sub)problemă care nu admite o rezolvare imediată, caz în care o
descompunem în două sau mai multe subprobleme şi pentru fiecare din ele se
continuă apelurile recursive (ale funcţiei).
În etapa finală a tehnicii Divide et Impera se produce combinarea
subproblemelor (rezolvate deja) prin secvenţele de revenire din apelurile
recursive.
EXEMPLE DE IMPLEMENTARE
PROGRAMARE DIVIDE ET IMPERA
1) Calculaţi suma numerelor de la 1 la n, cu metoda DEI;
2) Calculaţi produsul numerelor pare dintre a şi b, cu metoda DEI.
3) Determinaţi numărul elementelor pare cu indici impari din vector , cu
metoda DEI.
1) #include <iostream> 2) #include <iostream> 3) #include <iostream>
using namespace std; using namespace std; using namespace std;
int suma(int li, int ls) int op(int li, int ls) int v[50];
{int s1, s2, s, m; {int t1, t2, f, m; int op(int li, int ls)
if(li==ls) if(li==ls) {int t1, t2, f, m;
s=li; if(li%2==0) if(li==ls)
else f=li; if((li%2==0)&&(v[li]%2==1))
{m=(li+ls)/2; else f=1; f=v[li];
s1=suma(li,m); else else f=0; else
s2=suma(m+1,ls); {m=(li+ls)/2; {m=(li+ls)/2;
s=s1+s2;} t1=op(li,m); t1=op(li,m);
return s;} t2=op(m+1,ls); t2=op(m+1,ls);
int main() f=t1*t2;} f=t1+t2;}
{int n; return f;} return f;}
cin>>n; int main() int main()
cout<<suma(1,n);} {int a,b; {int n;
cin>>a>>b; cin>>n;
cout<<op(a,b);} for(int i=1;i<=n;i++) cin>>v[i];
cout<<op(1, n);}
TEHNICA DE PROGRAMARE
“GREEDY”
Metoda de programare Greedy se aplică problemelor de optimizare. Aceasta metoda
constă în faptul că se construieşte solutia optimă pas cu pas, la fiecare pas fiind selectat
în solutie elementul care pare „cel mai bun/cel mai optim” la momentul respectiv, în
speranta că această alegere locală va conduce la optimul global. Algoritmii Greedy sunt
foarte eficienti, dar nu conduc în mod necesar la o solutie optimă. Şi nici nu este
posibilă formularea unui criteriu general conform căruia să putem stabili exact dacă
metoda Greedy rezolvă sau nu o anumită problemă de optimizare. Din acest motiv, orice
algoritm Greedy trebuie însotit de o demonstratie a corectitudinii sale . Demonstratia
faptului că o anumită problemă are proprietatea alegerii Greedy se face de obicei prin
inductie matematică. Metoda Greedy se aplică problemelor pentru care se dă o mulţime
A cu n elemente şi pentru care trebuie determinată o submulţime a sa, S cu m elemente,
care îndeplinesc anumite condiţii, numite si conditii de optim.
EXEMPLE DE IMPLEMENTARE
PROGRAMARE GREEDY
1) Pentru cadourile pe care Moş Crăciun urmează să le cumpere copiilor cuminţi, Consiliul Polului Nord
a alocat suma de S eureni. Ştiind că în comerţul polar se utilizează n+1 tipuri de bancnote de valori 1,
e1, e2, e3,…, en şi faptul că Moşul trebuie să primească un număr minim de bancnote pentru suma
aprobată, să se determine numărul de bancnote din fiecare tip utilizat în plata sumei şi numărul total
de bancnote care i s-au alocat. Fișierul de intrare eureni.in conține pe prima linie numerele S n e.
Fișierul de ieșire eureni.out va conține mai multe linii: pe fiecare linie va fi scrisă valoare unei
bancnote folosită în plata sumei S și numărul de bancnote folosite, separate printr-un spațiu, în
ordinea descrescătoare a valorilor bancnotelor folosite. Pe ultima linie se va scrie numai numărul total
de bancnote folosite.
2) La un birou care se ocupă cu analiza proiectelor de investiţii, n investitori au depus până la termenul
legal, câte un proiect. Cunoscând timpul necesar pentru analizarea fiecărui proiect, scrieţi un program
care determină ordinea în care vor fi analizate proiectele, astfel încât timpul mediu de aşteptare pentru
investitori să fie minim. Pe prima linie a fişierului proiecte.in se găseşte un număr natural n
reprezentând numărul de proiecte depuse. Pe linia a doua, separate prin câte un spaţiu, se găsesc n
numere naturale t1, t2, …, tn, reprezentând timpii necesari pentru analizarea fiecărui proiect. Pe
prima linie a fişierului proiecte.out se vor găsi n numere naturale cuprinse între 1 şi n, reprezentând
ordinea în care vor fi analizate proiectele.
1) #include <iostream> void rezolvare(){
#include <fstream> for(int i = n; i >= 0; --i){
#include <cmath> if(s >= bancnota[i]){
using namespace std; int nrBancnote = s /
ifstream f("eureni.in"); bancnota[i];
ofstream g("eureni.out"); g << bancnota[i] << " " <<
int s, n, e; nrBancnote << '\n';
int bancnota[100], nrTotal = 0; s -= nrBancnote *
void citeste() bancnota[i];
{ int i; nrTotal += nrBancnote;}}}
f >> s >> n >> e; int main()
cout << s << '\n' << n << '\n' << e; { citeste();
bancnota[0] = 1; rezolvare();
for (i = 1; i <= n; ++i) g << nrTotal;
{bancnota[i] = bancnota[i - 1] * e;} return 0;}
f.close();}
2) #include <iostream> void rezolvare(){
#include <fstream> for(int i = 1; i <= n - 1; ++i){
#include <cmath> for(int j = i; j <= n; ++j)
using namespace std; if(dosare[nr[i]] >
ifstream f("proiecte.in"); dosare[nr[j]]) swap(nr[i], nr[j]);}}
ofstream g("proiecte.out"); void afisare(){
int n; for(int i = 1; i <= n; ++i) g << nr[i] << "
int dosare[100], nr[100]; ";}
void citeste() int main()
{ int i; { citeste();
f >> n; rezolvare();
for (i = 1; i <= n; ++i) afisare();
{nr[i] = i; return 0;}
f >> dosare[i];}
f.close();}
TEHNICA DE PROGRAMARE
DINAMICA
Este o tehnica de programare care se bazeaza pe o formula de recurenta
si una sau mai multe situatii de inceput. Aceste situatii reprezinta niste
subsolutii ale problemei pe care o abordam care, la randul lor, sunt formate
din alte subsolutii proprii.
Spre deosebire de divide et impera, programarea dinamica este mai
eficienta deoarece aceasta desparte problemele in subprobleme pe care le
suprapune. Pentru a evita recalcularea portiunilor care se suprapun,
rezolvarea se face pornind de la cele mai mici subprobleme si folosindu-ne
de rezolvarea acestora calculam subproblema imediat mai mare.
EXEMPLE DE IMPLEMENTARE
PROGRAMARE DINAMICA
1) Se consideră un triunghi de numere care are pe prima linie un număr, pe a
doua linie două numere, pe a treia linie trei numere, triunghiul având în total N
linii (1 ≤ N ≤ 1000). Scrieţi un program care să calculeze cea mai mare dintre
sumele numerelor ce apar pe drumurile ce pleacă din vârf şi ajung la bază,
astfel încât în fiecare drum succesorul unui număr se află pe rândul de mai jos
dedesubt sau pe diagonală la dreapta. Datele de intrare se citesc din fişierul
triunghi.in, începând cu numărul de linii şi continuând cu triunghiul de
numere. Programul va afişa pe ecran valoarea cerută.
2) Se dă un şir de N (1<N<1000) numere naturale, mai mici sau egale cu 1000.
Să se împartă elementele din şir în două submulţimi disjuncte, astfel încât
sumele elementelor din cele două submulţimi să fie cât mai apropiate.
1) using namespace std; void Afisare()
#include<iostream> {int j;
#include <fstream> for(j=S;!Sum[j];j--);
int N,X[1000],Sum[1000000],S; cout<<j<<" "<<S-j;}
void Citire() int main()
{ifstream fin("sume.in"); {Citire();
fin>>N; Rezolvare();
for(int i=1;i<=N;i++) Afisare();
{fin>>X[i]; return 0;}
S=S+X[i];}}
void Rezolvare()
{int i,j,S2=S/2;
Sum[0]=1;
for(i=1;i<=N;i++)
for(j=S2-X[i];j>=0;j--)
if(Sum[j])
Sum[j+X[i]]=1;}
2) using namespace std; void Afisare()
#include <fstream> {cout<<DP[1][1]<<"\n";}
#include <iostream> int main()
#define NMax 1000 {Citire();
int N,DP[NMax][NMax],A[NMax][NMax]; Rezolvare();
void Citire() Afisare();
{ifstream fin("triunghi.in"); return 0;}
fin>>N;
for (int i=1;i<=N;i++)
for (int j=1;j<=i;j++)
fin>>A[i][j];}
void Rezolvare()
{int i,j;
for (i=1;i<=N;i++)
DP[N][i]=A[N][i];
for (i=N-1;i>=1;i--)
for (j=1;j<=i;j++)
DP[i][j]=A[i][j] + max(DP[i+1][j],DP[i+1][j+1]);}

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