HDU1024 Max Sum Plus Plus (基础dp n个数求m个不相交子段和的最大值)

题目的大意是酱紫:

      输入两个数,m和n ,意思是n个数求m个不相交的(重读)子段,输出子段和的最大值。

举一个栗子:

     2 6             //  6个数  2个不相交子段
    -1 4 -2 3 -2 3     

       输出结果为 8    两个子段为{4},{3,-2,3};(当然这只是最大值其中一种,还可以是{4,3},{-2,3}等等)

主要思路:

       首先num[1000005]存n个数字。

       再来一个dp[1000005][1000005](肯定会爆内存,我们暂且先这么写

       这dp数组是个什么含义呢?dp[m][n]表示   以第n个数结尾的(重读)分成m个子段的和的最大值

        当然我们的目标肯定不是dp[m][n],我们的目标是max{dp[m][t]}(1<=t<=n)  为啥呢??因为最后的结果说不定是哪个数结尾呢,所以我们找一遍以所有数为结尾的可能就好了。

        转移方程:dp[i][j]=max  ( dp[i][j-1] + num[j],  dp[i-1][t] + num[j] );       i-1 <= t <= j-1; 

                                  (t的范围因为要分为i-1组所以一定是i-1个数开始到j-1个数)

        转移方程怎么推导的?    以第j个数结尾分i段只有两种情况,?1. 前j-1个数已经有i段了,第j个数一定要差到第i段的末尾,于是这种可能就是dp[i][j-1]+num[j]   ?2.已经分好了i-1段,j单独作为一段但是前i-1段不知道是前j-1个数中,几个数分出来的 ,所以循环找一下也就是dp[i-1][t]+num[j] (i-1  <=   t   <=j-1)    这个转移方程很重要一会儿优化要操作一下,那么到目前位置程序已经可以运行了,但是绝对爆内存,爆时间,内存就不说了,时间的话是O(n^3)得优化。。。笔者不才,可能有地方阐述不清。。

重点来了!!!!时间空间优化。

          我们先从时间优化入手,由转移方程可以知道,在求dp[i][j]的时候要用到dp[i][j-1]和dp[i-1][t](i-1 <= t <=j-1)

这个dp[i-1][t]是个循环试想可不可以重新创建一个数组now[n],来吧这个循环取代掉

         now[j-1]存上一层(i-1层)从0到j-1的最大值那么在求dp[i][j]的时候需要dp[i-1][t]的时候可以直接用now[j-1]替代

那么要求dp[i][j]只需要两个数据 1.dp[i][j-1]  2.now[j-1]。于是我们发现一个问题,需要的这两个数据和已经求好的dp[0~(i-1)]层没有任何关系,那还留着干嘛,所以可以直接将dp[m][n]优化为dp[m];

真·奥义·状态方程 !!! 

        dp[j] = max (dp[j-1] , now[j-1]) + num[j];

那么问题来了,这个now数组怎么求???

          我们可以在求dp[j](第i-1层的时侯)把now[j]记录下来,那么求第i层的时候就可以直接用了

设置一个maxn,如果求出来的dp【j】比maxn大,就更新掉maxn   

for(int j=i;j<n;j++){

    dp[j] = max (dp[j-1] , now[j-1]) + num[j];

    if(maxn>dp[j])maxn=dp[j];

    now[j]=maxn;

}

但是这样是不行的,为啥呢??因为求dp[j]的时候把now[j]更新了,但是当下一次循环也就是j+1的时候要用到now[j]但是now[j]已经不是原来的now[j]了,那怎么办??我们可以发现用完now[j-1]后这个数就没用了,下一次要用的是now[j]所以我们可以做个微小的变动,用完now[j-1]之后就更新掉他,就不会影响j+1的时候now[j]的使用了。。。美滋滋。。。

#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
int dp[1000005];
int num[1000005];
int now[1000005];
int main()
{
    int n,m;
    while(~scanf("%d%d",&m,&n))
    {
        for(int i=1; i<=n; i++)
            scanf("%d",&num[i]);
        memset(dp,0,sizeof(dp));
        memset(now,0,sizeof(now));
        int max1=-99999999;
        for(int i=1; i<=m; i++)
        {
            max1=-99999999;
            for(int j=i; j<=n; j++)
            {
                dp[j]=max(dp[j-1],now[j-1])+num[j];
                now[j-1]=max1;
                if(dp[j]>max1)max1=dp[j];
            }
        }
        printf("%d\n",max1);
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值