一、 回溯算法是一种组织搜索的一般技术,有“通用的解题法”之称,它可以系统地搜索一个问题的所有解或任意解。
1、应用回溯算法时,需要明确定义问题的解空间,问题的解空间应至少包含问题的一个最优解。
/2、定义了问题的解空间后,还应将解空间很好的组织起来,使得回溯法能方便的搜索解空间,通常组织成数或图的形式。
/3、从树根到叶子节点的任一路径表示解空间的一个元素。
二、回溯算法的基本思想:
在确定了解空间的基本结构后,回溯从开始结点出发,以深度优先的方式搜索整个解空间。这个开始结点成为一个活结点,同时成为当前的扩展结点。通过当前的扩展节点,搜索向深度方向进入一个新的结点。这个新的节点成为一个新的活结点,并成为当前的扩展结点。若在当前扩展结点处不能再向深度方向移动,则当前扩展结点成为一个死结点。此时回溯到最近的一个活节点处,并使当前的的活节点成为扩展结点。回溯算法以这样的方法递归搜索整个空间树,直至满足终止条件。
三、算法基本结构与相关问题:
1、递归回溯:
void Backtrack(int t)
{
if (t > n)
{
output(x);
}
else
{
for (int i = f(n,t); i < g(n,t); ++i)
{
x[t] = h(i);
if (Constraint(t)&&Bound(t))
Backtrack(t+1);
}
}
}
2、迭代回溯
如果采用树的非递归深度优先搜索遍历算法;也可以将回溯法表示为一个非递归的迭代过程。如下:
void iterativeBacktrack(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))
{
if (Solution(x)) output(x);
else ++t;
}// end if
else --t;
}// end for
}// end while
}// end function iterativeBacktrack
1、0-1背包问题。
2、旅行商问题。
子集数与排列数
3、装载问题。
4、图的m着色问题。
5、n皇后问题。
6、流水作业调度问题。
7、子集和问题。
四、题目:
问题:给出一组候选数字(C)和目标数字(T),找出C中所有的组合,使组合中数字的和为T。C中每个数字在每个组合中只能使用一次。
样例
给出一个例子,候选数字集合为[10,1,6,7,2,1,5] 和目标数字 8 ,
解集为:[[1,7],[1,2,5],[2,6],[1,1,6]]
代码:
class Solution {
public:
/**
* @param num: Given the candidate numbers
* @param target: Given the target number
* @return: All the combinations that sum to target
*/
vector<vector<int> > combinationSum2(vector<int> &num, int target) {
// write your code here
set<vector<int> > r;
vector<int> cur;
sort(num.begin(),num.end());
combination(cur,num,0,0,target,r);
vector<vector<int> > ret;
copy(r.begin(),r.end(),back_inserter(ret));
return ret;
}
void combination(vector<int> cur,vector<int> &num,int index,int curSum,int target,set<vector<int> >&ret)
{
if(curSum>target)
return;
if(curSum==target)
{
ret.insert(cur);
return;
}
if(index==num.size())
return;
combination(cur,num,index+1,curSum,target,ret);
cur.push_back(num[index]);
combination(cur,num,index+1,curSum+num[index],target,ret);
}
};