本题其实是经典(完全)背包问题的一个变种,只不过需要处理一下B[i]。
设dp[i]为花费i所得糖果最大值。
根据题目,共有n堆礼物,买k个第i堆礼物获得糖果A[i] * k + B[i] (k > 0),每堆礼物数不限。
我们可以把每一堆礼物中的每一个礼物单独看成一堆。那么一共有无穷堆共k组礼物。
belong[i] = i % n代表现在的第i堆糖果原来属于哪一堆。
这样理论上下面的代码可以求解:
1 int solve(){ 2 for(int i = 0; ; i++){ 3 int k = i % n; 4 for(int j = m; j >= W[k]; j--){ 5 dp[j] = max(dp[j], dp[j -W[i]] + A[k] + (i < n ? B[k] : 0)); 6 } 7 } 8 return ans = dp[m]; 9 }
这个应该比较直观,容易理解。
那么下面把这个循环改成两部分。
第一部分单独考虑取第一轮dp(原来每堆糖果各拿出一个)。
1 for(int i = 0; i < n; i++){ 2 for(int j = m; j >= W[i]; j--){ 3 dp[i] = max(dp[j], dp[j - W[i]] + A[i] + B[i]); 4 } 5 }
第二部分仍然是完全背包。
1 for(int i = 1; i <= m; i++){ 2 for(int j = 0; j < n; j++){ 3 if(i >= W[j]) dp[i] = max(dp[i], dp[i - W[j]] + A[j]); 4 } 5 }
这样就可得出答案了。
代码中的第二轮循环后面注释代码也是可行的。
acm.hdu.edu.cn/showproblem.php?pid=5410
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 7 const int maxn = 1e3 + 10; 8 int dp[maxn * 2]; 9 int w[maxn], a[maxn], b[maxn]; 10 int n, m; 11 12 void solve(){ 13 memset(dp, 0, sizeof dp); 14 //preprocessing:: 0-1 packing 15 for(int i = 0; i < n; i++){ 16 for(int j = m; j >= w[i]; j--){ 17 dp[j] = max(dp[j], dp[j - w[i]] + a[i] + b[i]); 18 } 19 } 20 //full packing 21 for(int i = 1; i <= m; i++){ 22 for(int j = 0; j < n; j++){ 23 if(i >= w[j]) dp[i] = max(dp[i - w[j]] + a[j], dp[i]); 24 } 25 } 26 /* 27 for(int i = 0; i < n; i++){ 28 for(int j = w[i]; j <= m; j++){ 29 dp[j] = max(dp[j], dp[j - w[i]] + a[i]); 30 } 31 } 32 */ 33 printf("%d ", dp[m]); 34 } 35 36 int main(){ 37 //freopen("in.txt", "r", stdin); 38 int T; 39 scanf("%d", &T); 40 while(T--){ 41 scanf("%d%d", &m, &n); 42 for(int i = 0; i < n; i++) scanf("%d%d%d", &w[i], &a[i], &b[i]); 43 solve(); 44 } 45 return 0; 46 }