常用算法之一——回溯

1 回溯法

回溯法的基本做法就是搜索,或者是一种组织的井井有条的,能够避免不必要搜索的穷举式搜索。这种方法适合与解一些组合数相当大的问题。

回溯法在问题的解空间树中,按照深度优先策略,从根节点出发搜索解空间树。算法搜索至解空间树的任意一点时,先判断该节点是否包含问题的解。如果肯定不包含,则跳过对该节点为根的子树的搜索,逐层向其祖先节点回溯;否则,进入该子树,继续按深度优先策略搜索,即走不通就掉头。

求解问题所有解:要回溯到根,且根节点的所有子树都已被搜索遍才结束;

求解任一解:只要搜索到问题的一个解就结束。

2 回溯算法的设计过程

setp1:确定问题得解空间;

step2:确定节点的扩展规则;

step3:搜索解空间;

3 回溯法的应用-八皇后问题

问题描述:要在n*n的国际象棋棋盘中放n个皇后,使任意两个皇后都不能互相吃掉。规则:皇后能吃掉同一行,同一列,同一对角线的任意旗子。求所有的解。

n=8

设八皇后Xi,分别在第i行(i=1,2,3...,8);

问题的解状态:可以用(1,X1),(2,X2)....,(8,X8) 表示8个皇后的位置(由于行号固定,所以可以简单记为(X1,...,X8));

问题的解空间:(X1,...,X8),1<=Xi<=8;(i=1,2,3,...,8),一共8^8个状态;

约束条件:八个(1,X1),...,(8,X8)不在同一行,同一列,同一对角线上;

可以表示为:i)不在同一列上:Xi != Xj;ii)不在同一对角线上:Xi - i != Xj - 1,Xi + i !=Xj + j;将前面两种情形表示为:

(abs(Xi-Xj)==abs(i-j)) or (Xi==Xj)

使用盲目的枚举算法:

queen()

{

int a[9];

for(a[1]=1;a[1]<=8;a[1]++)

......

for(a[8]=1;a[8]<=8;a[8]++)

if(check(a,8)==0) continue;

else

for(i=1;i<=8;i++)

printf(a[i]);

}

check(a[],n)

{

int i,j;

for(int i=2;i<=n;i++)

for(int j=1;j<=i-1;j++)

if(a[i]==a[j] or (abs(a[i]-a[j])==abs(i-j)))

return 0;

return 1;

}

check()函数需要双重循环,任意两个皇后之间都必须检查。

 

1 使用递归回溯算法:

check1(int a[],int n)

{

int i;

for(i=1;i<=n-1;i++)

if(abs(a[i]-a[n])=abs(i-n) or (a[i]-a[n]))

return 0;

return 1;

}

int a[100],n;

main()

{

intput(n);

backtrack(1);

}

backtrack(int k)

{

if(k>n)  找到一组解输出结果

else

for(int i=1;i<=n;i++)

{

a[k]=1;

if(check1(a,k)=1) backtrack(k+1);

}

}

假设这里一共有4个皇后,如果k=3时,a[3]>4(即a[3]=1~4都不能满足check的条件)才能满足check1条件,从而就不会再往下执行(不会调k=4) 所以返回第二层,重新改变a[2]的值,即for循环中的a[2]++,然后check1,可行的话再向下执行(k=3)。

递归终止条件:当a[1](第一层)的取值大于4时,此时程序就不能再向下执行,所以程序终止。[其余的a[i]也是这个道理。只是a[i]是第一层,它终止之后递归解结束了]

2 非递归回溯算法

backdate(int n)

{

a[1]=1;k=1;

while(k>0){

a[k]++;

while((a[k]<=n)and (check1(a,k)=0))  //搜索第k个皇后的位置

a[k]++;

if(a[k]<=n)

if(k=n) output(n) //找到一组解

else{

k++;      //继续为第k+1个皇后找到位置

a[k]=0;   //注意下一个皇后一定要从头开始搜索

else

k--;   //回溯

}

}

}

对a[k]>n,即不满足 a[k]<=n 表达式,说明a[k]已在棋盘外才能满足与前k-1个皇后不在同一对角线,行,列上。a[k]不满足所以需要进行回溯,即回到k-1个值a[k-1],然后重新计算a[k-1],得到之后再继续计算a[k](a[k]不包含,就跳过该节点为根的子树的搜索,逐层向其祖先节点(a[k-1])进行返回)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值