八皇后问题

来源:互联网 发布:巴适公交暂无数据 编辑:程序博客网 时间:2024/06/05 18:21

八皇后问题很老很经典了,一直有一点印象但没有好好的自己写过代码。


解决八皇后问题最关键的地方我认为应该是判断一个位置是否适合放入一个皇后。


有两种判断方法,先说最自然也是最容易想到的一种方法。


首先很容易得知每一行只能放一个皇后,可以用一个数组b[8]来表示八行每个皇后放在第几个位置。


要判断一个位置是否能放入一个新的皇后要做两个判断:前面几行有没有皇后放在这一列,两条对角线上是否有皇后。


用i来表示放入的列,now表示当前行,所以首先要检查 b[0..now-1]是否有等于i,如果有就表示这一列已经有皇后了。


然后是两边对角线, 要判断皇后board[j]是否影响到将要放入的位置要做两个判断,(应该很容易想到,就是斜率是否相等。)


i - j != b[i] - b[j] 

i - j != b[j] - b[i]

即 |i - j| != |b[i] - b[j]|

知道如何判断后,接下来的问题就简单了,利用回溯法递归。


每次递归判断是否已经放入八个皇后,是输出或者排列加一。否则判断0-7列是否有满足条件的位置,满足则进入下一级递归。


代码如下:

std::vector<int>board(8, 0);void MyNQueen(int now){ static int nums = 0; if (!board.size())  return; if (now == board.size())  //判断是否已经放入N个皇后 {  nums++;  printf("Case%d\n", nums);  for (auto i = 0; i < board.size(); i++) //打印棋盘,可以去掉  {   for (int j = 0; j < board.size(); j++)   {    if (j == board[i])     printf("%c ", 2);    else     printf(". ");   }   printf("\n");  } } else {  for (board[now] = 0; board[now] < board.size(); board[now]++)  {   int i;   for (i = 0; i < now; i++)   {    if (board[i] == board[now] || abs(i - now) == abs(board[i] - board[now]))  //判断该列可否放入皇后     break;   }   if (i == now)    MyNQueen(now + 1);  } }}


上面的这种判断方法效率比较低,我试过把N设为15就要跑很久了,我没等到跑完所以也不知道具体要多久,反正在电脑上不止五分钟了,下面说一种效率较高的办法,利用位运算来判断。


可以用一个整数来表示棋盘的状态,每一位代表这一行是否已放入皇后,设1为是0为否。由于整数位数限制,只能做小于32个皇后。大于32可以用bitset来做。


依然是两个判断,当前列与两条对角线是否有皇后。设int row表示放入皇后的列,l表示当前行左边不能放入皇后的位置,r表示当前行右边不能放入皇后的位置。


三个数的初始值均为0,设全1的数为up,pos =  up&~(row | l | r) 即可表示各个可以放入皇后的位置。


p = pos &(~pos +1) 即为pos最右边的1,选中它为当前放入位置,下一行的row,l,r则分别为row + p, (l + p) << 1, (r + p) >> 1。


这个地方要注意,每次递归的l与r都加上了之前所影响的位置。


代码如下:


#define N 15long sum = 0, upperlim =( 1 << N) - 1;;void MyBitNQueen(long row, long ld, long rd ){  if (row == upperlim)  sum++; else {  long pos = upperlim & ~(row | ld | rd);  while (pos)  {   long p = pos & -pos; //(~pos + 1)   pos -= p;   MyBitNQueen(row + p, (ld + p) << 1, (rd + p) >> 1);  } }}

这个很快,15个皇后也就6秒多搞定。真是棒!


15年4月2日更新

------------------------------------

今天突然看到一个用迭代的办法解决八皇后的代码,觉得很有意思。

理论上所有的递归都可以用迭代,但是有的真的很难想到啊……

#define N 8int sum=0;int *x=new int[N+1];bool place(int k){int i;for(i=1; i<k; i++){if(x[i]==x[k] || abs(i-k)==abs(x[i]-x[k]))return false;}return true;}void backtrack2()//迭代回溯{int i;x[1]=0;int k=1;while(k>0){x[k]+=1;//当前列加1的位置开始搜索while((x[k]<=N) && !place(k))//当前列位置是否满足条件x[k]+=1;//不满足条件,继续搜索下一个位置if(x[k]<=N)//存在满足条件的列{if(k==N)//是最后一个皇后,完成搜索{for(i=1; i<=N; i++)cout<<x[i]<<" ";cout<<endl;sum++;}else//不是,则处理下一个皇后{k++;x[k]=0;}}else//回溯{k--;}}}


参考:http://blog.csdn.net/cxllyg/article/details/8055596





0 0
原创粉丝点击