【算法问题】0-1背包问题

  0-1背包问题:有一个贼在偷窃一家商店时,发现有n件物品,第i件物品价值vi元,重wi磅,此处vi与wi都是整数。他希望带走的东西越值钱越好,但他的背包中至多只能装下W磅的东西,W为一整数。应该带走哪几样东西?这个问题之所以称为0-1背包,是因为每件物品或被带走;或被留下;小偷不能只带走某个物品的一部分或带走同一物品两次。

  在分数(部分)背包问题(fractional knapsack problem)中,场景与上面问题一样,但是窃贼可以带走物品的一部分,而不必做出0-1的二分选择。可以把0-1背包问题的一件物品想象成一个金锭,而部分问题中的一件物品则更像金沙。

  两种背包问题都具有最优子结构性质。对0-1背包问题,考虑重量不超过W而价值最高的装包方案。如果我们将商品j从此方案中删除,则剩余商品必须是重量不超过W-wj的价值最高的方案(小偷只能从不包括商品j的n-1个商品中选择拿走哪些)。

  虽然两个问题相似,但我们用贪心策略可以求解背包问题,而不能求解0-1背包问题,为了求解部分数背包问题,我们首先计算每个商品的每磅价值vi/wi。遵循贪心策略,小偷首先尽量多地拿走每磅价值最高的商品,如果该商品已全部拿走而背包未装满,他继续尽量多地拿走每磅价值第二高的商品,依次类推,直到达到重量上限W。因此,通过将商品按每磅价值排序,贪心算法的时间运行时间是O(nlgn)。

  为了说明贪心这一贪心策略对0-1背包问题无效,考虑下图所示的问题实例。此例包含3个商品和一个能容纳50磅重量的背包。商品1重10磅,价值60美元。商品2重20磅,价值100美元。商品3重30磅,价值120美元。因此,商品1的每磅价值为6美元,高于商品2的每磅价值5美元和商品3的每磅价值4美元。因此,上述贪心策略会首先拿走商品1。但是,最优解应该是商品2和商品3,而留下商品1。拿走商品1的两种方案都是次优的。

  但是,对于分数背包问题,上述贪心策略首先拿走商品1,是可以生成最优解的。拿走商品1的策略对0-1背包问题无效是因为小偷无法装满背包,空闲空间降低了方案的有效每磅价值。在0-1背包问题中,当我们考虑是否将一个商品装入背包时,必须比较包含此商品的子问题的解与不包含它的子问题的解,然后才能做出选择。这会导致大量的重叠子问题——动态规划的标识。

例子:0-1背包问题。总共有三件物品,背包可容纳5磅的东西,物品1重1磅,价值60元。物品2重2磅,价值100元,物品3重3磅,价值120元。怎么才能最大化背包所装物品的价值。

解答:我们可以得出物品一每磅价值60元,大于物品二的每磅50元和物品3的每磅40元。如果按照贪心算法的话就要取物品1。然而最优解应该取的是物品2和3,留下了1.

   在0-1背包问题中不应取物品1的原因在于这样无法将背包填满,空余的空间就降低了货物的有效每磅价值。

   我们可以利用动态规划来解0-1背包问题。

   假设c[i]表示第i件物品的重量,w[i]表示第i件物品的价值,f[i][j]表示背包容量为j,可选物品为物品1~i时,背包能获得的最大价值。

     用动态规划求解即先求出背包容量较小时能获得的最大价值,然后根据背包容量较小时的结果求出背包容量较大时的结果,也就是一个递推的填表过程。

   当没有可选物品时,背包能获得的最大价值为0。即表格可初始化为

   

   填表过程(即状态转移方程)是:

   

  "j<c[i]"表示第i件物品的重量大于当前背包的容量j,此时显然不放第i件物品。下面解释上述方程在“其它“情况下的意义:”将前i件物品放入容量为j背包中“这个问题,如果只考虑第i件物品放或者不放,那么就可以转化为只涉及前i-1件物品的问题,即:

    1. 如果不放第i件物品,则问题转化为只涉及”前i-1件物品放入容量为j的背包中“

    2. 如果放第i件物品,则问题转化为”前i-1件物品放入剩下的容量为j-c[i]的背包中“,此时能获得的最大价值就是f[i-1][j-c[i]]再加上通过放入第i件物品获得的价值获得的价值w[i]。

    则在”其他“情况下,f[i][j]就是1、2中最大的那个值。

    显然,可以从左下角利用状态转移方程依次逐行填表,得到f[3][5](表示可选物品为1、2、3,且背包容量为5时,能获得的最大价值),可见动态规划的确即为一个递推的过程。填表后如下表所示:

    

    代码如下:

int N = 3, V = 5;    //N是物品数量,V是背包容量
int c[4] = {0,1,2,3};
int w[4] = {0.60.100.120};
for (i = 0; i <= V; i++) { //逐行填表,i表示当前可选物品数,j表示当前背包的容量
    f[i][0] = 0;
    for (j = 1; j <= V; j++) {
        if (j < c[i]) {
            f[i][j] = f[i-1][j];
        } else {
            f[i][j] = max(f[i-1][j], f[i-1][j-c[i]] + w[i]);
        }
    }
}

  

背包问题初始化

  求最优解的背包问题中,事实上有两种不太相同的问法。有的题目要求”恰好装满背包“时的最优解,有的题目则没有要求必须把背包装满。这两种问法的区别是求解时的初始化不同。

  如果是第一种问法,要求恰好装满背包,那么在初始化时除了f[0][0]为0,其他f[0][1~V]均设为-∞ ,这样就可以包装最终得到的解释一种恰好装满背包的最优解。

  如果并没有要求必须是把背包装满,而是只希望价格尽量大,初始化时应该将f[0][1~V]全部设为0.

  这是为什么呢?可以这样理解:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可以在什么也不装且价值为0的情况下被”恰好装满“,其他容量的背包均没有合法的解,属于未定义的状态,应该被赋值为-∞。如果背包并非被必须装满,那么任何容量的背包都有一个合法解”什么都不装“,这个解的价值为0,所以初始状态的值也就全部为0了。

  

时间: 2024-10-27 06:58:11

【算法问题】0-1背包问题的相关文章

动态规划算法求解0,1背包问题

首先我们来看看动态规划的四个步骤: 1. 找出最优解的性质,并且刻画其结构特性: 2. 递归的定义最优解: 3. 以自底向上的方式刻画最优值: 4. 根据计算最优值时候得到的信息,构造最优解 其中改进的动态规划算法:备忘录法,是以自顶向下的方式刻画最优值,对于动态规划方法和备忘录方法,两者的使用情况如下: 一般来讲,当一个问题的所有子问题都至少要解一次时,使用动态规划算法比使用备忘录方法好.此时,动态规划算法没有任何多余的计算.同时,对于许多问题,常常可以利用其规则的表格存取方式,减少动态规划算

背包问题:0/1背包问题 普通背包问题(贪心算法只适用于普通背包问题)

//sj和vj分别为第j项物品的体积和价值,W是总体积限制. //V[i,j]表示从前i项{u1,u2,…,un}中取出来的装入体积为j的背包的物品的最大价值. 第一种:0/1背包问题 最大化 ,受限于  1)若i=0或j=0,  V[i,j] = 0 2)若j<si, V[i,j] = V[i-1,j] 3)若i>0且j>=si, V[i,j] = Max{V[i-1,j],V[i-1,j-si]+vi} 第二种:背包问题:在选择物品i装入背包时,可以选择物品i的一部分,而不一定要全部

动态规划算法实现部分——0/1背包问题

代码: import java.util.*; import java.util.Scanner; /* *动态规划思想解决0/1背包问题 */ public class Main{ public static void main(String[] args){ Scanner in=new Scanner(System.in); System.out.println("输入背包的容量"); int bagCap=in.nextInt(); //背包的容量 System.out.pri

贪心算法练习题:部分背包问题

/*----------------------------------------------------- 有n个物体,第i个物体的重量是wi,价值为vi, 选若干个物体,使得在总重量不超过c的情况下让总价值尽量高. 这里每个物体都可以只取走一部分,价值和重量按比例计算. 输入: 第一行输入两个整数表示n和c. 第2到第n+1行每行两个整数分别表示wi和vi. 输出: 第一行输出所选物品的总价值v和总重量w以及所选物品的种类数num.两两之间用空格分隔. 第二行到第n+1行按照输入物品的顺序

0/1背包问题(回溯法)

回溯法是一个既带有系统性又带有跳跃性的搜索算法.它在包含问题的所有解的解空间树中,按深度优先策略,从根结点出发搜索解空间树.算法搜索至解空间树的任意一结点时,先判断该结点是否包含问题的解.如果肯定不包含,则跳过对该结点为根的子树搜索,逐层向其祖先结点回溯:否则 ,进入该子树,继续按深度优先策略搜索. 问题的解空间 用回溯法解问题时,应明确定义问题的解空间.问题的解空间至少包含问题的一个(最优)解.对于 n=3 时的 0/1 背包问题,可用一棵完全二叉树表示解空间,如图所示: 求解步骤 1)针对所

0/1背包问题的动态规划法求解 —— Java 实现

0/1背包问题的动态规划法求解,前人之述备矣,这里所做的工作,不过是自己根据理解实现了一遍,主要目的还是锻炼思维和编程能力,同时,也是为了增进对动态规划法机制的理解和掌握. 值得提及的一个问题是,在用 JAVA 实现时, 是按算法模型建模,还是用对象模型建模呢? 如果用算法模型,那么 背包的值.重量就直接存入二个数组里:如果用对象模型,则要对背包以及背包问题进行对象建模.思来想去,还是采用了对象模型,尽管心里感觉算法模型似乎更好一些.有时确实就是这样,对象模型虽然现在很主流,但也不是万能的,采用

普林斯顿公开课:算法第0章,课程介绍

课程介绍 这门课程核心内容是算法和数据结构. 具体的算法和数据结构如下: 数据类型:堆栈.队列.背包.并查集.优先队列. 排序:快排.并排.堆排.基数排序 查找:BST.红黑BST.哈希表 图:BFS.DFS.Prim.Kruskai.Dijkstra 字符串:KMP.正则.TST.哈夫曼.LZW 高级:B树.后缀数组.最大流 为什么要学习算法 算法在各个领域中都有应用. 算法可以提高编程效率. 算法可以将现实生活中的物理公式转换成代码,算法可以模拟现实世界,然后发现世界的奥秘. 算法是很有趣的

为了打击网站被黑作弊行为,360搜索上线悟空算法2.0

我们在搜索浏览网页的时候,经常会出现正常网页跳转至含有色情,博彩等信息的恶意站点上去.这样严重影响到了用户的搜索体验,为了打击此类行为,360搜索正式上线了:"悟空算法2.0" 网站被黑是一种较为常见的现象,很多人通过黑客手段代码劫持,对正常的站点进行了恶意行为,现在这种现象更加的明显,这次上线的悟空算法在公告中显示:能更加准确快速地识别各种网站被黑客攻击的行为,降低恶意网站在搜索引擎中的不良展现和对用户的影响,严厉打击此类针对360搜索的作弊行为. 同时360搜索页提供了多种查杀工具

百度冰桶算法2.0简介

百度的各种算法的出台,其目的只有一个就是提高用户的体验度,用户体验好了,那么baidu就会重点关注网站,将其相关关键词更好的呈现给访问者,这就是为何大家一直青睐baidu,但是又不喜欢其的道理,因为其规律总是变化无穷,仍追求者需要不断的调整步伐. 那么随着手机移动端的市场到来,其占有率已经不可阻挡的成为了超过pc端的又一新起领域,而baidu的冰桶算法针对手机移动端,在全球估计也就只有guge了,而国内虽然pc端移动搜索市场其份额在不断的下滑,但是baidu注重移动端的结果,目前已经占据了市场绝

第十六章 贪心算法——0/1背包问题

1.问题描述: 给定n种物品和一背包.物品i的重量是wi,其价值为vi,背包的容量为C.问:应如何选择装入背包的物品,使得装入背包中物品的总价值最大? 形式化描述:给定c >0, wi >0, vi >0 , 1≤i≤n.要求找一n元向量(x1,x2,…,xn,), xi∈{0,1}, ∋ ∑ wi xi≤c,且∑ vi xi达最大.即一个特殊的整数规划问题. 2.最优性原理: 设(y1,y2,…,yn)是 (3.4.1)的一个最优解.则(y2,…,yn)是下面相应子问题的一个最优解: