434
社区成员




给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时,定义子段和为0。
要求算法的时间复杂度为O(n)。
输入有两行:
第一行是n值(1<=n<=10000);
第二行是n个整数。
输出最大子段和。
在这里给出一组输入。例如:
6
-2 11 -4 13 -5 -2
在这里给出相应的输出。例如:
20
按照平衡子问题的思路,将序列(a1,a2,a3....an)划分为长度相同的两个子序列(a1,a2....a[n/2])和(a[n/2]+1.....an),则会出现以下三种情况:
①序列的最大字段和=序列左半部分的最大字段和;
②序列的最大字段和=序列右半部分的最大字段和;
③最大字段和对应的子序列占据左右两部分。
求解子问题:
情况①和②可以用递归进行处理,而情况③可以转换为求 序列左半部分的最大字段和,右半部分的最大字段和,两者相加的结果,这三者谁的字段和结果最大的问题。
#include<iostream>
using namespace std;
int MaxSum(int a[ ], int left, int right)
{
int sum=0;
int center,leftsum,rightsum,s1,lefts,i,s2,rights,j;
if (left==right) //如果序列长度为1,直接求解
{
if (a[left]>0)
sum=a[left];
else
sum=0;
}
else
{
center=(left+right)/2; //划分
leftsum=MaxSum(a, left, center);//对应情况①,递归求解
rightsum=MaxSum(a, center+1, right);//对应情况②,递归求解
s1=0;
lefts=0;
//以下对应情况③,先求解s1
for (i=center; i>=left; i--)
{
lefts+=a[i];
if (lefts>s1)
s1=lefts;
}
s2=0;
rights=0; //再求解s2
for (j=center+1; j<=right; j++)
{
rights+=a[j];
if (rights>s2)
s2=rights;
}
sum=s1+s2; //计算情况③的最大子段和
if (sum<leftsum)
sum=leftsum;
//合并,在sum、leftsum和rightsum中取较大者
if (sum<rightsum)
sum=rightsum;
}
return sum;
}
int main()
{
int n;
int a[100];
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
cout<<MaxSum(a,0,n-1);
}
n=1时,T(n)=1;
n>1时,T(n)=2T(n/2)+n;
算法的时间复杂性为O(nlog2n)
分治法的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
更准确地说是,将规模为n的问题分解为k个规模较小的子问题,这些子问题相互独立且与原问题相同。递归地解决子问题,然后将解合并得到原问题的解。
分治法是一种设计算法的思想,大大优化了算法的运行时间复杂度,优化了程序,要能熟练使用,还需要继续进行不断地练习。