回溯算法-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: }
浙公网安备 33010602011771号