ACM ICPC Praktikum Kapitel 8 Backtracking bersicht Backtracking
ACM ICPC Praktikum Kapitel 8: Backtracking
Übersicht • • Backtracking Aufzählung aller Teilmengen Aufzählung aller Permutationen n-Königinnen-Problem
Backtracking Grundsätzlicher Ansatz: • Angenommen, Lösung sei gegeben als Vektor a=(a 1, a 2, …, an), ai 2 Si • Gegeben Teillösung (a 1, …, ak), erweitere sie durch weiteres Element. • Lösung vollständig: werte sie aus, sonst führe weiteres Backtracking über Elemente ak+1 2 Sk+1 durch
Backtracking 1. bool finished = FALSE; 2. 3. 4. 5. 6. backtrack(int a[], int k, data input) { int c[MAXCANDIDATES]; /* candidates for next position */ int ncandidates; /* next position candidate count */ int i; /* counter */ 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. } /* found all solutions yet? */ if (is_a_solution(a, k, input)) process_solution(a, k, input); else { k = k+1; construct_candidates(a, k, input, c, &ncandidates); for (i=0; i<ncandidates; i++) { a[k] = c[i]; backtrack(a, k, input); if (finished) return; /* terminate early */ } }
Backtracking Anwendungsspezifische Teile: • is_a_solution(a, k, input): testet auf vollständige Lösung • process_solution(a, k): wertet Lösung aus • construct_candidates(a, k, input, c, ncandidat es): füllt Feld c mit allen möglichen Kandidaten für ak
Aufzählung aller Teilmengen Sk = {TRUE, FALSE} gibt an, ob ak in der Teilmenge ist. is_a_solution(int a[], int k, int n) { return (k == n); } construct_candidates(int a[], int k, int n, int c[], int *ncandidates) { c[0] = TRUE; c[1] = FALSE; *ncandidates = 2; }
Aufzählung aller Teilmengen process_solution(int a[], int k) { gib Permutation aus } generate subsets(int n) { int a[NMAX]; } backtrack(a, 0, n);
Aufzählung aller Permutationen Sk = {1, …, n} process_solution(int a[], int k) { int i; /* counter */ for (i=1; i<=k; i++) printf(" %d", a[i]); printf("n"); } is_a_solution(int a[], int k, int n) { return (k == n); }
Aufzählung aller Permutationen construct_candidates(int a[], int k, int n, int c[], int *ncandidates) { int i; /* counter */ bool in_perm[NMAX]; /* what is now in the permutation? */ for (i=1; i<NMAX; i++) in_perm[i] = FALSE; for (i=0; i<k; i++) in_perm[ a[i] ] = TRUE; *ncandidates = 0; for (i=1; i<=n; i++) if (in_perm[i] == FALSE) { c[ *ncandidates] = i; *ncandidates = *ncandidates + 1; } } Aufruf: backtrack(a, 0, 3);
n-Königinnen-Problem: platziere n Königinnen auf n £ n Schachfeld so, dass sie sich nicht schlagen können. Test aller möglichen Platzierungen: (n 2)!/(n 2 -n)! n!), was schnell zur gigantischen Zahl wird
n-Königinnen-Problem Beschränkungen des Suchraums: • Jede Zeile muss genau eine Königin enthalten • Jede Spalte muss genau eine Königin enthalten • Reihenfolge der Königinnen spielt keine Rolle (zählt als eine Möglichkeit) • Also kann jede legale Positionierung der Königinnen als Permutation angesehen werden. • Ist Permutation legal, dann auch jeder Shift und die inverse Permutation
n-Königinnen-Problem int solution_count; /* how many solutions are there? */ process_solution(int a[], int k) { int i; /* counter */ solution_count ++; } is_a_solution(int a[], int k, int n) { return (k == n); }
n-Königinnen-Problem construct_candidates(int a[], int k, int n, int c[], int *ncandidates) { int i, j; /* counters */ bool legal_move; /* might the move be legal? */ *ncandidates = 0; for (i=1; i<=n; i++) { legal_move = TRUE; for (j=1; j<k; j++) { if (abs((k)-j) == abs(i-a[j])) /* diagonal threat */ legal_move = FALSE; if (i == a[j]) /* column threat */ legal_move = FALSE; } if (legal_move == TRUE) { c[*ncandidates] = i; *ncandidates = *ncandidates + 1; } } }
n-Königinnen-Problem main() { int a[NMAX]; int i; /* solution vector */ /* counter */ for (i=1; i<=10; i++) { solution_count = 0; backtrack(a, 0, i); printf("n=%d solution_count=%dn", i, solution_count); } }
- Slides: 14