乘积最大子数组
题目
解法一 前缀积
思路:
与求前缀和的思路类似,用一个数组保存前缀积。
但是由于0的存在,我们将所有的数组按照0分隔开。
因为 a*1=a的特殊性. 且是整数数组. 所以在非0数组中,我们可以知道前缀积的绝对值是在增大的。所以子数组越长,子数组的积的绝对值就越大。
我们对于当前数组a0-aj的前缀积a有如下判断:
(1)如果前缀积a的绝对值大于0.那么该前缀的任意子数组的前缀积都小于等于a.所以我们直接返回a即可。
(2)如果前缀积a的绝对值小于0.我们要做的便是找出一个前缀积小于0的最短子数组a0-ai。因为a0-i越短,意味着ai-aj越长,且因为a0-ai的前缀积<0.所以ai-aj的前缀积>0,且越长前缀积越大。
代码
//还是要发掘 题目中的信息。这道题是前缀积,其实最主要还是符号的判断。
class Solution {
//与最大连续子数组的和类似。但是因为这里有0,所以按照0将数组分割为数个小数组,依次求每个小数组的乘积最大子数组。
//因为是一个整数的乘积,所以不含0的话,绝对值一定是越来越大.其实只需要判断正负号。。。
public:
int maxProduct(vector<int>& nums) {
bool first=true; //是否为非0数组的第一个元素
bool haveZero=false;
vector <int>muti; //依次放入非0元素的前缀积。
int max=-65535;
for(int i=0;i<nums.size();i++){
//将非0的进入数组,计算此时的乘积最大子数组。
if(nums[i]!=0){
//前缀积的加入
muti.push_back(first?nums[i]:muti.back()*nums[i]);
first=false;
//当前的最大下标
int now_muti_index=muti.size()-1;
//求以now_muti_index为下标结尾的该被0分隔后的子数组的最大子数组积。
//如果前缀积>0.判断直接返回即可。
if(muti[now_muti_index]>max) max=muti[now_muti_index];
//最后前缀积<0,向前找第一个为负数的前缀积.
else {
//否则是指定位置的前缀积相除
for(int j=-1;j<now_muti_index-1;j++){
int tmp_muti=muti[j+1];
if(tmp_muti>0) continue;
//第一个为负数的前缀积
if ((muti[now_muti_index] / tmp_muti) > max ){
max=muti[now_muti_index] / tmp_muti;
}
break;
}
}
}
//遇到了0,分隔数组。
else {
first=true;
muti.clear();
haveZero=true;
}
}
if(haveZero) return max>0?max:0;
else return max;
}
};
解法二 动态规划(copy)
这个解法是copy的官方解法.也是利用了正负性的原理来展开的。
这个转移方程是真的牛(我还没接触过dp),通过和ai本身取大.避免开了0对整个数组带来的影响。遇到0时f(max)=f(min)=0. 0之后就又重新展开了.不得不说,牛p.
class Solution {
public:
int maxProduct(vector<int>& nums) {
vector <int> maxF(nums), minF(nums);
for (int i = 1; i < nums.size(); ++i) {
maxF[i] = max(maxF[i - 1] * nums[i], max(nums[i], minF[i - 1] * nums[i]));
minF[i] = min(minF[i - 1] * nums[i], min(nums[i], maxF[i - 1] * nums[i]));
}
return *max_element(maxF.begin(), maxF.end());
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/maximum-product-subarray/solution/cheng-ji-zui-da-zi-shu-zu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。