49、【图】N-皇后问题:二维信息判定(C/C++版)

题目描述

n−皇后问题是指将 n 个皇后放在 n×n的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。

在这里插入图片描述

现在给定整数 n,请你输出所有的满足条件的棋子摆法。

输入格式

共一行,包含整数 n。

输出格式

每个解决方案占 n行,每行输出一个长度为 n的字符串,用来表示完整的棋盘状态。其中 . 表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。每个方案输出完成后,输出一个空行。
注意:行末不能有多余空格。输出方案的顺序任意,只要不重复且没有遗漏即可。

数据范围

1≤n≤9

输入样例:

4

输出样例:

.Q…
…Q
Q…
…Q.

…Q.
Q…
…Q
.Q…

题目分析

(1)分析

1. 边界条件: 行、列、主对角线、副对角线均只能有一个皇后

由于该题为各种排列的组合情况且可按照某种顺序进行排列,因此可选用DFS算法。

具体思路:从起始点出发,按行放置皇后,若选取的下一行中的位置其对应的列、主对角线和副对角线没有其余皇后与之冲突,才进行放置,放置完后,将对应的列、主对角线和副对角线标记为已有皇后。若不满足其上述的任意条件,则再查找下一个位置进行查找。

2. 对主对角线方向进行分析,
在这里插入图片描述
主对角线方向元素为:
A[0][n]、A[1][n-1]、…、A[n-1][1]、A[n][0];
A[0][n-1]、A[1][n-2]、…、A[n-2][1]、A[n][0;
A[1][n]、A[2][n-1]、…、A[n-1][2]、A[n][1];

设副主角线上元素的行下标为y,列下标为x,可发现主对角所在下标满足这样一种性质:
y + x = b,其中b为常数,即行列之和为一个行数

3. 对副对角线方向进行分析,
在这里插入图片描述

副对角线反向元素为:
A[0][0]、A[1][1]、…、A[n-1][n-1]、A[n][n];
A[1][0]、A[2][1]、…、A[n-1][n-2]、A[n][n-1];
A[0][1]、A[1][2]、…、A[n-2][n-1]、A[n-1][n];

设副对角线上元素的行下标为y,列下标为x,可发现主对角所在下标满足这样一种性质:
y - x = b,其中b为常数,即行列之差为一个常数

4. 推广
在这里插入图片描述
棋盘中的下标,相当于坐标系下离散化的点。同一斜线上的元素,相当于符合一元线性关系的函数上的值,因此通过确定行和列之间的关系,来得到一个很恒定的常量b。通过设立一个数组A,用其下标来表示不同斜线下对应的常量b,当同一斜线上已有元素时,就让A[b] = 1,进行标记。

在y = x + b这条线上,b = -x + y,遍历时搜索到的范围为这条线的下半部分,导致-x + y < 0,出现负数。为了防止出现负数,将其进行规格化为n - x + y,保证对角线上元素的相对值相同即可。

算法实现

实现方式一:固定顺序,按行搜索

#include <iostream>

using namespace std;

const int N = 10;
int n;
char g[N][N];       // 存储图中元素,图的几何结构为从下往上坐标依次递增
// col[i]: 存储对应列是否已有元素
// dig[i]: 存储副对角线上是否已有元素
// udig[i]:存储主对角线上是否已有元素
bool col[N], dig[N], udig[N];       

// 行
void dfs(int u){
    if(n == u){
        for(int i = 0; i < n; i++)          puts(g[i]);     // 按行输出图
        puts("");
        return ;
    }
    
    for(int i = 0; i < n; i++){                             // 按列搜索
        if(!col[i] && !dig[u + i] && !udig[n - u + i]){     // 若列、主和副对角线上没有元素,则使用该位置
            g[u][i] = 'Q';
            col[i] = dig[u + i] = udig[n - u + i] = true;   // 标记该位置已有元素
            dfs(u + 1);
            col[i] = dig[u + i] = udig[n - u + i] = false;
            g[u][i] = '.';
        }
    }
    
}


int main(){
    cin >> n;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            g[i][j] = '.';
            
    dfs(0);
    
    return 0;
}

时间复杂度: O ( n ⋅ n ! ) O(n·n!) O(nn!)

实现方式二:固定顺序,全局搜索

#include <iostream>

using namespace std;

const int N = 10;
int n;
char g[N][N];
// 记录行、列、正斜线、反斜线的情况
bool col[N], row[N], dig[N], udig[N];       

void dfs(int y, int x, int s){  // 行、列和皇后个数
    if(x == n)  x = 0, y++;     // 当这一行的所有列访问完后,访问下一行

    if(y == n){                 // 棋盘全部搜索完
        if(s == n){             // 输出结果
            for(int i = 0; i < n; i++)      puts(g[i]);
            puts("");            
        }
        return ;
    }

    // 枚举不放皇后
    g[y][x] = '.';
    dfs(y, x + 1, s);       // 不放置皇后,往下一列搜索
    
    // 枚举放皇后
    if(!row[y] && !col[x] && !dig[y + x] && !udig[n + y - x]){
        g[y][x] = 'Q';
        row[y] = col[x] = dig[y + x] = udig[n + y - x] = true;
        dfs(y, x + 1, s + 1);       // 放置皇后,往下一列搜索
        row[y] = col[x] = dig[y + x] = udig[n + y - x] = false;
        g[y][x] = '.';
    }



}


int main(){
    cin >> n;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            g[i][j] = '.';

    dfs(0, 0, 0);

    return 0;

}

时间复杂度: O ( 2 n 2 ) O(2^{n^2}) O(2n2)

leetcode上N皇后

class Solution {
public:        
    vector<string> g;
    vector<vector<string>> res;
    void backtracking(int n, int row, vector<bool>& col, vector<bool>& dig, vector<bool>& udig) {
        if(row == n) {
            res.push_back(g);
            return ;
        }

        for(int i = 0; i < n; i++) {                                     // 按列来遍历
            if(!col[i] && !dig[i + row] && !udig[n - i + row]) {         // 该列、对应的主对角线、副对角线都没有放置皇后时,则可以放置
                g[row][i] = 'Q';
                col[i] = dig[i + row] = udig[n - i + row] = true;                                                
                backtracking(n, row + 1, col, dig, udig);                // 递归下一行
                col[i] = dig[i + row] = udig[n - i + row] = false;    
                g[row][i] = '.';
            }
        }

    }
    vector<vector<string>> solveNQueens(int n) {
        vector<bool> col(n), dig(n), udig(n);       // 列,主对角线、副对角线
        for(int i = 0; i < n; i++) {
            g.push_back(string(n, '.'));
        }
        backtracking(n, 0, col, dig, udig);
        return res;
    }
};

参考文章:
N皇后

nQueen-采取合适的方法表示主对角线和副对角线

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰阳星宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值