Tags: backTracking(回溯法)
http://blog.csdn.net/jarvischu/article/details/16067319
http://www.cnblogs.com/chinazhangjie/archive/2010/10/22/1858410.html
http://blog.csdn.net/chinajane163/article/details/48969353
具有限界函数的深度优先生成法称为回溯法。(回溯法 = 穷举 + 剪枝)
回溯法按深度优先策略搜索问题的解空间树。首先从根节点出发搜索解空间树,当算法搜索至解空间树的某一节点时,先利用剪枝函数判断该节点是否可行(即能得到问题的解)。如果不可行,则跳过对该节点为根的子树的搜索,逐层向其祖先节点回溯;否则,进入该子树,继续按深度优先策略搜索。
回溯法的基本行为是搜索,搜索过程使用剪枝函数来为了避免无效的搜索。剪枝函数包括两类:1. 使用约束函数,剪去不满足约束条件的路径;2.使用限界函数,剪去不能得到最优解的路径。
问题的关键在于如何定义问题的解空间,转化成树(即解空间树)。解空间树分为两种:子集树和排列树。两种在算法结构和思路上大体相同。
当问题是要求满足某种性质(约束条件)的所有解或最优解时,往往使用回溯法。
它有“通用解题法”之美誉。
二. 回溯法实现 – 递归和递推(迭代)
回溯法的实现方法有两种:递归和递推(也称迭代)。一般来说,一个问题两种方法都可以实现,只是在算法效率和设计复杂度上有区别。
【类比于图深度遍历的递归实现和非递归(递推)实现】
1. 递归
思路简单,设计容易,但效率低,其设计范式如下:
[cpp]
view plain
copy
- //针对N叉树的递归回溯方法
- void backtrack (int t)
- {
- if (t>n) output(x); //叶子节点,输出结果,x是可行解
- else
- for i = 1 to k//当前节点的所有子节点
- {
- x[t]=value(i); //每个子节点的值赋值给x
- //满足约束条件和限界条件
- if (constraint(t)&&bound(t))
- backtrack(t+1); //递归下一层
- }
- }
2. 递推
算法设计相对复杂,但效率高。
[cpp]
view plain
copy
- //针对N叉树的迭代回溯方法
- void iterativeBacktrack ()
- {
- int t=1;
- while (t>0) {
- if(ExistSubNode(t)) //当前节点的存在子节点
- {
- for i = 1 to k //遍历当前节点的所有子节点
- {
- x[t]=value(i);//每个子节点的值赋值给x
- if (constraint(t)&&bound(t))//满足约束条件和限界条件
- {
- //solution表示在节点t处得到了一个解
- if (solution(t)) output(x);//得到问题的一个可行解,输出
- else t++;//没有得到解,继续向下搜索
- }
- }
- }
- else //不存在子节点,返回上一层
- {
- t–;
- }
- }
- }
三. 子集树和排列树
1. 子集树
所给的问题是从n个元素的集合S中找出满足某种性质的子集时,相应的解空间成为子集树。
如0-1背包问题,从所给重量、价值不同的物品中挑选几个物品放入背包,使得在满足背包不超重的情况下,背包内物品价值最大。它的解空间就是一个典型的子集树。
回溯法搜索子集树的算法范式如下:
[cpp]
view plain
copy
- void backtrack (int t)
- {
- if (t>n) output(x);
- else
- for (int i=0;i<=1;i++) {
- x[t]=i;
- if (constraint(t)&&bound(t)) backtrack(t+1);
- }
- }<span style=“font-family:SimHei;”>
- </span>
2. 排列树
所给的问题是确定n个元素满足某种性质的排列时,相应的解空间就是排列树。
如旅行售货员问题,一个售货员把几个城市旅行一遍,要求走的路程最小。它的解就是几个城市的排列,解空间就是排列树。
回溯法搜索排列树的算法范式如下:
[cpp]
view plain
copy
- <span style=“font-size:18px;”>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)) backtrack(t+1);
- swap(x[t], x[i]);
- }
- } </span><span style=“font-family:SimHei;font-size:24px;”>
- </span>
四. 经典问题
(1)装载问题
(2)0-1背包问题
(3)旅行售货员问题
(4)八皇后问题
(5)迷宫问题
(6)图的m着色问题
本题的代码:
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> preCom;
int size = candidates.size();
backTracking(candidates, target, 0, preCom, res, size, 0);
return res;
}
void backTracking(vector<int>& candidates, int target, int preSum, vector<int>& preCom, vector<vector<int>>& res, int& size, int ind){
for (int i = ind; i < size; ++i){
int tmp = preSum + candidates[i];
if (tmp <= target){
preCom.push_back(candidates[i]);
if (tmp == target)
res.push_back(preCom);
if (tmp < target)
backTracking(candidates, target, tmp, preCom, res, size, i);
preCom.pop_back();
}
}
}
};