700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 算法分析与设计-八皇后问题(回溯法)

算法分析与设计-八皇后问题(回溯法)

时间:2018-07-18 13:32:40

相关推荐

算法分析与设计-八皇后问题(回溯法)

回溯法:

回溯的意义是在递归直到可解的最小问题后,逐步返回原问题的过程,而这里所说的回溯算法实际上是一个类似枚举的搜索尝试方法,它的主题思想是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。

回溯算法师尝试搜索算法中最基本的一种算法,其采用了一种“走不通就掉头”的思想,作为控制结构。在使用回溯算法解决问题中每向前走一步都有很多路径需要选择,但当没有决策信息或决策信息不充分时,只好尝试某一路径向下走,直至走到一定程度后得知此路不通时,再回溯到上一步尝试其他路径;当然在尝试成功时,则问题得解而算法结束。

回溯法典型例题-八皇后问题:

要在8*8的国际象棋棋盘中放八个皇后,使任意两个皇后都不能互相吃掉。规则:皇后能吃掉同一行、同一列、同一对角线的任意棋子。下图为一种方案,求所有的解。

第一种解决方法就是枚举法,这种方法较为简单,共有8^8个状态,但实际搜索当中并不会搜索那么多种状态,因为前面已经说明,回溯法采用的是“走不通就掉头”的策略,所以当我们枚举时候加一些相应的约束条件,可使算法的效率大大提高。

那么怎么确定约束条件呢?

约束条件有三个:

(1)不在同一列,即:xi !=xj(xi ,xj 分别代表第 i ,j 个皇后分别位于第 xi , xj 列);

(2)不在同一主对角线:xi - i != xj - j;

(3)不在同一副对角线:xi + i ! = xj + j;

(2)和(3)又可以合并为一个“不在同一对角线上”的约束条件,表示为:

abs( xi - xj ) != abs( i - j )abs()代表取绝对值。

故得到算法如下

算法一:

#include<iostream>#include<cstdio>#include<cmath>using namespace std;int check(int a[ ],int n){int i;for(i=1; i<=n-1; i++)if(abs(a[i]-a[n])==abs(i-n)||(a[i]==a[n]))return 0;return 1;}int main(){int a[9];for(a[1]=1; a[1]<=8; a[1]++){for(a[2]=1; a[2]<=8; a[2]++){if(check(a,2)==0)continue;for(a[3]=1; a[3]<=8; a[3]++){if(check(a,3)==0)continue;for(a[4]=1; a[4]<=8; a[4]++){if(check(a,4)==0)continue;for(a[5]=1; a[5]<=8; a[5]++){if(check(a,5)==0)continue;for(a[6]=1; a[6]<=8; a[6]++){if(check(a,6)==0)continue;for(a[7]=1; a[7]<=8; a[7]++){if(check(a,7)==0)continue;for(a[8]=1; a[8]<=8; a[8]++){if(check(a,8)==0)continue;else{for(int i=1; i<=8; i++)printf("%d ",a[i]);printf("\n");}}}}}}}}}}

枚举算法虽然有很好的可读性,也能从中体会到回溯的思想,但它只能解决“皇后个数为常量”的问题,却不能解决任意的n皇后问题,下面就是典型的非递归回溯算法:

算法二:

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>using namespace std;int a[30],n;bool check(int k){for(int i=1; i<k; i++)if(abs(a[i]-a[k])==abs(i-k)||a[i]==a[k])return false;return true;}void output(){for(int i=1; i<=n; i++)cout<<a[i]<<" ";cout<<endl;}void backdate(int n){int k=1;a[1]=0;while(k>0){a[k]++;while(a[k]<=n&&check(k)==false) //为第k个皇后搜索位置a[k]++;if(a[k]<=n){if(k==n) //找到一组解output();elsea[++k]=0;//下一个皇后要从头开始搜索}elsek--;//回溯}}int main(){cin>>n;backdate(n);}

递归回溯算法:

我们用b,d,c数组分别来记录棋盘上的n个列,2n-1个主对角线,2n-1个副对角线的占用情况。

说明:

用i,j分别 表示皇后所在的行列(或者说i号皇后在j列),同一主对角线上的行列下标的差一样,若用表达式 i-j 编号,则范围是 -n+1 ~ n+1,所以用i-j+n表示主对角线编号,范围就是1~ 2n-1。同样地,副对角线上行列下标的和一样,用表达式i+j编号,则范围2~2n。

算法三:

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>using namespace std;int a[20],b[20],c[20],d[20];int n,t,k;void output(){t++;cout<<t<<":";for(int k=1;k<=n;k++)cout<<" "<<a[k];cout<<endl;}void tryy(int i){for(int j=1;j<=n;j++){if(b[j]==0&&c[i+j]==0&&d[i-j+n]==0){a[i]=j; //摆放皇后b[j]=1; //占领第j列c[i+j]=1; //主对角线d[i-j+n]=1; //副对角线if(i<n)tryy(i+1); //搜索else //找到一组解output();b[j]=0;c[i+j]=0;d[i-j+n]=0; //重新设置为0}}}int main(){cin>>n;for(int i=1;i<=n;i++){b[i]=0;c[i]=0;c[n+i]=0;d[i]=0;}tryy(1);}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。