每日一题(16)——求子串最大积&最大和

求子串最大积                                                                     

 

问题:

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

解法1.

•我们把所有可能的(N-1)个数的组合找出来,分别计算它们的乘积,并比较大小。由于总共有N个(N-1)个数的组合,总的时间复杂度为 O(N^2)

显然这不是最好的解法。

解法2.

计算前i项乘积s[i-1]与后n-(i+1)项乘积t[i+1],,总的时间复杂度为 O(N^2)

显然这不是最好的解法。

解法3.

•假设 N 个整数的乘积为 P,针对 P 的正负性进行如下分析(其中,A(N-1)表示 N-1 个数的组合, P(N-1)表示 N-1 个数的组合的乘积):

1.  P0

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

Q0

说明数组中至少有两个0,那么N-1个数的乘积只能为0,返回0;

Q为正数

 返回Q,因为如果以0替换此时A(N-1)中的任一个数,P(N-1)所得到的为0,必然小于Q;

Q为负数

如果以0替换此时A(N-1)中的任一个数,所得到的P(N-1)为0,大于Q,乘积最大值为0。

•2.   P 为负数

        根据“负负得正”的乘法性质,自然想到从N个整数中去掉一个负数,使得P(N-1)为一个正数。而要使这个正数最大,这个被去掉的负数的绝对值必须是数组中最小的。

       我们只需要扫描一遍数组,把绝对值最小的负数给去掉就可以了。

•3.   P 为正数         

           类似P为负数的情况,应该去掉一个绝对值最小的正数值,这样得到的P(N-1)就是最大的。

 

•上面的解法采用了直接求 N个整数的乘积P,进而判断 P的正负性的办法,但是直接求乘积在编译环境下往往会有溢出的危险 (这也就是本题要求不使用除法的潜在用意☺),事实上可做一个小的转变,不需要直接求乘积,而是求出 数组中正数( + )、负数( - )和  的个数,从而判断 P 的正负性,其余部分与以上面的解法相同。
•      在时间复杂度方面,由于只需要遍历数组一次,在遍历数组的同时就可得到数组中正数(+)、负数(-)和 0 的个数,以及数组中绝对值最小的正数和负数,时间复杂度为 O(N)。

 

求子串最大和:                                                                     

 

《编程珠玑》第8章的一道题:求子串最大和:

一个具有n个浮点数的向量x,要求输出相邻子向量的最大和,如图:

程序返回值应为x[2..6]的总和,即187。

 

在这里直接给出最适合简单的解法:

从数组的最左边x[0]开始扫描,一直到最右端x[n-1]。记录所有遇到的最大总和子向量maxendinghere。数组的最大总和maxsofar的初始值为0。

 

  1. int MaxChildSum(int X[],int n)  
  2. {  
  3.     int maxsofar = 0;  
  4.     int maxendinghere = 0;  
  5.     for (int i=0; i<n; i++)  
  6.     {  
  7.         maxendinghere = max(maxendinghere+X[i], 0);  
  8.         maxsofar = max(maxendinghere, maxsofar);  
  9.     }  
  10.     cout<<maxsofar<<endl;  
  11.     return maxsofar;  
  12. }  

 

这个程序的思想就是利用maxendinghere这个变量,它保存这结束位置为i-1的最大子向量和,赋值语句:maxendinghere = max(maxendinghere+X[i], 0);,

判断maxendinghere+X[i],:若为正值,则将maxendinghere增大到i;若为负值,将maxendinghere重新置零。

复杂度只有O(n)。

算法的思量是求累加数组

 

题目扩展:

查找总和最接近0的子序列?

  1. void MostNearZero(int X[],const int& n)  
  2. {  
  3.     int temp[10];  
  4.     for (int i=0; i<n; i++)  
  5.     {  
  6.         temp[i] += X[i];  
  7.     }  
  8.        
  9.     int minSub=10000,tmp=0;  
  10.     int left,right;  
  11.     for(int i=0; i<n; i++)  
  12.     {  
  13.         for(int j=i+1; j<n; j++)  
  14.         {  
  15.             tmp = abs(temp[i]-temp[j]);  
  16.             if (tmp<minSub)  
  17.             {  
  18.                 minSub=tmp;  
  19.                 left=i;right= j;  
  20.             }  
  21.               
  22.         }  
  23.     }  
  24. }  


 

利用累加数组temp[i],temp[i]为元素0到i的和,temp[i]与temp[j]的差,当temp[i]-temp[j]=0时,则元素i到j-1的总和为0。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值