回溯法
一、回溯法算法框架
回溯法是一种枚举状态空间中所有可能状态的系统方法,它是一个一般性的算法框架,应用时须具体问题具体分析。
在回溯法的每一步,我们从一个给定的部分解a=(a_1, a_2, ... , a_k)开始,尝试着在最后添加元素来扩展这个部分解。扩展之后,我们必须测试他是否为一个完整的解——如果是的话,需要输出这个解、更新解的计数器或者其他任何你想做的事。如果仍不完整,我们必须检查这个部分是否有可能扩展成完整解。如果有可能,递归下去;如果没有可能,从a中删除新加入的最后一个元素,然后尝试该位置上的其他可能性。
下面是代码,使用了一个全局的finished标志来允许提前终止回溯过程。
bool finished = FALSE; // found all solutions yet?
void backtrack(int a[], int k, data input)
{
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;
}
}
}
is_a_solution用来测试向量a的前k个元素是否为一个完整的解,input参数用来给这个函数传递一些信息,例如目标解的长度。
construct_candidates根据a的前k-1个元素值,将第k个元素的所有候选值放到数组c中。候选值的个数为ncandidates。
process_solution可以用来输出解。
二、下面是两个用到上述框架的例子
1. 构造所有子集
1 #include <stdio.h> 2 #define NMAX 10 3 #define MAXCANDIDATES 10 4 5 int finished = 0; 6 void output_solution(int *a, int k) 7 { 8 int i; 9 printf("{"); 10 for(i=1; i<=k; ++i) { 11 if (a[i] == 1) 12 printf(" %d", i); 13 } 14 printf("}\n"); 15 } 16 void construct_candidates(int *a, int k,int *c, int *ncandidates) 17 { 18 c[0] = 1; 19 c[1] = 0; 20 *ncandidates = 2; 21 } 22 23 void backtrack(int *a, int k, int n) 24 { 25 int ncandidates; 26 int c[MAXCANDIDATES]; 27 int i; 28 29 if (k == n) { 30 output_solution(a,k); 31 } else { 32 k = k + 1; 33 construct_candidates(a,k,c,&ncandidates); 34 for(i=0; i<ncandidates; ++i) { 35 a[k] = c[i]; 36 backtrack(a,k,n); 37 if (finished) 38 return; 39 } 40 } 41 } 42 43 int main() 44 { 45 int a[NMAX]; 46 backtrack(a,0,4); 47 }
2. 八皇后
1 #include <stdio.h> 2 #define NMAX 20 3 #define N 8 4 int finished = 0; 5 int solution_count = 0; 6 void output_solution(int *a, int k) 7 { 8 int i; 9 for(i=1; i<=k; ++i) 10 printf("%d ",a[i]); 11 printf("\n"); 12 } 13 14 15 void construct_candidates(int *a, int k, int n, int *c, int *ncandidates) 16 { 17 //construct the kth candidate based on the previous k-1 candidates 18 int exist[NMAX]; 19 int i,j,ok; 20 for(i=1; i<=n; ++i) 21 exist[i] = 0; 22 for(i=1; i<k; ++i) 23 exist[a[i]] = 1; 24 25 *ncandidates = 0; 26 for(i=1; i<=n; ++i) { 27 ok = 1; 28 for(j=1; j<k; ++j) { 29 if (a[j]-j == i-k || a[j]+j == i+k ) { 30 ok = 0; 31 } 32 if (a[j] == i) 33 ok = 0; 34 } 35 if (ok) { 36 c[*ncandidates] = i; 37 *ncandidates = *ncandidates+1; 38 } 39 } 40 } 41 42 void backtrack(int *a, int k, int n) 43 { 44 int c[NMAX]; 45 int i; 46 int ncandidates; 47 48 if (k == n) { 49 output_solution(a,n); 50 solution_count++; 51 } else { 52 k = k + 1; 53 construct_candidates(a,k,n,c,&ncandidates); 54 for(i=0; i<ncandidates; ++i) { 55 a[k] = c[i]; 56 backtrack(a,k,n); 57 if (finished) 58 return; 59 } 60 } 61 } 62 63 void queen(int n) 64 { 65 int a[NMAX]; 66 backtrack(a,0,n); 67 printf("solution count = %d\n",solution_count); 68 } 69 int main() 70 { 71 queen(N); 72 return 0; 73 }
参考文献:<挑战编程程序设计竞赛训练手册 第八章>
浙公网安备 33010602011771号