传送门:
[1]:洛谷
[2]:BZOJ
参考资料:
[1]:追忆:往昔
•题解
上述参考资料的讲解清晰易懂,下面谈谈我的理解;
关键语句:
将此题转化为 "01背包" 类问题,关键就是上述语句;
据此,定义 dp[ i ][ j ] 表示前 i 个物品在钩子剩余 j 个的状态下所获得的最大喜悦值;
细节处理:
为了应对负数的情况,让 dp[ i ][ j ] 的下标 j 全部增加 2000 ,这样就可以表示在负数范围内的值了。
状态转移:
首先,初始化 dp[][] 数组为 -INF,并令 dp[0][2001]=0(初始手机上含有一个挂钩);
1 for(int i=1;i <= n;++i) 2 { 3 a[i]--; 4 for(int j=1;j <= 4000;++j) 5 { 6 dp[i][j]=max(dp[i][j],dp[i-1][j]); 7 if(dp[i][j] == -INF) 8 continue; 9 10 int k=min(j+a[i],4000); 11 dp[i][k]=max(dp[i][k],max(dp[i-1][j]+b[i],dp[i-1][k])); 12 } 13 }解释1:挂饰 i 可以提供 ai 个挂钩,但是,要把它挂到手机上需要消耗一个挂钩,所以,挂饰 i 额外提供 ai-1 个挂钩;
解释2:第6行的更新是必不可少的,手动测试如下样例便可明白:
5 0 0 0 0 0 0 0 0 0 10解释3:第 11 行,dp[ i ][ k ] 只受状态 dp[ i-1 ][ j ] 和 dp[ i-1 ][ k ] 的影响,为什么在判断的时候需要额外与 dp[ i ][ k ] 判断呢?
仔细看一下 k,如果 j+a[ i ] 超过上界 4000,那么 k = 4000,对于超过 4000 的肯定都更新到了 dp[ i ][ 4000 ]上;
所以,dp[ i ][ 4000 ] 会首其他状态的影响,而不只是上述两种状态的影响;
验证解释3的正确性:
将第 11 行代码改成如下所示代码(AC):
1 if(k < 4000) 2 dp[i][k]=max(dp[i-1][j]+b[i],dp[i-1][k]); 3 else 4 dp[i][k]=max(dp[i][k],max(dp[i-1][j]+b[i],dp[i-1][k]));而改成如下所示代码(WA):
1 dp[i][k]=max(dp[i-1][j]+b[i],dp[i-1][k]);
•Code
二维dp1 #include<bits/stdc++.h> 2 using namespace std; 3 #define INF 0x3f3f3f3f 4 #define mem(a,b) memset(a,b,sizeof(a)) 5 const int maxn=2e3+50; 6 7 int n; 8 int a[maxn]; 9 int b[maxn]; 10 int dp[maxn][maxn<<1]; 11 12 int Solve() 13 { 14 for(int i=0;i <= n;++i) 15 for(int j=0;j <= 4000;++j) 16 dp[i][j]=-INF; 17 18 dp[0][2001]=0; 19 for(int i=1;i <= n;++i) 20 { 21 a[i]--;///物品i本身需要占用一个钩子,所以其可以提供的钩子个数为a[i]-1 22 for(int j=1;j <= 4000;++j) 23 { 24 dp[i][j]=max(dp[i][j],dp[i-1][j]); 25 26 if(dp[i][j] == -INF) 27 continue; 28 29 int k=min(j+a[i],4000); 30 dp[i][k]=max(dp[i][k],max(dp[i-1][j]+b[i],dp[i-1][k])); 31 } 32 } 33 int ans=0; 34 for(int i=2000;i <= 4000;++i) 35 ans=max(ans,dp[n][i]); 36 return ans; 37 } 38 39 int main() 40 { 41 // freopen("C:\Users\hyacinthLJP\Desktop\in&&out\contest","r",stdin); 42 scanf("%d",&n); 43 for(int i=1;i <= n;++i) 44 scanf("%d%d",a+i,b+i); 45 46 printf("%d ",Solve()); 47 48 return 0; 49 }
•利用滚动数组降低dp的维数
•Code
一维dp(滚动数组)1 #include<bits/stdc++.h> 2 using namespace std; 3 #define INF 0x3f3f3f3f 4 #define mem(a,b) memset(a,b,sizeof(a)) 5 const int maxn=2e3+50; 6 7 int n; 8 int a[maxn],b[maxn]; 9 int dp[maxn<<1]; 10 11 int Solve() 12 { 13 mem(dp,-INF); 14 dp[2001]=0; 15 16 for(int i=1;i <= n;++i) 17 { 18 a[i]--; 19 if(a[i] > 0) 20 { 21 for(int j=4000;j >= 0;--j) 22 { 23 if(dp[j] == -INF) 24 continue; 25 int k=min(j+a[i],4000); 26 dp[k]=max(dp[j]+b[i],dp[k]); 27 } 28 } 29 else 30 { 31 for(int j=0;j <= 4000;++j) 32 { 33 if(dp[j] == -INF) 34 continue; 35 dp[j+a[i]]=max(dp[j]+b[i],dp[j+a[i]]); 36 } 37 } 38 } 39 int ans=0; 40 for(int i=2000;i <= 4000;++i) 41 ans=max(ans,dp[i]); 42 43 return ans; 44 } 45 int main() 46 { 47 scanf("%d",&n); 48 for(int i=1;i <= n;++i) 49 scanf("%d%d",a+i,b+i); 50 51 printf("%d ",Solve()); 52 53 return 0; 54 }