0%

最大字段和

前言

  • 问题: 给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n 例如,当(a[1],a[2],a[3],a[4],a[5],a[6])=(-20,11,-4,13,-5,-2)时,最大子段和为20。
  • 最大子段和是动态规划中的一种。

算法思路

  1. 划分:按照平衡子问题的原则,将序列(a1,a2,….,an)划分成成都相同的两个子序列(a1,…,an/2)和(an/2+1,…,an),则会出现以下三种情况。
    1. 最大字段和为(a1,…,an/2)
    2. 最大字段和为(an/2+1,…,an)
    3. 最大字段和为两者之间
  2. 求解子问题:对于划分阶段的情况,1和2可以递归求解,而第三种情况需要分别计算
  3. 合并:比较在划分阶段三种情况下的最大字段和,取三者之中较大者为原问题的解

源代码

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//
// Created by ZhiNian on 2019/4/24.
//
#include<stdio.h>
#define N 1000

int MaxSum(int a[],int left,int right){
int sum=0,midSum=0,leftSum=0,rightSum=0;
int center,s1,s2,lefts,rights;
if(left==right){
sum=a[left];
}
else{
center=(left+right)/2;
leftSum=MaxSum(a,left,center);
rightSum=MaxSum(a,center+1,right);
s1=0;lefts=0;
for(int i=center;i>=left;i--){
lefts+=a[i];
if(lefts>s1) s1=lefts;
}
s2=0;rights=0;
for(int j=center+1;j<=right;j++){
rights+=a[j];
if(rights>s2)s2=rights;
}
midSum=s1+s2;
if(midSum<leftSum)sum=leftSum;
else sum=midSum;
if(sum<rightSum)sum=rightSum;
}
return sum;
}

int main(){
int a[N]={0};
// printf("%d",sizeof(a));
for(int i=0;i<(sizeof(a)/4);i++){
scanf("%d",&a[i]);
if(a[i]==0)break;
}
printf("%d",MaxSum(a,0,sizeof(a)/4));
return 0;
}

/*
* 测试数据
* (-20,11,-4,13,-5,-2)
* 结尾就输入0
*/

运行结果

1