這一節的幾篇,都是解析動態規劃數組類題目,相對於後面的字符串類問題來說,還是比較容易的。
首先來看比較幾個簡單的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中必會的。後面我們會逐漸介紹有難度的題目