每日刷题(5)——回溯问题:N皇后、解数独、重新安排行程
创始人
2024-06-02 16:52:25
0

本篇文章题目来源于leetcode与代码随想录

前言

哈喽,大家好,我是小雨,今天做了代码随想录的回溯部分,其中连续三个困难题非常有价值,本篇文章就来聊聊这三个困难题:

51. N 皇后 - 力扣(Leetcode)

37. 解数独 - 力扣(Leetcode)

332. 重新安排行程 - 力扣(Leetcode)

Question 51. N 皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q''.' 分别代表了皇后和空位。

示例 1:

输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:'Q'表示皇后位置,'.'表示空位置,如图,4 皇后问题存在两个不同的解法。

提示:

  • 1 <= n <= 9

Solution

此题是很经典的回溯问题,我们从第一行开始放置皇后,尝试在每个位置放置皇后,然后递归到下一行,继续尝试放置皇后,直到所有行都被填满或无法继续放置皇后为止。如果在某一行无法找到合适的位置放置皇后,则需要回溯到上一行,并尝试在上一行的其他位置放置皇后。在实现回溯算法时,需要使用一个temp数组来记录每一行中皇后的位置,以及一个visited数组来记录当前位置是否可以走到。实现的具体代码如下:

Code

func getResult(temp []int)[]string{var result []stringn:=len(temp)for i := 0; i < len(temp); i++ {var s []bytefor j := 0; j < n; j++ {if j == temp[i]{s = append(s,'Q')}else{s = append(s,'.')}}result =append(result,string(s))}return result
}func solveNQueens(n int) [][]string {var temp []intvar result [][]stringvisited := make([][]int,n)for i := 0; i < len(visited); i++ {visited[i] = make([]int,n)}var dfs func(index int)dfs = func(index int) {if len(temp) == n {fmt.Println(temp)result = append(result,getResult(temp))return }for i := 0; i < n; i++ {if visited[index][i] > 0 {continue}//修改visited数组for j := 0; j < n; j++ {visited[index][j]++}for j := 0; j < n; j++ {visited[j][i]++}//斜右下x,y:=index+1,i+1for x < n && y < n {visited[x][y]++x++y++}//斜左下x,y = index+1,i-1for x < n && y >= 0 {visited[x][y]++x++y--}//放入temp中temp = append(temp,i)dfs(index+1)//还原visited数组//修改visited数组for j := 0; j < n; j++ {visited[index][j]--}for j := 0; j < n; j++ {visited[j][i]--}//斜右下x,y = index+1,i+1for x < n && y < n {visited[x][y]--x++y++}//斜左下x,y = index+1,i-1for x < n && y >= 0 {visited[x][y]--x++y--}//还原temptemp = temp[:len(temp)-1]}}dfs(0)return result}

Question 37. 解数独

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用 '.' 表示。

示例 1:

输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]

提示:

  • board.length == 9
  • board[i].length == 9
  • board[i][j] 是一位数字或者 '.'
  • 题目数据 保证 输入数独仅有一个解

Solution

解决数独问题也可以使用回溯算法,类似于 N 皇后问题。从第一个空格开始,尝试在该位置填入数字 1-9,然后递归到下一个空格,继续尝试填入数字,直到所有空格都被填满或无法继续填入数字为止。如果在某一格无法找到合适的数字填入,则需要回溯到上一个空格,并尝试填入其他数字。实现时,我们需要额外定义三个数组,来记录横向竖向和网格的数字出现情况,具体实现如下:

Code

func solveSudoku(board [][]byte)  {hvisited := make([][]int,9)svisited := make([][]int,9)xvisited := make([][]int,9)for i := 0; i < 9; i++ {hvisited[i] = make([]int,10)svisited[i] = make([]int,10)xvisited[i] = make([]int,10)}//初始化visited数组for i := 0; i < len(board); i++ {for j := 0; j < len(board[i]); j++ {temp:=board[i][j]if temp != '.'{hvisited[i][temp-'0']++svisited[j][temp-'0']++xvisited[(i/3)*3+(j/3)][temp-'0']++}}}var help func(x,y int)(int,int) = func(x, y int) (int, int) {x += y/9y %= 9return x,y}var visited func(x,y,i int)bool = func(x,y,i int)bool{if hvisited[x][i] >0 || svisited[y][i] > 0 || xvisited[(x/3)*3+(y/3)][i]>0{return true}else {return false}}var dfs func(x,y int)booldfs = func(x, y int) bool{if x == 9 {return true}if board[x][y] != '.'{if dfs(help(x,y+1)){return true}}else{for i := 1; i <= 9; i++ {if visited(x,y,i){continue}hvisited[x][i]++svisited[y][i]++xvisited[(x/3)*3+(y/3)][i]++board[x][y] = byte('0'+i) if dfs(help(x,y+1)){return true}board[x][y] = byte('.')hvisited[x][i]--svisited[y][i]--xvisited[(x/3)*3+(y/3)][i]--}}return false}dfs(0,0)}

Question 332. 重新安排行程

给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。

所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。

  • 例如,行程 ["JFK", "LGA"]["JFK", "LGB"] 相比就更小,排序更靠前。

假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。

示例 1:

输入:tickets = [["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]]
输出:["JFK","MUC","LHR","SFO","SJC"]

提示:

  • 1 <= tickets.length <= 300
  • tickets[i].length == 2
  • fromi.length == 3
  • toi.length == 3
  • fromitoi 由大写英文字母组成
  • fromi != toi

Solution

在这题中,定义一个hashmap,存储车站与到达车站的映射。首先遍历车票,将车票中出发站和到达站的映射进行存储,从起点开始,依次尝试每一张机票,检查它是否符合要求(即当前机场为起点,且这张机票没有被使用过),如果符合要求,则将其加入当前路径中,并继续搜索下一个机场;如果不符合要求,则回溯到上一个机场,并继续尝试其他机票。当路径长度等于机场数量时,说明我们已经找到了一条合法的路径(注意,题目要求按字典排序返回最小的行程组合,因此如果提前对映射中的到达站进行排序,那么第一个遍历到的结果就是字典序最小的结果),返回结果即可。

Code

func findItinerary(tickets [][]string) []string {m := make(map[string][]string)var result []stringvar temp []stringvar dfs func(s string)var flag boolfor i := 0; i < len(tickets); i++ {m[tickets[i][0]] = append(m[tickets[i][0]],tickets[i][1])}for _,v:=range m {sort.Slice(v, func(i, j int) bool {if v[i] < v[j]{return true}else{return false}})}temp = append(temp,"JFK")dfs = func(s string) {if flag {return }if len(temp) == len(tickets)+1{result = make([]string,len(temp))copy(result,temp)flag = truereturn}for i,v:=range m[s]{if v =="-1"{continue}temp = append(temp,v)m[s][i] = "-1"dfs(v)temp = temp[:len(temp)-1]m[s][i] = v}}dfs("JFK")return result
}

总结

在做回溯类题目时,最好能够画出树状图,确定回溯路线,对整个题目做个大概的模拟,如果模拟成功,即可开始实现。实现时,定义一些状态信息,随后编写回溯函数,注意,横向的遍历由for循环控制竖向的递归由dfs函数控制,最后再针对每种类型的题目进行减枝操作即可!

相关内容

热门资讯

关于冥界,魔界,人界等有关的小... 关于冥界,魔界,人界等有关的小说推荐推荐《诛仙》、《盘龙》、《琴帝》
虎丘检察院支持起诉追回赔偿款 本报讯 (王歆瑶 房楚楚) “多亏有你们帮忙,今年5月我终于拿到赔偿款!”近日,在虎丘区检察院回访中...
太仓科教新城织密安全“防护网” 本报讯 (龙美娟) 今年6月是第二十四个全国“安全生产月”。一个月来,太仓市高新区科教新城围绕“人人...
首届“苏州文学周”启幕 本报讯 (肖甜) 7月3日,“苏州文学周·2025”于第十五届江苏书展启幕,苏州好书榜发布,文学创作...
提升煤化工行业党建和思想政治工...   摘要:在能源行业转型升级的关键时期,煤化工行业党建和思想政治工作面临新的机遇与挑战。面对发展要求...
一粒石榴种子里的千年中国   梁华春  《古今图书集成》 中华书局影印版  一夜风雨,零落了初夏时节盛开的石榴花。驻足凝视,有...
前门就是正阳门吗? 前门就是正阳门吗?是。如果到前门去的话,那里写的就是正阳门。
说的多音字组词 说的多音字组词说 ① shuì 游说 说客 ② shuō 说话 说辞shuo 第一声 说话 shu...
个人贷款怎么贷 个人贷款怎么贷个人贷款怎么贷?急用钱,需要个人贷款,怎么贷呢?一般可以贷到多少钱?个人贷款多久能批款...
联盟是什么意思具体些 联盟是什么意思具体些联盟是一个阵营 在魔兽世界里 有联盟和部落2个对立的阵营就是一个阵营 联盟看起来...