首页 > 其他分享 >背包问题

背包问题

时间:2023-04-13 21:46:21浏览次数:33  
标签:状态 背包 回溯到 问题 物品 放入 dp

01背包问题

题目

有一个背包的容积为\(m\),有\(n\)个物品,每个物品的体积为\(w\),权重为\(v\),每个物品只能取\(1\)次放入背包中,背包所有物品权重和最大是多少?

求值

创建一个状态矩阵\(dp\),横坐标\(i\)表示物品编号,纵坐标\(j\)表示背包容量。\(dp[i][j]\)表示在已用背包容量为\(j\)的情况下,考虑第\(i\)件物品装不装时,背包的最大总价值,可知状态矩阵\(dp\)0行和第0列都为0
对于第\(i\)个物品,在已用背包容量为\(j\)时来说,物品只有放入背包和不放入背包两种情况,两者取其中的最大值作为\(dp[i][j]\)的值。若放入背包,当前最大权重等于考虑上一个物品放不放入背包时,当前背包已用容量减去这个物品所占的体积之后的背包容量的最大权重加上当前物品的权重;若不放入背包,当前最大权重等于考虑上一个物品放不放入背包时,当前已用背包容量的最大权重;取两者最大值作为\(dp[i][j]\)的值,即\(dp[i][j] = max(dp[i - 1][j - w[i] + v[i], dp[i - 1][j]])\),这称为01背包的状态迁移方程,也是解决本文所有背包问题的关键。将状态矩阵\(dp\)填充完毕之后,\(dp[n][m]\)就是背包的最大权重。算法实现如下:

for (int i = 1; i <= n; i++){
  for (int j = 1; j <= m; j++){
    if (j >= w[i]){
      dp[i][j] = mnax(dp[i - 1][j - w[i]] + v[i]);
    }
    else {
      dp[i][j] = dp[i - 1][j];
    }
  }
}

回溯

如何求出到底是哪些物品装入,哪些物品没装入呢?设\(m=8,n=4,w={2,3,4,5},v={3,4,5,8}\),可画出其状态矩阵\(dp\)

i\j 0 1 2 3 4 5 6 7 8
0 0 0 0 0 0 0 0 0 0
1 0 0 3 3 3 3 3 3 3
2 0 0 3 4 4 7 7 7 7
3 0 0 3 4 5 7 8 9 9
4 0 0 3 4 5 8 8 11 12

\(dp[4][8]\)开始回溯,根据状态迁移方程\(dp[i][j] = max(dp[i - 1][j - w[i] + v[i], dp[i - 1][j]])\)可知,若\(dp[i][j]\not=dp[i-1][j]\),则第\(i\)件物品一定被放入背包,若\(dp[i][j]=dp[i-1][j]\),则至少有一种情况物品\(i\)没被放入背包。由于\(dp[4][8]\not=dp[3][8]\),所以第\(4\)件物品被放入背包,现在问题就变成了已知背包容量为\(8-5=3\),物品数量为\(4-1=3\)最大权重为\(12-8=4\),求哪些物品被装入背包。根据状态迁移方程,状态回溯到\(dp[i-1][j-w[i]]\),即\(dp[3][3]\),而$dp[3][3]=dp[2][3],所以人为第\(3\)件物品没有放进背包;状态回溯到\(dp[2][3]\)\(dp[2][3]\not=dp[1][3]\),所以第\(2\)件物品被放入背包;状态回溯到\(dp[1][0]\),此时已不必再回溯,因为剩余的背包容量为\(0\)。得出答案,物品\(4,2\)被放入背包。算法实现如下:

int i = n, j = m;
int lis[n] = {0};
while (j != 0) {
  if (dp[i][j] == dp[i-1][j]) {
    i = i - 1;
  } 
  else {
  lis[i]++;
  j = j - w[i];
  i = i - 1;
  }
}

其中,数组\(lis\)表示物品装填情况,1为放入,0为不放入。

完全背包问题

题目

有一个背包的容积为\(m\),有\(n\)个物品,每个物品的体积为\(w\),权重为\(v\),每个物品可以取无限次放入背包中,背包所有物品权重和最大是多少?

求解

完全背包问题和01背包问题唯一的区别就是后者的每一种物品只能取1次,而前者的每一种物品可以取无限次。01背包状态迁移方程为\(dp[i][j] = max(dp[i - 1][j - w[i] + v[i], dp[i - 1][j]])\),若设\(k\)为一种物品被放入背包中的次数,则可以得到完全背包问题的状态迁移方程\(dp[i][j] = max(dp[i - 1][j - k * w[i] + k * v[i], dp[i - 1][j]])\),当\(k\)可以为0时,状态迁移方程可以简化为\(dp[i][j] = max(dp[i - 1][j - k * w[i] + k * v[i])(k=0,1,2,3,4···)\)。算法实现如下:

for (int i = 1; i <= n; i++) {
  for (int j = 1; j <= m; j++) {
    if (j >= w[i]) {
      for (int k = 0; k * v[i] <= j; k++) {
        dp[i][j] = max(dp[i - 1][j - k * w] + k * w[i], dp[i][j])
      }
    }
    else {
      dp[i][j] = dp[i - 1][j];
    }
  }
}

回溯

沿用上次的例子懒得想新数据,设\(m=8,n=4,w={2,3,4,5},v={3,4,5,8}\),可画出其状态矩阵\(dp\)

i\j 0 1 2 3 4 5 6 7 8
0 0 0 0 0 0 0 0 0 0
1 0 0 3 3 6 6 9 9 12
2 0 0 3 4 6 7 9 10 12
3 0 0 3 4 6 7 9 10 12
4 0 0 3 4 6 8 8 11 12

\(dp[4][8]\)开始回溯,根据状态迁移方程\(dp[i][j] = max(dp[i - 1][j - k * w[i] + k * v[i])(k=0,1,2,3,4···)\)可知,若\(dp[i][j]\not=dp[i-1][i]\),则第\(i\)件物品一定被放入背包,若\(dp[i][j]=dp[i-1][j]\),则至少有一种情况物品\(i\)没被放入背包,即\(k=0\)。由于\(dp[4][8]\)=dp[3][8],所以认为第\(4\)件物品没被放入背包;状态回溯到\(dp[3][8]\),由于\(dp[3][8]\)=dp[2][8],所以认为第\(3\)件物品没被放入背包;状态回溯到\(dp[2][8]\),由于\(dp[2][8]\)=dp[1][8],所以认为第\(2\)件物品没被放入背包;状态回溯到\(dp[1][8]\),由于\(dp[1][8]\notdp[0][8]\),所以物品1至少有1件被放入背包;状态回溯到\(dp[1][6]\),由于由于\(dp[1][6]\notdp[0][6]\),所以物品1又有1件被放入背包;状态回溯到\(dp[1][4]\),由于\(dp[1][4]\notdp[0][4]\),所以物品1又有1件被放入背包;状态回溯到\(dp[1][2]\),由于\(dp[1][2]\notdp[0][2]\),所以物品1又有1件被放入背包;状态回溯到\(dp[1][0]\),此时停止回溯,因为剩余的背包容量为0。得出答案,物品1被放入4次。算法实现如下:

int i = n, j = m;
int lis[n] = {0};
while (j != 0) {
  if (dp[i][j] == dp[i - 1][j]) {
    i = i - 1;
  }
  else {
    lis[i]++;
    j = j - w[i];
  }
}

其中数组\(lis\)中的数字表示物品被装入的次数。

标签:状态,背包,回溯到,问题,物品,放入,dp
From: https://www.cnblogs.com/mirrorflower/p/17307621.html

相关文章

  • ZOJ 3348 Schedule(map运用+网络流之最大流)(竞赛问题升级版)
    题目地址:ZOJ3348仍然是一道竞赛问题的网络流问题,但是这道题再用上次的竞赛建图方法就不行了,5000场比赛,明显会超时,于是需要换种建图思路了。上一道经典竞赛问题戳这里上一道的胜负转换是利用专门给比赛建一个点,通过对比赛双方的流向来控制胜负关系,这里的建图方法更加巧妙(膜拜想出这......
  • js的一些小问题集合
    1.等于号的应用functionreverse(){varcheckbox=document.getElementsByName("hobby");for(leti=0;i<checkbox.length;i++){if(checkbox[i].checked==true){//注意一个问题,在if中用双等于来作为正确的判断单等于号为赋值checkbox[i].checked=false;}elsecheck......
  • HDU 1864最大报销额(一维背包)
    题目地址:HDU1864刚上来看着挺麻烦的。。仔细看了看原来好简单好简单。。。只要去掉一些不符合要求的发票,剩下的就是最简单的背包问题了。。对于小数问题,只要*100就变成整数了。代码如下:#include<algorithm>#include<iostream>#include<cstring>#include<cstdlib>#include......
  • 打鱼还是晒网问题
    一、问题描述一个渔夫从1990年1月1日起开始“三天打鱼两天晒网”,问这人在以后的某一天是打鱼还是晒网二、设计思路:1:要求出总天数;2:考虑到闰年和平年的二月天数不同;3:打鱼还是晒网主要是找一个周期,明显为5,对5求余找余数;  三、程序流程图 四、代码实现#include<stdi......
  • 【element-ui】解决textarea show-word-limit挡住文字问题
    问题:“67/500”默认背景为白色已超出文本输入框,遮住部分上border,当文字到达右侧时会遮住部分文字,且无法点击该部分解决方案:背景透明色,文字放到右下角 html:<el-inputtype="textarea"autosize maxlength="500"show-word-limit v-model="form.keyIndustry"placeh......
  • 基于交替方向乘子法与纳什谈判的社区微网电能共享模型 代码主要做的是一个社区微网内
    基于交替方向乘子法与纳什谈判的社区微网电能共享模型主要内容:代码主要做的是一个社区微网内部产消者之间P2P电能交易与共享的问题。构建了基于合作博弈多产消者电能共享模型,在社区微网储能装置的约束下进行P2P电能交易,以社会福利最大化为目标函数,构建了P2P交易模型,并通过ADMM法......
  • 基于列约束生成法的两阶段鲁棒问题求解
    基于列约束生成法的两阶段鲁棒问题求解摘要:代码和资料主要是两阶段问题以及基于CCG算法的两阶段鲁棒问题求解,代码内容包括CCG算法的MATLAB编程以及python编程版本ID:4650692259018953......
  • 电脑问题记录
    网络适配器出现黄色叹号图中的位置出现黄色三角形叹号,且网络连不上,也不能连接wifi修复方法:修复注册表https://blog.csdn.net/KKKKKilop/article/details/115474877......
  • 第二天第四个问题
    问题描述:编写一个使用嵌套循环的程序,要求用户输入一个值,指出要显示多少行。然后程序将显示相应行数的星号,其中第一行包括一个星号,第二行包括两个星号,依次类推。每一行包含的字符数等于用户指定的行数,在星号不够的情况下,在星号前面加上句点。运行情况如下:enternumberofrows:5.......
  • 第二天第三个问题
    问题描述:编写上一个问题,但使用string对象而不是字符数组解决思路:于上个问题相同代码:#include<iostream>#include<cstring>#include<string>usingnamespacestd;intmain(){ stringa; stringb="done"; intans=0; cout<<"enterwords:"; cout<&......