zoukankan      html  css  js  c++  java
  • 23最大乘积子串

       题目描述:给一个浮点数序列,取最大乘积连续子串的值,例如 -2.5,4,0,3,0.5,8,-1,则取出的最大乘积连续子串为3,0.5,8。也就是说,上述数组中,3 0.5 8这3个数的乘积3*0.5*8=12是最大的,而且是连续的。

        提醒:子串子序列不同,子串要求连续,子序列不要求连续。

     

        分析:这个问题与“最大子数组”问题极为相似,最大子数组是求和的最大子串,该题是求积的最大子串。最大子数组中,最巧妙的算法是DP算法,思想以及源代码如下:

     

           先看数组a[0…i],已知它的最大子数组,记为res(i),同时,也可求得包含元素a[i]的最大子数组。记为sum(i)。

           对于数组a[0…i+1],它的最大子数组,要么包含元素a[i+1],要么不包含元素a[i+1]:

           包含元素a[i+1]的情况是:res(i+1) = sum(i+1)

           不包含元素a[i+1]的情况是res(i+1) = res(i)

    int DPmaxSum(int *a, int length)

    {

             int res = NEINFINITE;

             intsum = 0;

             inti;

            

             for(i= 0; i < length; i++)

             {

                      if(sum>= 0)

                      {

                              sum= sum + a[i];

                      }

                      else

                      {

                              sum= a[i];

                      }

                      res= (sum > res)?sum:res;

             }

             returnres;

    }

     

        注意到下面的代码,实际上是需要根据(sum+a[i] >= a[i])?的不同情况做出不同的选择:

                      if(sum >= 0)

                      {

                              sum = sum + a[i];

                      }

                      else

                      {

                              sum = a[i];

                      }

        sum+a[i] ? a[i],在求和的时候,只需要区分sum是否大于或者小于0即可,这是因为对于加法来说,如果a>b,那么对于任意的数a+c > b+c,而不管c是整数还是负数。

        但是对于乘法来说,如果a>b,那么ac和bc的大小并不一定能够确定,因为涉及到负数的问题。

        根据最大子数组的思想,假设包含a[i]的最大乘积为maxi,最小乘积为mini, 所以,mini < maxi,但是maxi* a[i], mini * a[i]与a[i]三个数之间的大小关系也是不确定的。所以,每次求maxi的同时,也需要求mini的大小,代码如下;

    double func(double *a,const int n) 

       double *maxA = new double[n]; 

       double *minA = new double[n]; 

       maxA[0] = minA[0] =a[0]; 

       double value = maxA[0]; 

       for(int i = 1 ; i < n ; ++i) 

       { 

            maxA[i] =max(max(a[i],maxA[i-1]*a[i]),minA[i-1]*a[i]); 

            minA[i] =min(min(a[i],maxA[i-1]*a[i]),minA[i-1]*a[i]); 

           value=max(value,maxA[i]); 

       } 

       return  value; 

    }

     

        相关问题:给定一个长度为N的整数数组,只允许用乘法,不能用除法,计算任意(N-1)个数的组合中乘积最大的一组,并写出算法的时间复杂度。

        分析:我们可以把所有可能的(N-1)个数的组合找出来,分别计算它们的乘积,并比较大小。由于总共有N个(N-1)个数的组合,总的时间复杂度为O( ),显然这不是最好的解法。下面的解答来自编程之美

    解法1

        假设a[1..n]为初始数组,

        s[i]表示从a[1]到a[i]的乘积,记s[0] = 1。所以s[i] = s[i-1] * a[i]。

        t[i]表示从a[i]到a[n]的乘积,记t[n+1] = 1,所以,t[i] = t[i+1]*a[i]。

        p[i]表示除a[i]之外,其他n-1个元素的乘积,所以,p[i] = s[i-1]*t[i+1].

       所以,只要从头至尾扫描一遍,就可求得s[1..n],然后从尾至头,就可求得t[1..n]。进而,线性时间内得到p[1..n],所以,总的时间复杂度为o(n)。

     

    解法2
        可以通过分析,进一步减少解答问题的计算量。假设N个整数的乘积为P,针对P的正负性进行如下分析:

     

    1:P=0

        说明数组中至少包含有一个0。假设除去一个0之外,其他N-1个数的乘积为Q,根据Q的正负性进行讨论:

        Q为0:说明数组中至少有两个0,那么N-1个数的乘积只能为0,返回0;
        Q为正数:则n-1个数的最大乘积为Q,返回Q

        Q为负数:返回0

     

    2P为负数

        因为 正*负=负,所以,可以从N个整数中去掉一个负数,这样其他n-1个数的乘积肯定为一个正数。而要使这个正数最大,这个被去掉的负数必须是数组中最大的(绝对值最小)。因而只需要扫描一遍数组,把绝对值最小的负数给去掉就可以了。

     

    3P为正数

        因为 负*负=正 正*正=正。所以,如果数组中存在正数值,那么应该去掉最小的正数值,如果数组中不存在数,则应该去掉最小的负数值(绝对值最大)

     

        上面的解法采用了直接求N个整数的乘积P,进而判断P的正负性的办法,但是直接求乘积在编译环境下往往会有溢出的危险(这也就是本题要求不使用除法的潜在用意),事实上可做一个小的转变,不需要直接求乘积,而是求出数组中正数(+)、负数(-)和0的个数,从而判断P的正负性,其余部分与以上面的解法相同。

        在时间复杂度方面,由于只需要遍历数组一次,在遍历数组的同时就可得到数组中正数(+)、负数(-)和0的个数,以及数组中绝对值最小的负数,绝对值最大的负数,最小的正数。时间复杂度为O(N)。

     

    (http://blog.csdn.net/v_july_v/article/details/8701148)

     

  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    详解以太坊世界状态
    VDF 不是工作量证明
    以太坊:Go-Ethereum: 编译运行
    【转】理解分布式账本技术: 经济学视角
    Counterfactual 项目:广义的以太坊状态通道
    Solidity 安全:已知攻击方法和常见防御模式综合列表
    Verge 攻击解析
    以太坊区块链的轻客户端
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7247164.html
Copyright © 2011-2022 走看看