八皇后__位运算优化搜索

 

八皇后__位运算优化搜索

分类: 全部博客 ACM_恶心模拟 ACM_好题经典题   364人阅读  评论(0)  收藏  举报
题目大意: 和A+B一样经典的八皇后问题,要求输出前三个字典序最小的解,以及总的方案数。

解题思路: 如果N比较小,那么随便搜都可以过。但如果N大等于10,就要求对程序进行优化。

         这题我很奇葩地用广搜来做,各种状态压缩压线飘过,搓的一逼。AC后去网上找了几篇解题报告,发现用位运算来优化深搜,非常飘逸。

         其中属Matrix67的文章分析地最透彻,最好理解。想深入理解可移步:http://www.matrix67.com/blog/archives/268。

         在这里我把集中常用的运算整理下:

[cpp]  view plain copy
  1. 去掉最后一位          | (101101->10110)           | x >> 1  
  2. 在最后加一个0         | (101101->1011010)         | x << 1  
  3. 在最后加一个1         | (101101->1011011)         | (x << 1) + 1  
  4. 把最后一位变成1       | (101100->101101)          | x | 1  
  5. 把最后一位变成0       | (101101->101100)          | (x | 1)-1  
  6. 最后一位取反          | (101101->101100)          | x ^ 1  
  7. 把右数第k位变成1      | (101001->101101,k=3)      | x | (1 << (k-1))  
  8. 把右数第k位变成0      | (101101->101001,k=3)      | x&(~(1 << (k-1))  
  9. 右数第k位取反         | (101001->101101,k=3)      | x ^ (1 << (k-1))  
  10. 取末三位              | (1101101->101)            | x & 7  
  11. 取末k位               | (1101101->1101,k=5)       | x & (1 << (k-1))  
  12. 取右数第k位           | (1101101->1,k=4)          | (x >> (k-1)) & 1  
  13. 把末k位变成1          | (101001->101111,k=4)      | x | (1 << (k-1))  
  14. 末k位取反             | (101001->100110,k=4)      | x ^ (1 << (k-1))  
  15. 把右边连续的1变成0    | (100101111->100100000)    | x & (x+1)  
  16. 把右起第一个0变成1    | (100101111->100111111)    | x | (x+1)  
  17. 把右边连续的0变成1    | (11011000->11011111)      | x | (x-1)  
  18. 取右边连续的1         | (100101111->1111)         | (x ^ (x+1)) >> 1  
  19. 去掉右起第一个1的左边 | (100101000->1000)         | x & (x ^ (x-1))  

测试数据:

13
1 3 5 2 9 12 10 13 4 6 8 11 7
1 3 5 7 9 11 13 2 4 6 8 10 12
1 3 5 7 12 10 13 6 4 2 8 11 9
73712


代码:
[cpp]  view plain copy
  1. /* 
  2. ID:imonlyc1 
  3. PROG:checker 
  4. LANG:C++ 
  5.  */  
  6.   
  7. #include <stdio.h>  
  8. #include <string.h>  
  9.   
  10.   
  11. int state,cnt;  
  12. int ans[15],n;  
  13.   
  14.   
  15. int GetCol(int a) //不用 log 节省时间  
  16. {  
  17.     switch (a)  
  18.     {  
  19.     case 1:  
  20.         return 1;  
  21.     case 2:  
  22.         return 2;  
  23.     case 4:  
  24.         return 3;  
  25.     case 8:  
  26.         return 4;  
  27.     case 16:  
  28.         return 5;  
  29.     case 32:  
  30.         return 6;  
  31.     case 64:  
  32.         return 7;  
  33.     case 128:  
  34.         return 8;  
  35.     case 256:  
  36.         return 9;  
  37.     case 512:  
  38.         return 10;  
  39.     case 1024:  
  40.         return 11;  
  41.     case 2048:  
  42.         return 12;  
  43.     case 4096:  
  44.         return 13;  
  45.     }  
  46. }  
  47.   
  48. void Dfs(int row, int ld, int rd, int deep) {  
  49.   
  50.     int pos, col;  
  51.   
  52.   
  53.     if (row != state) {  
  54.   
  55.         pos = state & ~(row | ld | rd);         //取合法的位置  
  56.         while (pos != 0) {  
  57.   
  58.             col = pos & -pos;                   //取最右边那一列,col = 1<<x。等于pos&((~pos)+1)  
  59.             pos = pos - col;                    //慢慢往左移动  
  60.             if (cnt < 3) ans[deep] = col;       //记录前三个的答案,ans[i]表示第i行放的是x列  
  61.                                                 //三个状态都要做相应改变,对角线一个向左一个向右  
  62.             Dfs(row + col, (ld + col) >> 1, (rd + col) << 1, deep + 1);  
  63.         }  
  64.     }  
  65.     else {  
  66.   
  67.         cnt++;  
  68.         if (cnt <= 3) {  
  69.   
  70.             for (int i = 1; i <= n - 1; i++)  
  71.                 printf("%d ",GetCol(ans[i]));  
  72.             printf("%d\n",GetCol(ans[n]));  
  73.         }  
  74.     }  
  75. }  
  76.   
  77.   
  78. int main()  
  79. {  
  80.     //freopen("checker.in","r",stdin);  
  81.     //freopen("checker.out","w",stdout);  
  82.     scanf("%d",&n);  
  83.     state = (1 << n) - 1;  
  84.   
  85.   
  86.     Dfs(0,0,0,1);  
  87.     printf("%d\n",cnt);  
  88.     return 0;  

你可能感兴趣的:(ACM_恶心模拟,全部博客,ACM_好题经典题)