C++求解最大子序列和问题

最大子列和问题

问题描述:

给定K个整数组成的序列{ N1, N2, ..., NK },“连续子列”被定义为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。

输入格式:

输入第1行给出正整数 K (<= 100000);第2行给出K个整数,其间以空格分隔。

输出格式:

在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。

输入样例:

6
-2 11 -4 13 -5 -2

输出样例:

20

完整代码:(实现的为算法4,下面有详解)

#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include <iostream>
//using namespace std;
int MaxSubsequenceSum(int a[],int n);
int _tmain(int argc, _TCHAR* argv[])
{
int n = 0;
int sum = 0;
scanf("%d",&n);
int *a = new int[n];
for(int i = 0;i<n;i++)
scanf("%d",&a[i]);
sum = MaxSubsequenceSum(a,n);
std::cout<<sum<<std::endl;
system("pause");
return 0;

}

int MaxSubsequenceSum(int a[],int N)
{
int Thissum,Maxsum,i;
Thissum =0;
Maxsum =0;
int l = 0;
for(i = 0;i<N;i++)
{
Thissum += a[i];
if(Thissum > Maxsum)
{
Maxsum = Thissum;
}
else if (Thissum < 0)
{
Thissum =0;
l++;
}
}
if(l == N)
{
return 0;
}
else
return Maxsum;
}

算法一

int MaxSubsequenceSum(int a[],int N)
 {
  int ThisSum,MaxSum,i,j,k;
       MaxSum=0;

int l =0;
     for (i=0;i<10;i++)
        for(j=i;j<10;j++)
         {
           ThisSum=0;
           for (k=i;k<=j;k++)
               ThisSum+=a[k];  
          if(ThisSum>MaxSum)
          MaxSum=ThisSum;
        }

if(l == N)
{
return 0;
}
else
return Maxsum;

}
我们来分析一下这个程序,首先这个程序肯定是完全正确的。运行时间为O(N³),这完全取决于第5行和第六行,第六行有一个含于三重嵌套for循环中的O(1)语句组成。第2行的循环大小为N。

第二个循环大小为N-i,他可能要小,但也可能是N。我们必须假设最坏的情况,而这可能会使得最终的界有些大。第三个循环的大小为j-i+1,我们也要假设它的大小为N。为此总数为O(1*N*N*N)=O(N³)。语句1总的开销只是O(1),而语句7和8总共开销也只不过O(N²),因为他们只是两层循环内部的简单表达式。

我们在将结合各个语句的开销大小,更精确的分析出答案是θ(N³),而上面的估计高出个因子6(不过这并不影响,因为常数不影响数量级)。下面是具体计算的过程:

精确分析由这个式子得出

最后答案是(N³+3*N²+2*N)/6

显然这个算法过于消耗时间在第5行和第6行上的计算过分的耗时了。现在有一种改进的算法2

我们可以通过撤除一个for循环来避免立方运行时间。

下面是这个算法的函数:

算法2

int MaxSubsequenceSum(int a[],int N)
{
 int ThisSum,MaxSum,i,j;
 MaxSum=0;

int l = 0;

 for (i=0;i<10;i++)
 { 
  ThisSum=0;
  for(j=i;j<10;j++)
  {
   
   
   ThisSum+=A[j];  
   if(ThisSum>MaxSum)
   MaxSum=ThisSum;
  }
 }

if(l == N)
{
return 0;
}
else
return Maxsum;

}

这个算法显然是O(N²)的还有一种更简单的算法其相对时间复杂度为O(NlogN)解法,现在我们来描述它。

这个算法可以体现递归算法的优势,该方法采用一种“分治”策略。其想法是吧问题分成两个大致相等的子问题,然后递归地对它们求解,这是分部分。“治”阶段将两个子问题的解合并到一起并可能再做些少量的附加工作,最后得到整个问题的解。

在这个问题中,最大子序列和可能出现在3个地方。或者整个出现在输入数据的左半部分,或者整个出现在右半部分。或者跨越输入数据的中部从而占据左右两半部分。前两种情况可以递归求解。第三种情况的最大和可以通过求出前半部分的最大和(包含前半部分的最后一个元素)以及后半部分的最大和(包含后半部分的第一个元素)而得到。然后将这两个和加在一起。

算法3
static int MaxSubSum(int a[],int Left,int Right)
{
 int MaxLeftSum,MaxRightSum;
 int MaxLeftBorderSum,MaxRightBorderSum;
 int LeftBorderSum,RightBorderSum;
 int Center,i;
 if (Left==Right)
 {
  if (a[Left]>0)
   return a[Left];
        else
   return 0;
 }
 Center =(Left+Right)/2;
  MaxLeftSum=MaxSubSum(A,Left,Center);
  MaxRightSum=MaxSubSum(A,Center+1,Right);

MaxLeftBorderSum=0;LeftBorderSum=0;
  for (i=Center;i>=Left;i--)
 {
  LeftBorderSum+=a[i];
  if (LeftBorderSum>MaxLeftBorderSum)
  
   MaxLeftBorderSum=RightBorderSum;
  
 }
   MaxRightBorderSum=0;RightBorderSum=0;
     for (i=Center+1;i<=Right;i++)
   {
    RightBorderSum+=a[i];
    if (RightBorderSum>MaxRightBorderSum)
        MaxRightBorderSum=RightBorderSum;       
   }
   return Max3(MaxLeftSum,MaxRightSum,MaxLeftBorderSum+MaxRightBorderSum);

}

int MaxSubsequenceSum(const int a[],int N)
{
 return MaxSubSum(a,0,N-1);
}

递归过程调用的一般形式是传递输入的数组以及左边界和右边界,它们界定额数组要被处理的部分。单行驱动程序通过传递数组以及边界0和N-1而启动该过程。

第一行至第四行处理基准情况。如果Left==Right,那么只有一个元素,并且当该元素非负时它就是最大和子序列。Left>Right的情况是不可能出现的,除非N是负数(不过程序中的小扰动有可能致使这种混乱产生)。第六行和第七行执行的两次递归调用。我们可以看到,递归调用总是对于小于原问题的问题进行,但程序总的小扰动有可能破换这个特性。第八行至第十二行以及第十三行至第十七行计算达到中间分界处的两个最大和的和数。这两个最大和的和为扩展到左右两边的最大和。伪例程Max3返回这三个有可能的最大和中的最大者。

显然,编程时,算法3比前面两种算法需要更多的精力。然而,程序短并不意味着程序号。正如我们在前面显示算法运行时间的表中已看到,除最小输入外,该算法比前面两个算法明显要快。

最后一种算法是最为有效的算法,

算法4:

int MaxSubsequenceSum(int a[],int N)
{
     int ThisSum,MaxSum,j;

int l = 0;
    ThisSum=MaxSum=0;

for(j=0;j<N;j++)
    {
           ThisSum+=a[j];
     if(ThisSum>MaxSum)
     MaxSum=ThisSum;
     else if(ThisSum<0)
     ThisSum=0;
    }

if(l == N)
{
return 0;
}
else
return Maxsum;

}

算法4的时间复杂度为O(N),这是线性的。该算法的有点是,它只对数据进行一次扫描,一旦A[i]被读入处理,它就不再需要被记忆。因此,如果数组在磁盘或者磁带上,它就可以被顺序读入,在主存中不必储存数组的任何部分。不仅如此,在任意时刻,算法都能对它已经读入的数据给出子序列问题的正确答案。具有这种特性的算法叫做联机算法.仅需要常量空间并以线性时间运行的联机算法几乎是完美的算法。

时间: 2024-12-30 15:04:48

C++求解最大子序列和问题的相关文章

[C++]四种方式求解最大子序列求和问题

问题 给定整数: A1,A2,-,An,求∑jk=iAk 的最大值(为方便起见,假设全部的整数均为负数,则最大子序列和为0) 比如 对于输入:-2,11,-4,13,-5,-2,答案为20,即从A2到A4 分析 这个问题之所以有意思.是由于存在非常多求解它的算法. 解法一:穷举遍历 老老实实的穷举出全部的可能,代码例如以下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 //计算并返回所

C++求解最大子序列和、位置问题

Maximum Subsequence Sum Given a sequence of K integers { N1, N2, ..., NK }. A continuous subsequence is defined to be { Ni, Ni+1, ..., Nj } where 1 <= i <= j <= K. The Maximum Subsequence is the continuous subsequence which has the largest sum of

最大子序列乘积----DP求解

问题起源于<数据结构与算法分析-C语言描述>一书中的习题2.12. 存在序列A(a1,a2,......,an ),(在此仅讨论序列A中元素均为整数的情况) 问:给出有效的算法求解最大子序列乘积. 一看此题,容易想到的是穷举所有的可能的子序列,求乘积后去最大值,代码如下. 1 int MaxProduct(int a[], int len) 2 { 3 int Max = a[0]; 4 for(int i = 0; i < len; ++i) 5 { 6 int thisMax = 1

最大子序列和,最小子序列和,最小正子序列和,最大子序列乘积

来自:<数据结构与算法分析——C语言描述>练习2.12 一. 最大子序列和 1.穷举法,O(N3) 1 int maxSequenceSum1(const int A[], int N) 2 { 3 int i, j, k, maxSum, thisSum; 4 5 maxSum = 0; 6 for (i = 0; i < N; i++) 7 { 8 for (j = i; j < N; j++) 9 { 10 thisSum = 0; 11 for (k = i; k <

基于C语言的算法总结(不定时更新)

这篇博客我准备写一些我见过的算法,虽然现在我见过的还很少,但我相信会越来越多,方便日后自己查阅 好了 开始了 求解最大子序列和的最有效的算法 1 int MaxSubsequenceSum(const int A[], int N) 2 { 3 int ThisSum, MaxSum, j; 4 // 定义本次循环的和 与 最大和 为0 5 ThisSum = MaxSum = 0; 6 // 循环求和 7 for (j = 0; j < N; j++) 8 { 9 ThisSum += A[j

每日算法之三十一:Combination Sum

给定一个整数序列,求解一个子序列,子序列之和等于给定目标值.子序列满足以下条件: 1)子序列是有序的 2)子序列的元素个数不限,可以是给定元素的重复元素. 3)结果中的子序列是唯一的 原题描述如下: Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T. The same repeat

《数据结构与算法分析 C语言描述》读书笔记——分治算法

书中用求解最大子序列和的方式介绍了分治算法(divide-and-conquer) 分治算法是一种相对快速的算法 运行时间为O(logN) 最大子序列和的问题如下: 给出一组整数 A1  A2 … AN 求∑jk=i Ak 若所有整数均为负 则最大子序列和为0 e.g. 输入-2, 11,-4, 13, -5, -2 输出20(A2到A4) 分治算法就如同字面描述的一样 先分再治 分 指的是将问题分为两部分几乎相同的子问题 进行递归求解 治 指的是将 分 的解通过简单的手段合并 得到最终解 对于

World CodeSprint 10

C: 题意: 给定一个长度为 $n$ 的序列 $a_i$,从 $a$ 序列中选出一个大小为 $k$ 的子序列使得子序列数字的  bitwise AND 值最大. 求问最大值是多少,并求出有多少个最大值序列. 解法: 从高位向低位枚举 bitwise AND 的二进制位,每一次优先让当前 bitwise AND 的最高位为1,如果可行则让此位为1 . 考虑已知一个 bitwise AND 的前缀 $S$,求解满足子序列 bitwise AND 前几位等于 $S$ 的方案数,只要求出所有的 $a_i

腾讯编程题

这是一个腾讯笔试的编程题: 我们常常会用到一个LCS的问题,本题的唯一的一个巧妙之处在于,最后求解的字符串变为的是原来的字符串与其reverse之后的字符串的最大LCS,这样本题就得到了解决. 最长公共子序列求解:递归与动态规划方法 在做OJ题目的时候,经常会用到字符串的处理.例如,比较二个字符串相似度.这篇文章介绍一下求两个字符串的最长公共子序列. 一个字符串的子序列,是指从该字符串中去掉任意多个字符后剩下的字符在不改变顺序的情况下组成的新字符串. 最长公共子序列,是指多个字符串可具有的长度最