神机百炼2.40-n皇后问题

n皇后问题导图

食用指南:

对该算法程序编写以及踩坑点很熟悉的同学可以直接跳转到代码模板查看完整代码
只有基础算法的题目会有关于该算法的原理,实现步骤,代码注意点,代码模板,代码误区的讲解
非基础算法的题目侧重题目分析,代码实现,以及必要的代码理解误区

题目描述:

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

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

    输入格式
    共一行,包含整数 n。

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

    数据范围
    1≤n≤9
    输入样例:
    4
    输出样例:
    .Q…
    …Q
    Q…
    …Q.

    …Q.
    Q…
    …Q
    .Q…

  • 题目来源:https://www.acwing.com/problem/content/845/

题目分析:

  • dfs经典题目,n皇后问题
  • 注意,给定棋盘行数n后,可能有多种合理结果

算法原理:

模板算法:

  • 传送门:DFS

n皇后问题:

  • 难点:
    行数可以使用row[]数组记录
    列数可以使用col[]数组记录
    一三象限角平分线使用row[y + x]表示
    二四象限角平分线使用row[y - x + n]表示

  • 放置棋子有三种放置方法:

    1. 按行放置,每行放置一个
    2. 按列放置,每列放置一个
    3. 按个放置,每个放置0 / 1个

    下面来讲解按列放置 & 按个放置

按列放置:
  • 共n列,递归n层,每层遍历一列的n个格
  • 对放置点的要求:
    不共行,不共列,不共一三角分线,不共二四角分线
  • 还原:
    当该列选择一个放置完成后,清除放置。
    再从同列其余格子中选择可放置点进行放置
  • 代码:
#include <iostream>
using namespace std;
const int N = 9 + 1;
int n;
char arr[N][N];
void init(){
    for(int i=1; i<=n; i++){
        for(int j=1; j<=n; j++){
            arr[i][j] = '.';
        }
    }
}
void print(){
    for(int i=1; i<=n; i++){
        for(int j = 1; j<=n; j++){
            cout <<arr[i][j];
        }
        cout<<endl;
    }
}
int row[N], col[N], dg[N],udg[N];
void dfs(int x){
    if (x == n+1) {print(); cout<<endl; return;}
    for(int i=1; i<=n; i++){
        if (!row[i] && !dg[x+i] && !udg[n+x-i]){
            arr[i][x] = 'Q';
            row[i] = 1, dg[x+i] = 1, udg[n+x-i] = 1;
            dfs(x+1);
            arr[i][x] = '.';
            row[i] = 0, dg[x+i] = 0, udg[n+x-i] = 0;
        }
    }
}
int main(){
    cin >>n;
    init();
    dfs(1);
    return 0;
}
按个放置:
  • 共n个棋子,递归n层,每层遍历n*n格
  • 对放置点的要求:同上
  • 还原:
    还原列标记 行标记 斜线标记
    还原后看下一格
  • 代码:
#include <iostream>
using namespace std;
const int N = 9+1;
int n;
char g[N][N];
bool row[N], col[N], dg[N], udg[N];

void init(){
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            g[i][j] = '.';
        }
    }
}

void print(){
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            cout<<g[i][j];
        }
        cout<<endl;
    }
}

void dfs(int x, int y, int cnt)
{
    if (y == n) y = 0, x++ ;

    if (x == n) { 
        if (cnt == n) { 
            print();
            cout<<endl;
        }
        return;
    }

    if (!row[x] && !col[y] && !dg[x + y] && !udg[x - y + n]) {
        g[x][y] = 'Q';
        row[x] = col[y] = dg[x + y] = udg[x - y + n] = 1;
        dfs(x, y + 1, cnt + 1);
        row[x] = col[y] = dg[x + y] = udg[x - y + n] = 0;
        g[x][y] = '.';
    }

    dfs(x, y + 1, cnt);
}


int main() {
    cin >> n;
    init();
    dfs(0, 0, 0);
    return 0;
}

代码误区:

1. 为什么一三角分线为x+y,二四角分线为x-y+n?

  • 很多时候就是试试看,尤其考试时,试试看发现正确了就记住结论
  • 先假设一个是x+y,一个是x-y
    防止越界,x-y后+n
  • 画图到二维数组中去验证
    二维数组角分线

本篇感想:

  • dfs部分不难,难在所谓的正反斜线的记录

  • 本篇很接近我们讲完模板之后的刷题博客了

  • 40篇达成,照例上美图:
    秋枫

  • 看完本篇博客,恭喜已登 《筑基境-初期》
    筑基境

距离登仙境不远了,加油 登仙境初期

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

starnight531

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

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

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

打赏作者

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

抵扣说明:

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

余额充值