问题描述:

给定一个整数序列,a0, a1, a2, …… , an(项可以为负数),求其中最大的子序列和。如果所有整数都是负数,那么最大子序列和为0;

例如:对于序列-2, 11, -4, 13, -5, –2。 所求的最大子序列和为20(从11到13,即从a1到a3)。

用于测试下面代码的的主函数代码如下:(注意要更改调用的函数名)

int main(int argc, char **argv)

{

vector a;

a.push_back(-2);

a.push_back(11);

a.push_back(-4);

a.push_back(13);

a.push_back(-5);

a.push_back(-2);

int result;

result = maxSubSum1(a); //在这里更改调用的函数名 cout<

}

方法一:对所有的子序列求和,在其中找到最大的

这是最容易想到的,也是最直接的,就是对所有的子序列求和,代码如下:

int maxSubSum1( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{

for(int j=i; j

{

int thisSum =0;

for( int k=i; k<=j; k++ )

{

thisSum += a[k];

}

if(thisSum>maxSum)

{

maxSum = thisSum;

}

}

}

return maxSum;

}

分析:

① 三层循环嵌套,时间复杂度为O(n^3);

② 包含有大量的重复计算,例如i=1时: 当 j=3,则计算a1+a2+a3;j=4,则计算a1+a2+a3+a4;其中a1+a2+a3是重复计算的。

另一种思路:上面的方法在每一次循环中,固定i,并把a[i]当做起点,下面的方法将a[i]当做终点。

int maxSubSum1_2( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{

for(int j=0; j

{

int thisSum =0;

for(int k=j; k<=i; k++)

{

thisSum +=a[k]; //从节点j开始 累加到节点i

if(thisSum>maxSum)

{

maxSum = thisSum;

}

}

}

}

return maxSum;

}

方法二:从某点开始的所有序列中,找最大的

如果你意识到,子序列总要有一个位置开始,那么变换一下循环方式,只要求出在所有位置开始的子序列,找到最大的。代码如下:

int maxSubSum2( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{

int thisSum =0;

for(int j=i; j

{

thisSum +=a[j]; //从节点i开始 累加到结尾

if(thisSum>maxSum)

{

maxSum = thisSum;

}

}

}

return maxSum;

}

分析:

① 两层循环嵌套,时间复杂度为O(n^2);

② 虽然比方法一要少,但同样包含重复计算。例如:当i=1时,要计算a1+a2+a3+a4+……;当i=2时,要计算a2+a3+a4+……;其中a2+a3+a4+……是重复的。

注意:下面是一个错误的方法,因为它的起始点固定了,每次都从a0开始,是不能保证遍历所有的子序列的。

int maxSubSum2_2( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{

int thisSum =0;

for(int j=0; j<=i; j++ )

{

thisSum +=a[j]; //从节点0开始 累加到节点i

if(thisSum>maxSum)

{

maxSum = thisSum;

}

}

}

return maxSum;

}

如果希望将固定终点,那么计算的时候就要从终点开始,依次往前累加。代码如下:

int maxSubSum2_3( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{

int thisSum =0;

for(int j=i; j>=0; j-- )

{//从节点i开始,向前累加到结尾0

thisSum +=a[j];

if(thisSum>maxSum)

{

maxSum = thisSum;

}

}

}

return maxSum;

}

方法三:从某一个正数开始

第一步:

到目前为止,题目的信息你只用到了:“最小子序列之和为0”(若有一项大于0,那么子序列的和一定大于或等于该项,也就大于0;因为若所有项都是负数,那么结果为0

如果你再挖掘一下题意:你就会发现,如果a[i]是负的,那么a[i]一定不是最终所有结果子序列的起始点。代码可以改造为:

int maxSubSum3_1( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{ //(相对方法2,新增)如果a[i]<=0,那么a[i]一定不是所要求的起点,所以直接跳过去(利用for循环中有i++)

if( a[i]<=0 )

continue;

int thisSum =0;

for(int j=i; j

{

thisSum +=a[j];

if(thisSum>maxSum)

{

maxSum = thisSum;

}

}

}

return maxSum;

}

第二步:

如果你又再一步发现:任何负的子序列,不可能作为最优子序列的前缀。

又因为上一步已经保证,序列以正数开头a[i]>0,所以若a[i]到a[j]之间元素的序列和 thisSum<=0时,则i+1和j之间元素不会为最优子序列的前缀,可以让i=j,即不需要判断在i和j之间元素开头。代码如下

int maxSubSum3_2( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{ //(相对方法2,新增)如果a[i]<=0,那么a[i]一定不是所要求的起点,所以直接跳过去(利用for循环中有i++)

if( a[i]<=0 )

continue;

int thisSum =0;

for(int j=i; j

{

thisSum +=a[j];

if(thisSum>maxSum)

{

maxSum = thisSum;

}

else if( thisSum <= 0 )

{ //(相对方法3_1 新添) thisSum = 0; i = j; }

}

}

return maxSum;

}

第三步:

如果你又进一步发现:因为要求序列开始元素大于0, 若以a[i]开头的序列,a[i]>0,那么可以知道,所求的最终子序列一定不会以a[i+1]开始, 因为若到相同的元素终止,那么从a[i]开始序列,一定大于从a[i+1]开始的序列。因为s[i, k]=s[i+1, k]+a[i] 例如:a1+a2+a3>0,而又由于这时a1>0,

那么所求子序列一定不会以a2开始,因为从a1开始会更大。 更进一步,如若一个子序列thisSum>0(其中thisSum是从第m项到第n项的和),那么序列一定不会以a[m]和a[n]之间的项开始。 因为一直thisSum第一个元素a[m]是大于0的,且以a[m]开始的所有子序列都是大于0的,因为若存在子序列小于0,就会提前返回了。 例如:若程序执行到thisSum (为a2+a3+a4),若thisSum>0,则能说明,a2>0,并且a2+a3>0, a2+a3+a4>0。那么 也可以跳过a[m]和a[n]之间的项,即另

i=j。

int maxSubSum3_3( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{ //(相对方法2,新增)如果a[i]<=0,那么a[i]一定不是所要求的起点,所以直接跳过去(利用for循环中有i++)

if( a[i]<=0 )

continue;

int thisSum =0;

for(int j=i; j

{

thisSum +=a[j];

if( thisSum>0 )

{ //(相对方法3_2 新添)

if(thisSum>maxSum)

{ maxSum = thisSum; }

i = j; //(相对方法3_2 新添)

}

else if( thisSum <= 0 )

{ //(相对方法3_1 新添)

thisSum = 0;

i = j;

}

}

}

return maxSum;

}

第四步:

最后如果你又了解一些程序结构上的优化的知识,那么你会发现下面的问题:

① 循环的分支可以改变一下,去除嵌套分支结构。

② 判断语句的分支中有共同部分,( i=j ),可以抽取出来。

以上两步以后,循环部分的代码编程 变成:

for(int i=0; i

{

if( a[i]<=0 )

continue;

int thisSum =0;

for(int j=i; j

{

thisSum +=a[j];

if(thisSum>maxSum)

{ maxSum = thisSum; }

else if( thisSum>0 )

{ //do nothing }

else if( thisSum <= 0 )

{ thisSum = 0; }

i = j;

}

}

③ 下面这步非常重要,如果你发现,内层循环的循环变量j 和 外层循环的循环变量i同步增长,那么你是否能够想到,外层循环可能没有存在的必要。在这里到底能不能去除外层循环,取决于外层循环中是否有额外的工作要做。这里的额外工作是是if(a[i]

<=0) 判断语句,如果你能发现内层循环的 if(thisSum < 0)的判断能够替代 if( a[i]<=0 ) 的工作。因为thisSum是由a[j]得到的。

到这里,代码就可以神奇的变为如下的形式:常量空间,线性时间

int maxSubSum3_4( const vector &a )

{

int maxSum = 0;

int thisSum = 0;

for(int j=0; j

{

thisSum += a[j];

if(thisSum>maxSum)

{ maxSum = thisSum; }

else if( thisSum>0 )

{ //do nothing }

else if( thisSum < 0 )

{ thisSum = 0; }

}

return maxSum;

}

分析:

① 只有一层循环,时间复杂度为O(n)。常量空间,线性时间,这是最优解法。

分享到:

2012-09-07 21:52

浏览 348

评论

最大子序列求和_最大子序列和问题相关推荐

  1. 最大子序列求和_最大子序列和问题  一步一步到最优

    在<数据结构和算法分析 C++描述>上看到了一个例子.看过之后,我就在想,这是怎么一步一步的递推出来的,想了好长时间,才整理成这篇博文. 问题描述: 给定一个整数序列,a0, a1, a2 ...

  2. 最大子序列求和_连续子序列最大和与乘积问题的分析

    问题描述 给定(可能是负的)整数序列A1, A2,...,AN, 寻找(并标识)使Sum(Ak)(k >=i, k <= j)的值最大的序列.如果所有的整数都是负的,那么连续子序列的最大和 ...

  3. 最大子序列求和_最大连续子序列和

    题外话:本文原文是之前在知乎专栏写的一篇文章,现在读来发现好多地方没有写清楚,所以现在做了些修改,希望把分析过程说的更明白一些.为什么要发布到慕课网来呢,主要是因为个人认为这里的IT氛围更好更专业. ...

  4. 最大子序列求和_算法——求最大子段和

    一.问题描述 给定由n个整数组成的序列(a_1,a_2,-,a_n),最大子段和问题要求该序列形如 的最大值(1≤i≤j≤n),当序列中所有整数均为负整数时,其最大子段和为0. 例如,序列(-20, ...

  5. c 最大子序列和_最大子序列和暴力法、分治+递归法、妙法

    你好,我是goldsunC 让我们一起进步吧! 最大子序列和 Question:给定整数(可能有负数),求的最大值(为方便起见,如果所有整数均为负数,则最大子序列和为0). 示例: IN : [-2, ...

  6. 算法:最大子序列求和问题

    最大子序列求和是指给定一组序列,求所有连续子序列的和中的最大值,例如给定数列: [5,-2,-5,6]最大子序列和是6:[1, 2, -3, 4, -5, 6, 7, 8, -9, 10]最大子序列和 ...

  7. java实现子序列最大和_“最大子序列和”算法 java

    maxSubSum各自是最大子序列和的4中java算法实现. 第一种算法执行时间为O(N^3),另外一种算法执行时间为O(N^2),第三种算法执行时间为O(nlogn),第四种算法执行时间为线性N p ...

  8. 最长递增子序列 子串_最长递增子序列

    最长递增子序列 子串 Description: 描述: This is one of the most popular dynamic programming problems often used ...

  9. java最大子序列和问题_最大子序列和问题(C语言)

    最大子序列和(maxSubSeqSum) 时间复杂度:T(N)=O(N3) int MaxSubSeqSum(int arrays[],int length){ int i,j,k,thisSum=0 ...

最新文章

  1. [MySQL 源码] 从buffer pool中获取空闲block流程
  2. 环形队列出队的元素怎么输出出来_队列的知识讲解与基本实现(数据结构)
  3. 最新福昕高级阅读编辑器
  4. python数字雨代码_电影黑客帝国中代码雨如何实现?简单!用 Python 就能实现!...
  5. 触发器详解——(三)T触发器
  6. 使用Riverbed SteelCentral NetProfiler,大海捞针不再难
  7. 时间在断断续续的故事上搁浅
  8. 大学生提高计算机应用能力,大学生计算机应用能力的培养策略
  9. webrtc下的媒体网络连接STUN、TURN、UDP、TCP
  10. Quartz的一般配置方法
  11. tiny11安装中文
  12. 大厂地震,疯狂裁员大换血,面试冲击大厂Android移动开发工程师就在此时
  13. 35 小白鼠排队 北大复试
  14. 学生HTML个人网页作业作品 (苹果商城HTML+CSS)---苹果商城8页 带报告
  15. nodejs+vue健身俱乐部网站
  16. 了解sentinel
  17. cas 自定义登陆异常提示
  18. python基于大数据的房产估价-实训头歌
  19. 6套粒子群算法(内含matlab代码)
  20. C++图像处理学习(二)之图像增强——五大灰度变换

热门文章

  1. python 对指定URL获取其子链接
  2. 数据库:数据库死机和掉电时如何让恢复数据
  3. Python 值传递与地址传递总结
  4. jQuery的核心函数
  5. android批量删除图片,Android RecyclerView单点、批量数据元素项目item的增加、删除和移动...
  6. 拆分工作簿为多个文件_掌握这几行代码,快速拆分Excel工作簿(内含源码)
  7. linux开放端口_Linux系统通过firewall限制或开放IP及端口 - 北方客888
  8. html5 input required oninvalid,为什么我的“ oninvalid”属性会让模式失败?
  9. 机房漏电产生的危害及安全隐患解决方案
  10. java内存模型 原子性_Java内存模型JMM 高并发原子性可见性有序性简介 多线程中篇(十)...