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