问题描述:
给定N个整数(有正有负),求它的最大子序列和,若全是负数则返回0。
例如:-84 33 -40 36 -34 4 5 88 74 -29
这10个数的最大子序列和是 173(74+88+4+5+(-34)+36)
解法:
1)穷举求解(三次循环,复杂度O(n^3))
for(i=0;i!=n;i++)/*对数组的元素全部遍历*/
{
for(j=i+1;j!=n;++j)/*从下一个位置开始遍历*/
{
sum=0; /*每次求上面两段位置和之前清0*/
for(k=i;k<=j;++k)/*求这两段位置的和*/
{
sum+=a[k];
if(sum>max)/*如果比最大值的话,记录下来*/
{
max=sum;
}
}
}
}
2)方法一的改进(二次循环,复杂度:O(n^2))
其实子序列的和我们并不需要每次都重新计算一遍。假设Sum(i, j)是A[i] ... A[j]的和,那么Sum(i, j+1) = Sum(i, j) + A[j+1]。利用这一个递推,我们就可以得到下面这个算法:
int MaxSequenceSum(int a[],int n)
{
int ThisSum,MaxSum; //ThisSum保存当前子序列和,MaxSum保存最大子序列和
int i,j;
MaxSum = 0;
for(i = 0;i < n;i++)
{
ThisSum = 0; //重要
for(j = i;j < n;j++)
{
ThisSum +=a[j];
if(ThisSum > MaxSum) //更新最大子序列和
MaxSum = ThisSum;
}
}
return MaxSum;
}
3)线性算法
long int cSum = 0,MaxSum = 0;
for(int i =0;i < n;i++)
{
scanf("%ld",&tmp);
if(i == 0)
MaxSum = tmp;//若不加这句,对于全是负数的情况输出的是0
cSum += tmp;
if(cSum > MaxSum)
MaxSum = cSum;
if(cSum < 0)
cSum = 0;
}
printf("%ld\n",MaxSum);
在这一遍扫描数组当中,从左到右记录当前子序列的和temp_sum,若这个和不断增加,那么最大子序列的和max也不断增加(不断更新max)。如果往前扫描中遇到负数,那么当前子序列的和将会减小。此时temp_sum 将会小于max,当然max也就不更新。如果temp_sum降到0时,说明前面已经扫描的那一段就可以抛弃了,这时将temp_sum置为0。然后, temp_sum将从后面开始将这个子段进行分析,若有比当前max大的子段,继续更新max。这样一趟扫描结果也就出来了。