Examen de atestare profesionala pentru absolventii claselor de matematica- informatica
BACKTRACKING: COMBINATORICA
An scolar 2014-2015
Profesor indrumator: Elev: Joc Genia Patriche Giulio Cezar Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
1 Motivarea alegerii temei
Am ales aceasta tema datorita gamei largi de probleme ce pot fi rezolvate prin metoda backtracking, cu un caracter atat practic, cat si interactiv, necesitand atat rationament, cat si imaginatie.
2 Noiuni teoretice
Metoda Backtracking se folosete n rezolvarea problemelor care cer generarea tuturor soluiilor posibile. Ea necesit un timp de calcul substanial i din acest motiv, se recomand evitarea ori de cte ori este posibil. O soluie a problemei care se rezolv folosind aceast metod poate fi reprezentat sub forma unui vector st care funcioneaz pe principiul stivei. Fiecare component a vectorului va fi inclus ntr-un domeniu finit pe care este definit o relaie de ordine ntre elemente.
2.1 Observaii
Nu pentru toate problemele este cunoscut de la nceput numrul de elemente din soluie. Unul dintre obiectivele prioritare care trebuie s fie urmrite de orice algoritm backtracking este minimizarea timpului de calcul i din acest motiv se ncearca introducerea a ct mai multe optimizri. De exemplu, daca dorim sa generam toate permutarile unei multimi finite A, nu are rost sa generam produsul cartezian AA...A, pentru ca apoi sa testam pentru fiecare element al acestuia, daca este sau nu permutare (nu are rost sa generam 1,1,1...1, pentru ca sa constatam ca nu am obtinut o permutare, cand de la a doua cifra 1 ne puteam da sema ca cifrele nu sunt distincte). In cazul in care se cere o singura solutie, se poate forta oprirea, atunci cand a fost gasita. Problemele rezolvate prin aceasta metoda necesita un timp indelungat de rulare. Din acest motiv, este bine sa utilizam metoda numai atunci cand nu avem la dispozitie un alt algoritm, mai eficient.
Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
2.2 Permutri
Se d o mulime cu n elemente A={a1,a2,,an}. Se cere s se genereze si s se afieze toate permutrile ei. Altfel spus, se cere s se afieze toate modurile n care se pot amesteca elementele mulimii A. Folosim pentru generare mulimea {1,2,,n}. Condiiile care se pun sunt urmtoarele: Fiecare element din vectorul X se ia din {1,2,,n}; Un element Xk este valid dac el nu a fost plasat anterior n vectorul soluie X; Cnd am generat n elemente cu condiiile de mai sus, atunci avem o soluie.
Se pot identifica mai multe modaliti de a verifica dac elementul Xk a fost plasat deja n vectorul soluie. Cele mai importante dou sunt: parcurgerea elementelor deja generate pentru a verifica daca Xk apare sau nu ntre ele; folosirea unui vector cu n elemente n care vom avea valori 0 sau 1 corespunztoare elementelor mulimii iniiale. Valoarea 1 va preciza faptul c elementul de pe poziia corespunztoare a fost plasat anterior n vectorul soluie, iar valoarea 0 c nu. Corespunztor acestor dou moduri de a verifica dac un element a mai fost sau nu plasat n vectorul soluie, avem 2 moduri de generare a permutrilor.
2.3 Aranjamente
Generm aranjamentele unei mulimi atunci cnd ni se cer toate modurile de a alege m elemente distincte dintre cele n ale mulimii (m<n). Aceast problem se rezolv foarte uor folosind metodele de generarea permutrilor. Singurele modificri presupun citirea numrului m, modificarea condiiei de soluie, care va fi k=m n loc de k=n i a numrului de elemente afiate.
2.4 Combinari
Generarea combinrilor unei mulimi presupune o condiie suplimentar fa de permutri sau aranjamente. Acest lucru se datoreaz faptului c generarea combinrilor Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
presupune alegerea n ordine strict cresctoare a elementelor care compun vectorul soluie. Astfel, condiia de continuare, sau de validare a unui element este aceea c el trebuie s fie strict mai mare dect cel plasat anterior. n acest mod asigurm faptul c elementele nu se vor
repeta i c vor fi generate n ordine strict cresctoare. Trebuie, ns, s avem grij s nu punem aceast condiie si asupra primului element din vectorul soluie, deoarece el nu are cu cine s fie comparat.
O optimizare a algoritmului de generare a combinrilor se poate obine pornind instruciunea for pentru plasarea unui element de la valoare urmtoare valorii generate anterior. Trebuie s avem grij la prima poziie, care nu are element anterior. Am putea iniializa X0 cu 0. Astfel nu mai trebuie s verificm dac elementul Xk este mai mare ca Xk-1.
3 Probleme
3.1 Problema comis-voiajorului
Un comis-voiajor pleaca dintr-un oras, trebuie sa viziteze un numar de orase si sa se intoarca in orasul de unde a plecat cu efort minim. Orice oras i este legat printr-o sosea de orice alt oras j print-un drum de A[i ,j] kilometri. Se cere traseul pe care trebuie sa-l urmeze comis-voiajorul, astfel incat sa parcurga un numar minim de kilometri . Rezolvare: #include <iostream> using namespace std; int s[10],a[10][10],n,i,j,v,p,vs,vs1,mint,cost; main() {
cout<<"Numar noduri=";cin>>n; for(i=1;i<=n;i++) for(j=i+1;j<=n;j++) cout<<"A["<<i<<"]["<<j<<"]=",cin>>a[i][j],a[j][i]=a[i][j]; cout<<"Nod de pornire:";cin>>v; s[v]=1; vs1=v; cout<<"Drumul trece prin:"<<v<<' '; p=v; for(i=1;i<n;i++) { Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
Doi frati, Andrei si Bogdan doresc sa-si imparta mai multe cadouri (n cadouri). Fiecare cadou va fi dat fie lui Andrei fie lui Bogdan si nici unul dintre cadouri nu poate fi impartit. Fiecare cadou are o valoare intreaga pozitiva. Fie A si B valorile totale ale cadourilor primite de Andrei, respectiv Bogdan. Scopul este de a minimize valoarea absoluta a diferentei A-B. Scrieti un program care calculeaza valorile cadourilor lui Andrei si valorile cadourilor lui Bogdan.
Rezolvare:
#include<fstream.h> #include<conio.h> #include<math.h> int st[30],n,min=1000,v[30],a,s,b,k=1;
int valid(int k) { for(int i=1;i<k;i++) { if (st[i]==st[k]) return 0; if (st[i]>st[i+1]) return 0; } return 1; }
Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
void back(int k) { for (int i=1;i<=n;i++) { st[k]=i; if (valid(k)) { if (solutie(k)) tipar(); back(k+1); } }} main() { clrscr(); fstream f("cadouri.in",ios::in); f>>n; for(int i=1;i<=n;i++) { f>>v[i]; s=s+v[i]; } back(k); cout<<"suma valorilor cadourilor lui Andrei este "<<s-b<< " iar suma valorilor cadourilor lui Bogdan este "<<b; cout<<endl<<"diferenta minima este "<<abs(s-2*b); getch(); }
3.3 Problema parcul de distracie
Intr-un parc de distractii sunt n puncte de atractie, numerotate de la 1 la n. Se cunoaste costul fiecaruia din aceste puncte. Un copil venit sa se distreze are la el suma S. Sa se stabileasca toate posibilitatile pe care le are, de a alege punctele de distractie, corespunzator cu banii pe care-i are, fara sa treaca de mai multe ori prin acelasi loc, si vizitand cel putin doua obiective. Cate solutii gasiti ? Gasiti o modalitate prin care copilul sa beneficieze de cat mai multe din punctele de distractie, cu o suma cat mai mica.
Rezolvare: #include<fstream.h> #include<conio.h> int st[30],n,k=1,a[30],s,sol,max,min,p,b[30],l; int valid(int k) { for(int i=1;i<k;i++) { if (st[i]==st[k]) return 0; if (st[i]>st[i+1]) return 0; } return 1; } int solutie(int k) { p=0; for(int i=1;i<=k;i++) p=p+a[st[i]]; return (k>=2 && p<=s); }
void tipar(int k) { for(int i=1;i<=k;i++) cout<<st[i]<<" "; cout<<endl; if (k>max) Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
{ min=p; max=k; for(i=1;i<=k;i++) b[i]=st[i]; l=k; } else if ( k==max && p<min ) { min=p; for (i=1;i<=k;i++) b[i]=st[i]; l=k; } void back(int k) { for (int i=1;i<=n;i++) { st[k]=i; if (valid(k)) { if (solutie(k)) { tipar(k); sol++; } back(k+1); } }} main() { fstream f("parc.in",ios::in); f>>n>>s; min=s; for(int i=1;i<=n;i++) f>>a[i]; back(k); cout<<"sunt "<<sol<<" solutii"<<endl; cout<<"solutia de lungime maxima si cost minim este: "; for(i=1;i<=l;i++) cout<<b[i]<<" "; getch(); }
3.4 Problema cartoanelor colorate
Se dau n cartoane numerotate de la 1 la n, fiecare avand cate o culoare si cate un numar inscris pe ele (culorile se codifica prin numere naturale). Sa se formeze toate sirurile posibile de m cartoane, astfel incat orice doua cartoane alaturate sa nu aiba aceeasi culoare si sa nu fie doua cartoane alaturate in care unul din ele contine un numar egal cu dublul numarului inscris pe celalalt. Se vor afisa numerele de ordine ale cartoanelor. Cate solutii are problema ?
Rezolvare: #include<fstream.h> #include<conio.h> int st[30],n,k,s,m; struct {int c,nr;} a[30]; void init() { st[k]=0; } int succesor() { if (st[k]<n) { st[k]++; return 1;} return 0;} int valid() { for(int i=1;i<k;i++) Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
if (st[i]==st[k]) return 0; if (a[st[k-1]].c==a[st[k]].c) return 0; if (a[st[k]].nr==2*a[st[k-1]].nr || a[st[k-1]].nr==2*a[st[k]].nr) return 0; return 1;} int solutie() { return (k==m); } void tipar() { for(int i=1;i<=k;i++) cout<<"cartonul "<<st[i]<<" "; cout<<endl;}
void back() { int AS; k=1;init(); while(k>0) { do {} while((AS=succesor()) && !valid()); if (AS) if (solutie()) { tipar();s++; } else {k++; init();} else k--;}}
Sa se afiseze toate lanturile ce se pot construi cu n piese de domino, stiind ca fiecare piesa de domino are inscrise doua numere din multimea {1,2,3,4,5,6}, iar doua piese se pot plasa in lant pe pozitii consecutive daca si numai daca au macar un numar in comun. (Piesele se pot roti). Datele de intrare sunt luate din fisierul domino.txt, carte contine pe prima linie numarul de piese n, iar pe urmatoarele n linii, separate prin spatii, cele doua valori pentru fiecare piesa. Piesele sunt numerotate de la 1 la n. Cate solutii are problema ?
Rezolvare: #include<fstream.h> #include<conio.h> int st[30],n,k,s; struct {int i,j;} a[30]; void init() { st[k]=0; } int succesor() { if(st[k]<n) { st[k]++; Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
void back() { int AS; k=1;init(); while(k>0) { do {} while ((AS=succesor()) && !valid()); if (AS) if (solutie()) {tipar();s++;} else {k++; init();} else k--;}}
Un avion NATO care transporta ajutoare pe care urma sa le parasuteze refugiatilor de la granitele Afganistanului a suferit o defectiune neasteptata in timpul zborului. Pentru a nu se prabusi, trebuie sa renunte la o parte din incarcatura de la bord. Se stie ca greutatea maxima pe care o poate transporta in continuare este G. Se cunoaste greutatea fiecarui pachet in parte, numarul total al pachetelor fiind n. De asemenea, numarul minim de pachete (pentru a putea contribui la supravietuirea populatiei din zona) este a, iar numarul maxim este b. Care sunt posibilitatile existente ?
Rezolvare: #include<fstream.h> #include<conio.h> int st[30],n,k=1,g[30],a,b,max;
Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
int valid(int k) { for(int i=1;i<k;i++) { if (st[i]==st[k]) return 0; if (st[i]>st[i+1]) return 0; } return 1;}
int solutie(int k) { int s=0; for(int i=1;i<=k;i++) s=s+g[st[i]]; return (a<=k && k<=b && s<=max);}
void back(int k) { for (int i=1;i<=n;i++) { st[k]=i; if ( valid(k) ) { if (solutie(k)) tipar(k); back(k+1);} }} main() { fstream f("nato.in",ios::in); f>>a>>b>>max>>n; for(int i=1;i<=n;i++) f>>g[i]; back(k); getch();}
3.7 Problema spaiul Schengen
Un turist doreste sa viziteze cateva orase din spatiul Schengen. Se cunosc pentru fiecare oras numarul de obiective turistice existente. Turistul doreste : -Sa viziteze cel putin k1 si ce mult k2 orase ; -Sa treaca o singura data prin fiecare oras ; -Numarul obiectivelor vizitate in prima jumatate a turneului sau european sa fie mai mare decat numarul obiectivelor vizitate in a doua jumatate ; -Sa viziteze cel putin p1 obiective turistice. Rezolvare: #include<fstream.h> #include<conio.h> int st[30],n,k=1,a[30],k1,k2,p; Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
int valid(int k) { for(int i=1;i<k;i++) { if (st[i]==st[k]) return 0; } return 1;}
int solutie(int k) { int s1=0,s2=0; for (int i=1;i<=k;i++) if (i<=k/2+1 && k%2==1 || i<=k/2 && k%2==0) s1=s1+a[st[i]]; else s2=s2+a[st[i]]; return (k1<=k && k<=k2 && s1+s2>=p && s1>s2); } void tipar(int k) { for(int i=1;i<=k;i++) cout<<"orasul "<<st[i]<<" "; cout<<endl;}
void back(int k) { for (int i=1;i<=n;i++) {st[k]=i; if (valid(k)) { if (solutie(k)) tipar(k); back(k+1);} }} main() {clrscr(); fstream f("schengen.in",ios::in); f>>n>>k1>>k2>>p; for(int i=1;i<=n;i++) f>>a[i]; back(k); getch(); }
3.8 Problema bebeluului
Planul unui apartament este reprezentat sub forma unei matrice dreptunghice cu m linii si n coloane. Peretii apartamentului sunt reprezentati prin valoarea 1, usile sunt reprezentate prin valoarea 2 daca sunt inchise sau 3 daca sunt deschise. Intr-o pozitie din apartament se afla un bebelus care stie sa mearga in patru labe, doar ortogonal, si care vrea sa exploreze apartamentul. Afisati planul apartamentului, in care marcati toate pozitiile in care poate ajunge bebelusul stiind ca el este mult prea mic pentru a putea deschide usile din apartament. Rezolvare: #include<fstream.h> #include<conio.h> struct drum{int x,y;}; int m,n,k,a[20][20]; drum D[400]; int x,y,xf,yf; Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
Un soldat trebuie sa parcurga un teren minat pentru a ajunge in propriile linii. Sa se determine cel mai scurt drum prin care soldatul ajunge nevatamat la camarazii sai. Se dau: pozitia initiala a soldatului, pozitiile minelor si se stie ca propriile linii se afla in afara terenului minat, de orice parte a sa. Deplasarea soldatului se poate face doar ortogonal. Daca terenul minat este codificat prin matricea unde cu 1 am notat o zona de teren minat, iar pozitia initiala a soldatului este (2,2) atunci o solutie este (2,2) (3,2) (3,1) Rezolvare: #include<fstream.h> #include<conio.h> struct drum{int x,y;}; int m,n,k,a[20][20]; Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
drum D[400],D1[400]; int min=400;
void tipar(int k) { if(k<min) { min=k; for(int i=1;i<=k;i++) D1[i]=D[i]; } } void iesire(int x,int y) { if(a[x][y]==0 && (x==m || y==n || x==1 || y==1)) { k++; a[x][y]=2; //marchez ca am trecut pe acolo D[k].x=x;D[k].y=y; //memorez drumul tipar(k); k--; a[x][y]=0; } else if(a[x][y]==0) { k++; a[x][y]=2; //marchez ca am trecut pe acolo D[k].x=x;D[k].y=y; //memorez drumul if (x>1) iesire(x-1,y); //in sus if (y>1) iesire(x,y-1); //la stg if (x<m) iesire(x+1,y); //in jos if (y<n) iesire(x,y+1); //la dr k--; a[x][y]=0; //eliberez camera pt alta solutie } }
Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
3.10 Problema Scufia Roie
Scufita Rosie pleaca la bunica sa prin padure. Padurea este de forma dreptunghiulara de dimensiune m*n. Casa Scufitei Rosii se afla in patratelul de coordonate (1,1) iar casa bunicii in patratelul de coordonate (m,n). Fiecare patratel al padurii contine o floare, un copac sau iarba. In patratelul de coordonate (x,y) al padurii se afla lupul. Dorinta Scufitei Rosii este sa ajunga la bunica sa astfel incat sa culeaga cat mai multe flori in drumul sau, flori care sa-i aduca o bucurie in plus bunicii sale. Bineinteles Scufita trebuie sa evite sa treaca prin apropierea lupului (sa nu treaca prin nici una din casutele vecine pe linie, coloana sau diagonala cu casuta lupului). Deplasarea Scufitei se poate face in oricare din directiile N,S,E,V cu cate un patratel la fiecare pas, fara a putea trece prin patratelele care contin copaci.
Rezolvare: //0-flori 1-copaci 2-iarba #include<fstream.h> #include<conio.h> struct drum{int x,y;}; int m,n,k,a[20][20],s; drum D[400],D1[400]; int max,l;
void iesire(int x,int y) { if (x==m && y==n) { k++; D[k].x=x;D[k].y=y; //memorez drumul tipar(k); k--; } else if(a[x][y]==0) { k++; s++; a[x][y]=3; //marchez ca am trecut pe acolo D[k].x=x;D[k].y=y; //memorez drumul if (x>1) iesire(x-1,y); //in sus if (y>1) iesire(x,y-1); //la stg if (x<m) iesire(x+1,y); //in jos if (y<n) iesire(x,y+1); //la dr k--; Ministerul eduatiei si cercetarii Colegiul National Alexandru Ioan Cuza Galati
s--; a[x][y]=0; //eliberez camera pt alta solutie } else if(a[x][y]==2) { k++; a[x][y]=3; D[k].x=x;D[k].y=y; if (x>1) iesire(x-1,y); //in sus if (y>1) iesire(x,y-1); //la stg if (x<m) iesire(x+1,y); //in jos if (y<n) iesire(x,y+1); //la dr k--; a[x][y]=2; } }