回溯与分支限界法
回溯法是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。 但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
术语
解空间:问题可行解的集合
回溯法:以深度优先的方式 系统搜索问题的解
避免无效搜索
剪枝函数(以下两类函数都是剪枝函数)
- 约束函数–用约束函数在扩展结点处剪去不满足约束的子树;
•见0-1背包问题 - 限界函数–用限界函数剪去得不到最优解的子树;
•见TSP问题
两种类型的解空间树
子集树
2^n个叶结点 01背包问题
搜索子集树的一般算法
(完全n叉树属于子集树)
//t表示当时搜的深度
//n一般是问题规模,是数组x的长度,诸如背包问题中石块的数量
//x是解集
void backtrack( int t)
{
//如果递归到最深层了,就输出结果,找到符合问题的一个解了
if(t>n) output(x);
else
for(int i=0;i<=1;i++)
//0是左子树,1是右子树
{
x[t]=i;
if(constrain(t)&&bound(t)) backtrack(t+1);
//如果不符合这一约束的话,函数会接着执行i=1看看是不是能进入backtrack(t+1),如果都不行的话才会回溯回backtrack(t-1)去重新决定x[t-1]这个是不是要改成1
}
}
排列树
n!个叶结点 TSP旅行商问题
搜索排列树的一般算法
//n是问题的规模
//t是当前搜索的深度
//x是解集
void backtrack( int t)
{
//依旧是搜索到最深一层时,输出结果
if(t>n) output(x);
else
//随着搜索的深入,x解集中已被选中的元素数量为t,剩下的可以选择的数量则为n-t
for(int i=t;i<=n;i++)
{
//搜索深度t可以代表当前我们要决定的元素是x[t]
//t的选择在这里有 t、t+1、t+2...、n这些可以选择
//因为1、2、..、t-1这些选择已经被选走了
swap(x[t],x[i]);
if (constrain(t)&&bound(t)) backtrack(t+1);
//如果不符合约束条件的话我们需要把x[t]换回来
swap(x[t],x[i]);
//然后继续搜索i=t+1这个可不可以,如果直到n都不可以,这时候我们回溯到backtrack(t-1)去重新决定x[t-1]要放什么元素
}
}
分支限界法的基本策略
(自己的不严谨语言描述版)
同回溯法一样,先构造出解空间,一般是树的数据结构,形成解空间树
同回溯法一样,选取一个结点作为扩展结点,之后与回溯法(深度搜索优先)不同的是,分支限界法在扩展结点处会先遍历其所有的子结点(分支)。然后根据剪枝函数,确定哪些子节点将导致不可行或者非最优解(具体根据题意是要找可行还是找最优解)。将这些子节点剔除,也就是将子节点以及他的分支剪掉。完成之后将剩下的这些子节点构造成当前的活结点表。之后从该表中选取一个结点为当前的扩展,之后继续重复上述过程。
根据从活结点表中选取新的扩展结点的方式不同可以分为
1.队列式FIFO分支限界法 和 2.优先队列式分支限界法 两种不同的分支限界法
其中队列式FIFO分支限界法,将活结点表组织成一个队列,并按队列的先进先出FIFO原则选取下一个结点作为当前扩展结点
优先队列式分支限界法,将活结点表组织成一个优先队列,并按优先队列中规定的结点优先级,选取剩余队列中优先级最高的下一个结点作为当前扩展结点;