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