回溯算法求解N皇后问题(Go语言)

回溯算法求解N皇后问题(Go语言)

介绍

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。请问有多少种满足条件的情况?

当 n = 4 时,有以下两种情况:

在这里插入图片描述

在这里插入图片描述

回溯算法

N皇后的传统解法是暴力递归,在我看来暴力递归的精髓就是尝试,N皇后问题要求任意两个皇后都不共行,不共列,不共斜线。因此在一个N*N的棋盘上放N个皇后,因此可以看成由上到下每一行放一个皇后的尝试模型。

func problemFunction(nums []type) [][]type {
	ans := [][]type{}
	temp := []type{}
	n := len(nums)
	var backTrack func(tmp []type)	// 回溯函数声明
	backTrack = func(tmp []type) {
		if len(tmp) == n { // 递归出口
			tmpcp := make([]type, n)
			copy(tmpcp, tmp)
			ans = append(ans, tmpcp)
			// ans = append(ans,append([]int(nil),temp...))
		}
		for i := 0; i < n; i++ {
          // 条件筛选
			temp = append(temp, nums[i])
			backTrack(temp)
			temp = temp[0 : len(temp)-1]
		}
	}
	backTrack(temp)
	return ans
}

动画演示

4皇后回溯

经过参考其他教程资料后,发现判断是否可以相互攻击通常不是直接向八个方向进行遍历,而是通过棋盘标记法进行判断。即每个皇后对其可以攻击到的棋盘位置进行+1,回溯撤回后-1。但是想不到的话直接每次都遍历其其可以攻击到的位置上是否有其它皇后也没问题。
在这里插入图片描述

代码

51. N 皇后 困难
// N皇后问题
func solveNQueens(n int) [][]string {
	ans := make([][]string, 0)
	// 生成nxn棋盘
	gen := func(n int) []string {
		ans := make([]string, n)
		for i := 0; i < n; i++ {
			for j := 0; j < n; j++ {
				ans[i] += "."
			}
		}
		return ans
	}
	temp := gen(n) // 生成棋盘
	// 判断当前位置放置皇后是否相互攻击
	isValid := func(r, c int) bool {
		// 纵向
		for i := 0; i < n; i++ {
			if temp[i][c] == 'Q' {
				return false
			}
		}
		// 横向
		for i := 0; i < n; i++ {
			if temp[r][i] == 'Q' {
				return false
			}
		}
		// 右下
		for i := 0; r+i < n && c+i < n; i++ {
			if temp[r+i][c+i] == 'Q' {
				return false
			}
		}
		// 右上
		for i := 0; r-i >= 0 && c-i >= 0; i++ {
			if temp[r-i][c-i] == 'Q' {
				return false
			}
		}
		// 左下
		for i := 0; r+i < n && c-i >= 0; i++ {
			if temp[r+i][c-i] == 'Q' {
				return false
			}
		}
		// 左上
		for i := 0; r-i >= 0 && c+i < n; i++ {
			if temp[r-i][c+i] == 'Q' {
				return false
			}
		}
		return true
	}
	// 放置皇后
	put := func(r, c int, role byte) {
		line := []byte(temp[r])
		line[c] = role
		temp[r] = string(line)
	}
	var backTrack func(r int) // 回溯函数声明
	backTrack = func(r int) {
		if r == n {
			tempcp := make([]string, n)
			copy(tempcp, temp)
			ans = append(ans, tempcp)
			return
		}
		for j := 0; j < n; j++ {
			if !isValid(r, j) {
				continue
			}
			// 做出选择
			put(r, j, 'Q')
			// 回溯
			backTrack(r + 1)
			// 撤销选择
			put(r, j, '.')
		}
	}
	// 执行回溯函数
	backTrack(0)
	return ans
}
52. N皇后 II 困难
func totalNQueens(n int) int {
    return len(solveNQueens(n))
}

// 生成nxn棋盘
func gen(n int) []string {
	ans := make([]string, n)
	for i := 0; i < n; i++ {
		for j := 0; j < n; j++ {
			ans[i] += "."
		}
	}
	return ans
}

// 判断当前位置放置皇后是否相互攻击
func isValid(board []string, r, c, n int) bool {
	// 纵向
	for i := 0; i < n; i++ {
		if board[i][c] == 'Q' {
			return false
		}
	}
	// 横向
	for i := 0; i < n; i++ {
		if board[r][i] == 'Q' {
			return false
		}
	}
	// 右下
	for i := 0; r+i < n && c+i < n; i++ {
		if board[r+i][c+i] == 'Q' {
			return false
		}
	}
	// 右上
	for i := 0; r-i >= 0 && c-i >= 0; i++ {
		if board[r-i][c-i] == 'Q' {
			return false
		}
	}
	// 左下
	for i := 0; r+i < n && c-i >= 0; i++ {
		if board[r+i][c-i] == 'Q' {
			return false
		}
	}
	// 左上
	for i := 0; r-i >= 0 && c+i < n; i++ {
		if board[r-i][c+i] == 'Q' {
			return false
		}
	}
	return true
}

// 放置皇后
func put(board []string, r, c int, role byte) {
	line := []byte(board[r])
	line[c] = role
	board[r] = string(line)
}

// N皇后问题
func solveNQueens(n int) [][]string {
	ans := make([][]string, 0)
	temp := gen(n)	// 生成棋盘
	var backTrack func(r int)	// 回溯函数声明
	backTrack = func(r int) {
		if r == n {
			tempcp := make([]string, n)
			copy(tempcp, temp)
			ans = append(ans, tempcp)
			return
		}
		for j := 0; j < n; j++ {
			if !isValid(temp, r, j, n) {
				continue
			}
			// 做出选择
			put(temp, r, j, 'Q')
			// 回溯
			backTrack(r + 1)
			// 撤销选择
			put(temp, r, j, '.')
		}
	}
	// 执行回溯函数
	backTrack(0)
	return ans
}

时间复杂度: O ( N ! ) O(N!) O(N!)

扩展阅读

Mathematician Answers Chess Problem About Attacking Queens

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值