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;
}