回溯法是个好东西, 当自己对一个问题没有任何思路的时候就可以用回溯法, 虽然效率是一个严重的问题, 但是却能给问题一个形象的解释, 或者可以从回溯法想到一个不错的算法也不一定
当遇到一个可以用到回溯法的时候需要按照如下步骤进行:
1. 确定问题的一个解空间树, 这个解空间树至少包含一个你需要的那个解, 否则这个树就完全没有意义了
2. 组织好这棵树, 弄明白这棵树的每一个节点代表什么, 每一个分支代表什么
3. 从这棵树的根节点不断的向下深搜, 当遇到不合适的节点的时候直接跳过以这个节点为根的子树
4. 当搜索到了叶子节点的时候就回溯
5. 不断的重复这个3, 4步骤
附加: 根据具体的问题可以定义限界条件, 最优值条件, 根据这两个条件可以剪枝了
事例一: 写出一个数1 - n 的全排列(排列数)
这棵树就是一颗排列树, 第 i 层就是第 i 个数排列数的可能性
第 i 层的每一个节点就是第 i 个排列数的 可能性
分支就是下一个排列数
得到的解:X = [x1, x2, x3, x4, .... xn] 就是解序列,X2 代表 第2 个排列数是x2; 这其中的X可能就有我们想要的X, 那么这个X序列就是解了
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const int MaxE = 1003, MaxV = 1003;
int n;
bool vis[MaxV]; // 纪录点在本次排列中被使用过
int tmp[MaxV]; //存中见结果
void backtrack(int cur) //cur表示当前层, 就是第cur个排列数的标号
{
if(cur > n) //如果层数到了叶子节点
{
for(int i = 1; i <= n; i++)
cout << tmp[i] << " ";
cout << endl;
return ;
}
for(int i = 1; i <= n; i++) //找当前层合适的数作为第cur个排列数
{
if(vis[i] == false)
{
vis[i] = true; //标记 i 被使用过了
tmp[cur] = i; //纪录当前层的排列数
backtrack(cur + 1); //找