动态规划
一、动态规划的核心
最近在力扣上刷多了几道动态规划问题的题,对动态规划形成了初步的了解。
动态规划问题与递归问题类似,都是基于将问题分解成更小的问题,求解小问题后得到最终的答案。动态规划与递归不同的是,动态规划是自底向上地求解问题,其小问题是相对于问题规模而言的,如求解最大子数组和问题,给定长度为n的数组求最大子数组和(问题规模为n),动态规划采用的是从数组长度为1(最小的问题规模)时开始设计,考虑下一阶段(长度为2)与何种因素相关,从而列得状态转移方程,即动态规划巧妙地利用之前计算过的结果集求解当前问题。
二、以最大子数组和为例
力扣第53题 最大子数组和
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。
-
确定边界情况
当数组长度为1时,放回
nums[0]
-
状态转移方程
记f(i)为长度为i的数组的最大子数组和,则
f(i) = max{f(i-1)+nums[i], nums[i]}
,即数组长度扩大1后,考虑是否将nums[i]
加入下一规模还是以nums[i]
单独开始为最大子数组和。最终求解
max{f(i)}, i∈[0, n]
。 -
Python语言实现
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
max = nums[0]
for i in range(1, len(nums)):
if nums[i] + nums[i-1] > nums[i]:
nums[i] += nums[i-1]
if nums[i] > max:
max = nums[i]
return max
可以看出,动态规划从最小的问题规模开始向上计算,规模扩大之后采取怎样的策略。
三、第64题. 最小路径和
给定一个包含非负整数的 m x n
网格 grid
,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
**说明:**每次只能向下或者向右移动一步。
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
"""
:type grid: List[List[int]]
:rtype: int
"""
m, n = len(grid), len(grid[0])
for i in range(1, m):
grid[i][0] = grid[i - 1][0] + grid[i][0]
for j in range(1, n):
grid[0][j] = grid[0][j - 1] + grid[0][j]
for i in range(1, m):
for j in range(1, n):
grid[i][j] = grid[i][j] + min(grid[i - 1][j], grid[i][j - 1])
return grid[m - 1][n - 1]