leetcode 51 N皇后
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为 8 皇后问题的一种解法。给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。 每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
示例:
输入: 4
输出: [
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。
复制代码
这道题我的思路是回溯+剪枝。
皇后之间不能攻击,西洋的皇后太霸道,不仅纵横无忌,还能斜着打,所以这也给了我们剪枝的手段。我们选择逐行进行递归,在递归的同时,可以将已确定位置的皇后所能攻击到的地方标记起来。
我们可以看到,如果(d,8)位置的皇后已经确定了位置,那么四个方向的延长线就都变成了禁区。把棋盘看做二维数组,(a,8)即为[0][0]位置。
判断依据:
横线上,只需要在确定一个位置后,直接进行下一行即可。
竖线上,将确定位置后所在列进行记忆化,之后的位置与出现过的所有列进行比对。
蓝色的“撇”,经过的所有格子有一个共同点,那就是横坐标加上纵坐标的结果是相同的。例如蓝线经过的每个格子都是3,这个结果只有蓝线上的格子符合。我们只需要将这个结果记忆化即可。
红色的“捺”,横坐标减去纵坐标的值进行记忆化。
上码!
func solveNQueens(n int) [][]string {
//n为边长。显而易见,n == 1 皇后只能独坐闺房,n <= 3的时候无解
if n == 1 {
return [][]string{{"Q"}}
}
if n <= 3{
return [][]string{}
}
var re [][]int
// pies为撇,nas为捺,变量名称有点混血,rows保存的是所在行的列坐标
DFS := func(rows []int, pies []int, nas []int, n int){}
DFS = func(rows []int, pies []int, nas []int, n int){
row := len(rows)
//rows的长度 == n 说明已经到了最后一行了,可以return了宝贝
if row == n {
// 此处是因为Go的切片是地址,往结果数组中加的时候一定要复制一份新的,不然会被后
// 序操作改掉。下边注释的是偷懒的写法,会浪费空间哦
newRows := make([]int, len(rows))
copy(newRows,rows)
re = append(re,newRows)
//re = append(re,append([]int{},rows...))
return
}
for col:= 0; col< n; col++ {
flag := true
//此处进行剪枝,按照上边说的进行判断,换成Map来存时间复杂度比较低,
//后边我会分享一下,这样写主要是图个简单,而且测试用例n最大不会超过10。
for k,v := range rows {
if v == col || pies[k] == (row+col-1) || nas[k] == (row-col-1){
flag = false
break
}
}
if flag{
//递归,其实正统的写法应当是先append到切片中,递归,然后从切片中剔除。
//这样写更酷一些。
DFS(append(rows,col),append(pies,(row+col-1)),append(nas,(row-col-1)),n)
}
}
}
DFS([]int{},[]int{},[]int{},n)
return bQ(re,n)
}
//为了满足题意。。。搞成字符串Q。真是多此一举哈哈
func bQ (re [][]int,n int) (result [][]string) {
for _,v := range re {
s := []string{}
for _,vv := range v{
str := ""
for i:=0;iif i == vv {
str += "Q"
}else{
str += "."
}
}
s = append(s,str)
}
result = append(result,s)
}
return
}
复制代码
OK,我们可以考虑用map来存储竖、撇、捺,聊胜于无。
func solveNQueens(n int) [][]string {
if n == 1 {
return [][]string{{"Q"}}
}
if n <= 3{
return [][]string{}
}
var re [][]int
//三个map,shus就是竖,扑面而来的爱国情怀。
shus,pies,nas := make(map[int]bool,n),make(map[int]bool,n),make(map[int]bool,n)
DFS := func(rows []int, n int){}
DFS = func(rows []int, n int){
row := len(rows)
if row == n {
aaaa := make([]int, len(rows))
copy(aaaa,rows)
//re = append(re,append([]int{},rows...))
re = append(re,aaaa)
return
}
for col:= 0; col< n; col++ {
//就是这里了,先把三个map对应位置搞成true,递归,然后搞成false,不影响下个循环,很
//玄幻,但是就是能工作哈哈,多玩玩就能想明白。
if !shus[col] && !pies[row+col-1] && !nas[row-col-1]{
shus[col] = true
pies[row+col-1] = true
nas[row-col-1] = true
DFS(append(rows,col),n)
shus[col] = false
pies[row+col-1] = false
nas[row-col-1] = false
}
}
}
DFS([]int{},n)
return bQ(re,n)
}
复制代码
其实,递归真的是一个很好玩的东西。虽然一不小心就爆栈,但她的思想真的是充满了智慧。她像极了一个老匠人,抽丝剥茧,化繁为易,她又时而涌现出少年才气,看似玩世不恭的外表下跳动着胸有成竹的心。总之,越玩越上瘾啊。
最后,分享一个公众号吧,叫做算法梦想家,来跟我一起玩算法,玩音乐,聊聊文学创作,咱们一起天马行空!