152. 乘積最大子數組
題目來源:https://leetcode-cn.com/problems/maximum-product-subarray/
題目
給你一個整數數組 nums ,請你找出數組中乘積最大的連續子數組(該子數組中至少包含一個數字),並返回該子數組所對應的乘積。
示例 1:
輸入: [2,3,-2,4]
輸出: 6
解釋: 子數組 [2,3] 有最大乘積 6。
示例 2:
輸入: [-2,0,-1]
輸出: 0
解釋: 結果不能爲 2, 因爲 [-2,-1] 不是子數組。
解題思路
思路:動態規劃
這道題跟之前【53. 最大子序和】 題有點類似,不過這裏要求的是乘積最大值。
因爲兩道題要求都是連續的,在這裏,我們可以這樣進行狀態設計,也就是以 nums[i] 結尾的連續子數組的最大值。
現在具體看下如何進行狀態設計、推導狀態轉移方程,進而加以實現。
因爲數組中存在着負數,所以有可能導致乘積會從最大變爲最小,同樣的,最小也可能變爲最大。這裏需要注意。
現在,先進行狀態設計:
d[i][j]
: 表示以 nums[i] 結尾的連續子數組的最值。在這裏,j 決定計算的是最大值還是最小值。
- 其中當 j = 0 時,表示計算的是最小值,
- 當 j = 1 時,表示計算的是最大值。
現在推導狀態轉移方程:
因爲是乘積的關係,nums[i] 數值的正負,與前面的狀態值是有聯繫的,具體如下:
- 當 nums[i] > 0 時:
- 與最大值的乘積依然是最大值
- 與最小值的乘積依然是最小值
- 當 nums[i] < 0 時:
- 與最大值的乘積變爲最小值
- 與最小值的乘積變爲最大值
- 當 nums[i] = 0 時,這裏無論最大最小值,最終結果都是 0,這裏其實可以合併在上面任意一種情況。
但是還有需要注意的地方,這裏前面的狀態值的正負也是會影響最終的最值。假如,前面的最大值是負數的情況下,也就是 dp[i-1][1] < 0,但這是 nums[i] > 0 的情況下,這裏就要重新考慮,此時的最大值應該是 dp[i][1] = nums[i]。
按照這個思路,將所有情況的狀態轉移方程都寫出來,具體如下:
dp[i][0] = min(nums[i], dp[i-1][0] * nums[i]) if nums[i] >= 0
dp[i][1] = max(nums[i], dp[i-1][1] * nums[i]) if nums[i] >= 0
dp[i][0] = min(nums[i], dp[i-1][1] * nums[i]) if nums[i] < 0
dp[i][1] = max(nums[i], dp[i-1][0] * nums[i]) if nums[i] < 0
具體的代碼實現如下。
代碼實現
class Solution:
def maxProduct(self, nums: List[int]) -> int:
if len(nums) == 0:
return 0
length = len(nums)
# 初始化
# dp 數組有兩個元素,一個存儲最大值,一個最小值
dp = [[0] * 2 for _ in range(length)]
# 初始化數組首元素爲最大值和最小值
dp[0][0] = nums[0]
dp[0][1] = nums[0]
# 開始遍歷
for i in range(1, length):
# 狀態轉移方程
if nums[i] > 0:
dp[i][0] = min(nums[i], dp[i-1][0] * nums[i])
dp[i][1] = max(nums[i], dp[i-1][1] * nums[i])
else:
dp[i][0] = min(nums[i], dp[i-1][1] * nums[i])
dp[i][1] = max(nums[i], dp[i-1][0] * nums[i])
# 因爲最終要求得最大值,那麼在 dp[i][1] 找得最大即可
# 初始化返回值
res = dp[0][1]
for i in range(1, length):
res = max(res, dp[i][1])
return res
實現結果
以上就是使用動態規劃,根據題意進行狀態設計,推導出狀態轉移方程,用代碼加以實現,進而解決《152. 乘積最大子數組》問題的主要內容。
歡迎關注微信公衆號《書所集錄》