回溯法:应用举例--八皇后问题

来源:互联网 发布:淘宝网怎样和微信绑定 编辑:程序博客网 时间:2024/05/21 21:39

回溯法:回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,即返回上一级递归调用,这种走不通就退回再走的技术为回溯法。正是这个原因,递归枚举算法常被称为回溯法,应用十分普遍。八皇后问题是回溯法应用的一个典型问题。

问题描述:玩过国际象棋的人都知道,皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。现如今给你一个8*8的棋盘,要求你放入8个皇后,使每个皇后不能直接攻击到其她皇后,输出有多少种方案。在这里,我们将问题泛化,输入任意的n,输出n*n的棋盘中放入n个皇后的方案数。

分析:用c[x]表示放置在第x行的皇后的列数,将问题转化为全排列生成问题,暴力枚举找出答案。在这里,我们在判断一个皇后放在某个位置是否合法时,可以直接循环扫描已经放好的皇后判断与之是否冲突(代码1),也可以设一个二维数组做标记来判断(代码2)。

注:如果在回溯法中使用了辅助的全局变量,一定要及时把他们恢复原状。特别的,如果函数有多个出口,则需要在每个出口初恢复被修改的值。(刘汝佳算法竞赛入门经典中原话,想了想确实如此,这个点是最容易忘记还不容易检查出来的地方,故拿出来提醒一下)

代码1:

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;const int maxn=105;int tot,n;int c[maxn];void search_queen(int cur){    if(cur==n) tot++; //d递归边界。只要走到这所有皇后必然不冲突    else {        for(int i=0;i<n;i++){            int ok=1;            c[cur]=i; //尝试把第cur行的杭后放到第i列            for(int j=0;j<cur;j++){ //检查是否与之前放好的皇后冲突,横向上肯定不会冲突,因此只需要检查纵向,斜向                if(c[cur]==c[j]||cur-c[cur]==j-c[j]||cur+c[cur]==j+c[j]){                     ok=0;                    break;                }            }            if(ok) search_queen(cur+1); //如果此位置合法,继续递归寻找下一个皇后的位置        }    }}int main () {    //freopen("in.txt", "r", stdin);    cin>>n;    search_queen(0);    cout<<tot<<endl;    return 0;}


代码二:

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;const int maxn=105;int tot,n;int c[maxn],vis[2][maxn];void search_queen(int cur){    if(cur==n) tot++;    else {        for(int i=0;i<n;i++){            if(!vis[0][i]&&!vis[1][i+cur]&&!vis[2][cur-i+n]) { //二维数组直接判断,顺序为:列,副对角线,主对角线                c[cur]=i;                vis[0][i]=vis[1][cur+i]=vis[2][cur-i+n]=1;//标记                search_queen(cur+1);                vis[0][i]=vis[1][cur+i]=vis[2][cur-i+n]=0;//递归完成后一定要修改回来            }        }    }}int main () {    //freopen("in.txt", "r", stdin);    cin>>n;    search_queen(0);    cout<<tot<<endl;    return 0;}




原创粉丝点击