最大子序乘积问题的分析

最大子序乘积的问题分析

首先,题目概述:

        这里的问题针对的是整形数组,即像A[10]={1,3-2,5,66,-45,63,0,2,1}这样的数组。

        因此,可以知道数组的数可以分为三种类型:

1.正数

2.零

3.负数

除此之外,还有乘法的特性:正正得正,正负得负,负负得正,以及乘零等于零。

明白了上述基本数学内容后,现在给出两种分析思路,都可以O(n)的时间解决问题


方法1:

             求取包含A[i-1]连续往左的最大和最小乘积,并与A[i]相乘,得到的结果和A[i]共同比较,从前面得到的三个数里再次挑选最大和最小值作为包含A[i]连续往左的最大和最小乘积。依次循环,同时每次循环都要比较包含A[i]连续往左的最大乘积和整个过程中的最大乘积,记录下来,最后返回。代码如下:

//以下代码是本人所写
    //从左往右遍历整个数组,记录乘以A[i]后的最大imax和最小imin乘积
    //imax和imin都是包含A[i]及向左连续的子序乘积
    //之所记录最大和最小,是因为乘法的特性——负负得正
    //A[i]之前的最小乘积可能因为A[i]>0,而使得最大乘积翻倍
    //因此需要记录下最大和最小的乘积方便求取最大的乘积
    int maxProduct(int A[], int n) {               
        
        if(n==1)
            return A[0];
            
        int imin=1,imax=1;
        int resmax=A[0];
        for(int i=0;i!=n;i++){
            int tmax,tmin;
            tmax=max(max(imin*A[i],imax*A[i]),A[i]);
            tmin=min(min(imin*A[i],imax*A[i]),A[i]);
            
            imax=tmax;
            imin=tmin;
            resmax=max(imax,resmax);
        }
        return resmax;
        
    }

方法2:

注意两点:1.遇到零则重新计算;2.不包含零的情况下,连续最大乘积必然从最左或者最右端开始,即不可能存在于中间。


现在对第二点给出证明如下:

假设非零数组中存在处于中间的最大子序乘积A2,那么位于A2左边的乘积记录为A1,位于A2右边的乘积记录为A3。且A2最大不仅说明A1和A3都比A2小,即A2>A1,A2>A3,而且说明A2*A1<A2 , A2*A3<A2.否则A2是最大子序乘积的前提就不成立。那么

当A2>0时,A1<1和A3<1,即A1和A3都是负数,那么显然A1*A2*A3比A2,与假设相反

        当A2<0时,A1>1和A3>1,即A1和A3都是正数,那么显然A1和A3都大于A2,与假设相反

因此,题设中的假设最大子序乘积位于数组中间是不成立的。也就能从上面的结论知道,最大子序乘积应该位于数组的两端,即包含最左或者最右的数

代码如下:

//这段代码非本人所写,原作者可以在discuss里找到
    //在看过此段代码后,立即明白了之前一直忽略的地方,总共有两处(我对原作者思路的复现):
    //1.如果数组中有零,则零应该被特殊对待,因为任何数乘零都等于零,而最大乘积的趋向正整数
    //2.对于非零数组段中的最大连续子段乘积只能出现在最左或者最右,即必须包含A[0]或则A[n-1]。具体证明将在csdn的博客中给出
    //基于上述观点2,我们只需要从数组的首位两端同时连乘,并比较取最大,然后根据观点1,当连乘中遇到0的结果,则从下一个数重新开始连乘。
    int maxProduct(int A[], int n) {               
        int b=1, f=1, res=A[0];         //b存储从左往右的连乘积,f存储从右往左的连乘积,res代表最大乘积
        for(int i=0; i<n; i++){         //遍历整个数组
            res=max(res, max(b*=A[i],f*=A[n-1-i])); //比较res,b和f取最大值赋给res
            if(b==0) b=1; if(f==0) f=1;             //A[i]或者A[n-1-i]为零,连乘从A[i+1]或者A[n-2-i]重新开始
        }
        return res;
    }



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值