CSDN真恶心

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

使用普通的回溯法解n皇后问题,当n>13时等待时间就开始难以忍受。但使用位操作的解法,当n=13时运行时间小于一秒,当n=16时在我的机器上运行时间为37秒。

要求在0.163s内解完13皇后,是USACO1.5的最后一题。以下是c++实现:

 

 1 #include <iostream>  
2 using namespace std;
3
4 int Ans;
5 int N;
6 int Mask;
7
8 void dfs ( int x, int ld, int rd )
9 {
10 if ( x == Mask )
11 {
12 Ans ++;
13 return;
14 }
15 int avail = ~ ( x | ld | rd ) & Mask;
16 int i;
17 while ( avail )
18 {
19 i = avail & ( -avail ); // 取最低位
20 avail -= i;
21 dfs ( x + i, ( ld + i ) << 1, ( rd + i ) >> 1 );
22 }
23 }
24
25 int main ()
26 {
27 cin >> N;
28 Mask = ( 1 << N ) - 1;
29 Ans = 0;
30 dfs ( 0, 0, 0 );
31 cout << Ans << endl;
32 system("pause");
33 return 0;
34 }

 

算法在每次递归时向下传递三个参数,实际上可以认为是三个二进制mask。这三个mask分别代表了

  1.已放置的棋子占有的列数

  2.会被棋子在左对角线上攻击到的列数

  3.会被棋子在右对角线上攻击到的列数

程序第15行在将三个mask叠加后取反,得到当前依旧可以放置棋子的位置,得到的结果放在 avali 中。

程序第19行取得avail的最低有效位,也就是从右往左数第一个出现1的位置。注意到 -avali 是对 avali 取补码而不是取反。得到的结果放在 i 中。

程序第20行用 avali 减去 i,表示该位置现在被占用。当 avali 不等于零时,就意味着该行中依旧有其它位置尚未尝试过,用 while 循环来遍历该行的所有结果。

程序第21行向下一层进行递归,更新三个mask。注意到 ld 和 rd 是在上一步的基础上继续左移/右移。

递归的终止条件有两个,一个是 mask == x,意味着所有的位置都已摆满,该棋盘成功。另一个是 avali == 0,意味着该棋盘需要被丢弃。

posted on 2011-12-27 12:21  Kid桑  阅读(221)  评论(0编辑  收藏  举报