Documente Academic
Documente Profesional
Documente Cultură
1. Descriereametodei
Această metodă generală de programare se aplică problemelor în care trebuie determinate toate
soluțiile problemei, soluţia se poate reprezenta sub forma unui vector X = (x1, ..., xn), iar x1∈A1, …
xn∈An unde mulţimile A1, ..., An sun tmulţimi finite având |Ai| = ai elemente. Pentru fiecare
problemă concretă sunt date anumite relaţii între componentel ex1, ...xn ale vectorului X, numite
condiţii interne.
Mulţimea finită A = A1 x A2 x... x An se numeşte spaţiul soluţiilo rposibile (este un produs cartezian).
Soluţiile posibile care satisfac condiţiile interne se numesc soluţii rezultat. Ceea ce ne propunem este
de a determina toate soluţiile rezultat, cu scopul de a le afişa sau de a allege dintre ele una care
maximizează sau minimizează o eventual funcţie obiectiv dată.
Pentru uşurarea înţelegerii metodei vom prezenta o rutină unică, aplicabilă oricărei probleme, rutină
care utilizează noţiunea de stivă.
- se alege primul element x1, c eaparţine lui A1;
- presupunând generate elementele x1…xk, vom căuta elemental xk+1. Vom avea 2 posibilităţi:
o nu s-a găsit un astfel de element, caz în care se reia căutarea, considerând generate
elementele x1 … xk-1, iar aceasta se reia de la următorul element din Ak, rămas
netestat.
o a fost găsit, caz în care se testează dacă acesta îndeplineşte anumite condiţii.
dacă le îndeplineşte, se testează dacă s-a ajuns la soluţie
dacă s-a ajuns la soluţie, se tipăreşt eşi se continuă algoritmul pentru
găsirea unei alte soluţii, considerându-se generate x1…xk, iar elemental
xk+1 se caută printer elementele mulţimii Ak+1 rămase netestate
dacă nu s-a ajuns la soluţie se reia algoritmul considerându-se generate
elementele x1, … xk, xk+1 și se caută elementul xk+2.
dacă nu le îndeplineşte se reia algoritmul considerând generate x1…xk, iar
elementul xk+1 se caută printre elementele mulţimii Ak+1 rămase netestate.
2. Implementareametodei
2.1. Variantaiterativă
- ev – pentru a ști dacă elementul găsit respectă condiția de continuitate și poate fi elementul xk al
soluției (este o variabilă logică ce are valoarea 1 -true, daca elemental este valid; altfel, are valoarea 0).
- n – dimensiunea soluției.
Obs. – în unele probleme (aranjamente, permutări, combinări etc) elementul xk este format dintr-o singură
valoare, iar în problemele în care trebuie găsit un traseu xk este format de regulă din două valori care
reprezintă coordonatele poziției în care se face deplasarea.
b) Subprograme
- subprogramul init() – de tip void. Inițializează elemental xk din vârful stivei. Acest subprogram
are rolul de a începe căutarea elementului xk de la începutul mulțimii Ak. În acest sens lui xk i se
atribuie o valoare a cărui successor va fi primul element din Ak.
- subprogramul valid() – de tip int. Verifică dacă valoarea de pe nivelul k îndeplineşte condiţiile
interne. În caz afirmativ se returnează valoarea 1, în caz contrar se returnează valoarea 0. Valoarea
returnată se atribuie variabilei ev.
- subprogramul solutie() – de tip int. Verifică dacă s-a ajuns sau nu la o soluţie finală. În caz
afirmativ se returnează valoarea 1, în caz contrar se returnează valoarea 0.
b) Subprograme
Se vor folosi aceleași subprograme ca și în cazul variantei iterative, doar tehnica backtracking
(subprogramul bkt()) va fi implementat recursiv. Forma general a acestuia va fi:
Primul apel al suprogramului are loc pentru primul nivel (de regula bkt(1)).
3. Probleme propuse
Un program citeşte o valoare naturală nenulă impară pentru n şi apoi generează şi afişează în ordine
crescătoare lexicografic toate combinaţiile formate din n cifre care îndeplinesc următoarele
proprietăţi:
- încep şi se termină cu 0;
- modulul diferenţei între oricare două cifre alăturate dintr-o combinaţie este 1.
Astfel, pentru n=5, combinaţiile afişate sunt, în ordine, următoarele: 01010, 01210. Dacă se rulează
acest program şi se citeşte pentru n valoarea 7, imediat după combinaţia 0101210 va fi afişată
combinaţia:
Se utilizează metoda backtracking pentru a genera toate cuvintele formate din două litere distincte
din muţimea {w,x,z,y} astfel încât niciun cuvânt să nu înceapă cu litera x şi niciun cuvânt să nu
conţină litera w lângă litera z. Cuvintele vor fi generate în ordinea wx, wy, zx, zy, yw, yx, yz.
Folosind aceeaşi metodă se generează toate cuvintele de două litere distincte din mulţimea
{w,x,z,y,t} astfel încât niciun cuvânt să nu înceapă cu litera x şi niciun cuvânt să nu conţină litera w
lângă litera z. Care sunt a treia şi a patra soluţie generată?
3.2. De încălzire
1. Generarea permutărilor
2. Generarea aranjamentelor
3. Generarea combinărilor
4. Generarea tuturor submulţimilor
5. Generarea tuturor partiților unui număr
6. Generarea tuturor partiţiilor unei mulţimi
7. Generarea produsului cartezian al n mulţimi
8. Problema celor n dame
4. Backtracking în plan
Metoda Backtracking în plan are câteva modificări:
- un element al soluției este format din mai multe valori (de regulă coordonatele în plan);
- trebuiesc codificate oarecum direcţiile de deplasare prin numere, litere, elemente, etc. (N, S, E, V).
Fie următoarea problemă: Într-un labirint, reprezentat ca o matrice L, cu n linii şi m coloane, cu
componente 0 sau 1, (1 semnificând perete, 0 culoar) se găseşte o bucată de brânză pe poziţia (xb,
yb) şi un şoricel pe poziţia (xs, ys). Afişaţi toate posibilităţile şoricelului de a ajunge la brânză, ştiind
că el se poate deplasa numai pe culoar, iar direcţiile posibile de mişcare sunt N, E, S, V. De exemplu,
pentru un labirint cu 4 linii şi 4 coloane de forma următoare, în care şoricelul se găseşte pe poziţia 1
1, iar brânza pe poziţia 4 4
0111
0111
0001
0000
Se va afișa:
(1,1), (2,1), (3,1), (3,2), (3,3), (4,3), (4,4) //prima solutie
(1,1), (2,1), (3,1), (3,2), (4,2), (4,3), (4,4) //a doua solutie
(1,1), (2,1), (3,1), (4,1), (4,2), (3,2), (3,3), (4,3), (4,4) //a treia solutie
(1,1), (2,1), (3,1), (4,1), (4,2), (4,3), (4,4) //a patra solutie
Rezolvare:
- pentru codificarea direcțiilor de deplasare vom folosi doi vectori dx și dy, asftel încât dx va
indica deplasarea pe axa x, dy deplasarea pe axa y, inițializați astfel: dx[] = {0,1,-
1,0,0},dy[] = {0,0,0,1,-1};(primul element (0,0) e de decor).
- Vom folosi a nouă variabilă tr care va reține poziția în care ne deplasăm. Poziția de pe nivelul
k va fi poziția de pe nivelul k-1 la care se adugă direcția de deplasare.
- Primul element al traseului va fi poziția în care se găsește șoricelul. Pentru ca aceasta să
rămână permanent în vector o vom trece pe poziția 0.
int n,m,a[10][10],ls,cs,lb,cb,nr,sol[10];
struct traseu {
int x,y;
}tr[10];
int dx[] = {0,1,-1,0,0},dy[] = {0,0,0,1,-1};
void citire(){
int i,j;
f>>n>>m;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++) f>>a[i][j];//citim labirintul
f>>ls>>cs>>lb>>cb;//citim poziția șoricelului și poziția finală
}
void border(){//marginea e perete
int i;
for(i=0;i<=n+1;i++) a[i][0] = a[i][m+1] = 1;
for(i=0;i<=m+1;i++) a[0][i] = a[n+1][i] = 1;
}
int solutie(int k) { //ajungem la solutie daca am ajuns la branza
if(tr[k].x== lb && tr[k].y == cb) return 1;
return 0;
}
void tipar(int k) {
int i;
g<<"("<<tr[0].x<<","<<tr[0].y<<")";//afisam punctual de plecare
for(i=1;i<=k;i++) g<<", ("<<tr[i].x<<","<<tr[i].y<<")";//afisam traseul
g<<"\n";
}
void init(int k){
sol[k] = 0;
}
int succesor(int k){
if(sol[k]<4) {
sol[k] = sol[k] + 1;//generam succesorul
tr[k].x = tr[k-1].x + dx[sol[k]];//calculam noua pozitie
tr[k].y = tr[k-1].y + dy[sol[k]];
return 1;
}
else
return 0;
}
int valid(int k){//verificam daca noua pozitie e valida
if(a[tr[k].x][tr[k].y]==1) return 0;//daca e perete nu e valida
int i;
//daca am mai fost in pozitia respectiva, nu e valida
for(i=1;i<k;i++) if(tr[i].x == tr[k].x && tr[i].y == tr[k].y) return 0;
return 1;
}
void bkt(int k){
init(k);
while(succesor(k)) {
if(valid(k)) {
if(solutie(k)) tipar(k);
else bkt(k+1);
}
}
}
int main()
{
citire();
border();
tr[0].x=ls;tr[0].y=cs;//pozitia de plecare
bkt(1);
return 0;
}
Aplicații
1. Fiind data o tabla de sah cu dimensiunea de nxn si un cal plasat in coltul din stanga sus, se
cere sa se afiseze o posibilitate de mutare a acestei piesede sah astfel incat sa treaca o
singura data prin fiecare patrat al tablei.
Alex are pe un raft n jucării. Pe fiecare jucărie se află scris unul dintre numerele 1, 2, ..., n. Nu există
două jucării pe care să fie scris acelaşi număr. După ce se joacă cu aceste jucării, Alex le pune din
nou pe raft, aranjate în şir, astfel încât să existe cel puţin k dintre ele în ordine crescătoare (nu
neapărat pe poziţii consecutive). Spre exemplu, pentru n=5 şi k=3 şirul de jucării 3 1 4 2 5 este
corect, dar 4 3 5 2 1 nu este corect. Primul şir are jucării are cel puţin trei jucării aşezate în ordine
crescătoare (de exemplu 1, 2, 5), iar cel de-al doilea nu are trei jucării în ordine crescătoare.
Cerinţă
Să se scrie un program care determină cea de a p-a modalitate de a aşeza jucăriile pe raft astfel încât
să respecte condiţia din enunţ, considerând modalităţile în ordine lexicografică.
Date de intrare
Fişierul de intrare jucarii.in are pe prima linie numerele naturale n k p separate prin câte un spaţiu.
Date de ieşire
Fişierul de ieşire jucarii.out va conţine o singură linie pe care vor fi scrise n numere naturale distincte
cuprinse între 1 şi n, separate prin câte un singur spaţiu, reprezentând cea de a p–a modalitate în
ordine lexicografică.
Restricţii
0 < k ≤ n < 11
0 < p ≤ maxim(numărul de modalităţi, 50050)
Un şir de numere x=(x1, x2, ..., xn) este mai mic în ordine lexicografică decât şirul y=(y1, y2, ...,
yn), dacă există un indice h, cu proprietăţile: x1=y1, …, xh-1=yh-1 şi xh<yh.
Exemple
jucarii.in jucarii.out Explicaţie
Cerinţă
Scrieţi un program care să determine efortul minim necesar pentru a urca pe o scară construită
conform restricţiilor problemei, precum şi o modalitate de a construi scara care va fi urcată cu efort
minim.
Date de intrare
Fişierul de intrare scara.in va conţine pe prima linie 4 numere naturale separate prin câte un spaţiu
H N M p (cu semnificaţia din enunţ).
Date de ieşire
Fişierul de ieşire scara.out va conţine
– pe prima linie va fi scris efortul minim necesar (cu 2 zecimale cu rotunjire);
– pe cea de a doua linie vor fi scrise N numere naturale nenule care reprezintă înălţimile celor N
trepte ale scării (în ordinea de la şosea către vilă), separate prin câte un spaţiu.
Restricţii şi precizări
0 < H 75
0 < N 8
0 < M < 14
0 p 10
Pentru datele de test, problema are întotdeauna soluţie.
Dacă există mai multe soluţii (modalităţi de a construi scara astfel încât să obţineţi efortul minim
dorit), veţi afişa prima soluţie în ordine lexicografică.
Spunem că vectorul x=(x1, x2, ..., xk) precedă în ordine lexicografică vectorul y=(y1, y2, ..., yk) dacă
există i1 astfel încât xj=yj, pentru orice j<i şi xi<yi.
Dacă a doua zecimală a efortului minim este 0, sau chiar ambele zecimale sunt 0 nu este necesar
să le afişaţi. Deci în exemplu s-ar fi putut scrie efortul minim 9 sau 9.0.
Exemplu
scara.in scara.out
10 4 5 2 9.00
1 4 2 3
Backtracking în plan:
1. Monkey - http://campion.edu.ro/arhiva/index.php?page=problem&action=view&id=713
2. Immortal OJI 2010 - http://campion.edu.ro/arhiva/index.php?page=problem&action=view&id=1078
Cei care au văzut filmul Nemuritorul, ştiu că fraza cu care nemuritorii încep lupta este "Nu poate să rămână
decât unul singur". Să încercăm să simulăm povestea nemuritorilor.
Cerinţă: Se cere să se determine o succesiune a luptelor ce pot fi purtate, astfel încât la final să rămână un
singur nemuritor.
Date de intrare: Fişierul de intrare immortal.in conţine pe prima linie trei valori naturale n m I, separate
prin câte un spaţiu, reprezentând numărul de linii, numărul de coloane ale zonei descrise şi respectiv
numărul de nemuritori existenţi iniţial. Următoarele I linii conţin fiecare câte două numere naturale x y
separate printr-un spaţiu, reprezentând poziţiile unde se găsesc iniţial cei I nemuritori (linia şi coloana).
Date de ieşire: Fişierul de intrare immortal.out va conţine I-1 linii, fiecare linie descriind o "luptă".
Luptele vor fi scrise în ordinea în care au avut loc. O linie va conţine 4 numere naturale care indică: primele
două poziţia de pe care pleacă un nemuritor la "luptă", ultimele două poziţia pe care acesta ajunge după
"luptă". Pentru ca "lupta" să fie corectă, în poziţia peste care nemuritorul "sare" trebuie să existe un
nemuritor care va "muri". O poziţie va fi specificată prin indicele de linie urmat de indicele de coloană.
Valorile scrise pe aceeaşi linie vor fi separate prin spaţii.
Restricţii
1 < n, m ≤ 20
1 < I ≤ min{15, n*m-1}
Pentru datele de test există întotdeauna soluţie.
Exemplu
immortal.in immortal.out
3 4 4 3 3 3 1 1 2 3 4
1 2 3 1 1 1 ================= (3,3) --> (3,1) dispare (3,2)
2 1 1 1 1 3 1| | * | | | (3,1) --> (1,1) dispare (2,1)
3 2 |---------------| (1,1) --> (1,3) dispare (1,2)
3 3 2| * | | | |
|---------------|
3| | * | * | |
=================