Week 45 @ 2024 算法周记【动态规划+回溯】

LC 2055. Plates Between Candles 蜡烛之间的盘子

https://leetcode.com/problems/plates-between-candles/

func platesBetweenCandles(s string, queries [][]int) []int { plates := make([]int, len(s)) candelsL := make([]int, len(s)) candelsR := make([]int, len(s)) candelsR[0] = -1 for i := 1; i < len(s); i++ { if s[i] == '|' { // candel plates[i] = plates[i-1] candelsR[i] = i } else { // plate plates[i] = plates[i-1] + 1 candelsR[i] = candelsR[i-1] } } candelsL[len(s)-1] = -1 for i := len(s)-2; i >= 0; i-- { if s[i] == '|' { // candel candelsL[i] = i } else { // plate candelsL[i] = candelsL[i+1] } } result := make([]int, len(queries)) for i, q := range queries { L := candelsL[q[0]] R := candelsR[q[1]] if L == -1 || R == -1 || L >= R { result[i] = 0 } else { result[i] = plates[R] - plates[L] } } return result }

预处理前缀和,做到 O(1) 计算一个点右侧的最近蜡烛、左侧的最近蜡烛和两点之间的盘子数

动态规划

动态规划问题的一般形式就是求最值,求解动态规划的核心问题是穷举

  1. 写出暴力解

  2. 利用 memory table / 数组优化暴力解

LC 509. Fibonacci Number 斐波那契数

https://leetcode.com/problems/fibonacci-number

递归解法

func fib(n int) int { if n == 0 || n == 1 { return n } return fib(n - 1) + fib(n - 2) }

迭代解法

func fib(n int) int { if n == 0 || n == 1 { return n } dp := make([]int, n + 1) dp[1] = 1 for i := 2; i <= n; i++ { dp[i] = dp[i-1] + dp[i-2] } return dp[n] }

迭代解法(进一步优化空间复杂度)

LC 322. 零钱兑换

https://leetcode.com/problems/coin-change

暴力解法(TLE)

动态规划

进一步优化常数时间(利用 MAX 代替 IntMax 来减少 if)

回溯

回溯算法和我们常说的 DFS 算法基本可以认为是同一种算法

抽象地说,解决一个回溯问题,实际上就是遍历一棵决策树的过程,树的每个叶子节点存放着一个合法答案。你把整棵树遍历一遍,把叶子节点上的答案都收集起来,就能得到所有的合法答案。

其核心就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」,特别简单。

LC 46. Permutations 全排列

https://leetcode.com/problems/permutations

LC 51. N-Queens N 皇后

https://leetcode.com/problems/n-queens/

LC 52. N-Queens II N 皇后 II

https://leetcode.com/problems/n-queens-ii/