题目
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
子数组 是数组的连续子序列。
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 400
示例 :
输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
思路
求最大非空连续子数组,一般采用dp,建立二维数组进行存储。
但是本题O(n2)会超时,因此需要压缩遍历次数
但是dp的思想应该不会变的,只是需要改变一下dp存储信息(首先确定为一维数组)
如果dpi[i-1]记录nums前i-1个节点的最大数值,以当前的i节点进行思考(先考虑局部),那么能不能大致确定i-1向i的转移方程呢?
dp[i] = max(和dp[i-1]组合)
但是问题来了:对于[-2,3,-4],i为2时,我们需要考虑-23乘积为负数的情况。虽然前i-1个节点最大值为3,但是更新dp[i]时,显然-4-2*3的结果更大。因此dp[i-1]需要记录(3,-6)两个数值,这可以采用pair记录。
那这两个数值代表的意义是什么呢?仔细一看就会发现,是最大值和最小值。
接下来就可以写状态转移方程了:
// dp[i].first代表最大值
// 转移方程代表是和前面子数组连起来大,还是当前nums[i]大
dp[i].first = max(dp[i-1].first*nums[i], max(dp[i-1].second*nums[i], nums[i]));
//dp[i].second代表最小值
// 同理
dp[i].second = min(dp[i-1].second*nums[i], min(dp[i-1].first*nums[i], nums[i]));
这时候就会有人问(没错就是我):对于dp[i],这里只有和前面数组连起来,和当前nums[i]两种情况。但是出现[3,-4,4]和[1,0,2]这样的咋整,也就是i和前面数组连不起来
可以把数往里带一下,可以发现对于[3,-4,4],dp[1].first=-4,dp[1].second=-12,dp[2].first=4。连不起来first就直接断开了,但是second保留
但是这地方有点绕,需要仔细体会一下。写题的时候虽然写出来状态方程了,但是没绕过来,一直没敢交…
可以发现,dp数组每次转移时只用了前一个dp[i-1]进行转移,所以没比较建立数组开那么大空间。采用两个数记录就行,诶,成贪心了。
代码
class Solution {
public:
int maxProduct(vector<int>& nums) {
int len = nums.size();
int maxx = nums[len-1], minn = nums[len-1];
int ans = maxx;
for(int i=len-2;i>=0;i--){
int ma = maxx, mi = minn;
maxx = max(nums[i]*ma, max(nums[i], nums[i]*mi));
minn = min(nums[i]*mi, min(nums[i], nums[i]*ma));
ans = max(ans, maxx);
}
return ans;
}
};