八皇后问题详解及代码实现(位运算算法)

原创
2016/07/22 09:51
阅读数 3.1K

        八皇后问题是一个以国际象棋为背景的问题:如何能够在8×8的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。当且仅当n = 1或n ≥ 4时问题有解。

以下使用位运算来求解N皇后的高效算法:

public class App {
    static int upperlimit;
    static int count;
    public static void main(String[] args) throws Exception {
        int n = 6;
        upperlimit =  (1 << n)-1;
        test(0,0,0);
        System.out.println(count);
    }
     
    static void test(int row,int ld,int rd){
        int pos, p;
         
        //row == upperlim,说明皇后全部放进去了 
         
        if(row != upperlimit){
             
            //row,ld,rd进行“或”运算,如:000101 ,1的列表示已经放置了皇后
            //pos 取反得 111010,1表示可以放皇后的列  
            //upperlimit 是上限值,控制二进制长度:
            //如:(~(row | ld | rd ) = 100001011101时,upperlimit = 111111,pos= 011101 
            pos = upperlimit & (~(row | ld | rd ));  
            while (pos != 0) {
                 
                // 找出可以放皇后的位置(默认从右到左),如:p=000100,表示该行右边第三个位置可以放皇后。
                // p就表示该行的某个可以放皇后的位置,把皇后放在这个位置上后,就把它从pos中移除并递归调用test过程。
                p = pos & (~pos + 1);  
                 
                // 把皇后放在这个位置上后,就把它从pos中移除并递归调用test过程。
                // 如:pos = 111100时,p = 000100,把皇后放在这个位置上后, pos = pos - p, pos=111000
                pos = pos - p;  
                //(row|p) 是计算已经在对应列上面的皇后
                //(ld | p)<< 1 是因为由ld造成的占位在下一行要右移一下;
                // (rd | p)>> 1 是因为由rd造成的占位在下一行要左移一下
                test((row|p),(ld|p)<<1,(rd|p)>>1);
            }
        }else {
            ++count;
        }
    }
}

初始化: upperlim =  (1 << n)-1;

调用参数:test(0, 0, 0);

函数带三个参数:

row 纵列

ld 左对角线

rd 右对角线

这是一个递归函数,程序一行一行地寻找可以放皇后的地方。函数带三个参数row、ld和rd,分别表示在纵列和两个对角线方向的限制条件下这一行的哪些地方不能放。位于该行上的冲突位置就用row、ld和rd中的1来表示。把它们三个并起来,得到该行所有的禁位,取反后就得到所有可以放的位置(用pos来表示)。

pos = upperlim & (~(row | ld | rd ));

p = pos & (~pos + 1) 找出可以放皇后的位置(默认从右到左),如:p=000100,表示该行右边第三个位置可以放皇后。

p就表示该行的某个可以放皇后的位置,把皇后放在这个位置上后,就把它从pos中移除并递归调用test过程。

如:pos = 111100时,p = 000100,把皇后放在这个位置上后, pos = pos - p, pos=111000

如果递归到某个时候发现row=upperlim了,说明n个皇后全放进去了,找到的解的个数加一。

 

(ld | p)<< 1 是因为由ld造成的占位在下一行要右移一下;

(rd | p)>> 1 是因为由rd造成的占位在下一行要左移一下

 图1:     

根据图1,第一行放皇后的位置,推算出第二行那些列不能放皇后

row = 000001
 ld = 000010
 rd = 000000
pos = upperlimit & (~(row | ld | rd ));  
pos = 111100,1的列都可以放皇后
p = pos & (~pos + 1);
p = 000100,所以该行,右边第三列放皇后

结果如图2:

图2:

根据图2,第二行放皇后的位置,推算出第三行那些列不能放皇后

row = 000101
 ld = 001100
 rd = 000010
pos = upperlimit & (~(row | ld | rd ));  
pos = 110000,1的列都可以放皇后
p = pos & (~pos + 1);
p = 010000

结果如图3:

图3:

根据图3,第三行放皇后的位置,推算出第四行那些列不能放皇后

row = 010101
 ld = 111000
 rd = 001001
pos = upperlimit & (~(row | ld | rd ));  
pos = 000010,1的列都可以放皇后
p = pos & (~pos + 1);
p = 000010

结果如图4:

图4:

根据图4,第四行放皇后的位置,推算出第五行那些列不能放皇后

row = 010111
 ld = 110100
 rd = 000101
pos = upperlimit & (~(row | ld | rd ));  
pos = 001000,1的列都可以放皇后
p = pos & (~pos + 1);
p = 001000

结果如图5:

图5:

根据图5,第五行放皇后的位置,推算出第六行那些列不能放皇后

row = 011111
 ld = 111000
 rd = 000110
pos = upperlimit & (~(row | ld | rd ));  
pos = 000000,1的列都可以放皇后,发现没有可放皇后的位置

程序采用了递归,也就是借用了编译系统提供的自动回溯功能。

 

转载本站文章请注明作者和出处 CodingMen – Coding.men

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
1 收藏
0
分享
返回顶部
顶部