n皇后问题是大家在递归里会碰到的一个经典问题。以前高中我学DFS的时候,老师首先让我看的就是八皇后。
不过这皇后的时间复杂度大家可想而知了。而接下来的位运算将这个效率重新提到一个高度。
我是以前在Matrix67大牛那里学的,最近数据结构实验刚好碰到n皇后,就在这里“复述”一遍吧。
- void doans(int r, int ld, int rd)
- {
- if(r != uplimit)
- {
- int pos = uplimit & ~(r | ld | rd);
- while(pos != 0)
- {
- int p = pos & (~pos + 1);
- pos -= p;
- doans(r + p, (ld + p) << 1, (rd + p) >> 1);
- }
- }
- else sum++;
- }
乍一看有点模糊吧?没错,这就是n皇后的递归函数了。我们一个个来解析。
首先uplimit是(1 << n) - 1,如果n是8的话uplimit就是255,看做二进制就是11111111。聪明的人一看就知道了,这里每一位就代表一个皇后。
而r代表每一列能放与否,如10001110就代表第2、3、4、8个能放。
所以开始一个if来判断皇后放齐了没。如果齐了,显然r也要等于11111111。
还有ld和rd分别是对角线的各位能放与否。
我们来看看下图(From Matrix67):
假设我们已经递归到第三行了(左图),这里可以看出r为101010也就是说二、四、六可以放。ld是100100(蓝色线),二、三、五、六可以放,rd为000111,。
好的,那么哪几个可以放呢?我们将r、ld、rd或运算一遍(或者r有或者ld有或者rd有),得到101111,也就是说这三个合起来的话就只能放第二格了。然后我非运算一遍,得到的是010000,非运算之后1代表可以放,0代表不可以放了。然后我们再与运算一遍就得到可以放的位置了:010000。
下一步,如果能放的位置不是0的话我们就开始放:
- int p = pos & (~pos + 1);
这一句我们可以用