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;
}