Max Sum Plus Plus 【HDU 1024】

题目连接 HDU 1024

大概题意

有n个数字组成的一个数组,让你从中找出m个不相交子序列(要求连续),使这m个子序列的和最大。

解题思路

虽然题目中没有给m的范围,但是m是一定要小于n的(n<1000000)
我们先想一个比较朴素的算法:
dp[i][j]:意思是将前j个数字分为i段得到的最大和,一定要取第j个数
那么状态转移方程就是:dp[i][j]=max(dp[i][j-1]+a[j],max(dp[i-1][k])+a[j]);(0<k<j)
意思也就是:
1)前 j-1 个数已经形成了 i 段,那么第j个数直接加到第i段就可以了
2)前 j-1 个数形成了 i-1 段,那么我们要枚举是从1~j-1之间哪个数之前形成的i-1段的和是最大的,然后加上第j个数就好了
那么代码就应该是这样的:

for(int i=1; i<=m; i++)
    {
        for(int j=i; j<=n; j++)
        {
            for(int k=j-1; k>=1; k--)
            {
                dp[i][j]=max(dp[i][j],max(dp[i][j-1]+a[j],dp[i-1][k]+a[j]));
            }
        }
    }
   
   最后max(dp[m][i])[1<=i<=n]就是答案

但是哇,现在的代码来看我们的dp[1000000][1000000]要开这么大才行,可是空间是不被允许的,并且现在的代码有三重循环,时间上也是不被允许的。

我们可以发现最后一个循环的作用是找出最大的dp[i-1][k],也就是只和i-1这个状态有关,那么我们是不必要每次都循环一次的,只需要在上一个状态的时候记录一下就可以了,我们用一个数组maxx[k]来表示前 j-1 个数分成 i-1 组的最大值,那么这个时候的状态转移方程就变成了:
dp[i][j] = max ( dp[i][j-1] + a[j] , maxx[j-1] + a[j] );
这样的话我们的时间复杂度大概降到了O(n*logn)是差不多可以的,因为m<n所以到不了这么大,但是我们的空间复杂度依旧很高,是实现不了的。
我们发现我们的状态转移方程式是只和上一个状态有关系的,也就是分成i组的时候是只和 i-1 这个状态有关系的,并且dp[i][j]这个数组和 i-1 其实是没有关系的,只有maxx[] 和 i-1 有关系,所以我们可以把dp[i][j]这个数组给化简为dp[j],这样的话我们的空间也是允许的啦(- -撒花- -)

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
long long dp[1000005],maxx[1000005],a[1000005];
long long inf=9223372036854775806;
long long F(long long x,long long y)//long long用max()会有一些问题,所以开了一个外部函数
{
    if(x>y)
        return x;
    return y;
}
int main()
{
    int m,n;
    while(~scanf("%d %d",&m,&n))
    {
        memset(dp,0,sizeof(dp));
        memset(maxx,0,sizeof(maxx));
        for(int i=1; i<=n; i++)
            scanf("%lld",&a[i]);
        long long mm;
        for(int i=1; i<=m; i++)
        {
            mm=-inf;
            for(int j=i; j<=n; j++)
            {
                dp[j]=F(dp[j-1]+a[j],maxx[j-1]+a[j]);
                                          /*这里的dp[j]和dp[j-1]的前提都是分
                              为i段,dp[j]的意思是 在取第j个的情况下分为i段的最大和,
                              dp[j-1]的意思是在取第j-1个的情况下的最大和  */
                maxx[j-1]=mm;//这里更新的是取第j-1个的情况下,前j-1个数分为i段的最大和
                               当数据要处理前j-1个数分为i+1段时,将会被用到
                mm=F(mm,dp[j]);//更新最大值
            }
        }
        printf("%lld\n",mm);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值