首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >回溯法求解八皇后问题

回溯法求解八皇后问题

作者头像
用户6021899
发布2021-04-19 15:18:08
发布2021-04-19 15:18:08
1.2K00
代码可运行
举报
运行总次数:0
代码可运行

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,可以快速解决此类问题。

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

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

回溯法指导思想——走不通,就掉头。

八皇后问题是使用回溯法解决的典型案例。算法的解决思路是: 从棋盘的第一行开始,从第一个位置开始,依次判断当前位置是否能够放置皇后,判断的依据为:同该行之前的所有行中皇后的所在位置进行比较,如果在同一列,或者在同一条斜线上(斜线有两条,为正方形的两个对角线),都不符合要求,继续检验后序的位置。如果该行所有位置都不符合要求,则回溯到前一行,改变皇后的位置,继续试探。如果试探到最后一行,则所有皇后摆放完毕。最后一定要记得将棋盘恢复原样,避免影响下一次摆放。

下面是C++版的源代码,用回溯法求解N皇后问题:

代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
#include <vector>
#include <array>
#include <string>
using namespace std;

const int N = 8; //N皇后问题
//attack NxN的二维数组,true可放值皇后的位置,false不可放皇后的位置

//实现在(x,y)处放置皇后,并根据规则更新attack数组。
void put_queen(int x, int y, array<array<bool,N>,N> &attack)
{
    //180°、0°、90°、-90°、225°、135°、-45°、45°,皇后的8个攻击方向
    static const int dx[] ={-1,1,0,0, -1,-1,1,1};
    static const int dy[] ={0, 0,1,-1,-1,1,-1,1};
    int i,j,new_x,new_y;
    attack[x][y] = 1; //不能再放皇后的位置设为1
    for(i=0;i<8;i++) //8个方向
    {
        for(j=1;j<N;j++)
        {
            new_x = x + dx[i]*j;
            new_y = y + dy[i]*j;
            if(new_x >=0 && new_x < N && new_y >=0 && new_y < N)
            {
                attack[new_x][new_y] = false;//不能再放皇后的位置设为False
            }
            else break;//棋盘越界则停止该方向上的搜索
        }
    }
}


//回溯法求解N皇后问题的递归函数
void backtrack(int row, vector<string> &queen, array<array<bool,N>,N> attack, vector<vector<string>> &solve)
{
    //k 表示当前分析到的行号
    //queen 存储皇后的位置
    //attack 存储皇后的攻击范围
    //solve 存储N皇后问题的全部解法
    //退出递归的条件
    if(row==N) //找到一组解
    {
        solve.push_back(queen); //将结果queen存储到solve
        return;
    }
    //核心部分
    for(int col =0; col< N; col++)//遍历各列,回溯试探皇后可放的位置
    {
        if(attack[row][col])//如果k行i列可放(可放为true)
        {
            array<array<bool,N>,N> temp = attack;//备份attack数组,以待回溯
            queen[row][col] = 'Q';//放置皇后--标记为“Q"
            put_queen(row,col,attack);//更新attack数组
            backtrack(row+1,queen,attack,solve); //递归试探下一行
            attack = temp;//恢复attack数组
            queen[row][col]='.';//恢复queen数组
        }
    }
}


//设置棋盘初始状态,调用求解函数
vector<vector<string>> solveNQueens()
{
    vector<vector<string>> solve;
    array<array<bool,N>,N> attack;
    vector<string> queen;
    //设置初始状态
    for(int i=0; i<N; i++)
    {
        for(int j=0; j<N; j++)
        {
            attack[i][j] = true;//初始全部位置可放皇后(可放则为true)
        }
        queen.push_back("");
        queen[i].append(N,'.');//初始全部位置都无皇后,以点号表示
    }
    backtrack(0,queen,attack,solve);//调用backtrack函数求解
    return solve;
}


int main()
{
    vector<vector<string>> solve = solveNQueens();
    cout <<"棋盘大小为"<<N <<"x"<<N<< "时,N皇后问题总共有"<< solve.size() <<"组解:"<<endl;
    //cout <<sizeof(bool)<<endl;
    for(int i=0; i< solve.size(); i++)
    {
        cout <<"第"<< i+1 <<"组解:"<<endl;
        for(int row=0;row<N; row++)
        {
            cout<<solve[i][row]<<endl;
        }
        cout<<endl;
    }
    return 0;
}

8皇后问题共有92组解。下图是最按行搜索的最后两组解。

12皇后问题共有14200组解。下图是最按行搜索的最后一组解。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python可视化编程机器学习OpenCV 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档