leetcode-----乘积最大子序列和

题目:

给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。

输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组

这一题刚开始,我想到的是加法的最大子序列和,于是我用来最巧妙的解法,就是遍历一遍,但是发现乘法和加法还是不太一样,这里前面如果有负数的累积,则在遇到一个负数,就可以变成一个很大的数,我当时并没有细想,于是,我就用了我的笨办法,就是分治;将原问题变成左右两个子问题,分别计算情况然后将左右情况汇总,得到最终情况;

class Solution {
public:
    
    array<int,6> sub_Pro(vector<int> &nums,int lo,int hi)
    {
        array<int,6> sub_res;
        if(lo==hi){
            if(nums[lo]>0){
                sub_res[0]=nums[lo];
                sub_res[1]=INT_MAX;
                sub_res[2]=nums[lo];
                sub_res[3]=INT_MAX;
            }
            else if(nums[lo]<0){
                sub_res[0]=INT_MIN;
                sub_res[1]=nums[lo];
                sub_res[2]=INT_MIN;
                sub_res[3]=nums[lo];
            }
            else{
                sub_res[0]=INT_MIN;
                sub_res[1]=INT_MAX;
                sub_res[2]=INT_MIN;
                sub_res[3]=INT_MAX;
            }
            sub_res[4]=nums[lo];
            sub_res[5]=nums[lo];
            return sub_res;
        }
        int mi=lo+(hi-lo)/2;
        array<int,6> left=sub_Pro(nums,lo,mi);
        array<int,6> right=sub_Pro(nums,mi+1,hi);
        if(left[4]>0){
                sub_res[0]=(right[0]>0)?left[4]*right[0]:left[4];
                sub_res[1]=(right[1]>0)?left[1]:left[4]*right[1];
        }
        else if(left[4]<0){
                sub_res[0]=(right[1]>0)?left[0]:left[4]*right[1];
                sub_res[1]=(right[0]<0)?left[4]:left[4]*right[0];
        }
        else {
            sub_res[0]=left[0];
            sub_res[1]=left[1];
        }
        if(right[4]>0){
            sub_res[2]=(left[2]>0)?right[4]*left[2]:right[4];
            sub_res[3]=(left[3]>0)?right[3]:right[4]*left[3];
        }
        else if(right[4]<0){
            sub_res[2]=(left[3]>0)?right[2]:right[4]*left[3];
            sub_res[3]=(left[2]<0)?right[4]:right[4]*left[2];
        }
        else{
            sub_res[2]=right[2];
            sub_res[3]=right[3];
        }
        sub_res[4]=left[4]*right[4];
        int temp=max(sub_res[0],sub_res[2]);
        if(left[2]>0&&right[0]>0)   temp=max(temp,left[2]*right[0]);
        if(left[3]<0&&right[1]<0)   temp=max(temp,left[3]*right[1]);
        sub_res[5]=max(left[5],right[5]);
        sub_res[5]=max(sub_res[5],sub_res[4]);
        sub_res[5]=max(sub_res[5],temp);
        return sub_res;
    }
    
    
    
    int maxProduct(vector<int>& nums) {
        array<int,6> res=sub_Pro(nums,0,nums.size()-1);

        return res[5];
    }
};

说实话,这样判断,真的很费时间去把条件都找对,真的写的很蠢;

时间复杂度为O(klgn),k表示每一次的操作时间;

刚才说到,就是遍历一遍,但是发现乘法和加法还是不太一样,这里前面如果有负数的累积,则在遇到一个负数,就可以变成一个很大的数,我看了别人的解法后,才知道,我其实可以每一次都把遍历到当前位置的最大值和最小值记下来,然后和下一个遍历的值进行计算;这样便于以后的每一个节点的计算。

在序列中计算出以任一个节点为终结点的子序列乘积,取最大值返回即可。

首先不妨尝试以 up(i) 表示第 i 个元素为子序列终结点的最大乘积:

若 nums[i]≥0,则有推导式 up(i)=max(up(i−1)∗nums[i],nums[i])

若 nums[i]<0,则以上推导式不成立。不妨以down(i) 表示第 i 个元素为子序列终结点的最小乘积,则有 up(i)=max(down(i−1)∗nums[i],nums[i])

因为涉及到 down(i) 函数,同理可得:

若 nums[i]≥0,则有推导式 down(i)=min(down(i−1)∗nums[i],nums[i])

若 nums[i]<0,则有推导式 down(i)=min(up(i−1)∗nums[i],nums[i])

因为 up(i),down(i)只与 up(i−1),down(i−1) 存在递推关系,不妨以up,down 表示每个位置上的最大、最小序列乘积。

code:

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        if(nums.empty()) return 0;
        int i, ret, pos, neg, temp;
        pos = nums[0];
        neg = nums[0];
        ret = nums[0];
        for(i = 1; i < nums.size(); i++){
            temp = pos;
            pos = max(nums[i], max(pos * nums[i], neg * nums[i]));
            neg = min(nums[i], min(temp * nums[i], neg * nums[i]));
            ret = max(pos, ret);
        }
        return ret;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值