采用比特位运算改进的N皇后算法

/**/ /************************************************************************/
/**/ /* 目前最快的N皇后递归解决方法                                          */
/**/ /************************************************************************/
#include 
< iostream >
using   namespace  std;
#include 
< time.h >
long  sum = 0 ,upperlim = 1 ;
// 该算法的思想是遍历每一行(虽然算法中没有直接体现行遍历),然后对每一行中的所有
// 有效列(即与上面行已放置皇后在列和左右对角线方向上不会造成冲突的列)进行尝试
// row是代表已经遍历的列,1表示该列已被皇后占有了
void  GetSum( long  row,  long  rightdiagonal,  long  leftdiagonal)
...
{
    
//每一列都被皇后占领了,表示该方案可行
    if (row != upperlim)
    ...
{
        
//下面进行行遍历
        
//row表示当前行中每一列放置了皇后,则该列在之后的行遍历中均不能再放置皇后了
        
//rightdiagonal表示在下一行的尝试中当前放置点的左一列位置(下一行左一列的点与当前放置
        
//点刚好形成了右对角线,会造成冲突)不可用
        
//同样的leftdiagonal表示在下一行的尝试中当前放置点的右一列(下一行右一列的点与当前放置
        
//点刚好形成了左对角线,会造成冲突)不可用
        long pos = upperlim & ~(row | leftdiagonal | rightdiagonal);
        
//这里得到该行中所有可以尝试的列(格),然后从右到左进行尝试
        
//如果遍历到的该行上的每一列已经都是无效的话则无需进行下一行的尝试了
        while (pos)
        ...
{
            
//该位操作能得到一个二进制数组中最右面的1的位置,如pos = 00010110进行如下操作之后
            
//p = 00000010,p的作用是取得当前遍历的行中所有有效列的最右面的一列(格)(其实每次取
            
//最左的列也行的)
            long p = pos & -pos;
            pos 
-= p;
            
//p代表的是当前行上放置的列,递归调用函数遍历下一行
            
//此时需将本行的尝试的列在row中标记上表明下一行不可再在此列放置皇后了
            
//同时,在下一行中本放置点的左一列和右一列也需标记为不可再放置
            GetSum(row+p, (rightdiagonal+p)<<1, (leftdiagonal+p)>>1);
        }

    }

    
else
    ...
{
        sum
++;
    }

}

int  main( int  argc,  char   * argv[])
...
{
    time_t tm; 
int n=15;
    
if(argc!=1)
        n
=atoi(argv[1]);
    tm
=time(0);
    
if((n<1)||(n>32))
    ...
{
        printf(
" heh..I can't calculate that. ");
        exit(
-1);
    }


    printf(
"%d Queens ",n);
    
//upperlim中前n位为1,用于标识探索是否已完成
    upperlim=(upperlim<<n) - 1;
    GetSum(
0,0,0);
    printf(
"Number of solutions is %ld, %d seconds ", sum,(int)(time(0)-tm));
    
return 0;
}


下面一段转的是人家用string类写的一个实现.效率较低,但是比较好懂

#include 
< iostream >
#include 
< string >
using   namespace  std;
// t表示被占领的行,s表示未测试过的行
void  queen( const   string  t,  const   string  s)
{
    
if (s=="") printf("%s  ",t.c_str());
    
else
        
//第一个for循环测试当前测试列的每一行
        for (int i=0; i<s.length(); i++{
            
bool safe=true;
            
//第二个for循环测试当前点与之前点是否处于同一对角线上
            
//由于字符串的设计巧妙,使判断冲突情况限制在了对角线的情况上而已
            for (int j=0;j<t.length();j++)
            
{
                
//列间隔等于行间隔
                if (t.length()-j==abs(s[i]-t[j]))
                
{
                    safe
=false;
                    
break;
                }

            }

            
if (safe)
                queen(t
+s[i], s.substr(0,i)+s.substr(i+1));
        }

}


int  main()
{
    
//string中的每一位代表一列,从左到右开始,而每一位中的数字代表了每一行
    
//由于每个位的数字都是唯一的,因此可以保证已测试列和待测试列之间不会有
    
//行相同的情况
    string s="01234567";
    queen(
"",s);
    exit(EXIT_SUCCESS);
    
return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值