新闻详情

新闻详情

首页 / 资讯中心 / 详情

八皇后问题的多种解法 #DFS #位运算 #状态压缩

发布时间:2026/10/1 7:55:27来源:尧图网络
八皇后问题的多种解法 #DFS #位运算 #状态压缩
https://www.luogu.com.cn/problem/P1219这个有趣的问题我将分三个版本为您解释主要差时间优化异体现在check函数上实现原理分别从循环到坐标规律实现的O(1)操作最后是位运算的极致优化check函数检测该位置放置王后是否合法即检查行列主对角线副对角线是否有王后。几种方式的差异体现在check函数的实现方式不同从而导致了时间差异。在主体dfs部分基本保持一致。3.1 循环check循环行列左上右下左下右上由于行列在dfs中我们就完成了规避所以可以减少两次循环别小看这两次循环check在dfs中被大量调用减少这两次循环通过时间可缩短至原来的4/5代码展示#include iostream #include vector using namespace std; const int N 15; int gra[N][N], n, used[N], cnt 0; vector int ans; bool check (int y, int x) { // ture - 冲突 for (int i y, j x; i 1 j 1; i--, j--) if (gra[i][j] 1 i ! y j ! x) return true; for (int i y, j x; i n j n; i, j) if (gra[i][j] 1 i ! y j ! x) return true; for (int i y, j x; j 1 i n; j--, i) if (gra[i][j] 1 i ! y j ! x) return true; for (int i y, j x; j n i 1; j, i--) if (gra[i][j] 1 i ! y j ! x) return true; return false; } void set (int y, int x) { gra[y][x] 1; } void unset (int y, int x) { gra[y][x] 0; } void dfs(int f) { if (f n) { cnt; if (cnt 3) { for (int i 0; i ans.size(); i) cout ans[i] ; cout endl; } } for (int i 1; i n; i) { if (used[i - 1]) continue; if (check(f, i)) continue; set(f, i); used[i - 1] true; ans.push_back(i); dfs(f 1); ans.pop_back(); used[i - 1] false; unset(f, i); } } int main() { cin n; dfs(1); cout cnt endl; return 0; }3.2 映射check我们可以观察一下规律那就是在一条主对角线上每个坐标啊的横纵坐标差是一样的注意此处可能产生负数所以在后续处理的时候我们会通过做差n的方式来规避负数因为差值最大也就为n了在每一条副对角线上每个点的横纵坐标之和是一致的也就是说每一条主对角线差唯一每一条副对角线和唯一。我们可以利用这个唯一的值来记录该直线上是否出现王后的状态。同上横纵在dfs过程就规避了我们只需要处理对角线只需要用两个一维数组记录每一条线是否出现王后代码展示#include iostream #include cmath #include vector using namespace std; const int N 15; int n, used[N], cnt 0; int mia[60], dep[30]; vector int ans; bool check (int y, int x) { if (mia[y - x n] 1) return true; if (dep[y x] 1) return true; return false; } void set (int y, int x) { mia[y - x n] 1; dep[y x] 1; } void unset (int y, int x) { mia[y - x n] 0; dep[y x] 0; } void dfs(int f) { if (f n) { cnt; if (cnt 3) { for (int i 0; i ans.size(); i) cout ans[i] ; cout endl; } } for (int i 1; i n; i) { if (used[i - 1]) continue; if (check(f, i)) continue; set(f, i); used[i - 1] true; ans.push_back(i); dfs(f 1); ans.pop_back(); used[i - 1] false; unset(f, i); } } int main() { cin n; dfs(1); cout cnt endl; return 0; }3.3 状态压缩mask——安全滤镜为什么要用mask我们拿一个32比特位的int来说假如说我们讨论的只是八皇后问题就像从前我们用二维数组一样需要确定一个边界不能越界这里的mask也是如此是为了确保投影不会越界怎么得到mask以一个8比特位的数据为例0000 0001假如我们现在棋盘大小是4 * 4 让1左移4位可以直接用按位左移操作 此时得到如下二进制数据0001 0000接下来对其减一就可以得到4个1就类似于十进制1000 - 1 999一样稍后你会发现这四个一的神奇作用0000 1111mask模版int mask (1 n) - 1; //n表示棋盘的大小pos——安全域利用或运算( | )合并所有的危险区域0000 0001 0000 0010 0000 0100此时所有的1都表示危险区域0000 0111再进行一次取反操作此时1表示安全位置1111 1000但是注意此时我们棋盘大小只有4位前面的4位数其实是不能去的这个时候就需要我们的mask了利用与运算的特性与1相与结果不变与0相与变为0mask 0000 1111 // 相与后结果如下 pos 0000 1000该操作之后1就是安全的不越界的可放置位置接下来 我们只需要挨个尝试所有的“1”位置即可pos模版pos mask (~ (scp1 | scp2 | scp3));cas——位运算手术刀为什么叫手术刀他可以精准抓取Lowbit即获得最右边的“1”神奇的pos -pos请原谅我目前的水平暂时无法严格证明这个神奇手术刀成立不过选取例子验证不难发现是正确的只能说发现这个规律的人非常厉害从来我们是讲二进制码直接转为字符串然后开始从右计“1”现在我们又了更快的工具——手术刀举例尝试pos 6 (0000 0110); -pos 1111 1001 1 - 1111 1010; pos -pos 0000 0010注意在取出来lowbit之后不要忘了在pos中把lowbit删掉哦删除操作只需要pos - p就好了就相当于在二进制的世界里抹去了零头和1320抹去最低位的非零数变成1300一样也就是我们说的抹零pos - pcas模版cas pos ~pos; pos - p;循环计0与__builtin_ctz()硬件魔法循环计0也是比较简单的代码实现不过多赘述for (int i 1; i n; i) { if (cas (1 (i - 1))) { col_num i; break; } }cpu电路魔法__builtin_ctz() 返回从右边开始遇到第一个1之前有多少个0对应坐标的话就1col_num __builtin_ctz(cas) 1代码示范#include iostream #include vector using namespace std; int mask, n, cnt 0; vectorint path; void dfs(int row, int col, int l, int r) { if (row n) { cnt; if (cnt 3) { for (auto x : path) cout x ; cout endl; } return; } int pos mask (~(col | l | r)); while (pos) { int cas9 pos -pos; pos - cas9; int col_num __builtin_ctz(cas9) 1; path.push_back(col_num); dfs(row 1, col | cas9, (l | cas9) 1, (r | cas9) 1); path.pop_back(); } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin n; mask (1 n) - 1; dfs(1, 0, 0, 0); cout cnt endl; }计0部分可用循环代替for (int i 1; i n; i) { if (cas9 (1 (i - 1))) { col_num i; break; } }
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

从RISC-V到KiCad:开源芯片设计全流程与落地实践 2026/10/2 2:12:56

从RISC-V到KiCad:开源芯片设计全流程与落地实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
自建图库第五天:图片审核链路与批量抓取实战 2026/10/2 2:12:49

自建图库第五天:图片审核链路与批量抓取实战

做智能协图云图库这个连续开发项目,今天是第五天。前四天把上传、归类、检索、协作这几块打通之后,图库已经能正常运转了,但有一个问题一直悬在头上:用户传进来的图片,怎么保证安全合规?网上找的素材&#…

阅读更多 →
Python全链路旅游推荐系统:从数据清洗到Flask部署 2026/10/2 2:12:49

Python全链路旅游推荐系统:从数据清洗到Flask部署

简介:本资源是一套面向计算机专业本科生的Python毕业设计实战方案,聚焦智能旅游推荐系统开发,适用于毕设选题、课程设计及机器学习实践者。资源完整包含学术论文、可运行源码与配套说明文档,解决个性化推荐算法落地、前后端协同开…

阅读更多 →
Python后端脚本:导出python123题库并打包zip 2026/10/2 2:12:49

Python后端脚本:导出python123题库并打包zip

简介:面向Python初学者的python123.io平台后端相关题目答案整理包,适合正在刷题或完成在线作业时需要参考思路的同学使用;压缩包内共32个py文件,整体仅14KB,均为可直接阅读的Python源码,覆盖基础语法、条件…

阅读更多 →
SoapUI实战指南:WebService接口测试的完整流程与避坑技巧 2026/10/2 2:12:48

SoapUI实战指南:WebService接口测试的完整流程与避坑技巧

我第一次对接WebService接口时,最懵的不是业务逻辑,而是“测试入口在哪儿”。那时候服务端是用Delphi XE2发布的WebService,对方就甩给我一个WSDL地址,说自己看。我没接触过SOAP,第一反应是拿Postman填个POST地址&…

阅读更多 →
Claude Code 与 VSCode 集成实战:新手入门与排错全指南 2026/10/2 2:12:47

Claude Code 与 VSCode 集成实战:新手入门与排错全指南

说实话,我本来没打算写这么一篇长长的教程,但最近在社区和群里看到太多人问同一个问题:Claude Code 怎么装进 VSCode?装完之后怎么用?为什么我一直报错?这些问题其实完全可以一篇讲完。Claude Code 是 Anth…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉