/*
N 皇后问题
N皇后问题是指在N*N的棋盘上要摆N个皇后
要求任何两个皇后不同行,不同列,也不在同一条斜线上
给定一个整数n,返回n皇后的摆法有多少种
n = 1,返回1
n = 2或3,2皇后和3皇后问题无论怎么摆放都不行,返回0
n = 8返回92
*/
/*
[ 1 ] 1*1 能 或 不能(讨论两个皇后的问题)
2皇后无解 0
[ ] [ ]
[ ] [ ]
3皇后无解 0
[ ] [ ] [ ]
[ ] [ ] [ ]
[ ] [ ] [ ]
4皇后2个解
[ ][1][ ][ ]
[ ][ ][ ][2]
[3][ ][ ][ ]
[ ][ ][4][ ]
反转 得到第二种解法
[ ][ ][1][ ]
[2][ ][ ][ ]
[ ][ ][ ][3]
[ ][4][ ][ ]
*/
func TestQueenNum1(t *testing.T) {
fmt.Println(QueenNum1(100))
}
func QueenNum1(n int) int { //最好尝试方法,但是可以加速常数项
if n < 1 {
return 0
}
// record[0] ? record[1] ? record[2]
record := make([]int, n) //record[i] -> i行的皇后,放在了第几列
return processQueen1(0,record,n)
}
//潜台词:record[0...i-1] (0,i-1)的皇后都存在record里,任何两个皇后一定都不共行、不共列、不共斜线
//目前来到第i行
//record[0...i-1]表示之前的行,放了的皇后位置
//n代表整体一共有多少行 0 ~ n-1 行有效 到了n 就越界了需要停
//返回值时,摆完所有的皇后,合理的摆法有多少种
func processQueen1(i int, record []int,n int) int {
if i == n { //终止行
return 1 //上面的状况 1种
}
//没有到终止为止,还有皇后要摆
res := 0
for j := 0; j < n; j++ { //当前行在i行,尝试i行所有的列 -> j
//当前i行的皇后,放在j列,会不会和之前(0,i-1)的皇后,不共行、列、斜线
//如果是,认为有效
//如果不是,认为无效
if isValid(record,i,j) {
record[i] = j
res += processQueen1(i+1,record,n)
}
}
return res
}
// record[0...i-1]你需要看,record[i...]不需要看
// 返回i行皇后,放在了j列,是否有效
func isValid(record []int, i, j int) bool {
for k := 0; k < i; k++ { //之前某个k行的皇后
if j == record[k] || Abs(record[k] - j) == Abs(i-k) {
return false
}
}
return true
}
func Abs(a int) int {
if a < 0 {
return -a
}
return a
}
// a行 b列 c行 d 列 两个皇后 冲突 有什么样的关系
// 肯定不会在行上打架
// 共列: b == d
// 共斜线: |a-c| == |b-d|
func AbsI32(i int32) int32 {
if i < 0 {
return -i
}
return i
}
//优化常数项,位运算
func TestQueenNum2(t *testing.T) {
for i := 0; i <= 32; i ++ {
if QueenNum1(i) != int(AbsI32(QueenNum2(int32(i)))) {
fmt.Println(i)
return
}
}
fmt.Println("pass")
}
// 请不要超过32皇后问题
func QueenNum2(n int32) int32 {
if n < 1 || n > 32 {
return 0
}
var limit int32 = (1 << n) - 1 // >= 0 && < 32
if n == 32 {
limit = -1
}
return processQueen2(limit,0,0,0)
}
// colLim 列的限制,1的位置不能放皇后,0的位置可以
// leftDiaLim 左斜线的限制,1的位置不能放皇后,0的位置可以
// rightDiaLim 右斜线的限制,1的位置不能放皇后,0的位置可以
func processQueen2(limit, colLim, leftDiaLim, rightDiaLim int32) int32 { //复杂度 O(N^N) 只是加速了常数时间
if colLim == limit { // base case
return 1
}
// 所有候选皇后的为孩子,都在pos上
var pos int32 = limit & (^(colLim | leftDiaLim | rightDiaLim))
var mostRightOne int32 = 0
var res int32 = 0
for pos != 0 {
mostRightOne = pos & (^pos + 1) // 提取出最右侧的1,剩下的位置都是0
pos = pos - mostRightOne // pos ^= mostRightOne
res += processQueen2(
limit,
colLim | mostRightOne,
(leftDiaLim | mostRightOne) << 1,
int32(uint32(rightDiaLim | mostRightOne) >> 1), //此处应该无符号右移
)
}
return res
}
/*
8皇后问题为例
-
列limit 0.....0 | 00 00 00 00
左limit 0.....0 | 00 00 00 00
右limit 0.....0 | 00 00 00 00
-
0行,左,右都没有限制
列limit 0.....0 | 10 00 00 00
左limit 0.....0 | 00 00 00 00
右limit 0.....0 | 00 00 00 00
1行,左右对角线限制
列limit 0.....0 | 10 00 00 00
左limit 0.....1 | 00 00 00 00 左移1位,左对角线不能放皇后的位置
右limit 0.....0 | 01 00 00 00 右移1位,右对角线不能放皇后的位置
限制会下传
用或计算总限制
总限制 0.....1 | 11 00 00 00
*/
递归——N皇后问题
于 2021-11-02 18:12:25 首次发布