多校第七场ECNU 1006 HDU 3905 Sleeping (二维DP)

n个数,需要至少减掉m个数(不要求连续)。求所有剩余连续的长度大于等于l的数串中的和的最大值

dp[i][j] 表示扫描第i个数已经已经去掉j个数,能获得最大值。

首先,第i数可以去掉,那么dp[i][j] = dp[i - 1][j - 1];

也可以不去掉,对于每个k( 0 <= k <= i - l),求的dp[k][j] + sum[i] - sum[k]的最大值,max{dp[i][j] = dp[i - 1][j - 1],dp[k][j] + sum[i] - sum[k]}就是当前的值。

直接枚举k 代价o(n)会超时,这里需要利用另一个数组寄存一下,dp_tmp[i][j]存入dp[k][j] + sum[i] - sum[k]的最大值,这样转移dp_tmp[i][j]的方程为

dp_tmp[i][j]=max{dp_tmp[i-1][j]+sum[i]-sum[i-1],dp[i-l][j]+sum[i]-sum[i-l]} 


对于求的dp[k][j] + sum[i] - sum[k]的最大值的代价就将为o(1)。时间、空间都为o(n^2) 。

HDU 上跑了218ms

 

#include <cstdio>
#include <cstring>
#define max(a,b) (a)>(b)?(a):(b)
#define min(a,b) (a)>(b)?(b):(a)

const int maxn=1005;
int n,m,l;
int dp[maxn][maxn];//记录当前状态状态 
int dp_tmp[maxn][maxn];
int sum[maxn];
int i,j;

int main ()
{
    //freopen ("data.in","r",stdin);
    //freopen ("out.txt","w",stdout);
    while (~scanf("%d%d%d",&n,&m,&l))
    {
        memset(dp,0,sizeof(dp));
        memset(dp_tmp,0,sizeof(dp_tmp));
        if(m+l>n){printf("0\n");continue;}
        sum[0]=0;
        for (i=1 ; i<=n ; ++i)
        {
            scanf("%d",sum+i);
            sum[i]+=sum[i-1];
        }
        
        for (i=1 ; i<=n ; ++i)
        {
            for (j=0 ; j<=m && j<=i ; ++j)
            { 
                dp[i][j]=dp[i-1][j];//初始化,转移的意义是当前不睡觉但是不构成更大的分数 
                if(j>0)dp[i][j]=max(dp[i][j],dp[i-1][j-1]);
                if(i-j>=l)dp_tmp[i][j]=max(dp_tmp[i-1][j]+sum[i]-sum[i-1],dp[i-l][j]+sum[i]-sum[i-l]);
                //printf("%d %d\n",dp_tmp[i-1][j]+sum[i]-sum[i-1],dp[i-l][j]+sum[i]-sum[i-l]);
                if(i-l>=0)dp[i][j]=max(dp_tmp[i][j],dp[i][j]);
                //printf("i=%d j=%d dp=%d,tmp=%d\n",i,j,dp[i][j],dp_tmp[i][j]);
                
            }
        }
        printf("%d\n",dp[n][m]);
    }
    return 0;
}


 

 

 

你可能感兴趣的:(多校第七场ECNU 1006 HDU 3905 Sleeping (二维DP))