Backtracking 回溯算法的基本框架
维基百科上的回溯法定义:Backtracking is a general algorithm for finding all (or some) solutions to some computational problems, notably constraint satisfaction problems, that incrementally builds candidates to the solutions, and abandons each partial candidate c (“backtracks”) as soon as it determines that c cannot possibly be completed to a valid solution.
- 回溯法通过枚举所有可能性来确保解答的正确
- 回溯法不会访问一种状态超过一次,从而保证了解法的效率
Implementation 回溯法的算法框架
~~~C++
bool finished = FALSE; /* found all solutions yet? */
backtrack(int a[], int k, data input) {
int c[MAXCANDIDATES]; /* candidates for next position */
int ncandidates; /* next position candidate count */
int i; /* counter */
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];
make_move(a, k, input);
backtrack(a, k, input);
unmake_move(a, k, input);
if (finished) return; /* terminate early */
}
}
}
~~~
- is_a_solution(a, k, input) 判断当前的部分解向量a[1…k]是否是一个符合条件的解
- construct_candidates(a, k, input, c, ncandidates) 根据目前状态,构造这一步可能的选择,存入c[]数组,其长度存入ncandidates
- process_solution(a, k, input) 对于符合条件的解进行处理,通常是输出、计数等
- make_move(a, k, input) and unmake_move(a, k, input) 前者将所采用的选择更新到原数据结构中,后者把这一选择从原数据结构中删除
参考博客:@五岳的全面解析回溯法:算法框架与问题求解
回溯法的两个经典应用及算法框架
Constructing All Subsets 列出所有子集
主要实现以下三个函数:
int is_a_solution(int a[], int k, int n) {
return (k == n);
}
void construct_candidates(int a[], int k, int n, int c[], int *ncandidates) {
c[0] = TRUE;
c[1] = FALSE;
*ncandidates = 2;
}
void process_solution(int a[], int k) {
int i;
printf("{");
for(i = 1; i <= k; i++)
if(a[i] == TRUE)
printf(" %d", i);
printf(" }\n");
}
调用backtrack:
generate_subsets(int n) {
int a[NMAX];
backtrack(a, 0, n);
}
Constructing All Permutations 全排列
主要实现以下三个函数:
construct_candidates(int a[], int k, int n, int c[], int *ncandidates) {
int i;
bool in_perm[NMAX]; /* Who is 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 += 1;
}
}
}
process_solution(int a[], int k) {
int i;
for (int i = 1; i <= k0; i++)
printf(" %d", a[i]);
printf("\n");
}
is_a_solution(int a[], int k, int n) {
return (k == n);
}
调用backtrack:
generate_permutations(int n) {
int a[NMAX];
backtrack(a, 0, n);
}
全排列的简化版回溯框架 (in Java):
public void backtrack(int[] num, int index) {
if (index == num.length) {
for (int j = 0; j < num.length; j++)
System.out.print(num[j]);
System.out.println();
}
for (int i = index; i < num.length; i++) {
swap(num, index, i);
backtrack(num, index + 1);
swap(num, index, i);
}
}
public void swap(int[] A, int i, int j) {
int temp = A[i];
A[i] = A[j];
A[j] = temp;
}