n皇后学习

article/2025/10/14 3:27:14

洛谷P1219

回溯

#include<cstdio>
const int N = 13;
int n, cnt, ans[N];
void dfs(int row) {if (row == n) {if (cnt < 3) {for (int i = 0; i < n; ++i) {if (i > 0)printf(" ");printf("%d", ans[i] + 1);}printf("\n");}++cnt;return;}for (int i = 0; i < n; ++i) {bool flag = true;for (int j = 0; j < row; ++j) {if (ans[j] == i || row - j == i - ans[j] || j - row == i - ans[j]) {flag = false;break;}}if (flag) {ans[row] = i;dfs(row + 1);}}
}
int main() {scanf("%d", &n);dfs(0);printf("%d\n", cnt);return 0;
}

但是最后13的时候会T
在这里插入图片描述

优化1

简单来说就是哪列哪一对角线被占了先缓存一下,到时候直接查表

#include<cstdio>
const int N = 13;
bool col[N], sub_diag[2 * N - 1], main_diag[2 * N - 1];
int n, cnt, ans[N];
void dfs(int row) {if (row == n) {if (cnt < 3) {for (int i = 0; i < n; ++i) {if (i > 0)printf(" ");printf("%d", ans[i] + 1);}printf("\n");}++cnt;return;}for (int col_idx = 0; col_idx < n; ++col_idx) {int sub_diag_idx = col_idx + row, main_diag_idx = col_idx - row + n - 1;if (col[col_idx] || sub_diag[sub_diag_idx] || main_diag[main_diag_idx])continue;col[col_idx] = sub_diag[sub_diag_idx] = main_diag[main_diag_idx] = true;ans[row] = col_idx;dfs(row + 1);col[col_idx] = sub_diag[sub_diag_idx] = main_diag[main_diag_idx] = false;}
}
int main() {scanf("%d", &n);dfs(0);printf("%d\n", cnt);return 0;
}

位运算

其实就是优化1改成用位运算

#include<cstdio>
const int N = 13;
int n, cnt, ans[N], col, sub_diag, main_diag;
void dfs(int row) {if (row == n) {if (cnt < 3) {for (int i = 0; i < n; ++i) {if (i > 0)printf(" ");printf("%d", ans[i] + 1);}printf("\n");}++cnt;return;}for (int col_idx = 0; col_idx < n; ++col_idx) {int sub_diag_idx = col_idx + row, main_diag_idx = col_idx - row + n - 1;if (((col >> col_idx) | (sub_diag >> sub_diag_idx) | (main_diag >> main_diag_idx)) & 1)continue;col ^= 1 << col_idx;sub_diag ^= 1 << sub_diag_idx;main_diag ^= 1 << main_diag_idx;ans[row] = col_idx;dfs(row + 1);col ^= 1 << col_idx;sub_diag ^= 1 << sub_diag_idx;main_diag ^= 1 << main_diag_idx;}
}
int main() {scanf("%d", &n);dfs(0);printf("%d\n", cnt);return 0;
}

位运算2

每一行的副对角线:row->row+n-1
所以只要右移row位,就能找到能一列被占用了
在这里插入图片描述
每一行主对角线:-row+n-1->-row+n-1+n-1
所以只要右移-row+n-1位,就能找到能一列被占用了
在这里插入图片描述
col | (sub_diag >> row) | (main_diag >> (n - 1 - row))就是被占用的列
~(col | (sub_diag >> row) | (main_diag >> (n - 1 - row)))就是可以用的列(当然还包括一些超过n的列)

#include<cstdio>
const int N = 13;
int n, cnt, ans[N], col, sub_diag, main_diag;
void dfs(int row) {if (row == n) {if (cnt < 3) {for (int i = 0; i < n; ++i) {if (i > 0)printf(" ");printf("%d", ans[i] + 1);}printf("\n");}++cnt;return;}int available = ((1 << n) - 1) & ~(col | (sub_diag >> row) | (main_diag >> (n - 1 - row)));while (available) {int p = available & (-available);available ^= p;col ^= p;sub_diag ^= p << row;main_diag ^= p << (n - 1 - row);for (int i = 0; i < n; ++i) {if ((1 << i) == p) {ans[row] = i;break;}}dfs(row + 1);col ^= p;sub_diag ^= p << row;main_diag ^= p << (n - 1 - row);}
}
int main() {scanf("%d", &n);dfs(0);printf("%d\n", cnt);return 0;
}

位运算3

col的第i位代表这一行第i列是否被占用
sub_diag的第i位代表这一行第i个副对角线是否被占用
main_diag的第i位代表这一行第i个主对角线是否被占用

dfs到下一层的时候,col不变
在第i行,假设第k个副对角线被占用了
那么在i+1行,第k-1个副对角线就被占用了
同理
在第i行,假设第k个主对角线被占用了
那么在i+1行,第k+1个主对角线就被占用了

#include<cstdio>
const int N = 13;
int n, cnt, ans[N];
void dfs(int row, int col, int sub_diag, int main_diag) {if (row == n) {if (cnt < 3) {for (int i = 0; i < n; ++i) {if (i > 0)printf(" ");printf("%d", ans[i] + 1);}printf("\n");}++cnt;return;}int available = ((1 << n) - 1) & ~(col | sub_diag | main_diag);while (available) {int p = available & (-available);available ^= p;for (int i = 0; i < n; ++i) {if ((1 << i) == p) {ans[row] = i;break;}}dfs(row + 1, col | p, (sub_diag | p) >> 1, (main_diag | p) << 1);}
}
int main() {scanf("%d", &n);dfs(0, 0, 0, 0);printf("%d\n", cnt);return 0;
}

舞蹈链

https://blog.csdn.net/qq_39942341/article/details/126681667?spm=1001.2014.3001.5501
行: n ∗ n n*n nn个格子
列: [ 1 , n ] \left[1,n\right] [1,n]用来记录这个数字填在了哪行
[ n + 1 , 2 n ] \left[n+1,2n\right] [n+1,2n]用来记录这个数字填在了哪列
[ 2 n + 1 , 4 n − 1 ] \left[2n+1,4n-1\right] [2n+1,4n1]用来记录这个数字填在了哪条副对角线(从 ( 0 , 0 ) (0,0) (0,0)开始)
[ 4 n , 6 n − 2 ] \left[4n,6n-2\right] [4n,6n2]用来记录这个数字填在了哪条主对角线(从 ( n − 1 , 0 ) (n-1,0) (n1,0)开始)

但是注意一点,对角线是无法完全覆盖的
比如下面的图,第3条副对角线就没有覆盖
在这里插入图片描述
所以如果行和列覆盖完了就可以返回了

#include<cstdio>
#include<cstring>
#include<algorithm>const int H = 13;
const int M = H * H, N = 6 * H - 2;
int n, board_cnt;
struct A {int a[H];
}board[100000];
bool cmp(const A& a, const A& b) {int i = 0;while (i < n && a.a[i] == b.a[i])++i;return a.a[i] < b.a[i];
};
class DLX {
public:static const int MAXN = M * 4 + N + 5;int left[MAXN], right[MAXN], up[MAXN], down[MAXN];int row[MAXN], col[MAXN], head[M+5], col_size[N+5], cnt;int ans, ans_row[H];DLX() :cnt(0), ans(0) {}void init(const int& N) {cnt = ans = 0;for (int i = 0; i <= N; ++i) {left[i] = i - 1;right[i] = i + 1;up[i] = down[i] = i;}left[0] = N;right[N] = 0;memset(head, -1, sizeof(head));memset(col_size, 0, sizeof(col_size));cnt = N + 1;}void link(const int& r, const int& c) {++col_size[c];row[cnt] = r;col[cnt] = c;up[cnt] = c;down[cnt] = down[c];up[down[c]] = cnt;down[c] = cnt;if (head[r] == -1) {head[r] = left[cnt] = right[cnt] = cnt;}else {right[cnt] = head[r];left[cnt] = left[head[r]];right[left[head[r]]] = cnt;left[head[r]] = cnt;}++cnt;}void remove(const int& c) {right[left[c]] = right[c];left[right[c]] = left[c];for (int i = down[c]; i != c; i = down[i]) {for (int j = right[i]; j != i; j = right[j]) {up[down[j]] = up[j];down[up[j]] = down[j];--col_size[col[j]];}}}void resume(const int& c) {for (int i = up[c]; i != c; i = up[i]) {for (int j = right[i]; j != i; j = right[j]) {up[down[j]] = j;down[up[j]] = j;++col_size[col[j]];}}right[left[c]] = c;left[right[c]] = c;}void dance(int dep) {if (right[0]>n) {for (int i = 0; i < n; ++i) {int x = (ans_row[i] - 1) / n;int y = (ans_row[i] - 1) % n + 1;board[board_cnt].a[x] = y;}++board_cnt;return;}int c = right[0];for (int i = right[c]; i != 0 && i <= n; i = right[i]) {if (col_size[i] < col_size[c]) {c = i;}}remove(c);for (int i = down[c]; i != c; i = down[i]) {ans_row[ans++] = row[i];for (int j = right[i]; j != i; j = right[j]) {remove(col[j]);}dance(dep + 1);for (int j = left[i]; j != i; j = left[j]) {resume(col[j]);}--ans;}resume(c);return;}
};
int main() {scanf("%d", &n);DLX solver;solver.init(6 * n - 2);for (int i = 0; i < n; ++i) {for (int j = 0; j < n; ++j) {int idx = i * n + j + 1;solver.link(idx, i + 1);solver.link(idx, n + j + 1);solver.link(idx, 2 * n + 1 + i + j);solver.link(idx, 4 * n + n + j - i - 1);}}solver.dance(0);std::sort(board, board + board_cnt, cmp);for (int i = 0; i < 3; ++i) {for (int j = 0; j < n; ++j) {if (j > 0)printf(" ");printf("%d", board[i].a[j]);}printf("\n");}printf("%d\n", board_cnt);return 0;
}

参考
https://zhuanlan.zhihu.com/p/22846106


http://chatgpt.dhexx.cn/article/sDKvV7bW.shtml

相关文章

N皇后(回溯算法)

题目 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。等价于要求任何两个皇后都不能在同一行、同一列以及同一条斜线上 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方案。 如图&#xff1a;黄色代表放…

求解n皇后

要求&#xff1a;在国际象棋上摆放n个皇后&#xff0c;使其不能互相攻击&#xff0c;即任意两个皇后都不能处于同一行、同一列或同一斜线上&#xff0c;问有多少种摆法 思路&#xff1a;很直观的想法就是在棋盘上一个一个皇后的摆&#xff0c;如果冲突&#xff0c;则摆放在另一…

n皇后 - 位运算版

n皇后问题是大家在递归里会碰到的一个经典问题。以前高中我学DFS的时候&#xff0c;老师首先让我看的就是八皇后。 不过这皇后的时间复杂度大家可想而知了。而接下来的位运算将这个效率重新提到一个高度。 我是以前在Matrix67大牛那里学的&#xff0c;最近数据结构实验刚好碰到…

n皇后最快算法详解

n皇后问题再经典不过了&#xff0c;想必大家也听说过。 再简单说一下吧&#xff0c;就是一个n*n的棋盘&#xff0c;放置n个皇后&#xff0c;使得竖着不攻击&#xff0c;横着不攻击&#xff0c;斜着不攻击。求有多少种方法。 &#xff08;国际象棋不是这么玩的呀 &#xff09; …

51. N 皇后

51. N 皇后 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方…

51. N皇后

51. N皇后 https://leetcode-cn.com/problems/n-queens/ 四皇后的解 n个皇后&#xff0c; nn 的棋盘上 根据要求 每行有且仅有一个皇后&#xff08;如果有一行没有那么必有一行至少两个皇后&#xff0c;不符合&#xff09; 递归回溯&#xff0c;每次尝试在一行中摆放皇后&…

递归算法——n皇后

** 递归算法——n皇后 ** n皇后问题&#xff1a; 输入整数n&#xff0c;要求n个国际象棋的皇后&#xff0c;摆在n*n的棋盘上&#xff0c;互相不能攻击&#xff0c;输出全部方案。 输入&#xff1a; 输入一个正整数N。 输出&#xff1a; 程序输出N皇后问题的全部摆法。 行里…

kettle连接mysql教程_KETTLE初学者使用教程

Kettle的建立数据库连接、使用kettle进行简单的全量对比插入更新:kettle会自动对比用户设置的对比字段,若目标表不存在该字段,则新插入该条记录。若存在,则更新。 Kettle简介:Kettle是一款国外开源的ETL工具,纯java编写,可以在Window、Linux、Unix上运行,数据抽取高效稳…

Kettle Spoon 安装配置详解

文章目录 1 概述2 安装2.1 软件下载2.2 JDK 环境变量配置2.3 数据库驱动包下载2.4 双击 Spoon.bat 启动 3 简单使用3.1 transformation 转换3.1.1 文件 - 新建 - 转换3.1.2 核心对象 - 输入 - 表输入3.1.3 核对对象 - 输出 - 插入/更新3.1.4 保存 - xxx.ktr 3.2 job 作业3.2.1 …

Spoon安装步骤

主数据库连接步骤 主对象树点击转换&#xff0c;双击DB连接 配置信息完成后点击测试成功 二&#xff0e;源数据库连接步骤 1.点击Connect,点击other repositories 2.点击Database Repository 编辑名称&#xff08;注意必须用英文&#xff09; 再点击数据库连接 配置选项 …

kettle下载安装使用教程

Kettle简介 Kettle是一款国外开源的ETL工具&#xff0c;纯java编写&#xff0c;可以在Window、Linux、Unix上运行&#xff0c; 数据抽取高效稳定。Kettle 中文名称叫水壶&#xff0c;该项目的主程序员MATT 希望把各种数据放到一个壶里&#xff0c;然后以一种指定的格式流出。K…

KETTLE使用教程

Kettle的建立数据库连接、使用kettle进行简单的全量对比插入更新&#xff1a;kettle会自动对比用户设置的对比字段&#xff0c;若目标表不存在该字段&#xff0c;则新插入该条记录。若存在&#xff0c;则更新。 Kettle简介&#xff1a;Kettle是一款国外开源的ETL工具&#xff0…

spoon mysql教程_kettle 教程(一):简介及入门

介绍 kettle 是纯 java 开发&#xff0c;开源的 ETL工具&#xff0c;用于数据库间的数据迁移 。可以在 Linux、windows、unix 中运行。有图形界面&#xff0c;也有命令脚本还可以二次开发。 安装 这边以 windows 下的配置为例&#xff0c;linux 下配置类似。 jdk 安装及配置环境…

kettle基础使用教程

文章目录 前言一、下载、安装二、启动软件三、转换的使用教程四、作业的使用教程总结 前言 Kettle简介&#xff1a;Kettle是一款国外开源的ETL工具&#xff0c;纯java编写&#xff0c;可以在Window、Linux、Unix上运行&#xff0c;数据抽取高效稳定。Kettle 中文名称叫水壶&…

ETL工具-Kettle Spoon教程

转自&#xff1a;https://blog.csdn.net/liaomin416100569/article/details/82798879 一 。Kettle Spoon简介 ETL&#xff08;Extract-Transform-Load的缩写&#xff0c;即数据抽取、转换、装载的过程&#xff09;&#xff0c;对于企业或行业应用来说&#xff0c;我们经常会遇…

KETTLE 使用教程

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 Kettle的建立数据库连接、使用kettle进行简单的全量对比插入更新&#xff1a;kettle会自动对比用户设置的对比字段&#xff0c;若目标表…

spoon mysql教程_Kettle-Spoon入门示例

Spoon 是Kettle的设计调试工具 1.驱动: a) 驱动错误 b) 驱动添加 2.端口错误&#xff1a;连接数据库端口不对 3.正常连接 4.表输入 a) 新建一个表输入&#xff0c;获取数据库表的数据 b) 预览数据 c) 当前表数据输出到另外一个同样的表 d) 当前表数据输出到另外一个同样的表 e)…

数据库转换工具 spoon使用

由于项目需求 需要把oracle数据库转换为mysql数据库&#xff0c;所以使用spoon转换&#xff0c;简单快捷 ETL Kettle Spoon简介 ETL&#xff08;Extract-Transform-Load的缩写&#xff0c;即数据抽取、转换、装载的过程&#xff09;&#xff0c;对于企业或行业应用来说&#…

spoon mysql教程_spoon新手入门教程

Kettle是一款国外开源的ETL工具&#xff0c;纯java编写&#xff0c;可以在Window、Linux、Unix上运行&#xff0c;数据抽取高效稳定。Kettle 中文名称叫水壶&#xff0c;该项目的主程序员MATT 希望把各种数据放到一个壶里&#xff0c;然后以一种指定的格式流出。Kettle这个ETL工…

Kettle工具简单使用(spoon)

1、添加测试数据 在navicat中随便找个表当做被转化的数据进行测试&#xff0c;以下表为例&#xff1a; 在SQL server数据库中创建表 2、下载spoon软件 下载路径&#xff1a;https://download.csdn.net/download/qq_57404736/85013576 打开文件夹&#xff0c;双击spoon.ba…