皇后问题 绝地灬酷狼 2022-04-13 12:28 222阅读 0赞 八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n1×n1,而皇后个数也变成n2。而且仅当 n2 ≥ 1 或 n1 ≥ 4 时问题有解。 皇后问题是非常著名的问题,作为一个棋盘类问题,毫无疑问,用暴力搜索的方法来做是一定可以得到正确答案的,但在有限的运行时间内,我们很难写出速度可以忍受的搜索,部分棋盘问题的最优解不是搜索,而是动态规划,某些棋盘问题也很适合作为状态压缩思想的解释例题。 进一步说,皇后问题可以用人工智能相关算法和遗传算法求解,可以用多线程技术缩短运行时间。本文不做讨论。 (本文不展开讲状态压缩,以后再说) # 一般思路: # N\*N的二维数组,在每一个位置进行尝试,在当前位置上判断是否满足放置皇后的条件(这一点的行、列、对角线上,没有皇后)。 # 优化1: # 既然知道多个皇后不能在同一行,我们何必要在同一行的不同位置放多个来尝试呢? 我们生成一维数组record,record\[i\]表示第i行的皇后放在了第几列。对于每一行,确定当前record值即可,因为每行只能且必须放一个皇后,放了一个就无需继续尝试。那么对于当前的record\[i\],查看record\[0...i-1\]的值,是否有j = record\[k\](同列)、|record\[k\] - j| = | k-i |(同一斜线)的情况。由于我们的策略,无需检查行(每行只放一个)。 public class NQueens { public static int num1(int n) { if (n < 1) { return 0; } int[] record = new int[n]; return process1(0, record, n); } public static int process1(int i, int[] record, int n) { if (i == n) { return 1; } int res = 0; for (int j = 0; j < n; j++) { if (isValid(record, i, j)) { record[i] = j; res += process1(i + 1, record, n); } }//对于当前行,依次尝试每列 return res; } //判断当前位置是否可以放置 public static boolean isValid(int[] record, int i, int j) { for (int k = 0; k < i; k++) { if (j == record[k] || Math.abs(record[k] - j) == Math.abs(i - k)) { return false; } } return true; } public static void main(String[] args) { int n = 8; System.out.println(num1(n)); } } # 优化2: # **分析:棋子对后续过程的影响范围:本行、本列、左右斜线。** ![20181129150412832.png][] **黑色棋子影响区域为红色** **本行影响不提,根据优化一已经避免** **本列影响,一直影响D列,直到第一行在D放棋子的所有情况结束。** **左斜线:每向下一行,实际上对当前行的影响区域就向左移动** **比如:** **尝试第二行时,黑色棋子影响的是我们的第三列;** **尝试第三行时,黑色棋子影响的是我们的第二列;** **尝试第四行时,黑色棋子影响的是我们的第一列;** **尝试第五行及以后几行,黑色棋子对我们并无影响。** **右斜线则相反:** **随着行序号增加,影响的列序号也增加,直到影响的列序号大于8就不再影响。** **我们对于之前棋子影响的区域,可以用二进制数字来表示,比如:** **每一位,用01代表是否影响。** **比如上图,对于第一行,就是00010000** **尝试第二行时,数字变为00100000** **第三行:01000000** **第四行:10000000** **对于右斜线的数字,同理:** **第一行00010000,之后向右移:00001000,00000100,00000010,00000001,直到全0不影响。** **同理,我们对于多行数据,也同样可以记录了** **比如在第一行我们放在了第四列:** ![20181129151741248.png][] **第二行放在了G列,这时左斜线记录为00100000(第一个棋子的影响)+00000010(当前棋子的影响)=00100010。** **到第三行数字继续左移:01000100,然后继续加上我们的选择,如此反复。** **这样,我们对于当前位置的判断,其实可以通过左斜线变量、右斜线变量、列变量,按位或运算求出(每一位中,三个数有一个是1就不能再放)。** 具体看代码: 注:怎么排版就炸了呢。。。贴一张图吧 ![20181129154913198.png][] public class NQueens { public static int num2(int n) { // 因为本方法中位运算的载体是int型变量,所以该方法只能算1~32皇后问题 // 如果想计算更多的皇后问题,需使用包含更多位的变量 if (n < 1 || n > 32) { return 0; } int upperLim = n == 32 ? -1 : (1 << n) - 1; //upperLim的作用为棋盘大小,比如8皇后为00000000 00000000 00000000 11111111 //32皇后为11111111 11111111 11111111 11111111 return process2(upperLim, 0, 0, 0); } public static int process2(int upperLim, int colLim, int leftDiaLim, int rightDiaLim) { if (colLim == upperLim) { return 1; } int pos = 0; //pos:所有的合法位置 int mostRightOne = 0; //所有合法位置的最右位置 //所有记录按位或之后取反,并与全1按位与,得出所有合法位置 pos = upperLim & (~(colLim | leftDiaLim | rightDiaLim)); int res = 0;//计数 while (pos != 0) { mostRightOne = pos & (~pos + 1);//取最右的合法位置 pos = pos - mostRightOne; //去掉本位置并尝试 res += process2( upperLim, //全局 colLim | mostRightOne, //列记录 //之前列+本位置 (leftDiaLim | mostRightOne) << 1, //左斜线记录 //(左斜线变量+本位置)左移 (rightDiaLim | mostRightOne) >>> 1); //右斜线记录 //(右斜线变量+本位置)右移(高位补零) } return res; } public static void main(String[] args) { int n = 8; System.out.println(num2(n)); } } 完整测试代码: 32皇后:结果/时间 暴力搜:时间就太长了,懒得测。。。 ![20181129154155159.png][] public class NQueens { public static int num1(int n) { if (n < 1) { return 0; } int[] record = new int[n]; return process1(0, record, n); } public static int process1(int i, int[] record, int n) { if (i == n) { return 1; } int res = 0; for (int j = 0; j < n; j++) { if (isValid(record, i, j)) { record[i] = j; res += process1(i + 1, record, n); } } return res; } public static boolean isValid(int[] record, int i, int j) { for (int k = 0; k < i; k++) { if (j == record[k] || Math.abs(record[k] - j) == Math.abs(i - k)) { return false; } } return true; } public static int num2(int n) { if (n < 1 || n > 32) { return 0; } int upperLim = n == 32 ? -1 : (1 << n) - 1; return process2(upperLim, 0, 0, 0); } public static int process2(int upperLim, int colLim, int leftDiaLim, int rightDiaLim) { if (colLim == upperLim) { return 1; } int pos = 0; int mostRightOne = 0; pos = upperLim & (~(colLim | leftDiaLim | rightDiaLim)); int res = 0; while (pos != 0) { mostRightOne = pos & (~pos + 1); pos = pos - mostRightOne; res += process2(upperLim, colLim | mostRightOne, (leftDiaLim | mostRightOne) << 1, (rightDiaLim | mostRightOne) >>> 1); } return res; } public static void main(String[] args) { int n = 32; long start = System.currentTimeMillis(); System.out.println(num2(n)); long end = System.currentTimeMillis(); System.out.println("cost time: " + (end - start) + "ms"); start = System.currentTimeMillis(); System.out.println(num1(n)); end = System.currentTimeMillis(); System.out.println("cost time: " + (end - start) + "ms"); } } [20181129150412832.png]: /images/20220413/f98620efe56547418f1c4e393331ea72.png [20181129151741248.png]: /images/20220413/92c2b45d6f054251949d45fe8537f832.png [20181129154913198.png]: /images/20220413/9afccc4b409c42948cebe6d0fd7a9467.png [20181129154155159.png]: /images/20220413/9d15b000a58b4041b0074c29c182f481.png
相关 皇后问题 问题 > 国际象棋中的皇后,可以横向、纵向、斜向移动。如何在一个NXN的棋盘上放置N个皇后,使得任意两个皇后都不在同一条横线、竖线、斜线方向上? 举个栗子,下图的绿色格 Bertha 。/ 2022年12月06日 12:52/ 0 赞/ 179 阅读
相关 算法分析---8皇后问题---N皇后问题 ![在这里插入图片描述][20210406171121133.png] package demo1; public class NkingsSort 一时失言乱红尘/ 2022年11月16日 05:51/ 0 赞/ 177 阅读
相关 八皇后问题 一、问题描述 ![Center][] 在8x8的国际棋盘上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能同一行、同一列、同一斜线上。 八皇后问题可以扩展为n ╰+哭是因爲堅強的太久メ/ 2022年07月14日 00:17/ 0 赞/ 218 阅读
相关 八皇后问题 回溯法求解八皇后问题 n皇后问题:n皇后问题是指在一个n\n的国际象棋棋盘上放置n个皇后,使得这n个皇后两两不在同一行,同一列,同一条对角线上,求合法的方案数。 如 小灰灰/ 2022年06月11日 08:49/ 0 赞/ 274 阅读
相关 八皇后问题 枚举法 include<iostream> using namespace std; int a[9]; int check(int n, 蔚落/ 2022年06月06日 00:38/ 0 赞/ 228 阅读
相关 八皇后问题 在国际象棋中,皇后是最强大的一枚棋子,可以吃掉与其在同一行、列和斜线的敌方棋子.八皇后问题是这样一个问题:将八个皇后摆在一张8\8的国际象棋棋盘上,使每个皇后都无法吃掉别的皇 Bertha 。/ 2022年05月18日 05:58/ 0 赞/ 226 阅读
相关 N 皇后问题 n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。 每一种解法包 喜欢ヅ旅行/ 2022年05月17日 04:52/ 0 赞/ 233 阅读
相关 皇后问题 八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横 绝地灬酷狼/ 2022年04月13日 12:28/ 0 赞/ 223 阅读
相关 八皇后问题 include <iostream> include <cmath> include <cstring> using namespace std 港控/mmm°/ 2022年01月31日 00:29/ 0 赞/ 296 阅读
相关 八皇后问题 八皇后问题,以国际象棋为背景:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任意两个皇后都不能处于 叁歲伎倆/ 2021年09月25日 15:34/ 0 赞/ 460 阅读
还没有评论,来说两句吧...