题目链接:https://ac.nowcoder.com/acm/contest/21865/D
大意是给出n个城墙,每个城墙有一定数量的法阵,破坏他们可以获得经验值,同时消耗时间。每个城墙有一个固定的最大停留时间。且最多可以破坏m个城墙。当破坏掉一个城墙后,它邻近的城墙的经验值将增加被破坏城墙的经验值,求出最多能够获得多少经验值。
先01背包预处理每块城墙能够获得的最大经验值,这一步不难想到。
我们定义dp ij 为在第i块城墙开始,长度为j的区间范围内所能获取的最大经验值,它的值取决于从左向右走还是从右向左走。
code:
#include<bits/stdc++.h> using namespace std; #define ll long long ll dp[10010][35]; ll f[250]; ll a[10010]; int main() { int n,m,t; cin>>n>>m>>t; for(int i=1;i<=n;i++) { memset(f,0,sizeof f); int k; scanf("%d",&k); for(int j=1;j<=k;j++) { int jy,sj; cin>>jy>>sj; for(int ff=t;ff>=sj;ff--) f[ff] = max(f[ff],f[ff-sj]+jy);// 0 1 背包求解 } dp[i][1] = a[i] = f[t];//将每个城墙能够获得的最大经验值存放起来,并在区间dp的数组中存放初始值 } //dp[i][j] 表示选择在第i个城墙 直到i+j-1个城墙的范围内的最大值。 j为区间的长度。 for(int len=2;len<=m;len++){ for(int l=1;l+len-1<=n;l++){ int r=l+len-1;//正下方这里最大值一定更新到了(i, m) dp[l][len] = max(dp[l][len] ,max(dp[l][len-1]*2+a[r] ,dp[l+1][len-1]*2+a[l])); //我们只考虑当前,当前l到r取决于 (l,len-1)+最右边城墙 (l+1,len-1)+最左边城墙 } } ll res=0; for(int i=1;i<=n;i++){ if(i+m-1>n) break; res = max(res,dp[i][m]); } cout<<res; }