当前位置: 首页 > news >正文

深入理解N皇后问题:从DFS到对角线优化

N皇后问题是一个经典的算法问题,要求在N×N的棋盘上放置N个皇后,使得它们互不攻击。本文将全面解析该问题的解法,特别聚焦于DFS算法和对角线优化的数学原理。

问题描述

在N×N的国际象棋棋盘上放置N个皇后,要求:

  • 任意两个皇后不在同一行

  • 任意两个皇后不在同一列

  • 任意两个皇后不在同一对角线

数据结构图解

1. 棋盘表示

char g[N][N];  // 棋盘矩阵
  • '.' 表示空位

  • 'Q' 表示皇后

示例(n=4初始状态):

. . . .
. . . .
. . . .
. . . .

2. 冲突检测数组

bool col[N];       // 列占用标记
bool dg[N*2];      // 正对角线占用标记
bool udg[N*2];     // 反对角线占用标记
对角线索引计算:
(0,0) (0,1) (0,2) (0,3)
(1,0) (1,1) (1,2) (1,3)
(2,0) (2,1) (2,2) (2,3)
(3,0) (3,1) (3,2) (3,3)
  • 正对角线(dg)u + i(行+列)

    • 例如:(1,2)的正对角线索引=1+2=3

    • 我们发现同一个对角线的上的坐标的x,y值相加是相等的,所以我们用dg[u+i]去标记

  • 反对角线(udg)n - u + i

    • 例如:当n=4时,(1,2)的反对角线索引=4-1+2=5

    • 反对角线,我们此时不能再次使用u+i了 

    • 但为了确保索引不重叠,我们使用n - u + i

      • u增加时,n - u减小

      • i增加时,i增大

      • 这样确保每条反对角线有唯一索引

    • 实际计算示例(n=4)

      坐标计算式索引值
      (0,3)4-0+3=77
      (1,2)4-1+2=55
      (2,1)4-2+1=33
      (3,0)4-3+0=11

算法执行流程图解

DFS递归过程

dfs(0)
├─ 尝试第0行第0列
│  ├─ 放置皇后
│  ├─ dfs(1)
│  │  ├─ 尝试第1行第2列
│  │  │  ├─ 放置皇后
│  │  │  ├─ dfs(2)
│  │  │  │  ├─ (冲突,回溯)
│  │  │  └─ 撤销皇后
│  │  └─ 尝试其他列...
│  └─ 撤销皇后
└─ 尝试第0行第1列├─ 放置皇后├─ dfs(1)│  ├─ 尝试第1行第3列│  │  ├─ 放置皇后│  │  ├─ dfs(2)│  │  │  ├─ 找到解│  │  │  └─ 输出棋盘│  │  └─ 撤销皇后│  └─ 尝试其他列...└─ 撤销皇后

示例解(n=4)

有效解之一:

. Q . .
. . . Q
Q . . .
. . Q .

对应的标记数组状态:

  • col: [1, 0, 1, 0] (第0、2列被占用)

  • dg: 索引1、3、4被占用

  • udg: 索引4、5、6被占用

完整代码

/*DFS			深度优先搜索*/
#include <stdio.h>
#include <stdbool.h>#define N 20int n;				//棋盘的大小(n*n)
char g[N][N];		//模拟棋盘,'.'表示空,'Q'表示皇后
//col表示列是否被占用,dg表示正对角线是否被占用,udg表示反对角线是否被占用
bool col[N], dg[N*2], udg[N*2];		void dfs(int u)	//u表示当前处理的行
{if (u == n)		//所有的行处理完毕,输出解,返回递归{for (int i = 0;i < n;i++)	//puts函数输出字符串,自动换行puts(g[i]);				//在二维数组g[][]中,单使用g[i]表示第i行的所有数据puts("");		//打印空格,隔开不同解return;}//尝试在当前行的每一列放置皇后for(int i = 0;i < n;i++)//检查列,正对角线,反对角线是否冲突if (!col[i] && !dg[u + i] && !udg[n - u + i]){g[u][i] = 'Q';		//防止皇后col[i] = dg[u + i] = udg[n - u + i] = true;		//标记占用dfs(u + 1);			//递归处理下一行col[i] = dg[u + i] = udg[n - u + i] = false;	//回溯,撤销原有的标记g[u][i] = '.';		//恢复棋盘}
}int main()
{scanf("%d", &n);//初始棋盘for (int i = 0;i < n;i++)for (int j = 0;j < n;j++)g[i][j] = '.';//从第0行开始DFSdfs(0);return 0;
}

时间复杂度分析

  • 最坏情况:O(n!)(需要尝试所有可能的排列)

  • 实际由于剪枝(冲突检测),运行效率高于纯暴力搜索

关键点总结

  1. 行处理顺序:逐行放置皇后,避免行冲突

  2. 冲突检测

    • 列冲突:col[i]

    • 正对角线冲突:dg[u+i]

    • 反对角线冲突:udg[n-u+i]

  3. 回溯机制:递归返回时撤销所有修改

这个算法通过DFS系统地探索所有可能的解空间,同时使用剪枝技术大幅提高效率。

相关文章:

  • Linux | Mfgtools 修改单独只烧写 Uboot,内核,文件系统
  • 用python写一个相机选型的简易程序
  • 视觉大模型专栏导航
  • 可视化大屏开发前提了解
  • MySQL 分布式架构与实战:从单机到集群的进阶之路(附生产级架构设计)
  • Spring AI Alibaba - Milvus 初体验,实现知识库效果
  • QT6 源(51)篇一:存储 c 语言字符串的类 QByteArray 的使用举例,采用 ascii 码字符集。函detach()的调用时机,
  • 【SSH 端口转发】通过SSH端口转发实现访问远程服务器的 tensorboard
  • 学习笔记——《Java面向对象程序设计》-内部类、匿名类、异常类
  • rsync+ Rocky Linux 主机同步部署实战
  • 4月26日星期六今日早报简报微语报早读
  • 创建可执行 JAR 文件
  • 如何在 Conda 环境中降级 Python 版本:详细指南
  • MyBatis 插件开发的完整详细例子
  • 通过门店销售明细表用Python Pandas得到每月每个门店的销冠和按月的同比环比数据
  • Pygame字体与UI:打造游戏菜单和HUD界面
  • 《淘宝API数据治理实践:采集字段标准化与数据质量监控体系》
  • 6.1 客户服务:智能客服与自动化支持系统的构建
  • 获取房源信息并完成可视化——网络爬虫实战1
  • C++模拟Java C#的 finally
  • 湖州通告13批次不合格食品,盒马1批次多宝鱼甲硝唑超标
  • 旧衣服旧纸箱不舍得扔?可能是因为“囤物障碍”
  • 上海发布一组人事任免信息:钱晓、翁轶丛任市数据局副局长
  • 给印度立“人设”:万斯访印祭出美国关税战新招,但效果存疑
  • 十三届全国政协经济委员会副主任张效廉严重违纪违法被开除党籍
  • 最大涨幅9800%!金价新高不断,引发期权“末日轮”效应,沪金期权多张合约大涨