2020-05-18 LeetCode 152 乘积最大子数组 c

题目:给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

示例1:

输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。

示例2:

输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

 电脑终于修好了,niiiiiiice

 这道题一开始是没有任何思路的,然后去把相关题目"最大子序和"、“除自身意外的数组的乘积”、”打家劫舍“、”三个数的最大乘积“分别做了一遍,饶了一圈回来感觉这就是道动态规划题,和”53 最大子序和“很像,难点就是方程建立时负数的处理上。最大子序和如果f[i-1]小于0可以直接扔掉,因为对结果是负增益;但是对于这道题来说,如果f[i-1]小于零,在后续遍历中乘以一个负数是可以变成正数的,因此如果更新的时候单纯取当前最大值可能会陷入局部最优,比如[2,-4,8,-1]中,最大值策略输出会是8,但是真正结果应该是64。

 联系到”628 三个数的最大乘积“的官方题解2,想到可以维护三个值,分别记录当前连续乘积最大值(fminf_{min})、连续乘积最小值(fmaxf_{max})和最终结果(answer),更新规则如下:
fmin[i]=min[fmin[i1]ni,fmax[i1]ni,ni]fmax[i]=max[fmax[i1]ni,fmax[i1]ni,ni]answer=max[answer,fmax[i]] f_{min}[i]=min[f_{min}[i-1]*n_i,f_{max}[i-1]*n_i,n_i] \\ f_{max}[i]=max[f_{max}[i-1]*n_i,f_{max}[i-1]*n_i,n_i] \\ answer=max[answer,f_{max}[i]]
 其中n就是nums[i],把最小值(一般是负数)和最大值分别记录,也就是记录两个绝对值最大的,这样更新的话就可以避免上面说的局部最优,最终返回answer即可。

代码实现:

#define max3(a,b,c) (a>b?a:b)>c?(a>b?a:b):c
#define min3(a,b,c) (a<b?a:b)<c?(a<b?a:b):c
#define max(a,b)  a>b?a:b

int maxProduct(int* nums, int numsSize){
    int minAns=nums[0],maxAns=nums[0],ans=nums[0];
    int i;
    for(i=1;i<numsSize;i++){
        int tmp=minAns;
        minAns=min3(minAns*nums[i],maxAns*nums[i],nums[i]);
        maxAns=max3(maxAns*nums[i],tmp*nums[i],nums[i]);
        ans=max(ans,maxAns);
    }
    return ans;
}

 这里有一个细节就是对minAns和maxAns更新时,先更新的那一个必须先保存原来的值,否则更新规则会错误,以代码中为例,如果不用tmp保存minAns,更新规则就是,
fmin[i]=min[fmin[i1]ni,fmax[i1]ni,ni]fmax[i]=max[fmin[i]ni,fmax[i1]ni,ni]answer=max[answer,fmax[i]] f_{min}[i]=min[f_{min}[i-1]*n_i,f_{max}[i-1]*n_i,n_i] \\f_{max}[i]=max[f_{min}[i]*n_i,f_{max}[i-1]*n_i,n_i] \\answer=max[answer,f_{max}[i]]
fmaxf_{max}的更新显然是不对的

运行结果:
2020-05-18_114841

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章