当前位置:   article > 正文

数据结构算法系列----背包问题(01,完全,多重)_01背包

01背包

一、01背包

1、01背包介绍

        "01背包"是一个经典的动态规划问题。在01背包中,给定一个背包容量和一组物品,每个物品都有自己的重量和价值。问题的目标是选择一些物品放入背包中,使得放入的物品总重量不超过背包容量,同时使得放入的物品总价值最大。

        "01"表示每种物品最多 只能选择一次,即要么放入背包中,要么不放入。这是与其他背包问题(如完全背包和多重背包)的区别之一。 

2、01背包核心代码

   我们设背包容量为w,每个物品的质量和价值分别为wi,vi 。 我们把这个容量为w的背包分成w份存储在dp数组中,分别是1,2,3......w,代表这当背包容量为1,2,3....w时这个背包最大最多能装多少价值的东西: 

  1. for(int i=0;i<n;i++){ //遍历每一个物品
  2. for(int j=w;j>=ww[i];j--){ // 从最大容量向下遍历
  3. dp[j]=max(dp[j],dp[j-ww[i]]+v[i]; //状态转移方程(得到每个容量的最大价值)
  4. }
  5. }

二、完全背包

1、完全背包解释

   完全背包问题是另一种常见的背包问题,与01背包问题不同的是,在完全背包问题中,每种物品可以选择 无限次 放入背包中。也就是说,对于每种物品,可以选择放入0个、1个、2个,直至放入尽可能多个,只要总重量不超过背包的容量。完全背包问题与01背包问题的主要区别在于状态转移方程的变化。

2、完全背包核心代码

  1. for(int i=0;i<n;i++){
  2. for(int j=ww[i];j<=w;j++){
  3. dp[j]=max(dp[j,dp[j-ww[i]]]+v[i]);
  4. }
  5. }
  • 外层循环for(int i=0;i<n;i++)遍历每个物品i
  • 内层循环for(int j=ww[i];j<=w;j++)从背包容量ww[i]开始,逐步遍历背包容量w,更新背包容量为j时的最大价值。
  • 在内层循环中,通过状态转移方程dp[j]=max(dp[j], dp[j-ww[i]]+v[i])来更新背包容量为j时的最大价值。
  • 其中,dp[j]表示当前背包容量为j时的最大价值,dp[j-ww[i]]+v[i]表示将第i个物品放入背包后的总价值,取两者的最大值作为更新后的最大价值。

     通过动态规划的方式填充dp数组,最终得到dp[w]即为问题的解,表示背包容量为w时放入所有物品所能获得的最大价值。

3、完全背包进阶(背包放满的最大价值)

1、核心代码:

  1. memset(dp, -0X3f, sizeof(dp));
  2. dp[0] = 0;
  3. for (int i = 1; i <= n; i++)
  4. for (int j = v[i]; j <= V; j++)
  5. dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
  6. if(dp[V]<0) dp[V]=0;
  • memset(dp, -0X3f, sizeof(dp));:将dp数组中的所有元素初始化为一个较大的负值(这里使用-0X3f),表示初始时背包中的最大价值为负无穷。
  • dp[0] = 0;:将背包容量为0时的最大价值初始化为0。
  • for (int i = 1; i <= n; i++):遍历每个物品i
  • for (int j = v[i]; j <= V; j++):从当前物品i的价值v[i]开始,逐步遍历背包容量V
  • dp[j] = max(dp[j], dp[j - v[i]] + w[i]);:更新背包容量为j时的最大价值,取当前最大价值dp[j]与放入第i个物品后的总价值dp[j - v[i]] + w[i]的较大值。
  • if(dp[V]<0) dp[V]=0;:如果背包容量为V时的最大价值为负值,则将其设为0,表示背包中没有物品时的最大价值为0。

  最终,通过动态规划的方式填充dp数组,得到dp[V]即为问题的解,表示背包容量为V时放入所有物品所能获得的最大价值。

2、例题

例题链接:https://ac.nowcoder.com/acm/problem/226516

完整ac代码:

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int N = 1010;
  5. const int INF = 0x3f3f3f3f;
  6. int n, V, v[N], w[N], dp[N];
  7. int main()
  8. {
  9. cin >> n >> V;
  10. for (int i = 1; i <= n; i++)
  11. {
  12. cin >> v[i] >> w[i];
  13. }
  14. memset(dp, 0, sizeof(dp));
  15. for (int i = 1; i <= n; i++)
  16. for (int j = v[i]; j <= V; j++)
  17. dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
  18. cout << dp[V] << endl;
  19. memset(dp, -0X3f, sizeof(dp));
  20. dp[0] = 0;
  21. for (int i = 1; i <= n; i++)
  22. for (int j = v[i]; j <= V; j++)
  23. dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
  24. if(dp[V]<0) dp[V]=0;
  25. cout << dp[V] << endl;
  26. return 0;
  27. }

      这里有个要注意在使用memset函数初始化数组时,传入的负数值不能太小。因为memset函数将传入的值按字节进行复制,如果传入的是太小的负数,可能会导致在某些编译器中出现意外的行为。

  比如在第二个memset之中不用0x3f用 -1000就会错乱。


三、多重背包

1、多重背包介绍

      多重背包问题是背包问题的一个变种,与0-1背包和完全背包不同,多重背包问题中每种物品的数量是有限的,即可以选择多个物品,但每种物品的数量是有限的。


2、核心代码

  1. for(int i=0;i<n;i++){
  2. int k=1;
  3. int x,w,v;
  4. cin>>x>>w>>v;
  5. while(k<=x){ //根据每种物品的数量进行了二进制拆分,有效地处理了每种物品的数量限制。
  6. cnt++;
  7. ww[cnt]=w*k;
  8. vv[cnt]=v*k;
  9. x-=k;
  10. k<<=1;
  11. }
  12. if(x){
  13. cnt++;
  14. ww[cnt]=w*x;
  15. vv[cnt]=v*x;
  16. }
  17. } //拆分之后用01 背包即可
  18. for(int i=1;i<=cnt;i++){
  19. for(int j=t;j>=ww[i];j--){
  20. dp[j]=max(dp[j],dp[j-ww[i]]+vv[i]);
  21. }
  22. }

      对这段这个二进制拆分(可以把这个和我之间写的快速幂一起理解,有点类似)有疑惑的同学可以参考B站:https://www.bilibili.com/video/BV1MA41177cg/?spm_id_from=333.1007.top_right_bar_window_history.content.click

3、例题

   链接:https://ac.nowcoder.com/acm/problem/235950

完整代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=100006;
  4. int main(){
  5. int n,t;
  6. int ww[N]={0},vv[N]{0},dp[N]={0};
  7. int cnt=0;
  8. cin>>n>>t;
  9. for(int i=0;i<n;i++){
  10. int k=1;
  11. int x,w,v;
  12. cin>>x>>w>>v;
  13. while(k<=x){
  14. cnt++;
  15. ww[cnt]=w*k;
  16. vv[cnt]=v*k;
  17. x-=k;
  18. k<<=1;
  19. }
  20. if(x){
  21. cnt++;
  22. ww[cnt]=w*x;
  23. vv[cnt]=v*x;
  24. }
  25. }
  26. for(int i=1;i<=cnt;i++){
  27. for(int j=t;j>=ww[i];j--){
  28. dp[j]=max(dp[j],dp[j-ww[i]]+vv[i]);
  29. }
  30. }
  31. cout<<dp[t]<<endl;
  32. return 0;
  33. }
  34. for(int i=0;i<n;i++){
  35. for(int j=ww[i];j<=w;j++){
  36. dp[j]=max(dp[j,dp[j-ww[i]]]+v[i]);
  37. }
  38. }

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

闽ICP备14008679号