当前位置:   article > 正文

【动态规划】01背包问题+查找背包物品_游戏设计中背包中一万个物品怎么快速查找到需要的物品

游戏设计中背包中一万个物品怎么快速查找到需要的物品

目录

一、0-1背包问题

二、问题分析

1、确定备忘录的具体含义

2、状态转移方程

3、初始化

4、遍历顺序及输出

5、回溯法求解最大价值时的背包物品 

三、总结 

四、完整代码

一、0-1背包问题

给定gif.latex?n种物品(每种物品均只有一个)和一背包。物品i的重量是gif.latex?Wi,其价值为gif.latex?Vi,背包的最大容量为gif.latex?c。怎样选择装入背包中的物品,使得其总价值最大?

例如:现有4种物品,其对应的重量和价值如图所示,另有一最大容量为5的背包,求该背包所能装下物品的最大价值?

物品 重量价值
024
113
246
335

二、问题分析

1、确定备忘录的具体含义

  1. dp[i][j]:任取第0~i件物品,放入容量为j的背包,能得到的最大价值
  2. 例:dp[1][2]=3的含义:
  3. 任取物品0~物品1,放入容量为2的背包,能得到的最大价值(取物品1放入背包,其价值最大,为3

dp[i][j] 的定义十分重要,状态方程的书写、数组初始化、遍历顺序和输出结果都必须严格按照该定义进行书写

2、状态转移方程

对于物品gif.latex?i,它只有放与不放两种状态,当它能放入背包时,状态转移方程只需取两种状态的最大值; 否则,取不放入时的最大价值。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAd3VzaGVuZ19oYXBweQ==,size_20,color_FFFFFF,t_70,g_se,x_16

  1. if (j < w[i])
  2. dp[i][j] = dp[i - 1][j];
  3. else
  4. dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);

 3、初始化

从状态转移方程可知,欲求dp[i][j],需先确定dp[i-1][j]和dp[i-1][j-w[i]]的值(即需保证dp[i][j]的左上角数据已经处理完成),故我们需对第0行和第0列进行初始化.

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAd3VzaGVuZ19oYXBweQ==,size_20,color_FFFFFF,t_70,g_se,x_16

  1. //初始化第0列(即背包容量为0)
  2. for (i = 0; i < n; i++)
  3. dp[i][0] = 0;
  1. //初始化第0行(即只有物品0)
  2. for (j = 1; j <= max_weight; j++) {
  3. if (w[0] <= j)
  4. dp[0][j] = v[0];
  5. else
  6. dp[0][j] = 0;
  7. }

4、遍历顺序及输出

遍历顺序:求解dp[i][j]只需提前知道其左上角的数据,故以下两种遍历方式均可。

1、先遍历背包再遍历物品(即物品数固定,背包容量逐增至最大,物品再加1)

 2、先遍历物品再遍历背包(即背包容量固定,物品数逐增至最大,背包容量再加1)

输出:dp[n-1][max_weight]: n件物品任取,放入容量为max_weight的背包,所得的最大价值。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAd3VzaGVuZ19oYXBweQ==,size_20,color_FFFFFF,t_70,g_se,x_16

  1. //先遍历物品再遍历背包
  2. for (i = 1; i < n; i++) { //遍历物品
  3. for (j = 1; j <= max_weight; j++) { //遍历背包
  4. if (j < w[i])
  5. dp[i][j] = dp[i - 1][j];
  6. else
  7. dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
  8. }
  9. }
  10. //先遍历背包再遍历物品
  11. for (j = 1; j <= max_weight; j++) { //遍历背包
  12. for (i = 1; i < n; i++) { //遍历物品
  13. if (j < w[i])
  14. dp[i][j] = dp[i - 1][j];
  15. else
  16. dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
  17. }
  18. }

5、求解最大价值时的背包物品 

由状态转移方程(dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]))可知,当dp[i][j]dp[i-1][j]时,物品i被放入背包中(判断条件),当dp[i][j]=0时,背包中已经没有物品(结束条件)。

  1. //回溯法求解装入物品
  2. i--; j--; //循环结束后,i=n,j=max_weight+1,故使用dp[n - 1][max_weight]须自减1
  3. cout << "最大价值时的背包物品为:" << endl;
  4. while (dp[i][j]&&i>=0) {
  5. if (dp[i][j] != dp[i - 1][j]) {
  6. cout << "物品" << i << endl;
  7. j -= w[i]; //装入物品i后背包的最大容量
  8. }
  9. i--; //物品i已经处理完成,接下来讨论物品i-1
  10. }

1、 使用dp[i][j](最大价值)前一定要对i,j进行处理,否则,运行时会抛出异常;

2、结束条件一定要加上i>=0,否则,运行结果可能会出现物品-1等错误。

3、该法得到的结果只为其中的一组最优解,可能还存在其他最优解,例如:当我们装入物品0(2,4)和物品3(3,5)时,其最大价值也为9.

三、总结 

 1、0-1背包问题是背包问题的基础。深入了解0-1背包问题(每件物品只有一个)是掌握完全背包问题(每件物品有无数个)、多重背包问题(不同物品数量不同)和分组背包(物品分为多组,每组最多选一个)的关键;

2、使用dp[i][j]数组时,一定要注意数组的界限问题,避免越界访问造成错误。比如:刚开始的时候,我认为背包不存在容量为0的情况,故没有将第一列初始化为0,导致输出出错,将dp[i][j]全部打印出来后,才知道是dp[1][1]出错了,因为dp[1][1]=max(dp[0][1],dp[0][0]+v[0]),需要用到dp[0][0],造成越界。所以,检验算法的正确性,可以先人工求解出dp[i][j]数组,再用程序将其打印出来,这样有利于我们快速找到问题所在。

1e96f5966f5f45099dcf8a9880f4b971.png

                                                                  转换表(打印)

转换表(人工)
 012345
0004444
1034777
2034779
3034789

3、背包问题一定要先明确辅助数组的具体含义再确定转换方程,根据转换方程来确定初始化、遍历顺序和输出结果。

四、完整代码

  1. #include<iostream>
  2. #include<algorithm>
  3. using namespace std;
  4. const int max_weight = 5;
  5. const int n = 4;
  6. int main() {
  7. int i, j;
  8. int w[n] = { 2,1,4,3 }; //重量
  9. int v[n] = { 4,3,6,5 }; //价值
  10. int dp[n][max_weight+1];//辅助数组
  11. //初始化第0列(即背包容量为0)
  12. for (i = 0; i < n; i++)
  13. dp[i][0] = 0;
  14. //初始化第0行(即只有物品0)
  15. for (j = 1; j <= max_weight; j++) {
  16. if (w[0] <= j)
  17. dp[0][j] = v[0];
  18. else
  19. dp[0][j] = 0;
  20. }
  21. //状态转移
  22. //先遍历物品再遍历背包
  23. for (i = 1; i < n; i++) { //遍历物品
  24. for (j = 1; j <= max_weight; j++) { //遍历背包
  25. if (j < w[i])
  26. dp[i][j] = dp[i - 1][j];
  27. else
  28. dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
  29. }
  30. }
  31. //输出
  32. cout << dp[n - 1][max_weight] << endl;
  33. //回溯法求解装入物品
  34. i--; j--; //循环结束后,i=n,j=max_weight+1,故使用dp[n - 1][max_weight]须自减1
  35. cout << "最大价值时的背包物品为:" << endl;
  36. while (dp[i][j]&&i>=0) {
  37. if (dp[i][j] != dp[i - 1][j]) {
  38. cout << "物品" << i << endl;
  39. j -= w[i]; //装入物品i后背包的最大容量
  40. }
  41. i--; //物品i已经处理完成,接下来讨论物品i-1
  42. }
  43. return 0;
  44. }

运行结果: 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/332970
推荐阅读
相关标签
  

闽ICP备14008679号