最大子段和(分治与动态规划典例)

来源:互联网 发布:mac pocomaker 编辑:程序博客网 时间:2024/06/05 21:56

最大子段和

 
问题: 给定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])=(-2,11,-4,13,-5,-2)时,最大子段和为20。
最大子段和是动态规划中的一种。

目录

  1. 1 分治法
  2. 2 动态规划法

分治法

编辑
算法描述如下
针对最大子段和这个具体问题本身的结构,我们还可以从算法设计的策略上对上述O(n^2)计算时间算法进行更进一步的改进。从问题的解结构也可以看出,它适合于用分治法求解。
如果将所给的序列a[1:n]分为长度相等的两段a[1:n/2]和a[n/2+1:n],分别求出这两段的最大子段和,则a[1:n]的最大子段和有三种情况:
(1) a[1:n]的最大子段和与a[1:n/2]的最大子段和相同
(2) a[1:n]的最大子段和与a[n/2+1:n]的最大子段和相同
(3) a[1:n]的最大子段和为a[i]+…+a[j],并且1<=i<=n/2,n/2+1<=j<=n。
对于(1)和(2)两种情况可递归求得,但是对于情况(3),容易看出a[n/2],a[n/2+1]在最大子段中。因此,我们可以在a[1:n/2]中计算出s1=max(a[n/2]+a[n/2-1]+…+a[i]),0<=i<=n/2,并在a[n/2+1:n]中计算出s2= max(a[n/2+1]+a[n/2+2]+…+a[i]),n/2+1<=i<=n。则s1+s2为出现情况(3)的最大子段和。据此可以设计出最大子段和问题的分治算法如下:
时间复杂度:O(NlogN)
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
51
52
#include<stdio.h>
#define MAX 100
int maxsub(int left,int right);
int a[MAX];
int main()
{
    inti;
    int count;
    scanf("%d",&count);
    for(i=0;i<count;i++)
    {
        scanf("%d",&a[i]);
    }
    printf("%d/n",maxsub(0,count-1));
    return0;
}
int maxsub(int left,int right)
{
    int center,i;
    int sum,left_sum,right_sum;
    int left_max,right_max;
    center=(left+right)/2;
    if(left==right)//此处重要,递归出口
if(A[left]>0)
        return a[left];
else
return 0;
    else
    {
        left_sum=maxsub(left,center);
        right_sum=maxsub(center+1,right);
        sum=0;
        left_max=0;
        for(i=center;i>=left;i--)
        {
            sum+=a[i];
            if(sum>left_max)
                left_max=sum;
        }
        sum=0;
        right_max=0;
        for(i=center+1;i<=right;i++)
        {
            sum+=a[i];
            if(sum>right_max)
                right_max=sum;
        }
        sum=right_max+left_max;
        if(sum<left_sum)
            sum=left_sum;
        if(sum<right_sum)
            sum=right_sum;
    }
    returnsum;
}

动态规划法

编辑
在对于上述分治算法的分析中我们注意到,若记b[j]=max(a[i]+a[i+1]+..+a[j]),其中1<=i<=j,并且1<=j<=n。则所求的最大子段和为max b[j],1<=j<=n。
由b[j]的定义可易知,当b[j-1]>0时b[j]=b[j-1]+a[j],否则b[j]=a[j]。故b[j]的动态规划递归式为:
b[j]=max(b[j-1]+a[j],a[j]),1<=j<=n。
代码如下:
时间复杂度:O(N)
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
#include <stdlib.h>
#include<stdio.h>
int main()
{
    int count;
    int a[100];
    int b[100];
    int i;
    int max;
    scanf("%d",&count);
    for(i=0; i<count; i++)
    {
        scanf("%d",&a[i]);
    }
    b[0]=a[0];
    max=b[0];
    for(i=1; i<count; i++)
    {
        if(b[i-1]>0)
            b[i]=b[i-1]+a[i];
        else
            b[i]=a[i];
        if(b[i]>max)
            max=b[i];
    }
    printf("%d\n",max);
    return 0;
}

联机算法:
常量空间,线性时间O(N)运行
int maxsubsequence(int array[],int n){    int thissum=0,maxsum=0;    for(int i=0;i<n;i++){        thissum+=array[i];        if(thissum<0)            thissum=0;        else if(thissum>maxsum)            maxsum=thissum;    }    return maxsum;}


1 0