1 题目
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续 子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
请注意,一个只包含一个元素的数组的乘积是这个元素的值。
示例 1:
输入: nums = 2,3,-2,4
输出: 6
解释: 子数组 2,3 有最大乘积 6。
示例 2:
输入: nums = -2,0,-1
输出: 0
解释: 结果不能为 2, 因为 -2,-1 不是子数组。
2 分析
寻找连续子数组,第一反应是用动态规划:
dpi=max(dpi−1∗numsi,dpi−1,numsi)dpi = max(dpi-1*numsi,dpi-1,numsi)dpi=max(dpi−1∗numsi,dpi−1,numsi)
进一步分析,如果dpi−1∗numsi<dpi−1dpi-1*numsi<dpi-1dpi−1∗numsi<dpi−1,其实有两种情况:
- numsi<0,dpi−1>0numsi<0,dpi-1>0numsi<0,dpi−1>0,那么dpi=dpi−1dpi =dpi-1dpi=dpi−1
- numsi>0,dpi−1<0numsi>0,dpi-1<0numsi>0,dpi−1<0,那么dpi=numsidpi =numsidpi=numsi
但有个问题,如果是第一种情况,那么如果numsi+1numsi+1numsi+1也小于0,dpi+1dpi+1dpi+1的最优取值应该是dpi−1∗numsi∗numsi+1dpi-1*numsi*numsi+1dpi−1∗numsi∗numsi+1,但这是无法用上面动态规划的公式得到的。所以,真正的动态规划公式应该是
dpij=maxk=i+1j−1{dpik∗dpk+1j}dpij = \max_{k=i+1}^{j-1}\{dpik*dpk+1j\}dpij=k=i+1maxj−1{dpik∗dpk+1j}
但这个公式好像更复杂了,需要罗列路径上所有的k,计算量很大。
仔细思考下,为什么我们不能认为数组长度增加,乘积一定变大呢?是因为新增的数组值可能为负,这个时候最大值需要通过之前的最小值计算得到。所以这题的关键是:当前最小的乘积,遇到负数后,可能变成最大的乘积。
因此,需要同时记录当前的最大值和最小值,另外需要ans记录目前乘积最大的值(有可能当前最大值超过不了历史最大值)。综上,动态规划的公式为
curMax=max(x,curMax∗x,curMin∗x) \text{curMax} = \max(x, \text{curMax}*x, \text{curMin} *x) curMax=max(x,curMax∗x,curMin∗x)
curMin=min(x,curMax∗x,curMin∗x) \text{curMin} = \min(x, \text{curMax}*x, \text{curMin} *x) curMin=min(x,curMax∗x,curMin∗x)
python
def maxProduct(nums):
n = len(nums)
if n==1:
return nums[0]
ans, curMax, curMin = nums[0], nums[0], nums[0]
for i in range(1,n):
x = nums[i]
print(x, curMax, curMin, ans)
newMax = max(x, curMax * x, curMin * x)
newMin = min(x, curMax * x, curMin * x)
curMax, curMin = newMax, newMin
ans = max(ans, curMax)
print(curMax, curMin, ans)
return ans