01背包问题

问题

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。 第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。 接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i
件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000 0<vi,wi≤1000

题解

题解(1)二维空间

(1)定义状态: f [ i ] [ j ] f[i][j] f[i][j]:前 i i i件物品,在容量为 j j j下的最优解(最大价值)

解释:当前状态 i i i依赖于上一个状态 i − 1 i-1 i−1,,即在f[0][0]时开始,有N件物品,则需要N次决 策,每一次对第 i 件物品的决策,状态f[i][j]不断由之前的状态更新而来。

(2)状态转移方程:当背包容量足够时可以选择装入第 i 件物品或者不装。
装入: f [ i ] [ j ] = f [ i − 1 ] [ j − w [ i ] + v [ i ] f[i][j] = f[i-1][j - w[i] + v[i] f[i][j]=f[i−1][j−w[i]+v[i]
不装: f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j] = f[i-1][j] f[i][j]=f[i−1][j]

因此状态转移方程为:
f [ i ] [ j ] = m a x { f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − w [ i ] + v [ i ] } f[i][j] = max\{f[i-1][j], f[i-1][j - w[i] + v[i]\} f[i][j]=max{f[i−1][j],f[i−1][j−w[i]+v[i]}
(3)边界条件
当背包容量不足时: j < w [ i ] j < w[i] j<w[i],此时下一状态前 i 个物品最优解即为前 i−1 个物品最优解。
当 前 状 态 f [ i ] [ j ] = f [ i − 1 ] [ j ] 当前状态f[i][j] = f[i-1][j] 当前状态f[i][j]=f[i−1][j]
代码:

#include <iostream>
using namespace std;const int N = 1010;
int V[N], W[N];
int f[N][N];
//int f[N];
int n, w;int main(void)
{cin >> n >> w;for(int i = 1; i <= n; i++){cin >> W[i] >> V[i];}for(int i = 1; i <= n; i++){for(int j = 1; j <= w; j++){if(W[i] > j)f[i][j] = f[i-1][j];else if(W[i] <= j)f[i][j] = max(f[i][j], f[i-1][j-W[i]] + V[i]);}}for(int i = 0; i <= n; i++){for(int j = 0; j <= v; j++)cout << f[i][j] << " ";cout << endl;}/*for(int j = 0; j <= w; j++)cout << f[j] << " ";*/cout << endl;cout << f[n][w] << endl;system("pause");return 0;
}

题解(2)状态压缩至一维

将二维状态 f [ i ] [ j ] f[i][j] f[i][j]压缩至一维 f [ j ] f[j] f[j],此时少了维度 i ,之所以可以压缩状态 i 是因为每一次的第 i 个状态只与上一状态 i - 1 有关,与其他无关,因此可以对物品维度进行压缩,直接在 N 件物品上寻找最优值。
(1)定义状态: f [ j ] f[j] f[j]:N 件物品,在背包容量为 j 时的最优解
注意1:对背包容量 j 进行遍历时要从 w (最大容量)开始,然后递次倒序遍历。
注意2之所以要倒序遍历,是因为在二维情况下,状态 f [ i ] [ j ] f[i][j] f[i][j]是由上一轮 i − 1 i - 1 i−1的状态得来的, f [ i ] [ j ] f[i][j] f[i][j]与 f [ i − 1 ] [ j ] f[i - 1][j] f[i−1][j]是独立的。而优化到一维后,如果我们还是正序,则有 f [ 较 小 体 积 ] f[较小体积] f[较小体积]更新到 f [ 较 大 体 积 ] f[较大体积] f[较大体积],则有可能本应该用第 i − 1 i-1 i−1轮的状态却用的是第 i 轮的状态。

(2)状态转移方程
f [ j ] = m a x { f [ j ] , f [ j − w [ i ] ] + v [ i ] } f[j] = max\{f[j], f[j - w[i]] + v[i]\} f[j]=max{f[j],f[j−w[i]]+v[i]}
代码:

#include <iostream>
using namespace std;const int N = 1010;
int V[N], W[N];
//int f[N][N];
int f[N];
int n, w;int main(void)
{cin >> n >> w;for(int i = 1; i <= n; i++){cin >> W[i] >> V[i];}for(int i = 1; i <= n; i++){for(int j = w; j >= W[i]; j--){if(W[i] > j)//f[i][j] = f[i-1][j];f[j] = f[j];else if(W[i] <= j)//f[i][j] = max(f[i][j], f[i-1][j-W[i]] + V[i]);f[j] = max(f[j], f[j - W[i]] + V[i]);}}/*for(int i = 0; i <= n; i++){for(int j = 0; j <= v; j++)cout << f[i][j] << " ";cout << endl;}*/for(int j = 0; j <= w; j++)cout << f[j] << " ";cout << endl;cout << f[w] << endl;system("pause");return 0;
}

求解在恰好装满背包的情况下具体装入方案

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

这里参考了另一篇博客,那里有推到有效状态和无效状态的证明
https://blog.csdn.net/Iseno_V/article/details/100697105

这里只介绍如何解决问题
也就是说在初始化转移矩阵上下文章,即f[0] = 0,else f[i] = -INF,这里的意思是说智能从f[0]开始算有效状态,其他均为无效状态。恰好把问题转化成背包容量为0时,装的物体体积为0是有效状态。

for(int i = 0; i < N; i++)f[i] = -100000000;f[0] = 0;for(int i = 1; i <= n; i++){for(int j = w; j >= W[i]; j--){//f[i][j] = max(f[i][j], f[i-1][j-W[i]] + V[i]);if(f[j] < f[j - W[i]] + V[i]){path[i][j] = 1;f[j] = f[j - W[i]] + V[i];}if(f[j] < 0)f[j] = -100000000;}}

下面介绍如何求解被选物品

(1)定义
需要定义一个路径矩阵path[N][N],表示有n(物品数)行m(背包重量)列,在进行状态转移时,若 f [ j ] < f [ j − W [ i ] ] + V [ i ] f[j] < f[j - W[i]] + V[i] f[j]<f[j−W[i]]+V[i]则在路径矩阵中赋值 1 ,其他为 0 ,这样求解出物品矩阵了

(2)输出
对背包物品个数 n 进行倒序循环,若path[i][w]为 1 ,则输出 W[i],并把当前背包容量减去W[i],即w - W[i],代码如下

int p = w;for(int i = n; i >= 0; i--)//路径输出 {if(w < 0) break;if(path[i][p]){cout << W[i] << ' ';p -= W[i];}}

完整代码

#include <iostream>
using namespace std;const int N = 1010;
int V[N], W[N];
//int f[N][N];
int f[N];
int n, w;
int path[N][N];int main(void)
{cin >> n >> w;for(int i = 1; i <= n; i++){cin >> W[i] >> V[i];}for(int i = 0; i < N; i++)f[i] = -100000000;f[0] = 0;for(int i = 1; i <= n; i++){for(int j = w; j >= W[i]; j--){//f[i][j] = max(f[i][j], f[i-1][j-W[i]] + V[i]);if(f[j] < f[j - W[i]] + V[i]){path[i][j] = 1;f[j] = f[j - W[i]] + V[i];}if(f[j] < 0)f[j] = -100000000;}}/*for(int i = 0; i <= n; i++){for(int j = 0; j <= v; j++)cout << f[i][j] << " ";cout << endl;}*/// for(int j = 0; j <= w; j++)//     cout << f[j] << " ";// cout << endl;// cout << f[w] << endl;int p = w;for(int i = n; i >= 0; i--)//路径输出 {if(w < 0) break;if(path[i][p]){cout << W[i] << ' ';p -= W[i];}}cout << endl;if (f[w] > 0){cout << f[w] << endl;//背包恰好装满了,输出结果}else{cout << "error" << endl;//背包不能恰好装满}system("pause");return 0;
}

动态规划——背包问题九解(01背包)相关推荐

  1. 动态规划/背包问题总结/小结——01背包、完全背包

    文章目录 前言 动态规划五步法 01背包模型 完全背包 总结 前言 背包问题也属于动态规划问题. 动态规划就是将复杂的大问题转化为一个小问题,然后将小问题转化为更小的容易求解的问题:通过将最小的容易求 ...

  2. 动态规划——背包问题整理(01背包+完全背包)

    1.引言 背包问题简单描述,其实就是有一堆物品同时具有一定价值和重量,现有一个背包可以承受最大重量m,那么要怎么选择在不超过背包最大重量的前提下,使背包中选择的物品价值最大. 最常见的背包问题又可以分 ...

  3. Java入门算法(动态规划篇2:01背包精讲)

    本专栏已参加蓄力计划,感谢读者支持❤ 往期文章 一. Java入门算法(贪心篇)丨蓄力计划 二. Java入门算法(暴力篇)丨蓄力计划 三. Java入门算法(排序篇)丨蓄力计划 四. Java入门算 ...

  4. 动态规划背包问题详解(二)---0-1背包问题

    /**  * 对于技术面试,你还在死记硬背么?  * 快来"播"沙糖橘吧,  * 用视频案例为你实战解读技术难点  * 聚焦Java技术疑难点,实战视频为你答疑解惑  * 越&qu ...

  5. 动态规划 —— 背包问题 P07 —— 有依赖背包

    [简化的问题] 这种背包问题的物品间存在某种"依赖"的关系,也就是说,i依赖于j,表示若选物品i,则必须选物品j. 为了简化起见,我们先设没有某个物品既依赖于别的物品,又被别的物品 ...

  6. 动态规划 —— 背包问题 P08 —— 泛化物品背包

    [定义] 泛化物品的概念:有这样一种物品,它并没有固定的费用和价值,而是它的价值随着你分配给它的费用而变化. 更严格的定义:在背包容量为V的背包中,泛化物品是一个定义域为0..V中的整数的函数h,当分 ...

  7. 动态规划 —— 背包问题 P05 —— 二维背包

    [问题] 对于每件物品,具有两种不同的体积,选择这件物品必须同时付出这两种代价,对于每种代价都有一个可付出的最大值(背包容量). 问:怎样选择物品可以得到最大的价值? 设:这两种代价分别为代价1和代价 ...

  8. 动态规划------背包问题详解

    文章目录 一.01背包 二.完全背包 三.多重背包 四.二维费用背包 一.01背包 有n件物品,每件物品占用的空间为w[i], 价值为p[i]. 有容量为 V 的背包.求在容量允许的范围下,背包装入物 ...

  9. 01背包问题详解(浅显易懂)

    01背包问题详解 01背包是一种动态规划问题.动态规划的核心就是状态转移方程,本文主要解释01背包状态转移方程的原理. 问题描述 01背包问题可描述为如下问题: 有一个容量为V的背包,还有n个物体.现 ...

最新文章

  1. BZOJ1861:[ZJOI2006]书架(Splay)
  2. 软件原型设计工具Axure RP安装教程
  3. 3ds Max制作客厅场景实例教程
  4. 微软开源项目NeuronBlocks - 像搭积木一样构建NLP深度学习模型
  5. GDCM:DICOM文件转储ADAC文件的测试程序
  6. @Transactional 注解的失效场景
  7. 2020年终回顾:时间会回答成长,成长会回答梦想
  8. java conf_JAVA 解析、编辑nginx.conf详解
  9. 怕入错行?这群技术人写了本“择业指南”
  10. java必学_Java基础系列之初识JAVA
  11. dz3.0数据库操作函数分析说明
  12. vue将每个路由打包成html,Ant Design Vue pro 动态路由的实现和打包
  13. c51语言编程实验报告,C51单片机实验报告程序.doc
  14. Unity 3D中实现敌人追踪
  15. 信号与电源完整性6:EMI电磁干扰分析及降低解决方案概述
  16. 2022国赛新大陆物联网Ubuntu系统维护(中职)
  17. 《西邮XUPT-ACM学子的文字》
  18. 独立游戏开发如何入门?
  19. 陈力:传智播客古代 珍宝币 泡泡龙游戏开发第28讲:PHP数组
  20. cherrytree安装出现see the logfile for detaile错误的处理

热门文章

  1. ASCII 碼: 转义字符,正则表达式,特殊字符,模式匹配
  2. 分享150个PHP源码,总有一款适合您
  3. gs 服务器文件,服务器gs
  4. 台积电:3纳米芯片工厂地址首选台湾 美国次之
  5. 小白思路理顺U-Net模型(keras)--预测部分
  6. Gif添加文字怎么操作?如何在线gif动图上添加文字?
  7. 联想开机启动项按哪个_联想重装系统按哪个键|联想电脑重装系统按什么键
  8. 软件性能测试场景设计,性能测试场景设计杂谈
  9. Duplicate entry '1106a210d0794c45a005ef034bc1b664' for key 'PRIMARY'
  10. 开源中国社区开源项目排行榜