代码改变世界

回溯算法-8皇后问题

2012-07-31 19:53  coodoing  阅读(547)  评论(0)    收藏  举报

8皇后问题表述:在n×n格的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于在n×n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。求不同的解的个数。

解向量:(x1, x2, … , xn)

显约束:xi = 1,2, … ,n

隐约束:

    1)不同列:xi != xj

    2)不处于同一正、反对角线:|i-j| != |x(i)-x(j)|

利用递归回溯和迭代回溯两种不同的方法实现:

   1: // 皇后问题等价于求所有满足条件的子集树
   2: public class NQueueProblem {
   3:  
   4: private int[] yPos;
   5: private int n;
   6: private static int sum;
   7:  
   8: public NQueueProblem(int n) {
   9:     this.n = n;
  10:     init(n);
  11: }
  12:  
  13: private void init(int n) {
  14:     yPos = new int[n];
  15:     for (int i = 0; i < n; i++) {
  16:         yPos[i] = 0;
  17:     }
  18: }
  19:  
  20: // 递归回溯方法进行求解 
  21: // 将皇后置于第t行
  22: private void backtrack(int t) {
  23:     if (t == n) {
  24:         sum++;
  25:         //输出当前解 
  26:         printSolution();
  27:     } else {
  28:         for (int j = 0; j < n; j++) {
  29:             yPos[t] = j; // 皇后放置在第t行j列  
  30:             if (isPlace(t)) {
  31:                 // 考虑第t+1行
  32:                 backtrack(t + 1);
  33:             }
  34:         }
  35:     }
  36: }
  37:  
  38: // 迭代回溯实现
  39: // 采用树的非递归深度优先遍历算法,将回溯法表示为一个非递归迭代过程
  40: private void iterativeBacktrack() {
  41:     int t = 0; //从第0行开始迭代  
  42:     yPos[t] = -1;
  43:     while (t>=0) {
  44:         yPos[t] += 1; // 向右移一列
  45:         /* 向右移至出最右列或可以放置皇后的列 */
  46:         while ((yPos[t] <n) && !(isPlace(t))) {
  47:             yPos[t] += 1;
  48:         }
  49:         // 向右移未移出棋盘 
  50:         if (yPos[t] < n)
  51:         {
  52:             // 已移至最后一行  
  53:             if (t == n-1) { 
  54:                 sum++;  
  55:                 printSolution();
  56:             } 
  57:             else {
  58:                 /* 向下移一行 */  
  59:                 t++;
  60:                 yPos[t] = -1;
  61:             }
  62:         } 
  63:         else {
  64:             // 即yPos[t]>n, 迭代到最后一列仍无解,则回溯到前一行  
  65:             t--;
  66:         }
  67:     }
  68: }
  69:  
  70: private boolean isPlace(int t) {
  71:     for (int i = 0; i < t; i++) {
  72:         if ((Math.abs(i - t) == Math.abs(yPos[i] - yPos[t]))
  73:                 || (yPos[i] == yPos[t])) {
  74:             return false;
  75:         }
  76:     }
  77:     return true;
  78: }
  79:  
  80: void printSolution() {
  81:     for (int i = 0; i < n; i++) {
  82:         System.out.print("[" + i + "," + yPos[i] + "]" + " ");
  83:         if (i == n - 1) {
  84:             System.out.println();
  85:         }
  86:     }
  87: }
  88:  
  89: public static void main(String[] args) {
  90:     NQueueProblem queen = new NQueueProblem(8);
  91:     System.out.println("递归回溯结果");
  92:     queen.backtrack(0);
  93:     System.out.println("迭代回溯结果");
  94:     queen.iterativeBacktrack();
  95:     
  96: }
  97: }

另外附上一种快速位运算,运算速度显著提高.

   1: public class Queen_Fastest {
   2:  
   3: public static int sum = 0, upperlimit = 1;
   4:  
   5: //放皇后时,从右到左递归放(右边是低位,左边是高位)
   6: /**
   7:  * 三个参数每一位代表一列,bit为1的位置不能放置皇后(与上面放置的皇后在45度角或垂直方向上有冲突)
   8:  * @param row 位为1的列说明上面某一行在此列己放置一个皇后
   9:  * @param ld 位为1的说明对应的左上角线己有皇后
  10:  * @param rd 位为1的说明对应的右上角线己有皇后
  11:  */
  12: public static void compute(int row, int ld, int rd) {
  13:     
  14:     if (row != upperlimit) {
  15:         int pos = upperlimit & ~(row | ld | rd);
  16:         //对当前行所有可以放置皇后的地方放置一个皇后,然后进入下一行
  17:         while (pos != 0)
  18:         {
  19:             int p = pos & -pos;    //取得最右边可放置皇后的位置
  20:             pos -= p;//在去掉这个位置,说明这个位置己以测试过
  21:             //放置下一行的皇后,修改三个参数垂直与45度角上以前放置皇后点据的下一行的位置
  22:             compute(row + p, (ld + p) << 1, (rd + p) >> 1);
  23:         }
  24:  
  25:     } else //row所有列都为1,说明己成功得到一个方案
  26:         sum++;
  27: }
  28:  
  29:  
  30: public static void main(String[] args) {
  31:     Calendar start;
  32:     int n = 8;
  33:     if (args.length > 0)
  34:         n = Integer.parseInt(args[0]);
  35:     start = Calendar.getInstance();
  36:     if ((n < 1) || (n > 32)) {
  37:         System.out.println(" 只能计算1-32之间\n");
  38:         return;
  39:     }
  40:     System.out.println(n + " 皇后\n");
  41:     upperlimit = (upperlimit << n) - 1;
  42:     compute(0, 0, 0);
  43:     System.out.println("共有" + sum + "种排列, 计算时间"
  44:             + (Calendar.getInstance().getTimeInMillis() - start.getTimeInMillis()) + "毫秒 \n");
  45: }
  46:  
  47: }

移位运算:http://vipan.com/htdocs/bitwisehelp.html