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;
    }

    程序执行结果截图:


  • 相关阅读:
    Oracle删除.dbf文件报错
    Java 7 新的 try-with-resources 语句,自动资源释放
    模式对话框提交form之后总是打开新的页面
    MyEclipse更改项目的发布目录
    IntelliJ IDEA 更换发布目录
    java.sql.SQLException: ORA-00942: 表或视图不存在
    编码那点事
    配置nginx实现windows/iis应用负载均衡
    MSMQ消息队列
    .NET 分布式技术比较
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/7044340.html
Copyright © 2011-2022 走看看