1.回溯法解题的通常步骤:
- 针对所给的问题,定义问题的解空间
- 确定易于搜索的解空间结构
- 以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索
2.基本的回溯策略【也可以说是编程框架】
a.递归回溯
参数t表示深度,这一搜索过程按深度优先方式进行,调用一次Backtrace(1)即可完成整个回溯搜索过程。
void Backtrace(int t) {
if (t > n)
//可行解
Output(x);
else
//遍历第t层的所有节点
//f(n,t)起始编号,g(n,t)终止编号
for (int i = f(n, t);i < g(n, t);i++) {
x[t] = h[i];
//满足约束条件和边界条件,遍历下一层
if (Constraint(t) && Bound(t))
Backtrace(t + 1);
}
}
b.迭代回溯
参数t表示深度
void IterativeBacktrace(void) {
int t = 1;
while (t > 0) {
if (f(n, t) <= g(n, t))
//遍历
for (int i = f(n, t);i <= g(n, t);i++) {
x[t] = h[i];
//剪枝
if (Constraint(t) && Bound(t)) {
//Solution(t)判断当前节点是否已经得到问题的可行解
if (Solution(t))
Output(x);
else
t++;
}
}
else
t--;
}
}
3.n皇后问题
问题描述
n*n棋盘上放n个皇后,皇后不能在同一行同一列或者同一斜线出现。
若两个皇后位置分别为(i,j)and (k,l),那么
i!=j,k!=l,i-j!=k-l,i+j!=k+l //约束条件
i,j,k,l<=n //边界条件
具体实现的代码如下:
#include <math.h>
#include <iostream>
class Queen {
//友元有权访问该类的所有成员
friend int nQueen(int);
private:
bool Place(int k);
void Backtrace1(int t);
void Backtrack2(void);
int n,//皇后个数
*x;//当前解
long sum;//当前已找到的可行方案数
};
bool Queen::Place(int k) {
for (int j = 1;j < k;j++)
if (abs(k - j) == abs(x[j] - x[k]) || x[j] == x[k])
return false;
return true;
}
void Queen::Backtrace1(int t) {
if (t > n)
sum++;
else {
for (int i = 1;i <= n;i++) {
x[t] = i;
if (Place(t))
Backtrace1(t + 1);
}
}
}
void Queen::Backtrack2(void) {
x[1] = 0;
int k = 1;
while (k > 0) {
x[k] += 1;
//剪枝
while ((x[k] <= n) && !(Place(k))) x[k] += 1;
if (x[k] <= n) {
if (k == n){
std::cout << "-----------------------------"<<std::endl;
for (int i = 1;i <= n;i++) {
for (int j = 1;j < x[i];j++)
std::cout << "*";
std::cout << "a" << std::endl;
}
sum++;
}
else {
k++;
x[k] = 0;
}
}
else k--;//不满足条件后的回溯
}
}
int nQueen(int n) {
Queen X;
//初始化X
X.n = n;
X.sum = 0;
int *p = new int[n + 1];
for (int i = 0;i <= n;i++)
p[i] = 0;
X.x = p;
X.Backtrace1(1);
//X.Backtrack2();
delete[]p;
return X.sum;
}
int main() {
int sum = nQueen(7);
std::cout << sum << std::endl;
system("pause");
return 0;
}
个人感觉回溯法就像是一个穷举过程,只不过在枚举过程中增加了一些提前剪枝的过程。递归法的实现更容易理解,但是迭代法可以培养思维,或许对解动态规划有点帮助。至于算法复杂度应该差不多吧,毕竟都是遍历了所有的可能解。
拓展一下(回溯法搜索排列树算法框架)
排列树:所给的问题是确定n个元素满足某种性质的排列时,相应的解空间称为排列树。
void Backtrack(int t) {
if (t > n)
Output(x);
else
for (int i =t;i <= n;i++) {
Swap(x[t],x[i]);
if (Constraint(t) && Bound(t))
Backtrace(t + 1);
Swap(x[t],x[i])
}
}
在调用Backtrack(1)执行回溯之前,先将变量数组x初始化为单位阵列(1,2,…,n)。