01背包的变形问题----背包恰好装满

在看本文之前建议先看一下我之前发过的01背包详解。
https://blog.csdn.net/Iseno_V/article/details/100001133

1. 问题的变形

在前面讲到的01背包问题中,现在我们把条件改为 “求当背包恰好装满时候取得的最大价值”。这样的问题其实本质上和原始的01背包问题区别不大,我们只需要做出一点小小的调整。
需要指出的是该问题其实可分为两个问题。
(1)背包能否恰好装满?
(2)如果能恰好装满,恰好装满时候的最大价值是多少?

2. 恰好装满时的处理方法

为了便于理解,这里给出一个用数组实现的01背包代码(空间复杂度未优化),和之前用vector容器实现的原理是一样的,

//01背包--不要求正好装满
#include <iostream>
using namespace std;
#define max(N1,N2) N1>N2?N1:N2
int main()
{int N, V;while (cin >> V >> N)//这里要求V<1000,N<100,否则后面的数组装不下{int v[100], w[100];//体积和价值int f[1000] = { 0 };//状态转移矩阵,这里将f数组全部初始化为0for (int i = 1; i <= N; i++){cin >> v[i] >> w[i];//输入原始数据}for (int i = 1; i <= n; i++){for (int j = V; j >= v[i]; j--){f[j] = max(f[j], f[j - v[i]] + w[i]);}}cout << f[V] << endl;//输出结果}return 0;
}

如果看不懂上面的代码一定要去本文开头给出的链接去看一看01背包的基本原理。

2.1 有效状态和无效状态

在普通的01背包中,由于不要求恰好装满,我们采用的初始化方式为

int f[1000] = { 0 };//状态转移矩阵,这里将f数组全部初始化为0

这个初始化的方法意思是:对任意的一个状态,把当前的价值初始化为0,代表背包为空时所包含的物体价值为0 。即认为不管背包当前容量,背包是空的那么价值就是0,,没装满背包的状态都是有效的状态,我们称之为有效状态
当要求恰好装满时,我们就不可以这么做了,因为我们认为背包没恰好装满的话,当前背包的状态无效,即无效状态只有恰好装满时候才是有效状态
我们不妨定义当背包的状态为无效状态时,f[i]的值是负无穷,这样我们就可以从状态转移矩阵中区分出有效状态和无效状态了。
先不关心如何计算f,有了这个定义,对于恰好装满的01背包问题,我们只需要判断f[V]中的值是否是负无穷,就知道背包能否装满了。当f[V]中的值不是负无穷,我们接下来需要设计状态转移方程,使f[V]就是背包恰好装满时候的最大价值。

2.2 01背包的有效状态都是根据以前的有效状态推导出来的

首先观察上面的代码,转移方程为

f[j] = max(f[j], f[j - v[i]] + w[i]);

这段代码时经过时间复杂度优化后的代码,在优化前是

for (int i = 1; i <= n; i++)
{for (int j = V; j >= 0; j--){if (j >= w[i])//如果背包装得下当前的物体{f[i][j] = max(f[i - 1][j], f[i - 1][j - w[i]] + v[i]);}else//如果背包装不下当前物体{f[i][j] = f[i - 1][j];}}
}

通过前面01背包基本原理的讲解,我们知道,这两段代码是等效的。
f[i][j] 的值是由 f[i - 1][j],f[i - 1][j - w[i]] 来推导出来的,也就是 f[i][j] 的取值只与前面的状态有关系。换句话说,所有的状态都是从以前的状态来推导出来的。再换句话说,所有的有效状态是从之前的有效状态和无效状态推导出来的!!!
接下来是重点!!!
接下来是重点!!!
接下来是重点!!!

如果无效状态无法推出有效状态,我们就不必关心无效状态(背包没装满)时候,背包内物体的总价值。

因为背包恰好装满时候我们只关心有效状态,而无效状态又无法推出有效状态。

也就可以把所有无效状态时背包的总价值都设置为负无穷。

我们来证明下无效状态无法推出有效状态
对于下面的状态转移方程

f[i][j] = max(f[i - 1][j], f[i - 1][j - w[i]] + v[i]);

(1)如果f[i - 1][j]和 f[i - 1][j - w[i]] 都是有效状态,那么f[i][j]是由有效状态推出的。

(2)如果f[i - 1][j]是无效状态, f[i - 1][j - w[i]] 是有效状态:
此时f[i][j]背包容量为j,f[i - 1][j - w[i]] 背包容量为j - w[i]且是恰好装满的。从f[i - 1][j - w[i]] 变换到f[i][j]相当于向一个已经装了总体积为j - w[i]物体的背包中放入一个体积为w[i]的物体,新的容量为j与当前背包容量j相同!!!
刚才我们说了无效状态可以看作负无穷,于是 f[i - 1][j - w[i]] + v[i]作为有效状态一定大于f[i - 1][j],所以可以得到新的有效状态f[i][j]。
所以此时f[i][j]是有效状态且是由有效状态f[i - 1][j - w[i]]推出的。

(3)如果f[i - 1][j]是有效状态, f[i - 1][j - w[i]] 是无效状态:
f[i - 1][j - w[i]] 是无效状态说明当前的背包内装的物体体积小于j - w[i],再放入一个体积为w[i]的物体后容量小于j,也就是说f[i - 1][j - w[i]] + v[i]也是无效状态。
此时因为f[i - 1][j]是有效状态,所以一定大于无效状态f[i - 1][j - w[i]] + v[i],则f[i][j]是一个由效状态f[i - 1][j]推导出的有效状态。
所以此时f[i][j]是有效状态且是由有效状态f[i - 1][j]推出的。

综上,只有有效状态可以推出有效状态。

2.3 无效状态的处理方法

由于c++中最小的负整型为16进制数 0x80000000,我们无法做到真正的负无穷。于是为了在计算无效状态时候依然采用同样的状态转移方程,我们需要用点小手段,即

#define INF 0x80000000
for (int j = V; j >= v[i]; j--)
{f[j] = max(f[j], f[j - v[i]] + w[i]);if (f[j] < 0)f[j] = INF;
}

如果算出来的是无效状态,强制赋值为0x80000000。
这样就相当于模拟了负无穷加上一个有限的正整数的计算过程,即负无穷加上任意有限正数还是负无穷。

3. 01背包恰好装满的代码实现

综上,我们发现,恰好装满与不是恰好装满只是在初始化过程上有差别而已。只需要在初始化时候把所有的无效状态初始化为负无穷就可以了。但是,f[0]需要初始化为0,因为背包容量为0时,装的物体体积为0是有效状态!

//01背包--恰好装满
#include <iostream>
using namespace std;
#define max(N1,N2) N1>N2?N1:N2
#define INF 0x80000000
int main()
{int V, N;while (cin >> V >> N)//输入背包容量和物体数{int v[1000], w[1000];int f[10000] ;//下面是初始化所有的无效状态for (int i = 0; i < 10000; i++){f[i] = INF;}//f[0]是有效状态f[0] = 0;//输入每个物体的体积和价值for (int i = 1; i <= N; i++){cin >> v[i] >> w[i];}//动态规划过程for (int i = 1; i <= N; i++){for (int j = V; j >= v[i]; j--){f[j] = max(f[j], f[j - v[i]] + w[i]);if (f[j] < 0)f[j] = INF;}}//判断能否恰好装满背包if (f[V] > 0){cout << f[V] << endl;//背包恰好装满了,输出结果}else{cout << "error" << endl;//背包不能恰好装满}}return 0;
}

01背包的变形问题----背包恰好装满相关推荐

  1. 01背包及其变种(物品无限背包、恰好装满背包)

    一.01背包问题 01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为C1,C2,-,Cn,与之相对应的价值为W1,W2,-,Wn.求解将那些物品装入背包可使总价值最大. 动态规划: ...

  2. 01背包问题、完全背包、多重背包、恰好装满

    01背包问题:最基本的背包问题,每个物品最多只能放一次. 完全背包问题:每种物品可以放无限多次. 多重背包问题:每种物品有一个固定的次数上限. [代码] import java.util.Scanne ...

  3. 01背包,恰好装满(劲歌金曲,uva 12563)

    关于不一定装满与恰好装满: http://blog.sina.com.cn/s/blog_150cffdab0102w0g9.html 参考代码:http://blog.csdn.net/acvay/ ...

  4. 【动态规划】完全背包:存钱罐(恰好装满)

    背包恰好装满问题: 设有n个物品,其重量(或占用空间)分别为w1, W.,...Wn.价值分别为V1,2....n. ← 给定一个总容量为W的背包,每个物品只能整个放入背包或不放.← 问:如何选择放入 ...

  5. Python3使用动态规划处理背包问题:完全背包(含背包恰好装满的情况)

    文章目录 题目介绍 题解1 题解2 题解3 完全背包问题是基于01背包的,如果对01背包问题不熟悉,可以参考: Python3使用动态规划处理01背包问题 题目介绍 原题链接:NC309 完全背包 描 ...

  6. 变种 背包问题_01背包及其变种(物品无限背包、恰好装满背包)

    一.01背包问题 01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为C1,C2,-,Cn,与之相对应的价值为W1,W2,-,Wn.求解将那些物品装入背包可使总价值最大. 动态规划: ...

  7. <动态规划>完全背包(最大价值,恰好装满最大价值)

    完全背包_牛客题霸_牛客网 (nowcoder.com) (10条消息) 01背包 完全背包是否恰好装满问题_Simple的博客-CSDN博客 public ArrayList<Integer& ...

  8. 【动态规划笔记】动态规划初始化细节问题:恰好装满背包

    求最优解的背包问题中,有的题目要求[恰好装满背包],有的题目并[没有要求]必须把背包装满 实现方法: 初始化有所不同 设:F[i]表示容量为i的背包能够装的最大价值 恰好装满背包: 初始化:    F ...

  9. //假设有一个能装入总体积为T的背包和n件体积分别为w1,w2....wn.的物品,能否从n件物品中挑选若干件恰好装满背包,即使w1+w2+....+wn=T,要求找出所有满足上述条件的解。例如:当T

    //背包问题 //假设有一个能装入总体积为T的背包和n件体积分别为w1,w2....wn.的物品,能否从n件物品中挑选若干件恰好装满背包,即使w1+w2+....+wn=T,要求找出所有满足上述条件的 ...

最新文章

  1. 增加了一行代码,让我们提高了3000%的性能
  2. wordpress漏洞_聊聊 WordPress 5.1.1 CSRF to RCE 漏洞
  3. Python中的sort() key含义
  4. [学习笔记]数字电路技术
  5. 服务器任务管理器详细信息,任务管理器服务器主机
  6. ubuntu12.04平台下a80编译环境搭建
  7. 中断处理程序与中断服务例程
  8. 从RGB扫描图到数字化等高线矢量图
  9. POJ3246-Balanced Lineup,好经典的题,做法和HDU-I hate it 一样~~
  10. C语言之volatile用法(二十一)
  11. mysql 递归查询所有父级
  12. 使用 p6spy,拦截到持久层执行的sql及参数
  13. 全国大学生智能汽车竞赛-室外光电组无人驾驶挑战赛-2019
  14. 凸性与久期在python中实现
  15. UI——day3.IOS设计规范
  16. linux卸载LILO命令,Linux lilo命令
  17. golang通过浏览器操作服务器中文件
  18. 基于 LSTM-Attention 的中文新闻文本分类
  19. 【语音去噪】基于切比雪夫+椭圆形低通滤波器语音去噪附GUI界面
  20. RecyclerView图片错乱复用问题

热门文章

  1. 使用Python脚本下载Bilibili相簿
  2. 一不小心踏进Android开发: TPMini大眼睛使用PS3蓝牙手柄(四)围绕linmctool挖掘SixAxis通讯协议
  3. css中四大定位模式
  4. IxChariot测试路由LAN到WAN和WAN到LAN吞吐量操作指南
  5. c语言浮点数菜鸟教程,C语言菜鸟基础教程之单精度浮点数与双精度浮点数
  6. 基于Java毕业设计材料提交管理系统源码+系统+mysql+lw文档+部署软件
  7. 亚马逊接口NextToken分页调用时AuthFailure问题的解决
  8. 全球及中国信息存储行业发展机遇与供需前景预测报告2022版
  9. 【Kotlin协程】基于RxJava项目的Coroutine改造
  10. js常用的循环,for of 和 for in等比较