B - 真·签到题 FZU - 2214(背包)
Given a set of n items, each with a weight w[i] and a value v[i], determine a way to choose the items into a knapsack so that the total weight is less than or equal to a given limit B and the total value is as large as possible. Find the maximum total value. (Note that each item can be only chosen once).
Input
The first line contains the integer T indicating to the number of test cases.
For each test case, the first line contains the integers n and B.
Following n lines provide the information of each item.
The i-th line contains the weight w[i] and the value v[i] of the i-th item respectively.
1 <= number of test cases <= 100
1 <= n <= 500
1 <= B, w[i] <= 1000000000
1 <= v[1]+v[2]+...+v[n] <= 5000
All the inputs are integers.
Output
For each test case, output the maximum value.
Sample Input
1 5 15 12 4 2 2 1 1 4 10 1 2
Sample Output
15
自己找了两个背包的代码敲了上去,第一个因为重复查找的次数太多TEL, 第二个因为数组空间开的太大爆了。
TEL代码
1 int n, W; 2 int w[MAX_N], v[MAX_N]; 3 4 int rec(int i, int j) { 5 int res; 6 if (i == n) { 7 res = 0; 8 } else if (j < w[i]) { 9 res = rec(i + 1, j); 10 } else { 11 res = max(rec(i + 1, j), rec(i + 1, j - w[i]) + v[i]); 12 } 13 return res; 14 } 15 16 void solve() { 17 printf("%d\n", rec(0, W)); 18 }
上面的代码可以优化,试产殉国的位置不再进行查询,但是要开一的dp[MAX_N][MAX_W]的数组,因为MAX_W的取值范围太大所以这种方法不能够用来解答此题。
优化代码
1 int dp[MAX_N][MAX_W]; 2 3 int rec(int i, int j) { 4 if (dp[i][j] >= 0) 5 return dp[i][j]; 6 7 int res; 8 if (i == n) { 9 res = 0; 10 } else if (j < w[i]) { 11 res = rec(i+1, j); 12 } else { 13 res = max(rec(i+1, j), rec(i+1, j-w[i]) + v[i]); 14 } 15 return dp[i][j] = res; 16 } 17 18 void solve() { 19 memset(dp, -1, sizeof(dp)); 20 printf("%d\n", rec(0, W)); 21 }
超大背包,因为此题的背包数量太多,用超大背包的话算法复杂度是O(2^n)级别,所以此题也不能用超大别抱来解决。超大背包只适合用来解决别博爱的数量很少但是重量和价值都很大的情况。
超大背包代码:
1 typedef long long ll; 2 3 int n; 4 ll w[MAX_N], v[MAX_N]; 5 ll W; 6 7 pair<ll, ll> ps[1 << (MAX_N / 2)]; 8 9 void solve() { 10 int n2 = n / 2; 11 for (int i = 0; i < 1 << n2; i++) { 12 ll sw = 0, sv = 0; 13 for (int j = 0; j < n2; j++) { 14 if (i >> j & 1) { 15 sw += w[j]; 16 sv += v[j]; 17 } 18 } 19 ps[i] = make_pair(sw, sv); 20 } 21 22 sort(ps, ps + (1 << n2)); 23 int m = 1; 24 for (int i = 1; i < 1 << n2; i++) { 25 if (ps[m-1].second < ps[i].second) { 26 ps[m++] = ps[i]; 27 } 28 } 29 30 ll res = 0; 31 for (int i = 0; i < 1 << (n-n2); i++) { 32 ll sw = 0, sv = 0; 33 for (int j = 0; j < n - n2; j++) { 34 if (i >> j & 1) { 35 sw += w[n2+j]; 36 sv += v[n2+j]; 37 } 38 } 39 if (sw <= W) { 40 ll tv = (lower_bound(ps, ps+m, make_pair(W - sw, INF)) - 1) -> second; 41 res = max(res, sv + tv); 42 } 43 } 44 printf("%d\n", res); 45 }
题解:
最大质量为1000000000,数组肯定不够用。
不过,总价值才5000,我们以价值为轴开辟记录剩余可载质量的一维数组,后面的做法就与01背包如出一辙。
AC代码
1 #include<cstdio> 2 #include<cstring> 3 int main(){ 4 int weight[5001], t, i, j, n, B, max_value, w, v; 5 scanf("%d", &t); 6 7 while (t--){ 8 scanf("%d%d", &n, &B); 9 memset(weight, 0, sizeof(weight)); 10 weight[0] = B, max_value = 0; 11 12 for (j = 0; j < n; ++j){ 13 scanf("%d%d", &w, &v); 14 for (i = max_value; i >= 0; --i){ 15 if (weight[i] - w > weight[i + v]) weight[i + v] = weight[i] - w; 16 } 17 for (i = max_value + 1; i <= 5000; ++i) if (weight[i]) max_value = i; 18 } 19 20 printf("%d\n", max_value); 21 } 22 return 0; 23 }
总结:通过这题也让我明白了自己对摸板代码的理解程度还远远不够,所以才会出现这样的情况。
加油!!!!!!!!!!