★、背包求方案数的时候,多重背包是不行的,因为产生重复的背包会有多种情况。

★、背包记录路径的时候,其实是不行的,因为更新了12的最优解,如果它依赖于6这个背包,然后你后面改变了6这个背包,就GG

1、01背包问题。

tot:总背包空间,vall[i]:每件物品的价值,w[i]:每件物品的重量

http://acm.hdu.edu.cn/showproblem.php?pid=2602

01背包明显可以只写一维的,所以二维的就不写了。

关于为什么可以只写一维的呢?这就和你枚举的顺序有关了。从tot 枚举 到 w[i]。那么是优先更新dp[比较大的数]

而且是从dp[i - 1][]那里更新过来的。至于后面枚举小的背包容量的时候,较大的背包容量是用不了的了,所以这里就可以避免有重复使用的bug。确保都是从dp[i-  1]枚举过来。而这个顺序反转了的话,刚好的完全背包的最优解。这个后面再说

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = 1e3 + ;
  20. int dp[maxn];
  21. int w[maxn], val[maxn];
  22. void work() {
  23. memset(dp, , sizeof dp);
  24. int n, tot;
  25. scanf("%d%d", &n, &tot);
  26. for (int i = ; i <= n; ++i) {
  27. scanf("%d", &val[i]);
  28. }
  29. for (int i = ; i <= n; ++i) {
  30. scanf("%d", &w[i]);
  31. }
  32. for (int i = ; i <= n; ++i) {
  33. for (int j = tot; j >= w[i]; --j) {
  34. dp[j] = max(dp[j], dp[j -w[i]] + val[i]);
  35. }
  36. }
  37. printf("%d\n", dp[tot]);
  38. }
  39.  
  40. int main() {
  41. #ifdef local
  42. freopen("data.txt", "r", stdin);
  43. // freopen("data.txt", "w", stdout);
  44. #endif
  45. int t;
  46. scanf("%d", &t);
  47. while (t--) work();
  48. return ;
  49. }

一个常数的优化:

我把tot加大到10000.然后提交就变成了655ms。下面来说说当总背包容量tot比较大的时候,该怎么优化。

对于第n件物品,我们的转移方程是dp[tot] = max(dp[tot], dp[tot - w[n]);  //这个就是答案

其实只需要一步就够了,因为我们需要的是dp[tot],不用再向下枚举了。但是根据上面的代码,是需要枚举到

for (j := tot; j >= w[n]; --j),是需要枚举到w[n]的,为什么呢?其实是为了给后面的做铺垫。因为我们并不知道这个是最后的一个背包,所以还是需要枚举到w[i]的,因为后面的背包可能需要用到dp[w[i]]这个背包的值。来更新最优解

那么我们可以算出一个下限,什么下限呢,就是后面的所有可能的背包中,最多需要用到那一个背包。

对于最后一个背包,他只需要用到dp[tot - w[n]]这个背包就够了。枚举到倒数第二种物品的时候,

他只需要用到dp[tot - w[n] - w[n - 1]]这个背包就够了。那么前面的背包,我们就不需要更新了。

感觉还是写张图比较好理解,以免我以后忘记。

现在考虑枚举到了倒数第二种物品,我们只需要更新红色那个区域就行了,因为最后一个物品只需要用到dp[tot - w[n]]

那么同理,需要更新红色那段区域,我们只需要知道[tot - w[n] - w[n - 1], tot]这段区域的最优值是谁就可以了,因为我们为了更新红色那段区域,对于倒数第二种物品,其重量是w[i],下限就是tot - w[n] - w[i],故按照这个思路递推回去第i件物品即可。

这个用来优化当tot比较大的时候,是有用的,我把tot和w[]都同时加上了一个fix值,结果TLE,不是TLE就是RE。还是找到合适的题目再写上来吧,

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = 1e3 + ;
  20. int dp[maxn];
  21. int w[maxn], val[maxn];
  22. int suffix_sum[maxn];
  23. void work() {
  24. memset(dp, , sizeof dp);
  25. int n, tot;
  26. scanf("%d%d", &n, &tot);
  27. for (int i = ; i <= n; ++i) {
  28. scanf("%d", &val[i]);
  29. }
  30. for (int i = ; i <= n; ++i) {
  31. scanf("%d", &w[i]);
  32. }
  33. suffix_sum[n + ] = ;
  34. for (int i = n; i >= ; --i) {
  35. suffix_sum[i] = w[i] + suffix_sum[i + ];
  36. }
  37. for (int i = ; i <= n; ++i) {
  38. int toUpdate = max(w[i], tot - suffix_sum[i + ]);
  39. for (int j = tot; j >= toUpdate; --j) {
  40. dp[j] = max(dp[j], dp[j -w[i]] + val[i]);
  41. }
  42. }
  43. printf("%d\n", dp[tot]);
  44. }
  45.  
  46. int main() {
  47. #ifdef local
  48. freopen("data.txt", "r", stdin);
  49. // freopen("data.txt", "w", stdout);
  50. #endif
  51. int t;
  52. scanf("%d", &t);
  53. while (t--) work();
  54. return ;
  55. }

一个常数的优化

关于dp的初始化。开始的时候dp[0] = 0表示容量为0的背包,能得到物品的价值是0.后面的就有两类了。

①、需要刚好装满tot个,那么,后面的就是全部都是-inf了,表示刚好刚好装满x个的时候,价值是负的,就是没有价值。

②、不需要的话,就全部都是0.

二维01背包,

POJ 1948

http://poj.org/problem?id=1948

给定n根木棒,要求全部用上,组成一个三角形,使得这个三角形的面积最大。

dp[i][j]表示组成的第一根木棒长度是i的时候,第二根木棒长度是j,第三根木棒的长度是dp[i][j]

那么对于周长是固定的话,那么dp数组开bool的就够了。dp[i][j] = 0表示这个方案不可行

比如dp[0][1] = true。表示第一根木棒是0,第二根木棒是1,第三根是all - 0 - 1。那么就全部木棒也用上了。

转移的话,if (dp[i][j]) then dp[i + val][j] = true;  dp[i][j + val] = true;

就是这个物品,可以去两组中的任意一组,都可以。

同样也是枚举顺序的问题,应该倒着来枚举,因为木棒只能用一次。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = + ;
  20. bool dp[maxn][maxn];
  21. int a[maxn];
  22. bool check (int a, int b, int c) {
  23. if (abs(a - b) >= c) return false;
  24. if (abs(a - c) >= b) return false;
  25. if (abs(b - c) >= a) return false;
  26. return true;
  27. }
  28. double calc(double a, double b, double c) {
  29. // cout << a << " " << b << " " << c << endl;
  30. double p = (a + b + c) / 2.0;
  31. // cout << p << endl;
  32. double ans = sqrt(p * (p - a) * (p - b) * (p - c));
  33. return ans * ;
  34. }
  35. void work() {
  36. int n;
  37. int all = ;
  38. scanf("%d", &n);
  39. for (int i = ; i <= n; ++i) {
  40. scanf("%d", &a[i]);
  41. all += a[i];
  42. }
  43. // dp[0][0] = dp[a[1]][0] = dp[0][a[1]] = true;
  44. dp[][] = true;
  45. int en = (all + ) / ;
  46. for (int i = ; i <= n; ++i) {
  47. for (int j = en; j >= ; --j) {
  48. for (int k = en; k >= j; --k) {
  49. if (j >= a[i] && dp[j - a[i]][k]) {
  50. dp[j][k] = true;
  51. }
  52. if (k >= a[i] && dp[j][k - a[i]]) {
  53. dp[j][k] = true;
  54. }
  55. }
  56. }
  57. }
  58. int ans = -;
  59. for (int i = ; i <= en; ++i) {
  60. for (int j = i; j <= en; ++j) {
  61. if (dp[i][j] && check(i, j, all - i - j)) {
  62. ans = max(ans, (int)calc(i, j, all - i - j));
  63. }
  64. }
  65. }
  66. printf("%d\n", ans);
  67. }
  68.  
  69. int main() {
  70. #ifdef local
  71. freopen("data.txt", "r", stdin);
  72. // freopen("data.txt", "w", stdout);
  73. #endif
  74. work();
  75. return ;
  76. }

https://vijos.org/p/1037

一题比较好的,具有很强想象力的01背包问题。

题意就是在n个数中,选出一些数字,分成2组,使得两组的和是相同的,现在需要使得这个和最大。

那么可以dp[i][j]表示前i组数中,这两组东西的差值是j的时候,较大的那组数的和是dp[i][j]。那么dp[n][0]是答案

对于每一个物品a[i],为了产生差值为j时的方案。都有4种情况,

1、不选它,不要了, 那么dp[i][j] = dp[i - 1][j];

2、选择它放去比较矮的那组,那么这个时候,要产生差值是j,需要原本的差值是j + a[i]。而且这个时候,最高的那个值没变化。

3、放去较高的那组,那么这个时候,要产生差值是j,需要原本的差值是j - a[i],而且他变高了,所以是dp[i - 1][j - a[i]] + a[i]

4、放去较矮的那组,而且超越了本来较高的那组,然后现在的差值是j,这个需要画个图,

为什么会想到这4总情况

因为它是3大类。

1、不用

2、放了之后,最大高度不改变。

3、放了之后,最大高度改变

这时候是,由dp[i - 1][a[i] - j] + j转移过来。

然后取四个的最大值就好了。

这题不容易想啊。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = + ;
  20. int a[maxn];
  21. int dp[maxn][ + ];
  22. void work() {
  23. int n;
  24. cin >> n;
  25. for (int i = ; i <= n; ++i) {
  26. cin >> a[i];
  27. assert(a[i] >= );
  28. }
  29. memset(dp, -0x3f, sizeof dp);
  30. dp[][] = ;
  31. for (int i = ; i <= n; ++i) {
  32. for (int j = ; j <= ; ++j) {
  33. if (j >= a[i]) {
  34. dp[i][j] = max(dp[i][j], dp[i - ][j - a[i]] + a[i]); //放去高的
  35. }
  36. if (a[i] >= j) {
  37. dp[i][j] = max(dp[i][j], dp[i - ][a[i] - j] + j);
  38. }
  39. dp[i][j] = max(dp[i][j], dp[i - ][j + a[i]]); //放在小的那里
  40. dp[i][j] = max(dp[i][j], dp[i - ][j]); //不用
  41. }
  42. }
  43. if (dp[n][] <= ) {
  44. cout << "Impossible" << endl;
  45. } else cout << dp[n][] << endl;
  46. }
  47. int main() {
  48. #ifdef local
  49. freopen("data.txt", "r", stdin);
  50. // freopen("data.txt", "w", stdout);
  51. #endif
  52. work();
  53. return ;
  54. }

new:

  1. #include <bits/stdc++.h>
  2. #define IOS ios::sync_with_stdio(false)
  3. using namespace std;
  4. #define inf (0x3f3f3f3f)
  5. typedef long long int LL;
  6. int dp[ + ][ + ];
  7. void work() {
  8. memset(dp, -0x3f, sizeof dp);
  9. int n;
  10. scanf("%d", &n);
  11. int val;
  12. for (int i = ; i <= n; ++i) {
  13. scanf("%d", &val);
  14. dp[i][val] = val;
  15. for (int j = ; j >= ; --j) {
  16. dp[i][j] = max(dp[i][j], dp[i - ][j]);
  17. if (j + val <= ) dp[i][j + val] = max(dp[i][j + val], dp[i - ][j] + val);
  18. if (j >= val) {
  19. dp[i][j - val] = max(dp[i][j - val], dp[i - ][j]);
  20. } else {
  21. dp[i][val - j] = max(dp[i][val - j], dp[i - ][j] - j + val);
  22. }
  23. }
  24. }
  25. // printf("%d\n", dp[2][3]);
  26. if (dp[n][] <= ) {
  27. printf("Impossible\n");
  28. } else printf("%d\n", dp[n][]);
  29. }
  30.  
  31. int main() {
  32. #ifdef local
  33. freopen("data.txt", "r", stdin);
  34. // freopen("data.txt", "w", stdout);
  35. #endif
  36. work();
  37. return ;
  38. }

其实这题有一个很简单的方法的,

就是和上面的三角形一样,dp[i][j]表示第一座的高度是i,第二座的高度是j,是否可能。

唉,一开始怎么想不到,不过这个是水过去的,评测机快吧。870ms

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = + ;
  20. int a[maxn];
  21. bool dp[ + ][ + ];
  22. void work() {
  23. int n;
  24. cin >> n;
  25. for (int i = ; i <= n; ++i) {
  26. cin >> a[i];
  27. assert(a[i] >= );
  28. }
  29. dp[][] = true;
  30. for (int i = ; i <= n; ++i) {
  31. for (int j = ; j >= ; --j) {
  32. for (int h = ; h >= ; --h) {
  33. // dp[j][h] = dp[j][h] || dp[j - a[i]][h] || dp[j][h - a[i]];
  34. if (j >= a[i]) {
  35. dp[j][h] = dp[j][h] || dp[j - a[i]][h];
  36. }
  37. if (h >= a[i]) {
  38. dp[j][h] = dp[j][h] || dp[j][h - a[i]];
  39. }
  40. }
  41. }
  42. }
  43. for (int i = ; i >= ; --i) {
  44. if (dp[i][i]) {
  45. cout << i << endl;
  46. return;
  47. }
  48. }
  49. cout << "Impossible" << endl;
  50. }
  51. int main() {
  52. #ifdef local
  53. freopen("data.txt", "r", stdin);
  54. // freopen("data.txt", "w", stdout);
  55. #endif
  56. work();
  57. return ;
  58. }

还有这题也不错。 http://www.cnblogs.com/liuweimingcprogram/p/6238454.html

https://vijos.org/p/1059

这就是一题暴力题,给定n组数字,每组数字能选出若干个,组成一个和值val。现在需要在这n组中,找出他们共有的和值。

明显对n组都做一次01背包,那么复杂度最坏1e8.但是我还是写了,居然127ms。

这里本来还想用一个常数的优化,但是是不行的,我要生成的是有多少个和值,而不是最优解。

dp[i][j]表示第i组,能否生成j这个和值。然后得到这个数组后,不应该用二分答案。因为有可能有些组没有这个值,然后有一个共同的更大的值。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = 1e2 + ;
  20. bool dp[maxn][maxn * maxn];
  21. vector<int>a[maxn];
  22. int mx[maxn];
  23. //int suffix_sum[maxn][maxn];
  24. int n;
  25. bool check(int val) {
  26. for (int i = ; i <= n; ++i) {
  27. if (!dp[i][val]) return false;
  28. }
  29. return true;
  30. }
  31. void work() {
  32. scanf("%d", &n);
  33. for (int i = ; i <= n; ++i) {
  34. int x;
  35. int sum = ;
  36. while (scanf("%d", &x)) {
  37. if (x == -) break;
  38. a[i].push_back(x);
  39. sum += x;
  40. }
  41. mx[i] = sum;
  42. }
  43. // for (int i = 1; i <= n; ++i) {
  44. // for (int j = a[i].size() - 1; j >= 0; --j) {
  45. // suffix_sum[i][j] = suffix_sum[i][j + 1] + a[i][j];
  46. // }
  47. // }
  48. for (int i = ; i <= n; ++i) {
  49. dp[i][] = true;
  50. }
  51. for (int i = ; i <= n; ++i) {
  52. for (int j = ; j < a[i].size(); ++j) {
  53. // int toUpdate = max(a[i][j], mx[i] - suffix_sum[i][j + 1]);
  54. for (int v = mx[i]; v >= a[i][j]; --v) {
  55. dp[i][v] = dp[i][v] || dp[i][v - a[i][j]];
  56. }
  57. }
  58. }
  59. for (int i = * ; i >= ; --i) {
  60. if (check(i)) {
  61. cout << i << endl;
  62. return;
  63. }
  64. }
  65. }
  66.  
  67. int main() {
  68. #ifdef local
  69. freopen("data.txt", "r", stdin);
  70. // freopen("data.txt", "w", stdout);
  71. #endif
  72. work();
  73. return ;
  74. }

https://vijos.org/p/1071

再来一题01背包,这个背包需要检查路径,而且需要检查是否合法。

感觉数据有点水,还不知道我的有没数据卡我的程序。

思路就是看看这n个数字中,有没有一些数字,和值是val。如果有多种情况,就输出-1,不可能,输出0.否则输出方案。

其实记录路径很简单的,这里不说了,主要是怎么确定他有多种解。

比如

18

6

1 2 3 4 5 6

这个是多种解的,3可以用1和2代替。

我的做法是把唯一解分成一组,另外的分成一组,然后两组再进行一次dp,如果能产生相同的数字,就不行,说明可以相互代替了。

dp[v].id,这个背包选了哪一个数字

dp[v].flag,这个背包可以由多少个背包转移过来。

dp[v].pre,这个背包的上一个背包。

说一说这里的小bug

3可以用1 +2代替,也可以用直接一个3来代替。我们选择1 + 2,然后记得标记已经生成了,就是3已经可以生成,不再去记录3的其他路径了。因为,还是上面那个例子。

在更新6的时候,18 = 6 + 12.是可以得。12在前面有被生成过。但是,它再次更新了12.12 = 6 + 6

这是不合法的,选了两次6了。所以,我们要记录唯一的路径。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. int a[];
  20. struct node {
  21. int id, pre;
  22. int flag;
  23. }dp[ * + ];
  24. set<int>ans;
  25. vector<int>one;
  26. vector<int>two;
  27. bool out[ * + ];
  28. bool visone[ * + ];
  29. bool dpone[ * + ];
  30. bool dptwo[ * + ];
  31. void work() {
  32. int tot, n;
  33. cin >> tot >> n;
  34. for (int i = ; i <= n; ++i) {
  35. cin >> a[i];
  36. }
  37. dp[].flag = ;
  38. dp[].id = dp[].pre = inf;
  39. int tim = ;
  40. for (int i = ; i <= n; ++i) {
  41. for (int j = tot; j >= a[i]; --j) {
  42. if (dp[j].flag && dp[j - a[i]].flag) {
  43. dp[j].flag++;
  44. continue;
  45. }
  46. if (dp[j].flag) continue;
  47. if (dp[j - a[i]].flag) {
  48. dp[j].flag++;
  49. dp[j].id = i;
  50. dp[j].pre = j - a[i];
  51. }
  52. }
  53. }
  54. // cout << dp[9].pre << endl;
  55. if (tot == ) {
  56. for (int i = ; i <= n; ++i) {
  57. cout << i << " ";
  58. }
  59. return;
  60. }
  61. // cout << dp[12].id << endl;
  62. if (dp[tot].flag == ) {
  63. cout << << endl;
  64. return;
  65. }
  66. if (dp[tot].flag >= ) {
  67. cout << - << endl;
  68. return;
  69. }
  70. int t = dp[tot].pre;
  71. ans.insert(dp[tot].id);
  72. while (t != ) {
  73. ans.insert(dp[t].id);
  74. t = dp[t].pre;
  75. }
  76. for (set<int> :: iterator it = ans.begin(); it != ans.end(); ++it) {
  77. out[*it] = true;
  78. one.push_back(*it);
  79. }
  80. for (int i = ; i <= n; ++i) {
  81. if (out[i]) continue;
  82. // cout << i << " ";
  83. two.push_back(i);
  84. }
  85. dpone[] = dptwo[] = true;
  86. for (int i = ; i < one.size(); ++i) {
  87. for (int j = tot; j >= a[one[i]]; --j) {
  88. dpone[j] = dpone[j] || dpone[j - a[one[i]]];
  89. }
  90. }
  91. for (int i = ; i < two.size(); ++i) {
  92. for (int j = tot; j >= a[two[i]]; --j) {
  93. dptwo[j] = dptwo[j] || dptwo[j - a[two[i]]];
  94. if (dptwo[j] && dpone[j]) {
  95. cout << - << endl;
  96. return;
  97. }
  98. }
  99. }
  100. for (int i = ; i < two.size(); ++i) {
  101. cout << two[i] << " ";
  102. }
  103. }
  104.  
  105. int main() {
  106. #ifdef local
  107. freopen("data.txt", "r", stdin);
  108. // freopen("data.txt", "w", stdout);
  109. #endif
  110. work();
  111. return ;
  112. }

又傻逼了一次,看了题解。发现别人是直接找它对立的,就是直接找sum - tot就行了。

然后,记录路径,就记录唯一的路径,然后如果有多重解,就可以把解的个数叠加上来。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. int a[];
  20. struct node {
  21. int id, pre;
  22. int flag;
  23. }dp[ * + ];
  24. set<int>ans;
  25. vector<int>one;
  26. vector<int>two;
  27. bool out[ * + ];
  28. bool visone[ * + ];
  29. bool dpone[ * + ];
  30. bool dptwo[ * + ];
  31. void work() {
  32. int tot, n;
  33. cin >> tot >> n;
  34. int sum = ;
  35. for (int i = ; i <= n; ++i) {
  36. cin >> a[i];
  37. sum += a[i];
  38. }
  39. dp[].flag = ;
  40. dp[].id = dp[].pre = inf;
  41. tot = sum - tot;
  42. int tim = ;
  43. // cout << tot << endl;
  44. for (int i = ; i <= n; ++i) {
  45. for (int j = tot; j >= a[i]; --j) {
  46. //记录路径,就记录唯一的路径
  47. if (dp[j].flag == && dp[j - a[i]].flag > ) {
  48. dp[j].flag = ;
  49. dp[j].id = i;
  50. dp[j].pre = j - a[i];
  51. }
  52. //如果有多重解
  53. else if (dp[j - a[i]].flag) {
  54. dp[j].flag += dp[j - a[i]].flag;
  55. }
  56. }
  57. }
  58. // cout << dp[110].flag << endl;
  59. if (dp[tot].flag == ) {
  60. cout << << endl;
  61. return;
  62. }
  63. if (dp[tot].flag >= ) {
  64. cout << - << endl;
  65. return;
  66. }
  67. int t = tot;
  68. while (t != ) {
  69. if (dp[t].id == inf) break;
  70. ans.insert(dp[t].id);
  71. t = dp[t].pre;
  72. }
  73. for (set<int> :: iterator it = ans.begin(); it != ans.end(); ++it) {
  74. cout << *it << " ";
  75. }
  76. }
  77.  
  78. int main() {
  79. #ifdef local
  80. freopen("data.txt", "r", stdin);
  81. // freopen("data.txt", "w", stdout);
  82. #endif
  83. work();
  84. return ;
  85. }

https://vijos.org/p/1153

dp[j][v]表示前i个物品,刚好用了j个,能否生成v这个价值。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = + ;
  20. int a[maxn];
  21. bool dp[ + ][ + ];
  22. struct node {
  23. int a, b;
  24. node(int aa, int bb) : a(aa), b(bb) {}
  25. bool operator < (const struct node & rhs) const {
  26. return (a - b) < (rhs.a - rhs.b);
  27. }
  28. };
  29. set<struct node> ss;
  30. void work() {
  31. int n;
  32. cin >> n;
  33. int sum = ;
  34. for (int i = ; i <= n; ++i) {
  35. cin >> a[i];
  36. sum += a[i];
  37. }
  38. dp[][] = true;
  39. int en = (n) / ;
  40. for (int i = ; i <= n; ++i) {
  41. for (int j = en; j >= ; --j) {
  42. for (int v = sum / ; v >= a[i]; --v) {
  43. dp[j][v] = dp[j][v] || dp[j - ][v - a[i]];
  44. }
  45. }
  46. }
  47. if (n == ) {
  48. cout << << " " << a[] << endl;
  49. return;
  50. }
  51. for (int i = sum / ; i >= ; --i) {
  52. if (dp[en][i]) {
  53. cout << i << " " << sum - i << endl;
  54. return;
  55. }
  56. }
  57. }
  58.  
  59. int main() {
  60. #ifdef local
  61. freopen("data.txt", "r", stdin);
  62. // freopen("data.txt", "w", stdout);
  63. #endif
  64. work();
  65. return ;
  66. }

https://vijos.org/p/1240  01背包的好题。

因为它的状态比较多,比较吓人。(但是这题有错误,夫妻条件不管才能过。)

说说正解吧。

dp[i][j][h][k]表示前i个房间,住了j对夫妻,一共住了h个男的,k个女的这个状态的最小费用。

然后就是暴力枚举每一个房间,更新数组了。

这里有一个很有趣的地方就是,夫妻房最多用1对就已经最优了,因为你用两对的话,可以把他们拆散,2个男的一件,2个女的一件。

所以这里如果考虑夫妻的,是可以卡到你超时的,如果注意到夫妻 只能用一对,那就不会超时。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. int dp[ + ][ + ][ + ];
  20. const int maxn = + ;
  21. int a[maxn];
  22. int cost[maxn];
  23. void work() {
  24. int nan, nv, room, toget;
  25. cin >> nan >> nv >> room >> toget;
  26. for (int i = ; i <= room; ++i) {
  27. cin >> a[i] >> cost[i];
  28. }
  29. memset(dp, 0x3f, sizeof dp);
  30. dp[][][] = ;
  31. toget = min(toget, );
  32. toget = ;
  33. for (int i = ; i <= room; ++i) {
  34. for (int j = toget; j >= ; --j) {
  35. for (int h = nan; h >= ; --h) {
  36. for (int k = nv; k >= ; --k) {
  37. if (k >= a[i]) {
  38. dp[j][h][k] = min(dp[j][h][k], dp[j][h][k - a[i]] + cost[i]);
  39. } else {
  40. dp[j][h][k] = min(dp[j][h][k], dp[j][h][] + cost[i]);
  41. }
  42. if (h >= a[i]) {
  43. dp[j][h][k] = min(dp[j][h][k], dp[j][h - a[i]][k] + cost[i]);
  44. } else {
  45. dp[j][h][k] = min(dp[j][h][k], dp[j][][k] + cost[i]);
  46. }
  47. if (j >= && h >= && k >= ) {
  48. dp[j][h][k] = min(dp[j][h][k], dp[j - ][h - ][k - ] + cost[i]);
  49. }
  50. // dp[0][h][k] = min()
  51. }
  52. }
  53. }
  54. }
  55. // cout << dp[0][2][1] << endl;
  56. int ans = inf;
  57. for (int i = ; i <= toget; ++i) {
  58. ans = min(ans, dp[i][nan][nv]);
  59. }
  60. if (ans == inf) {
  61. cout << "Impossible" << endl;
  62. return;
  63. }
  64. cout << ans << endl;
  65. }
  66.  
  67. int main() {
  68. #ifdef local
  69. freopen("data.txt", "r", stdin);
  70. // freopen("data.txt", "w", stdout);
  71. #endif
  72. work();
  73. return ;
  74. }

2、完全背包

关于完全背包,唯一需要变的就是枚举的方向,使得同一个物品能被多次使用。

http://acm.hdu.edu.cn/showproblem.php?pid=1114

因为需要恰好装满,所以初始化需要改变一下。

完全背包的题目,要求输出最小价值。然后一定要把给出的背包重量全部用完。

就是问一个背包为k的大小,n件物品,能装的最小价值,并且一定是用了k个背包容量。

用dp[i]表示背包容量为i得时候,能收录的最小价值,

边界:dp[0] = 0; 没容量,啥都干不了

else dp[i] = inf。一开始初始化为无穷大。

转移的话,dp[i] = min(dp[i], dp[i - weight[j]] + val[j])

枚举的话,次循环要顺着枚举。

因为这样才能确保它是能使用多次(完全背包嘛)

为什么顺着枚举就可以了呢?

因为考虑一下,当物品的重量为5,背包重量是10的时候。

顺着枚举for (int j = 5; j <= 10; ++j) 的话,

j = 5的时候,物品能枚举到是否加入背包,然后j = 10的时候,物品再次被枚举是否加入这个背包,也就是重复使用了

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = + ;
  20. int dp[maxn];
  21. int w[maxn];
  22. int val[maxn];
  23. void work() {
  24. int a, b;
  25. scanf("%d%d", &a, &b);
  26. int tot = b - a;
  27. int n;
  28. scanf("%d", &n);
  29. for (int i = ; i <= n; ++i) {
  30. scanf("%d%d", &val[i], &w[i]);
  31. }
  32. memset(dp, 0x3f, sizeof dp);
  33. dp[] = ;
  34. for (int i = ; i <= n; ++i) {
  35. for (int j = w[i]; j <= tot; ++j) {
  36. dp[j] = min(dp[j], dp[j - w[i]] + val[i]);
  37. }
  38. }
  39. if (dp[tot] == inf) {
  40. cout << "This is impossible." << endl;
  41. } else {
  42. printf("The minimum amount of money in the piggy-bank is %d.\n", dp[tot]);
  43. }
  44. }
  45.  
  46. int main() {
  47. #ifdef local
  48. freopen("data.txt", "r", stdin);
  49. // freopen("data.txt", "w", stdout);
  50. #endif
  51. int t;
  52. scanf("%d", &t);
  53. while (t--) work();
  54. return ;
  55. }

http://www.cnblogs.com/liuweimingcprogram/p/6195469.html  这是一题不错的完全背包

P1159岳麓山上打水

https://vijos.org/p/1159

dfsID,第一次听说这东西,但是感觉不太靠谱啊。

一开始的时候,想到了排个序后,然后进行dp,如果要输出字典序最小其实还是可以搞定的,就是2、3、比26小的话,还是可以的。

排序后,只要在转移的时候,如果这个背包有解了的话,就不转移就行了。

但是这题坑爹在需要个数最小,这就不是字典序了。

那么暴力枚举选了多少个桶,那么有2^n种选法。每一种都dp一次。

但是据说,据说在很少步之内就能算出解,然后210ms过了。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = + ;
  20. bool dp[maxn];
  21. int a[maxn];
  22. int sel[maxn];
  23. int tot, n;
  24. int ans[maxn];
  25. bool dfsID(int cur, int has, int up) {
  26. if (has == up) {
  27. for (int i = ; i <= tot; ++i) dp[i] = false;
  28. dp[] = true;
  29. for (int i = ; i <= has; ++i) {
  30. for (int j = ans[i]; j <= tot; ++j) {
  31. dp[j] = dp[j] || dp[j - ans[i]];
  32. }
  33. }
  34. if (dp[tot]) {
  35. cout << has << " ";
  36. for (int i = ; i <= has; ++i) {
  37. cout << ans[i] << " ";
  38. }
  39. cout << endl;
  40. }
  41. return dp[tot];
  42. }
  43. if (cur > n) return false;
  44. if (n - cur + + has < up) return false;
  45. ans[has + ] = a[cur];
  46. return dfsID(cur + , has + , up) || dfsID(cur + , has, up);
  47. }
  48. void work() {
  49. cin >> tot >> n;
  50. assert(tot > );
  51. for (int i = ; i <= n; ++i) {
  52. cin >> a[i];
  53. assert(a[i] > );
  54. }
  55. sort(a + , a + + n);
  56. for (int i = ; i <= n; ++i) {
  57. if (dfsID(, , i)) {
  58. return;
  59. }
  60. }
  61. }
  62.  
  63. int main() {
  64. #ifdef local
  65. freopen("data.txt", "r", stdin);
  66. // freopen("data.txt", "w", stdout);
  67. #endif
  68. IOS;
  69. work();
  70. return ;
  71. }

3、多重背包问题

有N种物品,每种物品的数量为C1,C2......Cn。从中任选若干件放在容量为W的背包里,每种物品的体积为W1,W2......Wn(Wi为整数),与之相对应的价值为P1,P2......Pn(Pi为整数)。求背包能够容纳的最大价值。

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1086

对于一个物品,如果他有c[i]件,那么我可以把它拆成2^0 + 2^1 + 2^k... + (c[i] - 2^(k + 1) + 1)

这样使得它转化为01背包问题后,任选一些,都能凑合成 <= c[i]的任何数字。

比如14 = 1 + 2 + 4 + 7。只有log个

这样就能结合到1--14之间的所有数字,为什么呢,因为1 + 2 + 4是能组合成1 -- 7之间的任何数字的了,这是根据二进制的思想。

  1

10

100

每个东西的位置“1”,都可以要,与不要,所以能组合成1---7的任何数字,然后这些数字和剩下的那个7,一结合,就是1--14的任何数字

然后转化成01背包求解。

复杂度是O(vn)的,现在n有sigma(log(c[i]))个。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. struct node {
  20. int val;
  21. int w;
  22. node(int aa, int bb) : val(aa), w(bb) {}
  23. node() {}
  24. }a[ * + ];
  25. const int maxn = + ;
  26. int dp[maxn];
  27. void work() {
  28. int n, tot;
  29. scanf("%d%d", &n, &tot);
  30. int lena = ;
  31. for (int i = ; i <= n; ++i) {
  32. int w, p, c;
  33. scanf("%d%d%d", &w, &p, &c);
  34. for (int i = ; i <= ; ++i) {
  35. if (( << i) < c) {
  36. c -= ( << i);
  37. a[++lena] = node(( << i) * p, ( << i) * w);
  38. } else {
  39. a[++lena] = node(p * c, w * c);
  40. break;
  41. }
  42. }
  43. }
  44. for (int i = ; i <= lena; ++i) {
  45. for (int j = tot; j >= a[i].w; --j) {
  46. dp[j] = max(dp[j], dp[j - a[i].w] + a[i].val);
  47. }
  48. }
  49. cout << dp[tot] << endl;
  50. }
  51.  
  52. int main() {
  53. #ifdef local
  54. freopen("data.txt", "r", stdin);
  55. // freopen("data.txt", "w", stdout);
  56. #endif
  57. work();
  58. return ;
  59. }

这个题目,可以使用上面说的一个常数的优化,使得变成62ms

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. struct node {
  20. int val;
  21. int w;
  22. node(int aa, int bb) : val(aa), w(bb) {}
  23. node() {}
  24. }a[ * + ];
  25. const int maxn = + ;
  26. int dp[maxn];
  27. int suffix_sum[maxn];
  28. void work() {
  29. int n, tot;
  30. scanf("%d%d", &n, &tot);
  31. int lena = ;
  32. for (int i = ; i <= n; ++i) {
  33. int w, p, c;
  34. scanf("%d%d%d", &w, &p, &c);
  35. for (int i = ; i <= ; ++i) {
  36. if (( << i) < c) {
  37. c -= ( << i);
  38. a[++lena] = node(( << i) * p, ( << i) * w);
  39. } else {
  40. a[++lena] = node(p * c, w * c);
  41. break;
  42. }
  43. }
  44. }
  45. for (int i = lena; i >= ; --i) {
  46. suffix_sum[i] = a[i].w + suffix_sum[i + ];
  47. }
  48. for (int i = ; i <= lena; ++i) {
  49. int toUpdate = max(a[i].w, tot - suffix_sum[i + ]);
  50. for (int j = tot; j >= toUpdate; --j) {
  51. dp[j] = max(dp[j], dp[j - a[i].w] + a[i].val);
  52. }
  53. }
  54. cout << dp[tot] << endl;
  55. }
  56.  
  57. int main() {
  58. #ifdef local
  59. freopen("data.txt", "r", stdin);
  60. // freopen("data.txt", "w", stdout);
  61. #endif
  62. work();
  63. return ;
  64. }

一个常数的优化

http://codeforces.com/problemset/problem/106/C

4、混合三种背包问题

留坑

5、二维费用背包问题

这类问题就是,对于每一个物品,都有两种不同的费用,a[i]和b[i],如果选取这个物品,就需要扣除分别的费用,现在给你两种不同的费用的总值,u和v,要求算出在不超过u和v的前提下能得到的最大价值。

所以可以设dp[i][j][k]表示前i种物品,第一种价值的背包容量是j,第二种背包容量是k时,得到的最大值。

dp[i][j][k] = max(dp[i][j][k], dp[i - 1][j - a[i]][k - b[i]] + val[i]);

那么怎么来枚举呢?枚举是有一定的顺序的。

第一,先枚举每件物品,因为每件物品只能选一次,就选能选多次,也是后面维度的枚举顺序要变化。

第二,枚举第三维。

第三,枚举第二维。

为什么需要这样呢?我们结合一个例题

http://acm.tju.edu.cn/toj/showp3596.html

给定n个物品,选出确定的m个,拥有背包容量为L。求最大价值。

设dp[m][L]表示选出了m件物品,背包容量为L时的最大价值。这里我省略了一维

第一,先枚举每件物品,看起来十分好理解,因为不能重新选择嘛。

至于为什么要先枚举第三维,那么我们先来枚举第二维,看看会怎样,结合题目的那个样例。

先枚举选出了k个物品,然后再枚举背包容量为j时得到的最大价值。

for (int i = 1; i <= n; ++i) { //枚举物品

  for (int k = 1; k <= min(i, m); ++k) { //枚举在前i个物品中,选了k个

    for (int j = L; j >= w[i]; --j) { //枚举背包容量

      dp[k][j] = max(dp[k][j], dp[k - 1][j - w[i]] + val[i]);

    }

  }

}

这样,模拟一下样例的话,是会算重的,dp[1][L]的时候选择了一次物品2,dp[1][L - 物品2的时间]也能选取物品2

那么这个时候dp[2][L] = max(dp[2][L], dp[1][L - 物品2的时间] + val[2]);//这里会算重复。

那么反过来,先枚举背包容量是j,再枚举选出了k件物品。

那么dp[1][L]根据dp[0][L - w[2]]算出,dp[2][L]根据dp[1][L - w[2]]算出,这个时候dp[1][L - w[2]]还没更新,所以不会算重复。

因为题目需要的是一定选出m个,那么dp[0][0...L] = 0,其他是-inf

因为,任何价值的背包,选出0个物品,都是没有价值的。

那为什么dp[1][0...L]是-inf呢。这是为了dp[2][L]从dp[1][]转移过来的时候,不会有价值,因为如果选取不了1个的话,是不能选取到2个的。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = + ;
  20. int Ti[maxn];
  21. int Vi[maxn];
  22. int dp[maxn][ + ];
  23. void work() {
  24. int n, m, L;
  25. scanf("%d%d%d", &n, &m, &L);
  26. for (int i = ; i <= n; ++i) {
  27. scanf("%d%d", &Ti[i], &Vi[i]);
  28. }
  29. for (int i = ; i <= m; ++i) {
  30. for (int j = ; j <= L; ++j) {
  31. dp[i][j] = -inf;
  32. }
  33. }
  34. for (int i = ; i <= L; ++i) {
  35. dp[][i] = ; //初始化
  36. }
  37. for (int i = ; i <= n; ++i) { //枚举物品为先,只能选一次
  38. for (int j = L; j >= Ti[i]; --j) {
  39. for (int k = ; k <= min(i, m); ++k) {
  40. dp[k][j] = max(dp[k][j], dp[k - ][j - Ti[i]] + Vi[i]);
  41. }
  42. }
  43. }
  44. if (dp[m][L] < ) dp[m][L] = ;
  45. cout << dp[m][L] << endl;
  46. }
  47.  
  48. int main() {
  49. #ifdef local
  50. freopen("data.txt", "r", stdin);
  51. // freopen("data.txt", "w", stdout);
  52. #endif
  53. int t;
  54. scanf("%d", &t);
  55. while (t--) work();
  56. return ;
  57. }

现在发现,上面说的枚举顺序有问题,其实是我对背包问题了解的不透。不好意思了

如果要防止dp[2][L]使用了dp[1][L - w[2]](已更新)的话,那么可以把m倒着来枚举,这个就是上面的01背包的枚举顺序,因为一开始我觉得m一定是1--min(当前物品个数,m)的嘛,因为i个物品就不能选出m个了。但是倒着枚举,这些都是-inf,是没事的。

下面这个才是二维费用背包的01背包写法。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = + ;
  20. int Ti[maxn];
  21. int Vi[maxn];
  22. int dp[maxn][ + ];
  23. void work() {
  24. int n, m, L;
  25. scanf("%d%d%d", &n, &m, &L);
  26. for (int i = ; i <= n; ++i) {
  27. scanf("%d%d", &Ti[i], &Vi[i]);
  28. }
  29. for (int i = ; i <= m; ++i) {
  30. for (int j = ; j <= L; ++j) {
  31. dp[i][j] = -inf;
  32. }
  33. }
  34. for (int i = ; i <= L; ++i) {
  35. dp[][i] = ; //初始化
  36. }
  37. for (int i = ; i <= n; ++i) {
  38. for (int k = m; k >= ; --k) {
  39. for (int j = L; j >= Ti[i]; --j) {
  40. dp[k][j] = max(dp[k][j], dp[k - ][j - Ti[i]] + Vi[i]);
  41. }
  42. }
  43. }
  44. if (dp[m][L] < ) dp[m][L] = ;
  45. cout << dp[m][L] << endl;
  46. }
  47.  
  48. int main() {
  49. #ifdef local
  50. freopen("data.txt", "r", stdin);
  51. // freopen("data.txt", "w", stdout);
  52. #endif
  53. int t;
  54. scanf("%d", &t);
  55. while (t--) work();
  56. return ;
  57. }

倒着枚举m

  1. #include <bits/stdc++.h>
  2. #define IOS ios::sync_with_stdio(false)
  3. using namespace std;
  4. #define inf (0x3f3f3f3f)
  5. typedef long long int LL;
  6. int dp[ + ][ + ];
  7. const int maxn = 1e2 + ;
  8. void work() {
  9. memset(dp, -, sizeof dp);
  10. int n, m, L;
  11. scanf("%d%d%d", &n, &m, &L);
  12. for (int i = ; i <= L; ++i) dp[][i] = ;
  13. for (int i = ; i <= n; ++i) {
  14. int w, val;
  15. scanf("%d%d", &w, &val);
  16. for (int j = m; j >= ; --j) {
  17. for (int k = L; k >= w; --k) {
  18. if (dp[j - ][k - w] >= )
  19. dp[j][k] = max(dp[j][k], dp[j - ][k - w] + val);
  20. }
  21. }
  22. }
  23. if (dp[m][L] < ) dp[m][L] = ;
  24. printf("%d\n", dp[m][L]);
  25. }
  26.  
  27. int main() {
  28. #ifdef local
  29. freopen("data.txt", "r", stdin);
  30. // freopen("data.txt", "w", stdout);
  31. #endif
  32. int t;
  33. scanf("%d", &t);
  34. while (t--) work();
  35. return ;
  36. }

http://acm.hdu.edu.cn/showproblem.php?pid=2159

二维费用背包 + 多重背包问题。

dp[s][m]表示前i种怪物,最多杀s个怪,拥有m点忍耐度,所得到的最大经验,

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. int n, m, k, s;
  20. const int maxn = 1e2 + ;
  21. int dp[maxn][maxn];
  22. int a[maxn];
  23. int b[maxn];
  24. void work() {
  25. memset(dp, , sizeof dp);
  26. for (int i = ; i <= k; ++i) {
  27. scanf("%d%d", &a[i], &b[i]);
  28. }
  29. for (int i = ; i <= k; ++i) {
  30. for (int j = ; j <= s; ++j) {
  31. for (int h = b[i]; h <= m; ++h) {
  32. dp[j][h] = max(dp[j][h], dp[j - ][h - b[i]] + a[i]);
  33. }
  34. }
  35. }
  36. if (dp[s][m] < n) {
  37. printf("-1\n");
  38. } else {
  39. for (int i = ; i <= m; ++i) {
  40. if (dp[s][i] >= n) {
  41. printf("%d\n", m - i);
  42. return;
  43. }
  44. }
  45. }
  46. }
  47.  
  48. int main() {
  49. #ifdef local
  50. freopen("data.txt", "r", stdin);
  51. // freopen("data.txt", "w", stdout);
  52. #endif
  53. while (scanf("%d%d%d%d", &n, &m, &k, &s) > ) work();
  54. return ;
  55. }

https://vijos.org/p/1334

二维费用背包 + 01背包

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = + ;
  20. int v[maxn];
  21. int w[maxn];
  22. int val[maxn];
  23. int dp[maxn][maxn];
  24. void work() {
  25. int n;
  26. int totv, totw;
  27. scanf("%d%d", &totv, &totw);
  28. scanf("%d", &n);
  29. for (int i = ; i <= n; ++i) {
  30. scanf("%d%d%d", &v[i], &w[i], &val[i]);
  31. }
  32. for (int i = ; i <= n; ++i) {
  33. for (int j = totv; j >= v[i]; --j) {
  34. for (int k = totw; k >= w[i]; --k) {
  35. dp[j][k] = max(dp[j][k], dp[j - v[i]][k - w[i]] + val[i]);
  36. }
  37. }
  38. }
  39. cout << dp[totv][totw] << endl;
  40. }
  41.  
  42. int main() {
  43. #ifdef local
  44. freopen("data.txt", "r", stdin);
  45. // freopen("data.txt", "w", stdout);
  46. #endif
  47. work();
  48. return ;
  49. }

CF:二维费用背包问题,

http://www.cnblogs.com/liuweimingcprogram/p/6226415.html

6、分组背包问题

分组背包,没一个组只能选一个的话,那么设dp[k][v]表示前k组中,背包容量是v时的最大价值

那么转移就是,枚举组k中的每一个新元素,最多只有一个进来,那么就是

dp[k][v] = max(dp[k - 1][v], dp[k - 1][v - w[i]] + val[i])

for () //枚举每一个组

  for () //枚举所有背包容量  实质就是说我要更新“当背包是v时,的最大价值”

    for () //每一个组的成员

为什么要这个顺序呢,第一枚举每一个组是肯定得。至于第二层,可以这样去想。

因为最多只有一个东西进来,那么枚举背包容量的时候,实质就是说我要更新“当背包是v时,的最大价值”。然后就是更新的问题了。更新的话,就是去这个组中找一个新元素给他。一次的更新只会更新背包容量是v时的最大价值,所以不会影响后面的。

http://acm.hdu.edu.cn/showproblem.php?pid=1712

基本的分组背包。每个物品只选一次

dp[v]表示前k组中,背包容量是v时的最大价值,

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. int n, m;
  20. const int maxn = 1e2 + ;
  21. int dp[maxn];
  22. int a[maxn][maxn];
  23. void work() {
  24. memset(dp, , sizeof dp);
  25. for (int i = ; i <= n; ++i) {
  26. for (int j = ; j <= m; ++j) {
  27. scanf("%d", &a[i][j]);
  28. }
  29. }
  30. for (int i = ; i <= n; ++i) {
  31. for (int j = m; j >= ; --j) {
  32. for (int k = ; k <= j; ++k) {
  33. dp[j] = max(dp[j], dp[j - k] + a[i][k]);
  34. }
  35. }
  36. }
  37. printf("%d\n", dp[m]);
  38. }
  39.  
  40. int main() {
  41. #ifdef local
  42. freopen("data.txt", "r", stdin);
  43. // freopen("data.txt", "w", stdout);
  44. #endif
  45. while (scanf("%d%d", &n, &m) != EOF && (n + m)) work();
  46. return ;
  47. }

http://acm.hdu.edu.cn/showproblem.php?pid=4341

分组背包,每组物品只能取前k件。

关于斜率那里,其实是可以先按斜率排序,然后再按距离原点排序。但是一开始没想到。只会暴力对x排序,对y排序,然后每一个都暴力for一次看看有没相同的斜率,用vector保存。但是这里有x是负数的情况,要把它反转再弄。

然后就是分组背包的套路了

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. int n, T;
  20. const int maxn = + ;
  21. struct node {
  22. int x, y, t, val;
  23. node() {}
  24. node(int xx, int yy, int tt, int vval) : x(xx), y(yy), t(tt), val(vval) {}
  25. bool operator < (const struct node & rhs) const {
  26. if (x != rhs.x) {
  27. return x < rhs.x;
  28. } else return y < rhs.y;
  29. }
  30. }a[maxn], b[maxn];
  31. vector<struct node>gg[maxn];
  32. int dp[ + ];
  33. void work() {
  34. memset(dp, , sizeof dp);
  35. for (int i = ; i <= maxn - ; ++i) {
  36. gg[i].clear();
  37. }
  38. int lena = , lenb = ;
  39. for (int i = ; i <= n; ++i) {
  40. int x, y, t, val;
  41. scanf("%d%d%d%d", &x, &y, &t, &val);
  42. if (x < ) {
  43. b[++lenb] = node(-x, y, t, val);
  44. } else a[++lena] = node(x, y, t, val);
  45. }
  46. sort(a + , a + + lena);
  47. sort(b + , b + + lenb);
  48. for (int i = ; i <= lenb; ++i) {
  49. b[i].x = -b[i].x;
  50. a[++lena] = b[i];
  51. }
  52. assert(lena == n);
  53. int tohash = ;
  54. for (int i = ; i <= n; ++i) {
  55. bool flag = false;
  56. for (int j = ; j <= tohash; ++j) {
  57. if (a[i].y * gg[j][].x == a[i].x * gg[j][].y) {
  58. gg[j].push_back(a[i]);
  59. flag = true;
  60. break;
  61. }
  62. }
  63. if (!flag) {
  64. gg[++tohash].push_back(a[i]);
  65. }
  66. }
  67. // cout << tohash << endl;
  68. for (int i = ; i <= tohash; ++i) {
  69. for (int j = T; j >= ; --j) {
  70. int sumt = , sumval = ;
  71. for (int k = ; k < gg[i].size(); ++k) {
  72. sumt += gg[i][k].t;
  73. sumval += gg[i][k].val;
  74. if (j < sumt) break;
  75. dp[j] = max(dp[j], dp[j - sumt] + sumval);
  76. }
  77. }
  78. }
  79. // printf("%d\n", dp[T]);
  80. static int f = ;
  81. printf("Case %d: %d\n", ++f, dp[T]);
  82. }
  83.  
  84. int main() {
  85. #ifdef local
  86. freopen("data.txt", "r", stdin);
  87. // freopen("data.txt", "w", stdout);
  88. #endif
  89. while (scanf("%d%d", &n, &T) != EOF) work();
  90. return ;
  91. }

去写了个对斜率排序的,能排序,关键是y是大于0的,建议大家都写写看,代码比上面的简单不少

其实意思就是,斜率公式本来是y1 / x1的,但是x1可能是0,不能弄。那么我们要对她们斜率排序的话

如果y1 / x1 > y2 / x2的,相当于x1 / y1 < x2 / y2,因为y是大于0的,可以交叉相成。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. int n, T;
  20. const int maxn = + ;
  21. struct node {
  22. int x, y, t, val;
  23. node() {}
  24. node(int xx, int yy, int tt, int vval) : x(xx), y(yy), t(tt), val(vval) {}
  25. bool operator < (const struct node & rhs) const {
  26. int one = x * rhs.y;
  27. int two = rhs.x * y;
  28. if (one != two) {
  29. return one < two;
  30. } return x * x + y * y < rhs.x * rhs.x + rhs.y * rhs.y;
  31. }
  32. bool operator == (const struct node & rhs) const {
  33. return x * rhs.y == rhs.x * y;
  34. }
  35. }a[maxn];
  36. vector<struct node>gg[maxn];
  37. int dp[ + ];
  38. void work() {
  39. memset(dp, , sizeof dp);
  40. for (int i = ; i <= maxn - ; ++i) {
  41. gg[i].clear();
  42. }
  43. for (int i = ; i <= n; ++i) {
  44. scanf("%d%d%d%d", &a[i].x, &a[i].y, &a[i].t, &a[i].val);
  45. }
  46. int tohash = ;
  47. sort(a + , a + + n);
  48. for (int i = ; i <= n; ++i) {
  49. bool flag = false;
  50. if (tohash > ) {
  51. if (a[i] == gg[tohash][]) {
  52. gg[tohash].push_back(a[i]);
  53. flag = true;
  54. }
  55. }
  56. if (!flag) {
  57. gg[++tohash].push_back(a[i]);
  58. }
  59. }
  60. for (int i = ; i <= tohash; ++i) {
  61. for (int j = T; j >= ; --j) {
  62. int sumt = , sumval = ;
  63. for (int k = ; k < gg[i].size(); ++k) {
  64. sumt += gg[i][k].t;
  65. sumval += gg[i][k].val;
  66. if (sumt > j) break;
  67. dp[j] = max(dp[j], dp[j - sumt] + sumval);
  68. }
  69. }
  70. }
  71. static int f = ;
  72. printf("Case %d: %d\n", ++f, dp[T]);
  73. }
  74.  
  75. int main() {
  76. #ifdef local
  77. freopen("data.txt", "r", stdin);
  78. // freopen("data.txt", "w", stdout);
  79. #endif
  80. while (scanf("%d%d", &n, &T) != EOF) work();
  81. return ;
  82. }

http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1326

并查集后即可

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19.  
  20. const int maxn = 1e3 + ;
  21. int w[maxn], val[maxn];
  22. int fa[maxn];
  23. int tofind(int x) {
  24. if (x == fa[x]) return x;
  25. else return fa[x] = tofind(fa[x]);
  26. }
  27. void tomerge(int x, int y) {
  28. x = tofind(x);
  29. y = tofind(y);
  30. fa[y] = x;
  31. }
  32. vector<int>gg[maxn];
  33. bool vis[maxn];
  34. int dp[maxn];
  35. void work() {
  36. int n, tot, k;
  37. scanf("%d%d%d", &n, &tot, &k);
  38. for (int i = ; i <= n; ++i) {
  39. scanf("%d%d", &val[i], &w[i]);
  40. fa[i] = i;
  41. }
  42. for (int i = ; i <= k; ++i) {
  43. int a, b;
  44. scanf("%d%d", &a, &b);
  45. tomerge(a, b);
  46. }
  47. for (int i = ; i <= n; ++i) {
  48. gg[tofind(i)].push_back(i);
  49. }
  50. for (int i = ; i <= n; ++i) {
  51. if (vis[tofind(i)]) continue;
  52. vis[tofind(i)] = true;
  53. for (int j = tot; j >= ; --j) {
  54. for (int h = ; h < gg[tofind(i)].size(); ++h) {
  55. if (j < w[gg[tofind(i)][h]]) continue;
  56. dp[j] = max(dp[j], dp[j - w[gg[tofind(i)][h]]] + val[gg[tofind(i)][h]]);
  57. }
  58. }
  59. }
  60. cout << dp[tot] << endl;
  61. }
  62.  
  63. int main() {
  64. #ifdef local
  65. freopen("data.txt", "r", stdin);
  66. // freopen("data.txt", "w", stdout);
  67. #endif
  68. work();
  69. return ;
  70. }

http://poj.org/problem?id=3046

这一题的话,想了很久,现在也不知道它究竟属不属于背包问题。

dp[i][j]表示在前i组蚂蚁中,生成长度为j的排列,有多少种可能。

那么,对于第i组,你可以选择1个、2个、....N[i]个

选择一个的话,那么可以在前i - 1组中,选择j - 1个,加上这一个,就是dp[i][j]的一个贡献。

直接转移复杂度是n * m * m,但是也能过题目,可以先直接转移,再用前缀和优化。

记得有MOD数的。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = * * + ;
  20. int dp[maxn];
  21. const int MOD = 1e6;
  22. int book[ + ];
  23. set<int>ss;
  24. int pre[maxn];
  25. void work() {
  26. int n, m, L, R;
  27. cin >> n >> m >> L >> R;
  28. int tot = ;
  29. for (int i = ; i <= m; ++i) {
  30. int x;
  31. cin >> x;
  32. book[x]++;
  33. ss.insert(x);
  34. }
  35. for (set<int> :: iterator it = ss.begin(); it != ss.end(); ++it) {
  36. tot += book[*it];
  37. }
  38. dp[] = ;
  39. for (int i = ; i <= tot; ++i) pre[i] = ;
  40. int now = ;
  41. for (set<int> :: iterator it = ss.begin(); it != ss.end(); ++it) {
  42. int x = *it;
  43. now += book[x]; //最多能产生这么长的序列
  44. for (int i = now; i >= ; --i) { //01背包,要倒着枚举
  45. int en = min(i, book[x]);
  46. // for (int j = 1; j <= en; ++j) { //这个组,选多少个进来
  47. // dp[i] += dp[i - j];
  48. // dp[i] %= MOD;
  49. // }
  50. int up = pre[i - ];
  51. int down;
  52. if (i - en == ) down = ;
  53. else down = pre[i - en - ];
  54. dp[i] += (up - down + MOD) % MOD;
  55. dp[i] %= MOD;
  56. }
  57. pre[] = ;
  58. for (int j = ; j <= tot; ++j) {
  59. pre[j] = (dp[j] + pre[j - ]) % MOD;
  60. }
  61. }
  62. int ans = ;
  63. for (int i = L; i <= R; ++i) {
  64. // cout << dp[i] << endl;
  65. ans += dp[i];
  66. if (ans >= MOD) ans %= MOD;
  67. }
  68. cout << ans << endl;
  69. }
  70.  
  71. int main() {
  72. #ifdef local
  73. freopen("data.txt", "r", stdin);
  74. // freopen("data.txt", "w", stdout);
  75. #endif
  76. work();
  77. return ;
  78. }

P1198最佳课题选择

https://vijos.org/p/1198

一开始还以为这题是泛化物品背包,然后去看背包9讲,很是抽象,不能领会,然后就自己YY,最后发现居然是简单的分组背包。

开始我是这样想的,如果要写n篇,那么第一种, 我可以写1、2、4...2^k篇,就是用上面的二进制思想,多重背包的思想。那么01背包后,就能得到第一种选多少篇来写了。然而有bug,因为如果写3篇的话,a * 3^b和a * 1^b + a * 2^b往往是不等价的。

那么我就干脆暴力,每一种都有可能写0、1、2、3、....n,每一组中,只选一个,那么就是标准的分组背包了。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. struct node {
  20. LL val;
  21. int w;
  22. node(int ww, LL vval) : w(ww), val(vval) {}
  23. };
  24. vector<struct node>gg[ + ];
  25. LL calc(int a, int b, int x) {
  26. LL ans = ;
  27. for (int i = ; i <= b; ++i) {
  28. ans *= x;
  29. }
  30. ans *= a;
  31. return ans;
  32. }
  33. LL dp[];
  34. void work() {
  35. // cout << calc(100, 5, 200) << endl;
  36. int n, m;
  37. cin >> n >> m;
  38. for (int i = ; i <= m; ++i) {
  39. int a, b;
  40. cin >> a >> b;
  41. for (int j = ; j <= n; ++j) {
  42. gg[i].push_back(node(j, calc(a, b, j)));
  43. }
  44. }
  45. for (int i = ; i <= - ; ++i) {
  46. dp[i] = 1e17L;
  47. }
  48. dp[] = ;
  49. for (int i = ; i <= m; ++i) {
  50. for (int v = n; v >= ; --v) {
  51. for (int j = ; j < gg[i].size(); ++j) {
  52. if (v >= gg[i][j].w) {
  53. dp[v] = min(dp[v], dp[v - gg[i][j].w] + gg[i][j].val);
  54. }
  55. }
  56. }
  57. }
  58. cout << dp[n] << endl;
  59. }
  60.  
  61. int main() {
  62. #ifdef local
  63. freopen("data.txt", "r", stdin);
  64. // freopen("data.txt", "w", stdout);
  65. #endif
  66. IOS;
  67. work();
  68. return ;
  69. }

7、有依赖的背包问题。

P1313金明的预算方案

https://vijos.org/p/1313

如果像这题一样,附件没有了其他附件的话,其实这题还是很好做的。

首先,用个vector[i]表示第i件主件有多少件附件,就是所有附件都push_back了,然后,对于没组的主件,都来一个01背包。

这样就知道在第i组中,拥有v的背包,能得到最大的价值是多少。

可以用一个明显的优化就是物廉价美的东西才要,贵的,而且价值不高的,就可以舍弃,这样使得物品数大大减少。

然后对于每一组,来一个分组背包就好了。

关于01背包的时候,怎么规定它一定要选了主件后,才能选一个附件呢?

我的做法是把附件的cost和val都加上主件的cost和val。这样使得选取了某一个附件的时候,隐含地选取了一个主件。

但是这样有bug,就是选择了两件附件的时候,这样就隐含地选了两个主件了,所以,判断选物品进来的时候,如果它本来已经有物品的了,那么,就应该减去主件的cost和主件的val了。

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = + ;
  20. struct node {
  21. int w, val;
  22. node(int ww, int vval) : w(ww), val(vval) {}
  23. };
  24. vector<struct node>vc[maxn];
  25. vector<struct node>gg[maxn];
  26. int mx[maxn][ + ];
  27. int dpZeroOne[maxn][ + ];
  28. int dp[ + ];
  29. void work() {
  30. int tot, n;
  31. cin >> tot >> n;
  32. for (int i = ; i <= n; ++i) {
  33. int a, b, which;
  34. cin >> a >> b >> which;
  35. if (which == ) {
  36. vc[i].push_back(node(a, a * b));
  37. } else {
  38. vc[which].push_back(node(a + vc[which][].w, a * b + vc[which][].val));
  39. }
  40. }
  41. for (int i = ; i <= n; ++i) {
  42. if (vc[i].size() == ) continue;
  43. for (int j = ; j < vc[i].size(); ++j) {
  44. for (int h = tot; h >= vc[i][j].w; --h) {
  45. if (dpZeroOne[i][h] == ) {
  46. dpZeroOne[i][h] = max(dpZeroOne[i][h], dpZeroOne[i][h - vc[i][j].w] + vc[i][j].val);
  47. } else {
  48. dpZeroOne[i][h] = max(dpZeroOne[i][h], dpZeroOne[i][h - vc[i][j].w + vc[i][].w] + vc[i][j].val - vc[i][].val); //减多了一次
  49. }
  50. }
  51. }
  52. for (int j = ; j <= tot; ++j) {
  53. if (dpZeroOne[i][j] == ) continue;
  54. if (mx[i][j] >= dpZeroOne[i][j]) {
  55. mx[i][j + ] = mx[i][j];
  56. continue;
  57. }
  58. mx[i][j] = dpZeroOne[i][j];
  59. mx[i][j + ] = mx[i][j];
  60. gg[i].push_back(node(j, mx[i][j]));
  61. }
  62. }
  63. for (int i = ; i <= n; ++i) {
  64. if (gg[i].size() == ) continue;
  65. // cout << i << "£º" << endl;
  66. // for (int j = 0; j < gg[i].size(); ++j) {
  67. // cout << gg[i][j].w << " " << gg[i][j].val << endl;
  68. // }
  69. for (int v = tot; v >= ; --v) {
  70. for (int j = ; j < gg[i].size(); ++j) {
  71. if (v >= gg[i][j].w) {
  72. dp[v] = max(dp[v], dp[v - gg[i][j].w] + gg[i][j].val);
  73. }
  74. }
  75. }
  76. }
  77. cout << dp[tot] << endl;
  78. }
  79.  
  80. int main() {
  81. #ifdef local
  82. freopen("data.txt", "r", stdin);
  83. // freopen("data.txt", "w", stdout);
  84. #endif
  85. IOS;
  86. work();
  87. return ;
  88. }

http://acm.hdu.edu.cn/showproblem.php?pid=3449

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <assert.h>
  7. #define IOS ios::sync_with_stdio(false)
  8. using namespace std;
  9. #define inf (0x3f3f3f3f)
  10. typedef long long int LL;
  11.  
  12. #include <iostream>
  13. #include <sstream>
  14. #include <vector>
  15. #include <set>
  16. #include <map>
  17. #include <queue>
  18. #include <string>
  19. const int maxn = + ;
  20. int n, tot;
  21. struct node {
  22. int w, val;
  23. node(int ww, int vval) : w(ww), val(vval) {}
  24. };
  25. int each[maxn];
  26. vector<struct node>vc[maxn];
  27. vector<struct node>gg[maxn];
  28. int dp[ + ];
  29. int mx[ + ];
  30. void work() {
  31. for (int i = ; i <= n; ++i) {
  32. vc[i].clear();
  33. gg[i].clear();
  34. int x, num;
  35. scanf("%d%d", &x, &num);
  36. each[i] = x;
  37. for (int j = ; j <= num; ++j) {
  38. int w, val;
  39. scanf("%d%d", &w, &val);
  40. vc[i].push_back(node(w + x, val));
  41. }
  42. }
  43. for (int i = ; i <= n; ++i) {
  44. memset(dp, , sizeof dp);
  45. for (int j = ; j < vc[i].size(); ++j) {
  46. for (int h = tot; h >= vc[i][j].w; --h) {
  47. if (dp[h]) {
  48. dp[h] = max(dp[h], dp[h - vc[i][j].w + each[i]] + vc[i][j].val);
  49. } else dp[h] = max(dp[h], dp[h - vc[i][j].w] + vc[i][j].val);
  50. }
  51. }
  52. memset(mx, , sizeof mx);
  53. for (int j = ; j <= tot; ++j) {
  54. if (dp[j] == ) {
  55. mx[j + ] = mx[j];
  56. continue;
  57. }
  58. if (mx[j] >= dp[j]) {
  59. mx[j + ] = mx[j];
  60. continue;
  61. }
  62. mx[j + ] = dp[j];
  63. gg[i].push_back(node(j, dp[j]));
  64. }
  65. // for (int j = 0; j < gg[i].size(); ++j) {
  66. // cout << gg[i][j].w << " " << gg[i][j].val << endl;
  67. // }
  68. }
  69. memset(dp, , sizeof dp);
  70. for (int i = ; i <= n; ++i) {
  71. if (gg[i].size() == ) continue;
  72. for (int v = tot; v >= ; --v) {
  73. for (int j = ; j < gg[i].size(); ++j) {
  74. if (v >= gg[i][j].w) {
  75. dp[v] = max(dp[v], dp[v - gg[i][j].w] + gg[i][j].val);
  76. }
  77. }
  78. }
  79. }
  80. cout << dp[tot] << endl;
  81. }
  82.  
  83. int main() {
  84. #ifdef local
  85. freopen("data.txt", "r", stdin);
  86. // freopen("data.txt", "w", stdout);
  87. #endif
  88. while (scanf("%d%d", &n, &tot) > ) work();
  89. return ;
  90. }

背包九讲 && 题目的更多相关文章

  1. 【DP_背包专题】 背包九讲

    这段时间看了<背包九讲>,在HUST VJUDGE上找到了一个题单,挑选了其中16道题集中做了下,选题全部是HDU上的题,大多是简单题.目前做了点小总结,大概提了下每道题的思路重点部分,希 ...

  2. My背包九讲——概述

    文章目录 什么是背包问题 背包问题的分类 [第一讲 01背包问题](https://blog.csdn.net/qq_34261446/article/details/103705068) 第二讲 完 ...

  3. 背包九讲PDF

    本资料仅限个人学习交流使用,不得用于商业用途. 背包九讲PDF:https://pan.baidu.com/s/17rTxMwCo9iSTOW77yucdXQ   提取码:xbqa

  4. dd大牛的《背包九讲》

    P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最 ...

  5. 直接抱过来dd大牛的《背包九讲》来做笔记

    P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最 ...

  6. 摘自 dd大牛的《背包九讲》

    P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最 ...

  7. 背包九讲(Orz)

    P01: 01背包问题 题目 有\(N\)件物品和一个容量为\(V\)的背包.第\(i\)件物品的费用是\(c[i]\),价值是\(w[i]\).求解将哪些物品装入背包可使这些物品的费用总和不超过背包 ...

  8. dd 在度娘上看到的一个大牛的《背包九讲》 (:

    P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最 ...

  9. [转]dd大牛的《背包九讲》

    P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最 ...

随机推荐

  1. 黑马程序员_ Objective-c 之Foundation笔记(一)

    结构体  NSRange: 用来表示范围 创建 NSRange r1 = {2, 4} NSRange r2 = {.location = 2, .length = 4} NSRange r3 = N ...

  2. .net 生成 静态页面

    .net 生成 静态页面 <!--Main.Aspx--> <%@ page language="C#" %> <%@ import namespac ...

  3. 在 Typescript 2.0 中使用 @types 类型定义

    在 Typescript 2.0 中使用 @type 类型定义 基于 Typescript 开发的时候,很麻烦的一个问题就是类型定义.导致在编译的时候,经常会看到一连串的找不到类型的提示.解决的方式经 ...

  4. LoadRunner测试结果分析01 转载至zhangzhe的新浪博客

    LoadRunner测试结果分析之我见 LoadRunner生成测试结果并不代表着这次测试结果的结束,相反,这次测试结果的重头戏才刚刚开始.如何对测试结果进行分析,关系着这次测试的成功与否.网上关于L ...

  5. 1、win32创建窗口函数(windows程序内部运行机制)

    利用win32创建窗口函数,主要操作步骤为: 1.设计一个窗口类 2.注册窗口类 3.创建窗口 4.显示及窗口更新 5.消息循环 6.窗口过程函数   (1)设计一个窗口类 设计窗口类,这样的类型已经 ...

  6. How secure FB Messenger is?

    It's reported that FB Messenge is the most secure App for instant messaging service. Let's see if FB ...

  7. python常用库

    本文由 伯乐在线 - 艾凌风 翻译,Namco 校稿.未经许可,禁止转载!英文出处:vinta.欢迎加入翻译组. Awesome Python ,这又是一个 Awesome XXX 系列的资源整理,由 ...

  8. JPA merge(obj) 方法

    JPA中的merge类似Hibernate中的saveOrUpdate方法,当数据库中存在id=2的Person,在em.close()时会发送一条update语句,而当数据库中不存在id=2的Per ...

  9. 《SSM框架搭建》三.整合spring web

    感谢学习http://blog.csdn.net/zhshulin/article/details/37956105#,还是修改了spring到最新的版本和接口开发示例 根据前一篇日志,已经有了myb ...

  10. DNS服务器原理

    19.1 什么是DNS   主机名自动解析为 IP 就很重要!那就是 DNS.   19.1.1 用网络主机名取得IP的历史渊源   单一档案处理上网的年代: /etc/hosts   利用某些特定的 ...