2018-03-15 13:11:12

背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。

相似问题经常出现在商业、组合数学,计算复杂性理论、密码学和应用数学等领域中。

一、0/1背包问题

背包问题是个NPC问题,01背包可以通过动态规划算法在伪多项式时间内给出解。

0/1背包问题的特点是,每种物品仅仅有一件,且需要选择放或者不放。

在0/1背包问题中,物品i或者被装入背包,或者不被装入背包,设xi表示物品i装入背包的情况,则当xi=0时,表示物品i没有被装入背包,xi=1时,表示物品i被装入背包。根据问题的要求,有如下约束条件和目标函数:

于是,问题归结为寻找一个满足约束条件式2.1,并使目标函数式2.2达到最大的解向量X=(x1, x2, …, xn)。

0/1背包问题可以看作是决策一个序列(x1, x2, …, xn),对任一变量xi的决策是决定xi=1还是xi=0。在对xi-1决策后,已确定了(x1, …, xi-1),在决策xi时,问题处于下列两种状态之一:
(1)背包容量不足以装入物品i,则xi=0,背包不增加价值;
(2)背包容量可以装入物品i,则xi=1,背包的价值增加了vi。
这两种情况下背包价值的最大者应该是对xi决策后的背包价值。令V(i, j)表示在前i(1≤i≤n)个物品中能够装入容量为j(1≤j≤C)的背包中的物品的最大值,则可以得到如下动态规划函数:

式2.3表明:把前面i个物品装入容量为0的背包和把0个物品装入容量为j的背包,得到的价值均为0。
式2.4的第一个式子表明:如果第i个物品的重量大于背包的容量,则装入前i个物品得到的最大价值和装入前i-1个物品得到的最大价值是相同的,即物品i不能装入背包;第二个式子表明:如果第i个物品的重量小于背包的容量,则会有以下两种情况:
(1)如果把第i个物品装入背包,则背包中物品的价值等于把前i-1个物品装入容量为j-wi的背包中的价值加上第i个物品的价值vi;
(2)如果第i个物品没有装入背包,则背包中物品的价值就等于把前i-1个物品装入容量为j的背包中所取得的价值。显然,取二者中价值较大者作为把前i个物品装入容量为j的背包中的最优解。

举个例子:

例如,有5个物品,其重量分别是{2, 2, 6, 5, 4},价值分别为{6, 3, 5, 4, 6},背包的容量为10。
根据动态规划函数,用一个(n+1)×(C+1)的二维表V,V[i][j]表示把前i个物品装入容量为j的背包中获得的最大价值。

第一阶段,只装入前1个物品,确定在各种情况下的背包能够得到的最大价值;
第二阶段,只装入前2个物品,确定在各种情况下的背包能够得到的最大价值;
依此类推,直到第n个阶段。最后,V(n,C)便是在容量为C的背包中装入n个物品
时取得的最大价值。

如何确定装入背包的具体物品?

从V(n,C)的值向前推,如果V(n,C)>V(n-1,C),表明第n个物品被装入背包,前n-1个物品被装入容量为C-wn的背包中;否则,第n个物品没有被装入背包,前n-1个物品被装入容量为C的背包中。依此类推,直到确定第1个物品是否被装入背包中为止。由此,得到如下函数:

public class Knapsack {
static int knapsack(int[] v, int[] w, int W) {
int n = v.length;
int[][] V = new int[n + 1][W + 1];
int[] x = new int[n + 1];
for (int i = 0; i < W + 1; i++) {
V[0][i] = 0;
}
for (int i = 0; i < n + 1; i++) {
V[i][0] = 0;
} for (int i = 1; i <= n; i++) {
for (int j = 1; j <= W; j++) {
if (j < w[i - 1]) V[i][j] = V[i - 1][j];
else V[i][j] = Math.max(V[i - 1][j - w[i - 1]] + v[i - 1], V[i - 1][j]);
}
} int j = W;
for (int i = n; i >= 1; i--) {
if (V[i][j] == V[i - 1][j]) x[i] = 0;
else {
j -= w[i - 1];
x[i] = 1;
}
} for (int i = 1; i <= n; i++) {
System.out.println(x[i]);
}
return V[n][W];
} public static void main(String[] args) {
System.out.println(knapsack(new int[]{6, 3, 5, 4, 6}, new int[]{2, 2, 6, 5, 4}, 10));
}
}

相关的优化处理:

时间复杂度已经无法进一步进行优化了,但是空间复杂度还是有优化余地的,通过递推式可以看到,每次下一行的值的产生仅仅依赖于上一行的前面两个值,因此,我们可以将二维数组优化成一维数组进行存储。

    static int polish(int[] v, int[] w, int W){
int n = v.length;
int[] m = new int[W + 1];
m[0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = W; j >= w[i - 1]; j--) {
m[j] = Math.max(m[j - w[i - 1]] + v[i - 1], m[j]);
}
}
return m[W];
}

另外,在初始化的时候,如果是题目没有要求必须得最终装满背包,则直接使用上述代码即可,如果题目中指出必须装满背包,则在初始化的时候,除了V[0][0] = 0外,其余的0件物品,j个重量,抑或j个重量,0件物品都是不满足装满背包的条件的,应该初始化为负无穷大。

二、完全背包问题

完全背包问题同样给出了n件物品的重量和价值,并且给出了背包的大小W,但是和0/1背包不同的是,在完全背包问题中,每件物品可以选1,2,3...直到背包放不下为止。

完全背包是0/1背包问题的一个扩展,也同样是一个非常经典的问题。

运用类比的思想,我们可以将完全背包转化成0/1背包,具体的转化可以有下面两种方式:

1、将每件物品看成W/w[i]件,价值不变;

2、将每件物品看成v[i]*2^k,重量为w[i]*2^k,想法就是利用二进制的角度看问题,任何多种选择都可以通过这些二进制数相加得到,这种方法的分解个数显然要小很多,非常聪明。

如果从递推式的角度来解决问题,可以得到一个非常好的解答:

V[i][j] = max{V[i - 1][j], V[i][j - w[i]] + v[i]}

对于每一个V[i][j]都可以看成要么不选择第i件,要么选择第i件且可以多选,那么就可以很容易的得到上述的递推式。

下面使用一维数组进行实现,你会发现除了内层的顺序变了,其他的都没有改变。

    static int polish(int[] v, int[] w, int W){
int n = v.length;
int[] m = new int[W + 1];
m[0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = w[i - 1]; j <= W; j--) {
m[j] = Math.max(m[j - w[i - 1]] + v[i - 1], m[j]);
}
}
return m[W];
}

动态规划-背包问题 Knapsack的更多相关文章

  1. 对背包问题(Knapsack Problem)的算法探究

    对背包问题(Knapsack Problem)的算法探究 至繁归于至简,这次自己仍然用尽可能易理解和阅读的解决方式. 1.问题说明: 假设有一个背包的负重最多可达8公斤,而希望在背包中装入负重范围内可 ...

  2. js动态规划---背包问题

    //每种物品仅有一件,可以选择放或不放 //即f[i][w]表示前i件物品恰放入一个容量为w的背包可以获得的最大价值. //则其状态转移方程便是:f[i][w]=max{f[i-1][w],f[i-1 ...

  3. P1060 开心的金明(动态规划背包问题)

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:"你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NN元钱 ...

  4. 动态规划——背包问题python实现(01背包、完全背包、多重背包)

    目录 01背包问题 完全背包问题 多重背包问题 参考: 背包九讲--哔哩哔哩 背包九讲 01背包问题 01背包问题 描述: 有N件物品和一个容量为V的背包. 第i件物品的体积是vi,价值是wi. 求解 ...

  5. PHP实现动态规划背包问题

    有一堆货物,有各种大小和价值不等的多个物品,而你只有固定大小的背包,拿走哪些能保证你的背包带走的价值最多 动态规划就是可以记录前一次递归过程中计算出的最大值,在之后的递归期间使用,以免重复计算. &l ...

  6. POJ 1276 Cash Machine -- 动态规划(背包问题)

    题目地址:http://poj.org/problem?id=1276 Description A Bank plans to install a machine for cash withdrawa ...

  7. 【优化算法】变邻域搜索算法解决0-1背包问题(Knapsack Problem)代码实例 已

    01 前言 经过小编这几天冒着挂科的风险,日日修炼,终于赶在考试周中又给大家更新了一篇干货文章.关于用变邻域搜索解决0-1背包问题的代码.怎样,大家有没有很感动? 02 什么是0-1背包问题? 0-1 ...

  8. 动态规划------背包问题(c语言)

    /*背包问题: 背包所能容纳重量为10:共五件商品,商品重量用数组m存储m[5]={2,2,6,5,4}, 每件商品的价值用数组n存储,n[5]={6,3,5,4,6};求背包所能装物品的最大价值. ...

  9. 购物单 && 动态规划 && 背包问题

    题目叙述的言语倒是蛮多的: 王强今天很开心,公司发给N元的年终奖.王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子: 主件 附件 电脑 ...

随机推荐

  1. 爬虫实战【3】Python-如何将html转化为pdf(PdfKit)

    前言 前面我们对博客园的文章进行了爬取,结果比较令人满意,可以一下子下载某个博主的所有文章了.但是,我们获取的只有文章中的文本内容,并且是没有排版的,看起来也比较费劲... 咋么办的?一个比较好的方法 ...

  2. 【node】------node连接mongodb操作数据库------【巷子】

    1.下载第三方模块mongodb cnpm install mongodb --save 2.检测是否连接成功 1.引入第三方模块mongodb并创建一个客户端 const MongoClient = ...

  3. c# 调用声音文件

    一.使用C#自带的SoundPlayer using System.Media; SoundPlayer sound = new SoundPlayer("声音.wav"); so ...

  4. 转!!git如何撤销上一次commit(或已push)

    原博文地址 : https://www.cnblogs.com/lyy-2016/p/6509707.html git如何撤销上一次commit操作 1.第一种情况:还没有push,只是在本地comm ...

  5. uchome四大常用入口文件

    一.四大常用入口文件 cp.php           编辑日志.相册.活动等等相关编辑操作基本上都从这个文件入口 do.php          登录.注册.找回密码.相册批量上传.在需要密码的情况 ...

  6. (2.5)DDL增强功能-触发器trigger

    SQL Server:触发器详解   1. 概述 2. 触发器的分类 3. Inserted和Deleted表 4. 触发器的执行过程 5. 创建触发器 6. 修改触发器: 7. 删除触发器: 8. ...

  7. android 异步线程刷新UI 以及 JSON解析 以及 url get请求

    import android.os.Handler; import android.os.Message; 1. Handler mHandler = new Handler() { @Overrid ...

  8. jenkins下搭建c++持续集成环境

      运行环境为centos 5.8 1.下载tomcat,并解压 2.下载jenkins,把jenkins.war放到tomcat的webapp目录下 3.在tomcat的bin目录下执行./star ...

  9. win10用键盘控制鼠标

    打开控制面板 进入轻松使用 更改鼠标的工作方式 勾选启用鼠标键 点击设置鼠标键 应用确定 ctrl+shift+numlock开启鼠标键 小键盘8426上下左右,5类似鼠标单击 ctrl+小键盘加速, ...

  10. Linux系统——FTP

    FTP连接及传输模式1. 控制连接:TCP21,用于发送FTP命令信息2. 数据连接:TCP20,用于上传.下载数据3. 数据连接的建立类型:(1)主动模式:服务器制动发起数据连接首先由客户端向服务端 ...