n皇后问题
n皇后是一个经典的递归回溯问题,以八皇后问题为例提供三种解法:递归、非递归、位运算。
优秀博客链接1:https://www.jianshu.com/p/d16707207de8
优秀博客链接2:https://blog.csdn.net/xiexinxinlove/article/details/38797227
问题描述
八皇后问题是在一个 88 的棋盘上放置皇后,要求其放置后满足*同一行,同一列,同一斜线上**没有重复的皇后出现。
试问有多少种摆盘方式?
递归回溯
算法思想
递归的思路很清晰:
- 就是对每一行的8列逐个进行判断,如果可以放置就进行下一行的尝试;
- 如果一行都不能放置,则退回上一行(回溯),那么就会回到上一行继续检索剩余列
**注:很容易想到需要一个88的矩阵来存放皇后,这里可以优化一下
- 因为对于每一个可行解,同一行仅一个皇后存放
- 所以用n元组x[1:n]表示n后问题的解,x[i]表示皇后i放在第i行的第x[i]列
- 由于任意两个皇后不在同一列,所以解向量x[i]互不相同
————————————————————————————————————
Dfs()递归,Judge()判断可行解
1 |
|
关键是Judge()判断可行解了,若两个皇后的位置分别是(i,j)和(k,l)
- 同行不重复:x[i]表示的就是不同行的
- 同列不重复:x[i]&&x[k]
- 同对角线不重复:
- 若2个皇后处于同一斜线,则 k-i = l-j (斜率为1的斜线)或 k-i = j-l(斜率为-1的斜线)
- 即 abs(i-k)$$abs(x[i]-x[k])
——————————————————————————————————————————————1
2
3
4
5
6
7bool Judge(int k){ //判断第k行的x[k]是否可行
for(int i = 1; i < k; ++i){ //遍历1~k行的皇后
if((abs(k-i)==abs(a[k]-a[i]) || a[k]==a[i]))
return false;
}
return true;
}
迭代回溯
迭代回溯就是递归转非递归,Judge()不用改,Dfs()改成solve();
关于转换,属于不看答案可能这辈子都想不到系列;
只能说多磨练多思考吧。
——————————————————————————————————————————————
1 |
|
优雅的位运算
位运算确实优雅
关于位运算的解法,优秀博客链接1里介绍的很详细,只稍作总结
算法思想
对于寻找第k行可放置的皇后,用8位二进制数记录1~k-1行产生的冲突;
- 1表示有冲突,那么第k行不能放置
- 0表示无冲突,第k行可以尝试放置
如果8位二进制全为1,显然这一行不能放了,于是回溯;
参考优秀博客链接1中的例子,a、b、c分别记录同列冲突、右斜线冲突、左斜线冲突;d = a|b|c
得到1k-1行产生的冲突(如果对d取反就是第k可放置的皇后);d)
`bit = (d+1) & (得到一个可放置的皇后的位置;
递归寻找下一行需要更新a,b,c
a=a|bit, b=(b|bit)>>1, c=(c|bit)<<1;
寻找同一行的其它皇后需要只更新d
d |= bit;
***注:代码中的
b=limit&((b|bit)>>1), c=limit&((c|bit)<<1)limit=(1<<n)-1`为什么要n个1和b、c进行与运算呢?
这属于唯一尚未明白的地方,先放在这吧~
另外很多博客里的位运算解法与上面的稍有不同,但思路一样,可以参考优秀博客链接2
————————————————————————————————————————————
1 |
|
洛谷·P1219 [USACO1.5]八皇后
洛谷的这道就是n皇后问题,n的范围[6,13];
亲测使用递归回溯、非递归回溯时,最后一个测试n=13会超时(凭啥别人写的递归就能过);
但位运算肯定能AC啦。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!