N皇后问题是指在N*N的棋盘上要摆N个皇后,要求任何两个皇后不同行、不同列,也不在同一条斜线上。
给定一个整数n,返回n皇后的摆法有多少种。
举例: n=1,返回1。
n=2或3,2皇后和3皇后问题无论怎么摆都不行,返回0。
n=8,返回92。

  1. public class Code09_NQueens {
  2. public static int num1(int n) {
  3. if (n < 1) {
  4. return 0;
  5. }
  6. int[] record = new int[n]; // record[i] --> i 行的皇后放在了第几列
  7. return process1(0, record, n);
  8. }
  9. // i:表示来到了第几行
  10. // record:之前摆放的皇后,都在这个数组中,即之前 i-1 行的皇后的摆放位置
  11. // n:表示整体一共多少行
  12. // 返回值是:摆完所有的皇后,合理的摆法有多少种
  13. public static int process1(int i, int[] record, int n) {
  14. if (i == n) {
  15. return 1;
  16. }
  17. int res = 0;
  18. // 在 i 行,尝试所有的列:j
  19. for (int j = 0; j < n; j++) {
  20. // 当前i行的皇后,放在第j列,会不会和之前(0...i-1)的皇后共行/共列/共斜线
  21. if (isValid(record, i, j)) {
  22. // 如果是,则认为该位置是无效的
  23. // 如果不是,则认为该位置是有效的
  24. record[i] = j;
  25. res += process1(i + 1, record, n);
  26. }
  27. }
  28. return res;
  29. }
  30. public static boolean isValid(int[] record, int i, int j) {
  31. for (int k = 0; k < i; k++) {
  32. if (j == record[k] || Math.abs(record[k] - j) == Math.abs(i - k)) {
  33. return false;
  34. }
  35. }
  36. return true;
  37. }
  38. public static int num2(int n) {
  39. if (n < 1 || n > 32) {
  40. return 0;
  41. }
  42. int upperLim = n == 32 ? -1 : (1 << n) - 1;
  43. return process2(upperLim, 0, 0, 0);
  44. }
  45. // colLim : 列的限制,1的位置不能放皇后,0的位置可以(采用二进制来计算)
  46. // leftDiaLim :左斜线的限制,1的位置不能放皇后,0的位置可以(采用二进制来计算)
  47. // rightDiaLim :右斜线的限制,1的位置不能放皇后,0的位置可以(采用二进制来计算)
  48. public static int process2(int upperLim, int colLim, int leftDiaLim,
  49. int rightDiaLim) {
  50. if (colLim == upperLim) {
  51. return 1;
  52. }
  53. int pos = 0;
  54. int mostRightOne = 0;
  55. // 所有候选的皇后位置都在 pos 上,1为可选位置,0为不可选位置
  56. pos = upperLim & (~(colLim | leftDiaLim | rightDiaLim));
  57. int res = 0;
  58. while (pos != 0) {
  59. mostRightOne = pos & (~pos + 1);
  60. pos = pos - mostRightOne;
  61. res += process2(upperLim, colLim | mostRightOne,
  62. (leftDiaLim | mostRightOne) << 1,
  63. (rightDiaLim | mostRightOne) >>> 1);
  64. }
  65. return res;
  66. }
  67. public static void main(String[] args) {
  68. int n = 14;
  69. long start = System.currentTimeMillis();
  70. System.out.println(num2(n));
  71. long end = System.currentTimeMillis();
  72. System.out.println("cost time: " + (end - start) + "ms");
  73. start = System.currentTimeMillis();
  74. System.out.println(num1(n));
  75. end = System.currentTimeMillis();
  76. System.out.println("cost time: " + (end - start) + "ms");
  77. }
  78. }

二 采用回溯法

  1. /**
  2. * 算法:回溯法
  3. * 问题:八皇后
  4. * @author win
  5. */
  6. public class EightQueen {
  7. public static int num = 0;//累计方案
  8. public static final int MAXQUEEN = 8;
  9. public static int[] cols = new int[MAXQUEEN];//定义cols数组,表示八列棋子摆放的位置
  10. /***
  11. *
  12. * @param n 填第n列的皇后
  13. */
  14. public void getCount(int n){
  15. boolean [] rows = new boolean[MAXQUEEN];//记录每列每个方格是否可以放皇后。为true表示不能放
  16. for(int m =0;m<n;m++){
  17. rows[cols[m]] = true;
  18. int d = n - m;//斜对角
  19. //正斜方向
  20. if(cols[m] - d>=0){
  21. rows[cols[m] - d] = true;
  22. }
  23. //反斜方向
  24. if((cols[m]+d)<=MAXQUEEN-1){
  25. rows[cols[m] + d] = true;
  26. }
  27. }
  28. //到此知道了哪些位置不能放皇后
  29. for(int i = 0;i<MAXQUEEN;i++){
  30. if(rows[i]){
  31. //不能放
  32. continue;
  33. }
  34. cols[n] = i;
  35. if(n<MAXQUEEN-1){
  36. getCount(n+1);
  37. }else{
  38. //如果n = MAXQUEEN-1;表示找到了一套完整的方案
  39. num++;
  40. printQueen();
  41. }
  42. //下面可能仍有合法位置
  43. }
  44. }
  45. private void printQueen() {
  46. System.out.println("第"+num+"种方案");
  47. for(int i = 0;i<MAXQUEEN;i++){
  48. for(int j=0;j<MAXQUEEN;j++){
  49. if(i == cols[j]){
  50. System.out.print("0 ");
  51. }else{
  52. System.out.print("+ ");
  53. }
  54. }
  55. System.out.println();
  56. }
  57. }
  58. public static void main(String[] args) {
  59. EightQueen eightQueen = new EightQueen();
  60. eightQueen.getCount(0);
  61. }
  62. }