• 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
    ", 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;
    }
  • 相关阅读:
    【Leetcode】120.Triangle
    【编程珠玑】第一章位图排序
    【Python】Xml To Excel
    【Django】git建仓上传时遇到的小问题
    【C++】 网络编程 01
    2018.09.20python学习第八天
    2018.09.19python作业
    2018.09.19python学习第七天part2
    2018.09.19python学习第七天part1
    2018.09.18python学习第六天part3
  • 原文地址:https://www.cnblogs.com/sdlwlxf/p/4592483.html
Copyright © 2020-2023  润新知