N皇后问题(golang版)
问题描述
在N*N的棋盘上要摆N个皇后,要求任何两个皇后不同行、不同列,也不在同一斜线上。
给定一个整数n,返回n皇后的摆法有多少种。
n=1,返回1.
n=2或3,2皇后和3皇后问题无论怎么摆都不行,返回0
n=8,返回92
位运算解法:
现在有个8*8的棋盘
我们把可以放的位置用1标记,第一行所有的位置都可以放,所有第一行是1 1 1 1 1 1 1 1
我们一开始在0,Q的位置放一个皇后
那么第二行的哪些位置可以放皇后呢?
第一行的左斜线是1,0 垂直线是 1,1 右斜线是1,2,这三种位置都不能放
左斜线是:1 0 0 0 0 0 0 0 (第一行放Q的位置左移一位)
垂直线是:0 1 0 0 0 0 0 0 (第一行放Q的位置)
右斜线是:0 0 1 0 0 0 0 0 (第一行放Q的位置右移一位)
三者进行或运算得到
1 1 1 0 0 0 0 0
取反再跟1 1 1 1 1 1 1 1进行与运算得到
0 0 0 1 1 1 1 1
这些为1的位置就可以放皇后
我们假设放到1,4的位置
第三行哪些位置可以放皇后呢?
左斜线是:0 0 0 1 0 0 0 0 (之前的左斜线左移一位 加上第二行放Q的位置左移一位)
垂直线是:0 1 0 0 1 0 0 0 (之前的垂直线 加上第二行放Q的位置)
右斜线是:0 0 0 1 0 1 0 0 (之前的右斜线右移一位 加上第二行放Q的位置右移一位)
三者进行或运算得到
0 1 0 1 1 1 0 0
取反再跟1 1 1 1 1 1 1 1进行与运算得到
1 0 1 0 0 0 1 1
第四行在为1的位置放置皇后
周而复始,如果能得到垂直线都填满了,就算是成功得到一种解法
也就是垂直线==1 1 1 1 1 1 1 1
package main
import (
"fmt"
"math"
)
func num1(n int) int {
if n < 1 {
return 0
}
record := make([]int, n)
return process1(0, record, n)
}
func process1(i int, record []int, n int) int {
if i == n {
return 1
}
res := 0
for j := 0; j < n; j++ {
if isValid(record, i, j) {
record[i] = j
res += process1(i+1, record, n)
}
}
return res
}
func isValid(record []int, i, j int) bool {
for k := 0; k < i; k++ {
if j == record[k] || math.Abs(float64(record[k]-j)) == math.Abs(float64(i-k)) {
return false
}
}
return true
}
func main() {
fmt.Println(num1(8))
fmt.Println(num2(8))
}
func num2(n int) int {
if n < 1 || n > 32 {
return 0
}
limit := (1 << n) - 1
return process2(limit, 0, 0, 0)
}
func process2(limit, collim, left, right int) int {
if collim == limit {
return 1
}
mostRight := 0
//得到能放皇后的位置 (用1标识)
pos := limit & (^(collim | left | right))
res := 0
for pos != 0 {
mostRight = pos & (^pos + 1) //拿到最右边的1
pos = pos - mostRight //能放的位置--
res += process2(limit, collim|mostRight, (left|mostRight)<<1, (right|mostRight)>>1)
}
return res
}