回溯法

一、回溯法

回溯法可以系统的搜索一个问题的所有解或者任意解。它在问题的解空间树中,按深度优先策略从根节点出发搜索解空间树,算法搜索至解空间树的任意一个结点时,先判断该节点如(子树)是否包含问题的解,如果肯定不包含,则跳过对其子树的搜索,逐层向其根节点回溯。否则,则按照深度优先的策略搜索子树。当回溯到根,且根节点的所有子树都已被搜索遍才结束。这种以深
度优先方式系统搜索问题解的算法称为回溯法,适用于解决组合数较大的问题。

例:n皇后问题
n皇后问题要求在一个n行n列的棋盘上放置n个皇后,使得皇后彼此之间不受攻击,按照国际象棋的规则,一个皇后可以攻击与之处于同一行或同一列或同一条斜线上的其他任何棋子。因此,n皇后问题等价于要求在一个n*n格的棋盘上放置n个皇后,使得任意两个皇后不在同一行、同一列和相同的对角线上。


int Place(int *Column, int index){
int i;
for(i=1; i<index; i++) {
int Column_differ=abs(Column[index]-Column[i]);
int Row_differ=abs(index-i);
if(Column[i] == Column[index] || Column_differ == Row_differ)
/*有皇后与其在同列或同一斜线上*/
return 0;
}
return 1; /*没有皇后与其同行、同列或同对角线*/
} 
void N_Queue(int n){
int Column_num[n+1]; /*数组下标表示皇后所在的行,数组元素的值
int index=1; /*皇后的个数*/ 表示皇后所在的列*/
int i; /*循环变量*/
int answer_num=0; /*解个数*/
for(i=1; i <= n; i++) /*初始化数组Column*/
Column_num[i]=0;
while(index > 0){
Column_num[index]++;
while(Column_num[index] <= n && !Place(Column_num ,index)) 
/*寻找皇后的位置*/
Column[index]++; 
if(Column_num[index] <= n){
if(index== n){ /*如果是最后一行时,即有一个解时*/
answer_num++; 
for(i=1; i<=n; i++)
Column_num [index]++;
}
else{ /*继续寻找下一个皇后*/
index++; 
Column_num[index]=0;
}
}else
index--; /*当前皇后无法放置回溯至上个皇后*/
}

分支限界法

一、分支限界法

分支限界法类似于回溯法,也是一种在问题的解空间树T上搜索问题解的算法。但:1.分支限界法与回溯法的求解目标不同。回溯法的求解目标是找出满足约束条件的所有解,而分支限界法的求解目标是找出满足约束条件的一个解,或是满足约束条件的最优解。2.分支限界法与回溯法在解空间树T上的搜索方式也不相同。回溯法以深度优先的方式搜索解空间树T,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树T。

分支限界法的思想
分支限界法首先将根结点加入活结点表(用于存放活结点的数据结构),接着从活结点表中取出根节点,使其称为当前扩展结点,一次性生成其所有孩子结点,判断孩子结点是舍弃还是保留,舍弃那些导致不可行解或者导致非最优解的孩子结点,其余的被保 留在活结点表中。再从活结点表中取出一个活结点作为当前扩展结点,重复上述过程,一直持续到找到所需的解或活结点表为空时为止。活结点表的实现通常有2种方法:一是先进先出队列;二是优先级队列。

例:0-1背包问题。假设有4个物品,其重量分别为(4, 7, 5, 3), 价值分别为(40, 42, 25, 12),背包容量W=10。首先,将给定物品按单位重量价值从大到小排序,结果如下:

应用贪心法求得近似解为(1, 0, 0, 0),获得的价值为40,这作为0-1背包问题的下界。
如何求得0-1背包问题的一个合理的上界?考虑最好情况,背包中装入的全部是第一个物品且可以将背包装满,则可以得到上界的计算方法:ub=40+(10-4)×(v1/w1)=40×60=100。于是,得到了目标函数的界[40, 100]。