ACM 子串和

子串和

时间限制:5000 ms  |  内存限制:65535 KB
难度:3
 
描述
给定一整型数列{a1,a2...,an},找出连续非空子串{ax,ax+1,...,ay},使得该子序列的和最大,其中,1<=x<=y<=n。
 
输入
第一行是一个整数N(N<=10)表示测试数据的组数)
每组测试数据的第一行是一个整数n表示序列中共有n个整数,随后的一行里有n个整数I(-100=<I<=100),表示数列中的所有元素。(0<n<=1000000)
输出
对于每组测试数据输出和最大的连续子串的和。
样例输入
1
5
1 2 -1 3 -2
样例输出
5

因为提前知道了要用动态规划思想,所以大大减少了难度。尽管如此,我还是用了两个小时才思考出答案。最后思路是正确的,但是最佳答案是边输入边判断。这样直接就减去了数组存储的过程。
动态规划,关键就是存储小问题的答案,避免重复计算小问题。大问题划分为小问题,这是分治的思想。
这个题的话存在这样一个公式

b[n] = max(a[n] + b[n-1], a[n])    when n > 0
b[0] = a[0]                when n = 0

子串和的最大值 = 数组b[n]的最大值
#include <stdio.h>
#include <stdlib.h>

#define max(a,b) (a>b)?a:b

int main()
{
    int N;
    scanf("%d", &N);
    while (N--)
    {
        int i = 0, n = 0, m = 0, a, b;
        scanf("%d", &n);

        scanf("%d", &a);
        m = b = a;
        for(i = 1; i < n; i++)
        {
            scanf("%d", &a);
            b = max(a + b, a);
            m = max(b,m);
        }
        printf("%d\n", m);
    }
    
    return 0;
}

 

在阅读《数据结构与算法 C语言描述》后,发现这个题还有好多解法,撇开时间复杂度是O(n^3)和O(n^2)的算法不谈。

书中也谈到了一个分治算法,没有使用动态规划,时间复杂度是O(NlogN),符合标准的分治递归算法的时间复杂度,原理写成公式一眼明了

f(串)= Max {f(左子串),f(右子串),f(左右串)}

f(左右串)是从中间元素向左右两边扩展的所有子串的最大和

 

有了上面的规律后不难写出程序,下面的程序没有经过测试,不过原理是正确的

static int maxSubSum(const int a[], int left, int right)
{
    if(left == right)
    {
        if(a[left] > 0)
            return a[left];
        else
            return 0;
    }

    int maxLeft = 0, maxRight = 0, maxLeftBorder = 0, maxRightBorder = 0;
    int center = (left + right) / 2;
    maxLeft = maxSubSum(a, left, center);
    maxRight = maxSubSum(a, center+1, right);

    for(int i = center; i >= left; --i)
    {
        int maxLeftBorderTemp = maxLeftBorder + a[i];
        if(maxLeftBorderTemp > maxLeftBorder)
            maxLeftBorder = maxLeftBorderTemp;
    }

    for(int i = center+1; i <= right; ++i)
    {
        int maxRightBorderTemp = maxRightBorder + a[i];
        if(maxRightBorderTemp > maxRightBorder)
            maxRightBorder = maxRightBorderTemp;
    }

    int maxBorder = maxLeftBorder + maxRightBorder;
    return maxLeft > maxRight ? (maxLeft > maxBorder?maxLeft:maxBorder) : (maxRight>maxBorder?maxRight:maxBorder);
}

 

还有一个时间复杂度同为O(n)的算法,非常经典,这种算法我自己是肯定想不出来的

书中的号称是最优的算法,叫做联机算法,仅需要常量空间并以线性时间运行。

经过研究这也是利用了动态规划思想。

b[n] = max(a[n] + b[n-1], a[n])    when n > 0
b[0] = a[0]                when n = 0

同样是这个公式,我们可以改成这种样式

b[n] = max(b[n-1], 0) + a[n]     when n > 0
b[0] = a[0]               when n = 0

利用这个公式,就可以得出下面的程序

int maxSubSub(const int a[], int N)
{
    int b, m, j;

    b = m = 0;
    for(j = 0; j < N; j++)
    {
        b += a[j];

        if(b > m)
            m = b;
        else if(b < 0)
            b = 0;
    }
    return m;
}

 

posted @ 2015-06-21 21:30  sdlwlxf  阅读(303)  评论(0编辑  收藏  举报