zoukankan      html  css  js  c++  java
  • 集训第五周 动态规划 最大子段和

    A - 最大子段和
    Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u
    Submit Status

    Description

    Given a sequence a[1],a[2],a[3]......a[n], your job is to calculate the max sum of a sub-sequence. For example, given (6,-1,5,4,-7), the max sum in this sequence is 6 + (-1) + 5 + 4 = 14. 
     

    Input

    The first line of the input contains an integer T(1<=T<=20) which means the number of test cases. Then T lines follow, each line starts with a number N(1<=N<=100000), then N integers followed(all the integers are between -1000 and 1000). 
     

    Output

    For each test case, you should output two lines. The first line is "Case #:", # means the number of the test case. The second line contains three integers, the Max Sum in the sequence, the start position of the sub-sequence, the end position of the sub-sequence. If there are more than one result, output the first one. Output a blank line between two cases. 
     

    Sample Input

    2 5 6 -1 5 4 -7 7 0 6 -1 1 -6 7 -5
     

    Sample Output

    Case 1: 14 1 4
    Case 2: 7 1 6
     
    这是一个动态规划题,可使用dp[i]表示以a[i]结尾的的最大子段和,那么这就是一个子结构,如何保证其最优性呢?
    可列出状态转移方程为dp[i]=max(dp[i-1]+a[i],a[i]),如果前面的子段和是负数,那么就无需加上它,因为这对于a[i]来说是一种拖累
     
    #include <iostream>
    #include <cstdio>
    using namespace std;
    const int Max=1e5+10;
    const int inf=0x3f3f3f3f;
    struct node
    {
        int l,val;
    }dp[Max];
    int main()
    {
        int T,ca=1;
        for(scanf("%d",&T);T;T--)
        {
            int n,tmp,ans,pre,post;
            ans=-inf;
            scanf("%d",&n);
            dp[0].val=0;dp[0].l=1;
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&tmp);
                if(dp[i-1].val+tmp>=tmp)
                {
                    dp[i].val=dp[i-1].val+tmp;
                    dp[i].l=dp[i-1].l;
                }
                else
                {
                    dp[i].val=tmp;
                    dp[i].l=i;
                }
                if(dp[i].val>ans)
                {
                    ans=dp[i].val;
                    pre=dp[i].l;
                    post=i;
                }
            }
            printf("Case %d:
    %d %d %d
    ",ca++,ans,pre,post);
            if(T-1) puts("");
        }
        return 0;
    }
    View Code

    尽管这道题可以使用dp方程形式去解决,也可以用一般方法去观察

    6 -1 5 4 -7

    sum     ans

    6          6

    5          5

    10        10

    14        14

    7          7

    我们把数列从i=1处累加,每次取出ai处的累加值,最后得到了数列的最大子段和14,然而这样的方法并非总是正确的,例如

    -2 5 -10 7 7 4

    sum    ans

    -2       -2

    3        3

    -7      -7

    0        0

    7        7

    11      11

    很显然,答案是18而非11,说明累加并不一定是从i=1开始的,假如程序能够察觉应该从i=4处开始累加,那么答案就会正确了

    那么程序需要实现两个功能,起点更新和答案更新。(这样之所以正确在于它枚举了所有区间,类似于尺取法)

    起点的特性是什么呢?那个sum值不能是负数,正确性显然,如果加上一个负数值反而会变小,注意并不是说ai不能为负数,如(6 -1 5)

    由此可知,当一个sum值为负时,就应该更新起点,把sum值置为0。然而应该把起点位置置为此时的i吗?

    答案是NO,注意,由于累加的特性,此时的ai就是导致累加值为负的罪魁,(此时的ai肯定也是负数),如果将它算进去,就违背了之前的讨论。

    还存在一个问题:起点更新和答案更新应该谁前谁后?

    考虑到一种情况,此时的答案是负数,如果起点更新在答案更新之前,那么相当于人为地增加了1个0作为错误干扰信息,使得答案变为0

    所以答案更新必须在起点更新之前,这一点在写dp时一样重要

    #include <iostream>
    #include <cstdio>
    using namespace std;
    const int Max=1e5+10;
    const int inf=0x3f3f3f3f;
    int main()
    {
        int T,ca=1;
        for(scanf("%d",&T);T;T--)
        {
            int n,tmp,ans,sum,pre,anpre,anpost;
            ans=-inf;
            scanf("%d",&n);
            pre=1;sum=0;
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&tmp);
                sum+=tmp;
                if(sum>ans)
                {
                    ans=sum;
                    anpost=i;
                    anpre=pre;
                }
                if(sum<0)
                {
                    sum=0;
                    pre=i+1;
                }
            }
            printf("Case %d:
    %d %d %d
    ",ca++,ans,anpre,anpost);
            if(T-1) puts("");
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Java 高阶 —— try/catch
    Java 高阶 —— native 关键字与 JNI
    python库学习笔记——分组计算利器:pandas中的groupby技术
    编程模式(schema) —— 表驱动法(table-driven)
    python中元组tuple
    .Net Framwork类库
    SMB带宽限制
    WindDbug应用
    Python学习笔记
    Python递归遍历目录下所有文件
  • 原文地址:https://www.cnblogs.com/zsyacm666666/p/4723485.html
Copyright © 2011-2022 走看看