动态规划定义
初识动态规划:如何巧妙解决“双十一”购物时的凑单问题?
动态规划理论:一篇文章带你彻底搞懂最优子结构、无后效性和重复子问题
动态规划实战:如何实现搜索引擎中的拼写纠错功能?
递归代码模板
分治代码模板
动态规划定义
MIT 动态规划课程最短路径算法
LeetCode题目:
1.最长公共子序列
class Solution {
func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int {
let text1Arr = Array(text1), text2Arr = Array(text2)
let m = text1.count, n = text2.count
// 构造二维数组 全0
var arr = Array(repeating: Array(repeating: 0, count: m + 1), count: n + 1)
for i in 1..
2.三角形最小路径和
-Bottom-Up 技巧是从倒数第二层开始递推
class Solution {
func minimumTotal(_ triangle: [[Int]]) -> Int {
var dp = triangle
for i in (0..
-Up-Bottom 递归加记忆化搜索
class Solution {
var memDict = Dictionary()
func minimumTotal(_ triangle: [[Int]]) -> Int {
return helper(i: 0, j: 0, triangle: triangle)
}
func helper(i: Int, j: Int, triangle: [[Int]]) -> Int{
if i == triangle.count - 1 {
return triangle[i][j]
}
guard memDict["\(i) * \(j)"] != nil else {
let left = helper(i: i + 1, j: j, triangle: triangle)
let right = helper(i: i + 1, j: j + 1, triangle: triangle)
memDict["\(i) * \(j)"] = min(left, right) + triangle[i][j]
return memDict["\(i) * \(j)"]!
}
return memDict["\(i) * \(j)"]!
}
}
3.不同路径
-Bottom-Up DP方程递推出最终结果
class Solution {
func uniquePaths(_ m: Int, _ n: Int) -> Int {
if m == 1 || n == 1 {
return 1
}
var arr = Array(repeating: Array(repeating: 0, count: n), count: m)
for i in 0..
-Up-Bottom* 递归 + 记忆化搜索
class Solution {
var memDict: Dictionary = Dictionary()
func uniquePaths(_ m: Int, _ n: Int) -> Int {
return countPath(m: m, n: n, row: 0, col: 0)
}
func countPath(m: Int, n: Int, row: Int, col: Int) -> Int {
if !validSqure(m: m, n: n, row: row, col: col) { return 0 }
if isAtEnd(m: m, n: n, row: row, col: col) { return 1 }
//memoize
guard memDict["\(row) * \(col)"] != nil else {
memDict["\(row) * \(col)"] = countPath(m: m, n: n, row: row + 1, col: col) + countPath(m: m, n: n, row: row, col: col + 1)
return memDict["\(row) * \(col)"]!
}
return memDict["\(row) * \(col)"]!
}
func validSqure(m: Int, n: Int, row: Int, col: Int) -> Bool {
if row > m - 1 { return false }
if col > n - 1 { return false }
return true
}
func isAtEnd(m: Int, n: Int, row: Int, col: Int) -> Bool {
if row == m - 1 { return true }
if col == n - 1 { return true }
return false
}
}
4.不同路径 II
-Up-Bottom* 递归并处理特殊情况
class Solution {
var memDict: Dictionary = Dictionary()
func uniquePathsWithObstacles(_ obstacleGrid: [[Int]]) -> Int {
guard obstacleGrid[obstacleGrid.count - 1][obstacleGrid.first!.count - 1] != 1 else { return 0 }
// 横条
if obstacleGrid.count == 1 {
if obstacleGrid.first!.contains(1) {
return 0
} else {
return 1
}
}
// 竖条
if obstacleGrid.first!.count == 1 {
for arr in obstacleGrid {
if arr.first == 1 {
return 0
}
}
return 1
}
return countPath(grid: obstacleGrid, i: 0, j: 0)
}
func countPath(grid: [[Int]], i: Int, j: Int) -> Int {
if !validCourt(grid: grid, i: i, j: j) { return 0 }
if grid[i][j] == 1 { return 0}
if isAtEnd(grid: grid, i: i, j: j) {
if hasBarrierBehind(grid: grid, i: i, j: j) {
return 0
} else {
return 1
}
}
guard memDict["\(i) * \(j)"] != nil else {
memDict["\(i) * \(j)"] = countPath(grid: grid, i: i, j: j + 1) + countPath(grid: grid, i: i + 1, j: j)
return memDict["\(i) * \(j)"]!
}
return memDict["\(i) * \(j)"]!
}
func validCourt(grid: [[Int]], i: Int, j: Int) -> Bool {
if (i > grid.count - 1) || (j > grid.first!.count - 1) {
return false
}
return true
}
func isAtEnd(grid: [[Int]], i: Int, j: Int) -> Bool {
if (i == grid.count - 1) || (j == grid.first!.count - 1) {
return true
}
return false
}
func hasBarrierBehind(grid: [[Int]], i: Int, j: Int) -> Bool {
if j == grid.first!.count - 1 {
//右边竖条
for k in i...grid.count - 1 {
if grid[k][grid.first!.count - 1] == 1 {
return true
}
}
} else {
//底下横条
for k in j...grid.first!.count - 1 {
if grid[grid.count - 1][k] == 1 {
return true
}
}
}
return false
}
}
-Bottom-Up DP*方程递推出结果先给两边处理了然后正常递推
class Solution {
func uniquePathsWithObstacles(_ obstacleGrid: [[Int]]) -> Int {
guard obstacleGrid[obstacleGrid.count - 1][obstacleGrid.first!.count - 1] != 1 else {
return 0
}
// 横条
if obstacleGrid.count == 1 {
if obstacleGrid.first!.contains(1) {
return 0
} else {
return 1
}
}
// 竖条
if obstacleGrid.first!.count == 1 {
for arr in obstacleGrid {
if arr.first == 1 {
return 0
}
}
return 1
}
var arr = obstacleGrid
let m = arr.count, n = arr.first!.count
for i in 0..
5.不同路径 III
6.最长递增子序列
class Solution {
func lengthOfLIS(_ nums: [Int]) -> Int {
guard nums.count > 1 else {
return 1
}
var dpArr = Array(repeating: 1, count: nums.count)
var res = 0
for i in 1.. nums[j] {
dpArr[i] = max(dpArr[i], dpArr[j] + 1)
}
}
res = max(dpArr[i], res)
}
return res
}
}
7.打家劫舍
-升维DP
class Solution {
func rob(_ nums: [Int]) -> Int {
guard nums.count > 0 else {
return 0
}
var dpArr = Array(repeating: Array(repeating: 0, count: 2), count: nums.count)
dpArr[0][0] = 0
dpArr[0][1] = nums[0]
for i in 1..
-不升维DP
class Solution {
func rob(_ nums: [Int]) -> Int {
guard nums.count > 0 else {
return 0
}
if nums.count == 1 {
return nums[0]
}
var dpArr = Array(repeating: 0, count: nums.count)
dpArr[0] = nums[0]
dpArr[1] = max(nums[0], nums[1])
for i in 2..
-优化掉不升维DP中的数组
class Solution {
func rob(_ nums: [Int]) -> Int {
var pre = 0
var now = 0
for i in nums {
let temp = max(pre + i, now)
pre = now
now = temp
}
return now
}
}
8.打家劫舍 II
class Solution {
//首尾相连,就他它转化成两个分别来求,最后取大的
func rob(_ nums: [Int]) -> Int {
var nums = nums
guard nums.count > 0 else {
return 0
}
if nums.count == 1 {
return nums[0]
}
if nums.count == 2 {
return max(nums.first!, nums.last!)
}
let tempLast = nums.removeLast()
let dpArrContainFirst = nums
nums = nums + [tempLast]
let tempFirst = nums.removeFirst()
let dpArrContainLast = nums
nums = [tempFirst] + nums
var dpArr1 = Array(repeating: 0, count: dpArrContainFirst.count)
dpArr1[0] = dpArrContainFirst[0]
dpArr1[1] = max(dpArrContainFirst[0], dpArrContainFirst[1])
for i in 2..
9.零钱兑换
-DP
class Solution {
func coinChange(_ coins: [Int], _ amount: Int) -> Int {
guard amount > 0 else {
return 0
}
var dpArr = Array(repeating: amount + 1, count: amount + 1)
dpArr[0] = 0
for i in 1...amount {
for j in 0.. amount ? -1 : dpArr[amount]
}
}
-超出时间限制的DFS + 回溯
func coinChange(_ coins: [Int], _ amount: Int) -> Int {
var res = Array()
var curCoinArray = Array()
dfs(coins: coins.sorted(by: >), amount: amount, cur: 0, res: &res, curCoinArray: &curCoinArray)
return res.count == 0 ? -1 : res.first!
}
func dfs(coins: [Int], amount: Int, cur: Int, res: inout Array, curCoinArray: inout [Int]) {
if cur == amount {
if res.count != 0 {
if res.first! >= curCoinArray.count {
res.removeFirst()
res.append(curCoinArray.count)
}
} else {
res.append(curCoinArray.count)
}
return
}
if cur > amount { return }
for (index, i) in coins.enumerated() {
curCoinArray.append(coins[index])
dfs(coins: coins, amount: amount, cur: cur + i, res: &res, curCoinArray: &curCoinArray)
curCoinArray.removeLast()
}
}
10.零钱兑换 II
-DP
class Solution {
func change(_ amount: Int, _ coins: [Int]) -> Int {
if amount == 0 {
return 1
}
var dpArr = Array(repeating: 0, count: amount + 1)
dpArr[0] = 1
for coin in coins {
if coin > amount { continue }
for x in coin...amount {
dpArr[x] += dpArr[x - coin]
}
}
return dpArr.last!
}
}
-超时的DFS 不好再继续优化(剪枝条件不好找)
class Solution {
func change(_ amount: Int, _ coins: [Int]) -> Int {
var res = Set>()
var cur = Array()
dfs(amount: amount, coints: coins, res: &res, cur: &cur)
return res.count
}
func dfs(amount: Int, coints: [Int], res: inout Set>, cur: inout [Int]) {
if amount == 0 {
res.insert(cur.sorted())
return
}
if amount < 0 {
return
}
for i in coints {
cur.append(i)
dfs(amount: amount - i, coints: coints, res: &res, cur: &cur)
cur.removeLast()
}
}
}
11.买卖股票的最佳时机
class Solution {
func maxProfit(_ prices: [Int]) -> Int {
let prices = [0] + prices
var dpArr = Array(repeating: Array(repeating: 0, count: 2), count: prices.count)
dpArr[0][0] = 0
dpArr[0][1] = Int(-1000000000)
for i in 1...prices.count - 1 {
for j in 0...1 {
if j == 0 {
dpArr[i][j] = max(dpArr[i - 1][1] + prices[i], dpArr[i - 1][0])
} else {
// j == 1
dpArr[i][j] = max(-prices[i], dpArr[i - 1][1])
}
}
}
return dpArr[prices.count - 1][0]
}
}
12.买卖股票的最佳时机 II
-DP
class Solution {
func maxProfit(_ prices: [Int]) -> Int {
let prices = [0] + prices
var dpArr = Array(repeating: Array(repeating: 0, count: 2), count: prices.count)
dpArr[0][0] = 0
dpArr[0][1] = Int(-1000000)
for i in 1...prices.count - 1 {
for j in 0...1 {
if j == 0 {
dpArr[i][j] = max(dpArr[i - 1][1] + prices[i], dpArr[i - 1][0])
} else {
dpArr[i][j] = max(dpArr[i - 1][0] - prices[i], dpArr[i - 1][1])
}
}
}
return dpArr[prices.count - 1][0]
}
}
贪心
class Solution {
func maxProfit(_ prices: [Int]) -> Int {
var everyProfit = 0
var res = 0
for (index, i) in prices.enumerated() {
if index == prices.count - 1 {
break
}
everyProfit = prices[index + 1] - i
if everyProfit > 0 {
res += everyProfit
}
}
return res
}
}
13.买卖股票的最佳时机 III
class Solution {
func maxProfit(_ prices: [Int]) -> Int {
let usePrices = [0] + prices
var dpArr = Array(repeating: Array(repeating: Array(repeating: 0, count: 2), count: 3), count: usePrices.count)
dpArr[0][0][0] = 0
dpArr[0][0][1] = -1000000
dpArr[0][1][0] = 0
dpArr[0][1][1] = -1000000
dpArr[0][2][0] = 0
dpArr[0][2][1] = -1000000
for i in 1..
14.最佳买卖股票时机含冷冻期
class Solution {
func maxProfit(_ prices: [Int]) -> Int {
let prices = [0] + prices
var dpArr = Array(repeating: Array(repeating: 0, count: 2), count: prices.count)
dpArr[0][0] = 0
dpArr[0][1] = -10000000
for i in 1...prices.count - 1 {
for j in 0...1 {
if j == 0 {
dpArr[i][j] = max(dpArr[i - 1][1] + prices[i], dpArr[i - 1][0])
} else {
// j == 1
dpArr[i][j] = max(dpArr[i - 1][0] - prices[i], dpArr[i - 1][1])
}
}
}
return dpArr[prices.count - 1][0]
}
}
15.买卖股票的最佳时机 IV
class Solution {
func maxProfit(_ k: Int, _ prices: [Int]) -> Int {
guard k > 0 else {
return 0
}
let usePrices = [0] + prices
var dpArr = Array(repeating: Array(repeating: Array(repeating: 0, count: 2), count: k + 1), count: usePrices.count)
for i in 0...k {
for j in 0...1 {
if j == 0 {
dpArr[0][i][j] = 0
} else {
dpArr[0][i][j] = -1000000
}
}
}
for i in 1..
16.买卖股票的最佳时机含手续费
class Solution {
func maxProfit(_ prices: [Int], _ fee: Int) -> Int {
let prices = [0] + prices
var dpArr = Array(repeating: Array(repeating: 0, count: 2), count: prices.count)
dpArr[0][0] = 0
dpArr[0][1] = Int(-1000000)
for i in 1...prices.count - 1 {
for j in 0...1 {
if j == 0 {
dpArr[i][j] = max(dpArr[i - 1][1] + prices[i], dpArr[i - 1][0])
} else {
dpArr[i][j] = max(dpArr[i - 1][0] - prices[i] - fee, dpArr[i - 1][1])
}
}
}
return dpArr[prices.count - 1][0]
}
}
17.完全平方数
class Solution {
func numSquares(_ n: Int) -> Int {
var dpArr = Array(repeating: 0, count: n + 1)
let sqrtN = Int(sqrt(Double(n)))
dpArr[0] = 0
for i in 1...n {
dpArr[i] = i
for j in 1...sqrtN {
if i - j * j >= 0 {
dpArr[i] = min(dpArr[i], dpArr[i - j * j] + 1)
}
}
}
return dpArr.last!
}
}
18.编辑距离(重点)
class Solution {
func minDistance(_ word1: String, _ word2: String) -> Int {
if word1 == "" || word2 == "" {
return max(word1.count, word2.count)
}
let word1Arr = Array(word1)
let word2Arr = Array(word2)
var dpArr = Array(repeating: Array(repeating: 0, count: word2Arr.count + 1), count: word1Arr.count + 1)
// 哨兵初始化
for i in 0...word2Arr.count {
dpArr[0][i] = i
}
for i in 0...word1Arr.count {
dpArr[i][0] = i
}
for i in (1...word1Arr.count) {
for j in (1...word2Arr.count) {
if word1Arr[i - 1] == word2Arr[j - 1] {
dpArr[i][j] = dpArr[i - 1][j - 1]
} else {
dpArr[i][j] = min(dpArr[i][j - 1] + 1, dpArr[i - 1][j] + 1, dpArr[i - 1][j - 1] + 1)
}
}
}
return dpArr[word1.count][word2.count]
}
}
19.跳跃游戏
-DP
class Solution {
func canJump(_ nums: [Int]) -> Bool {
guard nums.count > 1 else {
return true
}
if nums.count == 2 {
return nums.first! >= 1 ? true : false
}
if nums.first! == 0 {
return false
}
var dpArr = nums
var res = 0
for i in 1..= nums.count - 1) ? true : false
}
}
-贪心
class Solution {
func canJump(_ nums: [Int]) -> Bool {
if nums.count == 1 {
return true
}
var cover = 0
var i = 0
while cover < nums.count - 1 {
if i > cover {
return false
}
cover = max(i + nums[i], cover)
i += 1
}
return true
}
}
20.跳跃游戏 II
-DP
class Solution {
func jump(_ nums: [Int]) -> Int {
if nums.count == 1 {
return 0
}
if nums.count == 2 {
return 1
}
var dpArr = Array(repeating: 10000000, count: nums.count)
dpArr[0] = 0
var cover = nums.first!
for i in 1.. nums.count - 1 {
cover = nums.count - 1
}
for j in i...cover {
dpArr[j] = min(dpArr[i - 1] + 1, dpArr[j])
}
cover = max(cover, i + nums[i])
}
return dpArr.last!
}
}
贪心
class Solution {
var level = 0
func jump(_ nums: [Int]) -> Int {
guard nums.count > 1 else {
return 0
}
var cover = 0
var i = 0
while cover < nums.count - 1 {
cover = max(cover, i + nums[i])
i += 1
}
level += 1
jump(Array(nums[0...i - 1]))
return level
}
}
21.乘积最大子数组
class Solution {
func maxProduct(_ nums: [Int]) -> Int {
guard nums.count > 1 else {
return nums.first!
}
var res = -100000000
var dpArr = Array(repeating: Array(repeating: 0, count: 2), count: nums.count + 1)
dpArr[0][0] = 1
dpArr[0][1] = 1
for i in 1...nums.count {
dpArr[i][0] = nums[i - 1]
dpArr[i][1] = nums[i - 1]
}
for i in 1..
22.最小路径和
class Solution {
func minPathSum(_ grid: [[Int]]) -> Int {
var dpArr = grid
for i in 1..
23.最大子序和
class Solution {
func maxSubArray(_ nums: [Int]) -> Int {
guard nums.count > 1 else {
return nums.first!
}
var dpArr = [-1000000] + nums
var res = -1000000
for i in 1..
24.最大正方形
class Solution {
func maximalSquare(_ matrix: [[Character]]) -> Int {
var dpArr = Array(repeating: Array(repeating: 0, count: matrix.first!.count + 1), count: matrix.count + 1)
var res = 0
for i in 1..
25.青蛙过河
class Solution {
func canCross(_ stones: [Int]) -> Bool {
if stones.count == 2 {
if stones.last! > 1 {
return false
} else {
return true
}
}
var dict: Dictionary> = Dictionary>()
dict.updateValue([1], forKey: stones[1] + 1)
dict.updateValue([2], forKey: stones[1] + 2)
for i in stones[2...] {
guard let setValue = dict[i] else {
continue
}
for s in setValue {
if var set1 = dict[i + s - 1] {
set1.insert(s - 1)
dict[i + s - 1] = set1
} else {
dict.updateValue( [s - 1], forKey: i + s - 1)
}
if var set2 = dict[i + s] {
set2.insert(s)
dict[i + s] = set2
} else {
dict.updateValue([s], forKey: i + s)
}
if var set3 = dict[i + s + 1] {
set3.insert(s + 1)
dict[i + s + 1] = set3
} else {
dict.updateValue([s + 1], forKey: i + s + 1)
}
}
}
if dict.keys.contains(stones.last!) {
return true
} else {
return false
}
}
}
26.解码方法
27.任务调度器
28.最长有效括号
class Solution {
func longestValidParentheses(_ s: String) -> Int {
guard s.count >= 2 else {
return 0
}
let sArr = Array(s)
var dpArr = Array(repeating: 0, count: s.count)
var res = 0
for i in 1.. 0 {
// sArr[i - 2]存在
dpArr[i] = 2 + dpArr[i - 2]
} else {
// sArr[i - 2]不存在
dpArr[i] = 2
}
} else {
// 没匹配上 ")" ".....))" 考察 sArr[i - dp[i - 1] -1]
if i - dpArr[i - 1] - 1 < 0 {
// sArr[i - dpArr[i - 1] -1] 不存在
dpArr[i] = 0
} else {
// sArr[i - dpArr[i - 1] -1] 存在
if sArr[i - dpArr[i - 1] - 1] == ")" {
// "...).....))"
dpArr[i] = 0
} else {
// "...(.....))" 考察 sArr[i - dpArr[i - 1] - 2]
if i - dpArr[i - 1] - 2 < 0 {
// sArr[i - dpArr[i - 1] - 2] 不存在
dpArr[i] = 2 + dpArr[i - 1]
} else {
// sArr[i - dp[i - 1] - 2] 存在
dpArr[i] = 2 + dpArr[i - 1] + dpArr[i - dpArr[i - 1] - 2]
}
}
}
}
}
res = max(res, dpArr[i])
}
return res
}
}
29.矩形区域不超过 K 的最大数值和
30.分割数组的最大值
31.学生出勤记录 II
32.戳气球
33.使用最小花费爬楼梯
34.最大矩形
35. 不同的子序列
36.赛车