这一节的几篇,都是解析动态规划数组类题目,相对于后面的字符串类问题来说,还是比较容易的。
首先来看比较几个简单的DP题目,巩固一下前面所学套路。
64. Minimum Path Sum
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
题目解析:
貌似63题的通过率还低些,不过就不解析了,DP框架不变,由于障碍物的关系略微修改边界和状态转移方程。
路径和还是经常和大家碰面的问题,比如在二叉树的时候。
这道题仍然不变算法框架,f(i,j)代表到达i,j点时的最小路径和,然后我们研究f(i,j)和f(i-1,j),f(i,j-1)之间的关系。对于边界的定义也略微不同。直接看代码,此次已优化了空间,这样写起来索引也好懂。。
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
dp = [0] * n
# 第一行边界
dp[0] = grid[0][0]
for i in range(1, n):
dp[i] = dp[i-1] + grid[0][i]
for i in range(1, m):
for j in range(n):
if j == 0: # 第一列边界
dp[j] += grid[i][j]
else:
# 关系式
dp[j] = min(dp[j], dp[j-1]) + grid[i][j]
return dp[n-1]
120. Triangle
Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[ [2], [3,4], [6,5,7], [4,1,8,3] ]
The minimum path sum from top to bottom is 11
(i.e., 2 + 3 + 5 + 1 = 11).
题目解析:
还是路径问题,算法框架和状态转移方程其实都和上面的差不多啦,不同的是数组每行的数目不同,需额外注意索引和边界问题。另外对于dp数组的更新比较麻烦,索性用了两个dp数组。
class Solution:
def minimumTotal(self, triangle: List[List[int]]) -> int:
n = len(triangle)
if n == 1:
return triangle[0][0]
if n ==2:
return triangle[0][0] + min(triangle[1][0], triangle[1][1])
dp_ = [0] * n
dp_[0] = triangle[0][0] + triangle[1][0]
dp_[1] = triangle[0][0] + triangle[1][1]
for i in range(3, n+1): # i是行数 索引为i-1
dp = [0] * n
for j in range(i): # 每行 j代表列索引
if j == 0:
dp[j] = dp_[j] + triangle[i-1][0]
elif j == i-1:
dp[j] = dp_[j-1] + triangle[i-1][j]
else:
dp[j] = min(dp_[j], dp_[j-1]) + triangle[i-1][j]
dp_ = dp
return min(dp_)
152. Maximum Product Subarray
Given an integer array nums
, find the contiguous subarray within an array (containing at least one number) which has the largest product.
题目解析:
前面有一道Maximum Subarray 最大连续和,图森破了就不介绍了。这道题是乘积,没有小数,但是会有0和负数。
1. 出现0的情况下重新往后算,另外0也可能是最大值;
2. 重点考虑负数*负数得到较大的乘积的形式,因此我们要记录两个值,一个是连续乘积最大值(正数),一个是连续乘积最小值(负数);当前数字为是正数或负数,需要用不同的方式更新这两个记录。
下面我们看代码,我们用dp_pos[i]和dp_neg[i]分别表示这两个值在i处的情况,当然我们可以优化为O(1)空间的。由于题目思路稍微复杂点,我们先写成O(n)空间的,理解了思路后再优化。
class Solution:
def maxProduct(self, nums: List[int]) -> int:
if not nums:
return 0
l = len(nums)
if l == 1:
return nums[0]
dp_pos = [0] * l
dp_neg = [0] * l
dp_pos[0] = max(nums[0], 0)
dp_neg[0] = min(nums[0], 0)
for i in range(1, l):
num = nums[i]
if num == 0:
continue
elif num > 0:
# 当前数字为正
dp_pos[i] = max(num, dp_pos[i-1] * num)
dp_neg[i] = min(num, dp_neg[i-1] * num)
else:
# 当前数字为负
dp_pos[i] = max(num, dp_neg[i-1] * num)
dp_neg[i] = min(num, dp_pos[i-1] * num)
return max(dp_pos)
这是几道稍加变形的DP题目,属于DP中必会的。后面我们会逐渐介绍有难度的题目