最大子序列和问题

最大子序列和问题以及确定序列起终点位置 在学习数据结构遇到的第一个问题就是一个最大子序列和的问题,以PAT(点击打开链接)上的一道题作为例子来总结一下求解这类问题时一些常用的方法。网上讲述子列和问题的博客及文章已经很多了,这里就不在阐述穷举法和递归法的方式来求解了,有需求的小伙伴可以去谷歌然后百度一下。这篇博文重点讲解一下最优时间复杂度的的线性处理方式和一点点关于这个问题的扩展。 首先上的这幅图就是PAT上的那道题,想深入了解就自己点 阅读详情

原文链接 在hust快乐的学习 http://blog.youkuaiyun.com/hs794502825/article/details/7956730

问题描述:

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

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

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

[cpp]  view plain  copy
  1. int main(int argc, char **argv)  
  2. {  
  3.      vector<int> a;  
  4.     a.push_back(-2);  
  5.     a.push_back(11);  
  6.     a.push_back(-4);  
  7.     a.push_back(13);  
  8.     a.push_back(-5);  
  9.     a.push_back(-2);  
  10.     int result;  
  11.     result = maxSubSum1(a); //在这里更改调用的函数名 cout<<result<<endl; //正确结果为 20 return 0;  
  12. }  


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

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

[cpp]  view plain  copy
  1. int maxSubSum1( const vector<int> &a )  
  2. {   
  3.     int maxSum = 0;  
  4.     for(int i=0; i<a.size(); i++ )  
  5.     {   
  6.         for(int j=i; j<a.size(); j++ )  
  7.         {   
  8.             int thisSum =0;  
  9.             for( int k=i; k<=j; k++ )  
  10.             {   
  11.                 thisSum += a[k];   
  12.             }  
  13.             if(thisSum>maxSum)  
  14.             {   
  15.                 maxSum = thisSum;  
  16.             }  
  17.         }  
  18.     }  
  19.     return maxSum;  
  20. }  


分析:

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

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

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

[cpp]  view plain  copy
  1. int maxSubSum1_2( const vector<int> &a )  
  2. {   
  3.     int maxSum = 0;   
  4.     for(int i=0; i<a.size(); i++ )  
  5.     {   
  6.         for(int j=0; j<i; j++ )  
  7.         {   
  8.             int thisSum =0;   
  9.             for(int k=j; k<=i; k++)  
  10.             {   
  11.                 thisSum +=a[k]; //从节点j开始 累加到节点i  
  12.                 if(thisSum>maxSum)  
  13.                 {   
  14.                     maxSum = thisSum;   
  15.                 }  
  16.             }  
  17.         }  
  18.     }  
  19.     return maxSum;   
  20. }  


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

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

[cpp]  view plain  copy
  1. int maxSubSum2( const vector<int> &a )  
  2. {   
  3.     int maxSum = 0;  
  4.     for(int i=0; i<a.size(); i++ )  
  5.     {   
  6.         int thisSum =0;  
  7.         for(int j=i; j<a.size(); j++ )  
  8.         {   
  9.             thisSum +=a[j]; //从节点i开始 累加到结尾  
  10.             if(thisSum>maxSum)  
  11.             {   
  12.                 maxSum = thisSum;   
  13.             }  
  14.         }  
  15.     }  
  16.     return maxSum;  
  17. }  


分析:

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

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

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

[cpp]  view plain  copy
  1. int maxSubSum2_2( const vector<int> &a )  
  2. {   
  3.     int maxSum = 0;   
  4.     for(int i=0; i<a.size(); i++ )  
  5.     {   
  6.         int thisSum =0;  
  7.         for(int j=0; j<=i; j++ )  
  8.         {   
  9.             thisSum +=a[j]; //从节点0开始 累加到节点i  
  10.             if(thisSum>maxSum)  
  11.             {   
  12.                 maxSum = thisSum;   
  13.             }  
  14.         }  
  15.     }  
  16.     return maxSum;  
  17. }  


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

[cpp]  view plain  copy
  1. int maxSubSum2_3( const vector<int> &a )  
  2. {   
  3.     int maxSum = 0;   
  4.     for(int i=0; i<a.size(); i++ )  
  5.     {   
  6.         int thisSum =0;  
  7.         for(int j=i; j>=0; j-- )  
  8.         {//从节点i开始,向前累加到结尾0  
  9.             thisSum +=a[j];  
  10.             if(thisSum>maxSum)  
  11.             {   
  12.                 maxSum = thisSum;   
  13.             }  
  14.         }  
  15.     }  
  16.     return maxSum;  
  17. }  


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

第一步:

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

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

[cpp]  view plain  copy
  1. int maxSubSum3_1( const vector<int> &a )  
  2. {   
  3.     int maxSum = 0;  
  4.     for(int i=0; i<a.size(); i++ )  
  5.     { //(相对方法2,新增)如果a[i]<=0,那么a[i]一定不是所要求的起点,所以直接跳过去(利用for循环中有i++)  
  6.         if( a[i]<=0 )  
  7.           continue;  
  8.         int thisSum =0;  
  9.         for(int j=i; j<a.size(); j++ )  
  10.         {   
  11.             thisSum +=a[j];  
  12.             if(thisSum>maxSum)  
  13.             {   
  14.                 maxSum = thisSum;   
  15.             }  
  16.         }  
  17.     }  
  18.     return maxSum;  
  19. }  


第二步:

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

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

[cpp]  view plain  copy
  1. int maxSubSum3_2( const vector<int> &a )  
  2. {   
  3.     int maxSum = 0;  
  4.     for(int i=0; i<a.size(); i++ )  
  5.     { //(相对方法2,新增)如果a[i]<=0,那么a[i]一定不是所要求的起点,所以直接跳过去(利用for循环中有i++)  
  6.         if( a[i]<=0 )  
  7.           continue;  
  8.         int thisSum =0;  
  9.         for(int j=i; j<a.size(); j++ )  
  10.         {   
  11.             thisSum +=a[j];  
  12.             if(thisSum>maxSum)  
  13.             {   
  14.                 maxSum = thisSum;   
  15.             }  
  16.             else if( thisSum <= 0 )  
  17.             { //(相对方法3_1 新添) thisSum = 0; i = j; }  
  18.         }  
  19.     }  
  20.     return maxSum;  
  21. }  


第三步:

如果你又进一步发现:因为要求序列开始元素大于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。

[cpp]  view plain  copy
  1. int maxSubSum3_3( const vector<int> &a )  
  2. {   
  3.     int maxSum = 0;  
  4.     for(int i=0; i<a.size(); i++ )  
  5.     { //(相对方法2,新增)如果a[i]<=0,那么a[i]一定不是所要求的起点,所以直接跳过去(利用for循环中有i++)  
  6.         if( a[i]<=0 )  
  7.           continue;  
  8.         int thisSum =0;  
  9.         for(int j=i; j<a.size(); j++ )  
  10.         {   
  11.             thisSum +=a[j];  
  12.             if( thisSum>0 )  
  13.             { //(相对方法3_2 新添)  
  14.                 if(thisSum>maxSum)  
  15.                 { maxSum = thisSum; }  
  16.                 i = j; //(相对方法3_2 新添)  
  17.             }  
  18.             else if( thisSum <= 0 )  
  19.             { //(相对方法3_1 新添)   
  20.                 thisSum = 0;   
  21.                 i = j;  
  22.             }  
  23.         }  
  24.     }  
  25.     return maxSum;  
  26. }  


第四步:

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

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

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

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

[cpp]  view plain  copy
  1. for(int i=0; i<a.size(); i++ )   
  2. {   
  3.       if( a[i]<=0 )   
  4.         continue;   
  5.       int thisSum =0;   
  6.       for(int j=i; j<a.size(); j++ )   
  7.       {   
  8.         thisSum +=a[j];   
  9.         if(thisSum>maxSum)   
  10.             { maxSum = thisSum; }   
  11.         else if( thisSum>0 )   
  12.             { //do nothing }   
  13.         else if( thisSum <= 0 )   
  14.             { thisSum = 0; }   
  15.             
  16.                 i = j;   
  17.       }   
  18. }  


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

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

[cpp]  view plain  copy
  1. int maxSubSum3_4( const vector<int> &a )   
  2. {   
  3.     int maxSum = 0;   
  4.     int thisSum = 0;   
  5.     for(int j=0; j<a.size(); j++ )   
  6.     {   
  7.         thisSum += a[j];   
  8.         if(thisSum>maxSum)   
  9.             { maxSum = thisSum; }   
  10.         else if( thisSum>0 )   
  11.             { //do nothing }   
  12.         else if( thisSum < 0 )   
  13.         { thisSum = 0; }   
  14.     }   
  15.     return maxSum;   
  16. }  


分析:

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

最大子序列和问题·联机算法 最大子序列和问题·联机算法 题目 给定(可能有负的)整数A1,A2,...,ANA_1, A_2, ..., A_NA1​,A2​,...,AN​,求∑k=1nAk\sum_{k=1}^n A_k∑k=1n​Ak​的最大值。(为方便起见,如果所有整数均为负数,则最大子序列和为0) 易知 若AiA_iAi​<0,则AiA_iAi​不可能为最大子序列和的起点。(因为总能通过Ai+1A_{i+1}Ai+1​作起点优化最大子序列) 设序列其各项和为Aij,其各项和为∑k=ijAk=Ai+Ai+1+...+A 阅读详情

相关推荐

最大的子序列和问题

最大的子序列和问题是一个很经典的问题,各种考试面试中也经常碰到。这问题的解决不难,关键是通过这个问题体会一些算法的思路,学习思考怎么解决问题。 问题是这样的:给定整数A1,A2,...,An(正负不限),Ai,...,Aj(1 最直接也最容易想到的办法(当然也就是最暴力的方法),就是求出所有子序列的和,然后找出最大的。按照这个思路写出的C++代码如下: int Method1

tyst08的专栏 657

LeetCode面试热题九

目录(序号为leetcode题号) 166. 分数到小数 169. 多数元素 171. Excel 表列序号 172. 阶乘后的零 179. 最大数 189. 轮转数组 190. 颠倒二进制位 191. 位1的个数 198. 打家劫舍 200. 岛屿数量

保持热爱,持续学习 719

AI 代码生成工具的质量评估体系

基础需求层包括响应速度(Response Time)和界面友好性(UI Friendliness),期望需求层涵盖智能提示(Smart Suggestions)和个性化配置(Personalization),兴奋需求层则涉及多模态交互(Multimodal Interaction)和代码进化(Code Evolution)。在金融领域,代码审计合规性(Compliance Audit)权重应提升至40%,而在Web开发中,API兼容性(API Compatibility)的权重需达到35%。

2503_92418637的博客 3373

【2013统考真题】已知一个整数序列A=(a0, a1...an-1),其中0<a,<n(0≤i<n).若存在az=ap2=…=am=X且m >n/2(0≤p,<n, 1≤k≤m),则称x为A的主元素

【2013统考真题】已知一个整数序列A=(ao, a1,..., an-1),其中0

m0_57180439的博客 902

4D毫米波雷达

详解4D成像毫米波雷达

sangba2019的博客 2393

ARM SMMUv3设备树配置实战:从零开始为RK3588搭建IOMMU环境

本文详细介绍了在RK3588平台上配置ARM SMMUv3设备树的实战经验。从理解硬件架构、设备树节点定义,到内核驱动集成与调试,提供了完整的IOMMU环境搭建指南,并分享了解决StreamID冲突、中断配置等常见问题的技巧,帮助开发者高效完成控制器初始化。

l3m4n的博客 884

2022年上学期程序设计实践-第二次考试

集合S={(a,b,c) ∣ 1≤a<b<c≤n, a⊕b⊕c=0},其中 x⊕y 表示x与y按位异或运算,比如 9⊕10=1001(2)⊕1010(2)=0011(2)=3。求集合S的大小。

qq_43329195的博客 397

从原理图到PCB:手把手教你绘制DRV8301驱动板(附完整代码与避坑指南)

本文详细介绍了如何从原理图设计到PCB布局,手把手教你绘制DRV8301驱动板,特别适用于FOC(磁场定向控制)系统开发。内容涵盖元器件选型、电源树架构设计、栅极驱动电路、PCB布局技巧、焊接调试及代码配置,并附有完整代码与常见问题避坑指南,帮助工程师快速掌握DRV8301驱动板的设计与优化。

weixin_33716941的博客 396

CSP-S 2023 提高级 第一轮(初赛) 完善程序(2)

观察选项,A和C中涉及到了a[i]或a[i-1],pre的下标i和a的下标i之间存在pre[i] == a[mid+i]这样的关系,而pre[i]和a[i]没有直接关系,表达式中不应该出现a。例如,当原序列为 [1,2,1,2]时,要计算子序列 [1]、[2]、[1]、[2]、[1,2]、[2,1]、[1,2]、[1,2,1]、[2,1,2]、[1,2,1,2]的最大值之和,答案为 18。,pre[i]更新后的意义为pre[0]~pre[i]的最大值,也就是a数组区间[mid, mid+i]的最大值。

君义NOIP的博客 3686

告别AT串口线!用STM32F407的USB FS口直连EC800M进行物联网通信

本文介绍了一种创新的物联网通信方案,利用STM32F407的USB FS接口直连EC800M模块,替代传统的AT串口线连接。通过详细的硬件对比、STM32CubeMX配置和USB驱动调试步骤,展示了如何实现更高效的AT指令通信,同时降低硬件复杂度和功耗。

weixin_29189987的博客 706

Kafka延迟队列实现方案全解析

$ \text{时间轮} = \text{环形数组} + \text{分层设计} $$需要补充代码实例或特定场景的优化方案可进一步细化,此大纲已覆盖核心技术路径。

lz989796的博客 817

最大子序列和问题的求解

本文给出求解最大子序列和问题的4个算法

m0_62361897的博客 1629

Python 爬虫实战:抓取百科网站上某个词条的详细信息

百科网站(如维基百科)为用户提供了丰富的知识内容,涵盖了从历史、文化到科技等几乎所有领域的知识。对于需要大量数据进行分析、研究或机器学习项目的开发者而言,抓取这些网站的数据显得尤为重要。通过编写一个 Python 爬虫,我们能够轻松地抓取百科网站上的某个词条及其详细信息,从而获取并整理这些知识内容。本篇博客将深入介绍如何通过 Python 编写爬虫来抓取百科网站(如维基百科)的某个词条的详细信息,包括标题、正文内容、参考文献等。requests:发送 HTTP 请求,获取页面内容。

2201_76125261的博客 1670

最大子序列和问题、蓝桥:和最大子序列

import java.util.Scanner; public class 最大子序列和问题 { public static void main(String[] args) { // TODO Auto-generated method stub Scanner sc=new Scanner(System.in); int n=sc.nextInt(); int[] a=...

Claire的博客 270

【深度学习】脸部修复,CodeFormer,论文,实战

Blind face restoration是一个高度 ill-posed 的问题,通常需要辅助引导来 1) 改善从降级输入到期望输出的映射,或者 2) 补充在输入中丢失的高质量细节。在本文中,我们展示了在一个小的代理空间中学到的离散码本先验可以通过将盲目的面部恢复建模为 a code prediction task来大大减少恢复映射的不确定性和模糊性,同时为生成高质量面部提供丰富的视觉元素。

q742971636的博客 2994

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

求解最大连续子序列和问题———分治法

一、问题描述 给定一个有n(n >= 1)个整数的序列,求出其中最大连续子序列的和。例如序列(34,-20,30,-50,60,-20,30,41,-30,-10)最大子序列和为111,序列(-2,11,-4,13,-5,-2)最大子序列和为20。规定一个序列的最大连续子序列和至少为0。 二、问题求解 1、对于含有n个整数的序列,若n=1,表示序列仅有一个元素,该元素大于0,则返回该元素,否则返回0。 if(left == right){ if(a[left]>=0) return a[l

留小乙的博客 1万+

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

算法小题:最大连续子序列和问题

最大连续子序列和问题前言问题描述解法一分析代码解法二分析代码解法三分析代码解法三(番外版)分析代码实验结果 前言 刷题的时候遇到了这个最大连续子序列和的问题,查阅研究了一下。记录在此。希望能够便人便己。 问题描述 给定一个数组a[ n ] ,数组元素均为自然数集(有正数,有负数),请求出该数组一个连续的子序列,使得这个子序列的和值最大,示例如下 a[] = {1, 2, -9, 5, 6, -3, 7, 8, -89, 10} 那么它的最大连续子序列为 {5,6,-3,7,8} ,和值 = 23 说明:偷

xiaopan的博客 2721

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

最大子序列和问题求解与分析

一、问题描述 最大子序列和问题是要在 个数中寻找一个子序列和,该子序列和是所有个数中选择的子序列中的最大值。形式化为: , ...

gaoxueyi551的专栏 478

最大子序列和问题的解

最大的子序列和问题:给定整数A1,A2,......,AN(可能有负数),求∑Ak(k=i...j)的最大值。(为方便起见,如果所有整数均为负数,则最大子序列和为0) 例:输入-2,11,-4,13,-5,-2时,答案为20(从A2到A4) 解法一: int MaxSubsequenceSum( const int A[], int N ) { int ThisSum, M

ziyoudefeng的专栏 713

PAT上的最大子序列和问题

PAT上的最大子序列和问题 Given a sequence of K integers { N1, N2, ..., NK }. A continuous subsequence is defined to be { Ni, Ni+1, ..., Nj } where 1 Maximum Subsequence is the continuous subsequence whi

yangxuanhe的专栏 994

动态规划法-最大连续子序列和问题

我们有一个数字序列,例如 1, -2, 3, 4, -10, 6。我们的目标是找出这个序列中连续数字相加得到的最大和。

qq_52291558的博客 1567

NOJ-1576-最大连续子序列和问题

最大连续子序列和问题 描述: 给定一整数序列A0,A1, A2,… An-1 (可能有负数),求A0An-1的一个连续子序列AiAj,使得Ai到Aj的和最大。 输入: 先输入一个正整数n(1<n<1000),再输入n个整数。 输出: 输出最大连续子序列的和。 输入样例: 102 -1 5 -7 2 -1 4 -2 4 -5 输出样例: 7 思路 代码还没有AC,NOJ评测机炸了,但是这个O(n)的思路大概是对的:sum[i]为前i个数字的连续子序列的最大和。如果sum[i-1]<0,那

Phoenix_ZengHao的博客 901
上一篇: Java transient关键字使用小记
下一篇: Zookeeper分布式服务框架
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值