zoukankan      html  css  js  c++  java
  • 八皇后问题——递归+回溯法

        八皇后问题,是一个古老而著名的问题。是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击。即随意两个皇后都不能处于同一行、同一列或同一斜线上。问有多少种摆法。 高斯觉得有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解。后来有人用图论的方法解出92种结果。
    求解过程:
        採用遍历的办法,就是採用将每种情况都验证的办法终于找出问题的解,可是蛮力遍历的话,须要遍历的数据量太大,计算时间花费太大,所以在遍历的过程中使用回溯法去掉很多不可能的分支,使问题的规模减小很多。
        一个可行解能够这样表示,用一个数组pos[N](N表示皇后的个数,八皇后即为8)表示每一行的皇后应该放在第几列。从将第一个棋子放在第1行的第1列開始一直遍历完这个棋子固定在这个位置的全部解,然后再将第一行的棋子固定在第一行的第二列,再次遍历全然部的解。直到第一行的棋子放在最后的一列,再遍历完,那么全部的解就都找出来了。由于程序代码中已经凝视很具体,所以这里不再反复凝视了。

    程序代码例如以下:

    #include <iostream>
    #include <bitset>
    using namespace std;
    
    #define N 4 /*设置棋盘宽度*/
    
    char pos[N]; /*每一行的这一个棋子放置的位置:0~7*/
    bitset<N> stat[N]; /*每一行的空暇位置(除去被行列对角线冲突的位置)*/
    bitset<N> mask[N][N]; /*保存回溯过程中曾经的状态。由于在对每一行回溯时当时的状态都不一样
    					    所以这个单元的大小是stat的N倍。以便保存N行各自的初始状态*/
    int g_count; /*统计有多少种解法*/
    
    void print() /*打印出当前的可行解*/
    {
        int i, j;
        cout<<endl;
        for(i=0; i < N; i++)
    	{
            for(j=0; j < pos[i]; j++) 
    			cout<<" - ";
            cout<<" $ ";
            for(j=pos[i]+1; j < N; j++) 
    			cout<<" - ";
            cout<<endl;
        }
    }
    
    void queen(int n) /*皇后问题求解函数*/
    {
        int i, j;
        if(~stat[n] == 0) /*假设这一行没有空暇位置。这个分支求解失败*/
    		return;
     
        for(i=0; i < N; i++)
    	{
            if(!stat[n].test(i)) /*test(i)測试第i个bit是否为1。为1返回ture*/
    		{
                pos[n] = i; 
                if(n+1 == N) /*找到一个解*/
    			{
                    print();
                    g_count++;
                    return;
                }
                for(j=n+1; j < N; j++)
    			{
                    mask[n][j] = stat[j]; /*进行新的遍历前保存当前未探測行的空暇状态*/
    				/*从j=n+1開始保存,前面j=0~n的位置已经保存过了,再保存意义也不大*/
                    stat[j].set(i); /*纵向标记非空暇位置*/
                    if(i+j-n < N) stat[j].set(i+j-n); /*正对角线方向标记非空暇位置*/
    				/*正对角线直线方程:I=kN+b(k=1)->i=n+b->b=i-n==>i-n+j即表示新的I值*/
                    if(i+n-j >= 0) stat[j].set(i+n-j); /*反对角线方向标记非空暇位置*/
    				/*反对角线直线方程:I=kN+b(k=-1)->i=-n+b->b=i+n==>-(j)+i+n即表示新的I值*/
                }
                queen(n+1); /*本行探測完成,进行下一行的探測*/
                for(j=n+1; j < N; j++) 
    				stat[j] &= mask[n][j]; /*探測失败。回退(返回上一次的状态)*/
            }
        }
    }
    int main(int argc, char* argv[])
    {
        int i,j;
    
        g_count=0;
        for(i=0;i<N;i++) 
    		stat[i].reset();
        for(i=0; i < N; i++) 
    		for(j=0; j < N; j++) 
    			mask[i][j].reset();
        cout<<"result is:";
        queen(0);
        cout<<endl<<"总共同拥有"<<g_count<<"种排法."<<endl<<endl;
    	system("pause");
    
        return 0;
    }

    程序执行结果截图:


  • 相关阅读:
    bzoj3505 数三角形 组合计数
    cogs2057 殉国 扩展欧几里得
    cogs333 荒岛野人 扩展欧几里得
    bzoj1123 BLO tarjan求点双连通分量
    poj3352 road construction tarjan求双连通分量
    cogs1804 联合权值 dp
    cogs2478 简单的最近公共祖先 树形dp
    cogs1493 递推关系 矩阵
    cogs2557 天天爱跑步 LCA
    hdu4738 Caocao's Bridge Tarjan求割边
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/7044340.html
Copyright © 2011-2022 走看看