目录
问题描述
问题分析(dp五部曲)
1.确定状态
dp[i][j][0]、dp[i][j][1]分别表示未使用魔法和使用了魔法的情况下容量为j的包装0-i的物品的最大价值。
2.递推公式
·对于dp[i][j][0]:
dp[i][j][0] = j>=w[i] ? max(dp[i-1][j][0], dp[i-1][j-w[i]][0]+v[i]) : dp[i-1][j][0]
对于dp[i][j][1]:
使用了魔法的状态,可能是之前使用,也可能是当前这次使用
1)如果是之前使用了 dp[i][j][1] = j<w[i] ? dp[i-1][j][1] : max(dp[i-1][j][1],dp[i-1][j-w[i]][1]+v[i])
2)如果是此次使用 if(j>=w[i]+k) dp[i][j][1] = dp[i-1][j-w[i]-k][0] + 2*v[i]
取最大即可
3.初始化:
dp[0][i][0] = j>=w[0] ? v[0] : 0;
dp[0][i][1] = j>=w[0]+k ? 2*v[0] : 0;
4.遍历顺序:
for i in range(n):
for j in range(m+1):
for k in range(2):
5.画表手推
对于示例数据:
n=3 m=10 g=3
5 10 4 9 3 8
k == 0 j 0 1 2 3 4 5 6 7 8 9 10
5 10 i 0 0 0 0 0 0 10 10 10 10 10 10
4 9 1 0 0 0 0 9 10 10 10 10 19 19
3 8 2 0 0 0 8 9 10 10 17 18 19 19
k == 1 j 0 1 2 3 4 5 6 7 8 9 10
5 10 i 0 0 0 0 0 0 0 0 0 20 20 20
4 9 1 0 0 0 0 9 9 9 18 20 20 20
3 8 2 0 0 0 8 9 9 16 18 20 20 26
AC-code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e3+5, maxm=1e4+5;
int w[maxn],v[maxn],n,m,k;
int dp[maxn][maxm][2];
int main()
{
cin >> n >>m >>k;
for(int i=0; i<n; i++) scanf("%d%d",&w[i],&v[i]);
for(int i=0; i<=m; i++)
{
dp[0][i][0] = i>=w[0] ? v[0] : 0;
dp[0][i][1] = i>=w[0]+k ? 2*v[0] : 0;
}
for(int i=1; i<n; i++)
for(int j=1; j<=m; j++)
{
if(j>=w[i]+k)
{
dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j-w[i]][0]+v[i]);
int v1 = dp[i-1][j-w[i]-k][0] + 2*v[i], v2 = dp[i-1][j-w[i]][1]+v[i], v3 = dp[i-1][j][1];
dp[i][j][1] = max(v1,max(v2,v3));
}
else if(j>=w[i])
{
dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j-w[i]][0]+v[i]);
dp[i][j][1] = max(dp[i-1][j][1],dp[i-1][j-w[i]][1]+v[i]);
}
else
{
dp[i][j][0] = dp[i-1][j][0];
dp[i][j][1] = dp[i-1][j][1];
}
}
cout << max(dp[n-1][m][0],dp[n-1][m][1]);
return 0;
}