Note de curs, probleme algoritmică, 18 octombrie 2013
From Algopedia
Jump to navigationJump to search
În acest curs am discutat despre calcularea ciclului hamiltonian de cost minim într-un graf prin backtracking, optimizat ulterior prin memoizare.
Un evaluator on-line pentru această problemă poate fi găsit la adresa http://www.infoarena.ro/problema/hamilton.
Versiunea 1
Să se rezolve problema prin backtracking recursiv, cu variabile globale.
Complexitate timp: O((N - 1)! * N)
Complexitate spațiu: O(N * N)
#include <stdio.h>
const int NMAX = 20;
int N, M;
int costMuchie[NMAX][NMAX];
bool exista[NMAX];
int permutare[NMAX];
int SOL;
void ciclu(int pozitie, int costPartial) {
if (pozitie == N + 1) { // s-a generat o permutare
if (costMuchie[permutare[N]][1] != 0) { // exista muchia care inchide ciclul
costPartial += costMuchie[permutare[N]][1]; // costul muchiei se adauga
if (SOL > costPartial) {
SOL = costPartial;
}
}
} else {
int i;
for (i = 2; i <= N; ++i) {
if (costMuchie[permutare[pozitie - 1]][i] != 0) {// exista muchie
if (!exista[i]) {
exista[i] = true;
permutare[pozitie] = i;
ciclu(pozitie + 1, costPartial + costMuchie[permutare[pozitie - 1]][i]);
exista[i] = false;
}
}
}
}
}
int main(void) {
int i;
int a, b, c;
// citirea datelor
scanf("%d %d", &N, &M);
for (i = 0; i < M; ++i) {
scanf("%d %d %d", &a, &b, &c);
++a;
++b;
costMuchie[a][b] = c;
}
// calcularea solutiei
SOL = 0x7fffffff; // "+infinit" pe int
permutare[1] = 1;
exista[1] = true;
ciclu(2, 0);
// afisarea solutiei
printf("%d\n", SOL);
return 0;
}
Versiunea 2
Să se rescrie rezolvarea anterioară astfel încât:
- variabila globală bool exista[NMAX] să fie înlocuită cu un parametru întreg, folosit pe biți (int exista);
- variabila globală int permutare[NMAX] să fie înlocuită cu un parametru întreg, reprezentând doar ultimul nod vizitat (int nodAnterior);
- variabila globală int SOL să fie eliminată prin returnarea soluției de către funcția recursivă (int ciclu).
Complexitate timp: O((N - 1)! * N)
Complexitate spațiu: O(N * N)
#include <stdio.h>
const int NMAX = 20;
int N, M;
int costMuchie[NMAX][NMAX];
int ciclu(int pozitie, int costPartial, int nodAnterior, int exista) {
if (pozitie == N + 1) { // s-a generat o permutare
if (costMuchie[nodAnterior][1] != 0) { // exista muchia care inchide ciclul
costPartial += costMuchie[nodAnterior][1]; // costul muchiei se adauga
return costPartial;
}
return 0x7fffffff; // "+infinit" ~= ciclu invalid
} else {
int SOL = 0x7fffffff;
int solutiePotentiala;
int i;
for (i = 2; i <= N; ++i) {
if (costMuchie[nodAnterior][i] != 0) {// exista muchie
if ((exista & (1 << i)) == 0) {
solutiePotentiala = ciclu(pozitie + 1, costPartial + costMuchie[nodAnterior][i], i, exista ^ (1 << i));
if (SOL > solutiePotentiala) {
SOL = solutiePotentiala;
}
}
}
}
return SOL;
}
}
int main(void) {
int i;
int a, b, c;
// citirea datelor
scanf("%d %d", &N, &M);
for (i = 0; i < M; ++i) {
scanf("%d %d %d", &a, &b, &c);
++a;
++b;
costMuchie[a][b] = c;
}
// calcularea solutiei
int SOL = ciclu(2, 0, 1, 1 << 1);
// afisarea solutiei
printf("%d\n", SOL);
return 0;
}
Versiunea 3
Să se rescrie rezolvarea anterioară astfel încât:
- să se elimine parametrii int pozitie și int costPartial, fiind redundanți;
- să se optimizeze execuția algoritmului, prin memoizarea funcției recursive.
Complexitate timp: O(N * 2N * N)
Complexitate spațiu: O(N * 2N)
#include <stdio.h>
const int NMAX = 20;
const int NMAXPOW2 = 1 << NMAX;
int N, M;
int costMuchie[NMAX][NMAX];
int Ciclu[NMAX][NMAXPOW2];
int ciclu(int nodAnterior, int exista) {
if (Ciclu[nodAnterior][exista] == 0) {
int SOL;
if (exista == ((1 << N) - 1) << 1) { // s-a generat o permutare
if (costMuchie[nodAnterior][1] != 0) { // exista muchia care inchide ciclul
SOL = costMuchie[nodAnterior][1]; // costul muchiei se adauga
} else {
SOL = 0x3fffffff; // "+infinit"/2 ~= ciclu invalid
}
} else {
SOL = 0x3fffffff;
int solutiePotentiala;
int i;
for (i = 2; i <= N; ++i) {
if (costMuchie[nodAnterior][i] != 0) {// exista muchie
if ((exista & (1 << i)) == 0) {
solutiePotentiala = costMuchie[nodAnterior][i] + ciclu(i, exista ^ (1 << i));
if (SOL > solutiePotentiala) {
SOL = solutiePotentiala;
}
}
}
}
}
Ciclu[nodAnterior][exista] = SOL;
}
return Ciclu[nodAnterior][exista];
}
int main(void) {
int i;
int a, b, c;
// citirea datelor
scanf("%d %d", &N, &M);
for (i = 0; i < M; ++i) {
scanf("%d %d %d", &a, &b, &c);
++a;
++b;
costMuchie[a][b] = c;
}
// calcularea solutiei
int SOL = ciclu(1, 1 << 1);
// afisarea solutiei
printf("%d\n", SOL);
return 0;
}