Backtracking

Download Report

Transcript 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=(a1,a2,…,an), ai 2 Si
• Gegeben Teillösung (a1,…,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
Problem: platziere n Königinnen auf n £ n
Schachfeld so, dass sie sich nicht
schlagen können.
Test aller möglichen Platzierungen:
(n2)!/(n2-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=%d\n",i,solution_count);
}
}