动态规划经典案例详解(背包问题)
[编程题]【动态规划】背包问题
[编程题]【动态规划】背包问题[编程题]【动态规划】背包问题题⽬信息问题:现有背包。
其中有四个商品。
价值-体积如下* 物品编号: 1 2 3 4* 物品体积: 2 3 4 5* 物品价值: 3 4 5 6* 问:如何才能保证在背包容量为8的情况下装的价值最⼤?思路背包问题,动态规划思路1、构建dp表,dp【i】【j】代表该i编号的背包为⽌容量为j的最⼤价值。
先填表加深理解。
2、填完表如果要找出价值为k的容量的背包加⼊的物品标号只需要从i=dp.length-1,j=k的dp数组元素处回溯。
填完表如下(借视频中up主的结果):总结结论如下(借视频中up主的总结):理解了上述总结的思路,其实代码就很容易写出来了。
代码如下Java代码package interviewcode;import ng.annotation.Target;import java.util.ArrayList;import java.util.Arrays;* @author* @create 2020/8/11 - 10:52* @descp:* 问题:现有背包。
其中有四个商品。
价值体积如下* 物品编号: 1 2 3 4* 物品体积: 2 3 4 5* 物品价值: 3 4 5 6* 问:如何才能保证在背包容量为8的情况下装的价值最⼤?** 步骤1:填表之后的结果:* 0 0 0 0 0 0 0 0 0* 0 0 3 3 3 3 3 3 3* 0 0 3 4 4 7 7 7 7* 0 0 3 4 5 7 8 9 9* 0 0 3 4 5 7 8 9 10* 步骤2:回溯*///⽅法:动态规划public class P28_背包问题 {public static void main(String[] args) {int[] size = new int[]{0,2,3,4,5}; //第0位放⼀个默认的0值,为了下边⽅便取size[i]就为i号物品的体积int[] money = {0,3, 4, 5, 6}; //第0位放⼀个默认的0值,为了下边⽅便取money[i]就为i号物品的价值int target = 14; //指定的背包⼤⼩//调⽤int[][] ints = dpWrite(size, money, target);ArrayList<Integer> huisu = huisu(ints, size, target);System.out.println("您输⼊的背包⼤⼩是:"+target);System.out.println("添加的物品编号是:"+huisu);System.out.println("最⼤价值:"+ints[size.length-1][target]);}public static int[][] dpWrite(int[] size,int[] money,int targetValue){//这⾥构造⼀个例如4*8的dp,⾏代表截⽌到i背包的最优组合的价值,j代表的背包的容量值int[][] dp = new int[size.length][targetValue+1];//初始化第0⾏的值for(int j=0;j<=targetValue;j++){dp[0][j] = 0;}//初始化第⼀列的值for(int i=1;i<size.length;i++){dp[i][0] = 0;//dp[i][1] = 0;}//初始化中间值ifor(int i=1;i<size.length;i++){for (int j=1;j<=targetValue;j++){//如果第i号背包的体积⼩于当前的j背包容量,就保持和dp[i-1]值相同,即没放⼊if(size[i]>j){dp[i][j] = dp[i-1][j];}else{/*即如果当前i号物品体积可以放⼊的话,就看预留该体积后剩余的价值在i-1号物品中的最⼤值加上该物品的价值和是否是⼤于dp[i-1][j]的值,谁⼤取谁*/dp[i][j] = Math.max(dp[i-1][j-size[i]>0?j-size[i]:0]+money[i],dp[i-1][j]);}}}//这样就填表完成了。
动态规划——背包问题python实现(01背包、完全背包、多重背包)
动态规划——背包问题python实现(01背包、完全背包、多重背包)参考:⽬录描述:有N件物品和⼀个容量为V的背包。
第i件物品的体积是vi,价值是wi。
求解将哪些物品装⼊背包,可使这些物品的总体积不超过背包流量,且总价值最⼤。
⼆维动态规划f[i][j] 表⽰只看前i个物品,总体积是j的情况下,总价值最⼤是多少。
result = max(f[n][0~V]) f[i][j]:不选第i个物品:f[i][j] = f[i-1][j];选第i个物品:f[i][j] = f[i-1][j-v[i]] + w[i](v[i]是第i个物品的体积)两者之间取最⼤。
初始化:f[0][0] = 0 (啥都不选的情况,不管容量是多少,都是0?)代码如下:n, v = map(int, input().split())goods = []for i in range(n):goods.append([int(i) for i in input().split()])# 初始化,先全部赋值为0,这样⾄少体积为0或者不选任何物品的时候是满⾜要求dp = [[0 for i in range(v+1)] for j in range(n+1)]for i in range(1, n+1):for j in range(1,v+1):dp[i][j] = dp[i-1][j] # 第i个物品不选if j>=goods[i-1][0]:# 判断背包容量是不是⼤于第i件物品的体积# 在选和不选的情况中选出最⼤值dp[i][j] = max(dp[i][j], dp[i-1][j-goods[i-1][0]]+goods[i-1][1])print(dp[-1][-1])⼀维动态优化从上⾯⼆维的情况来看,f[i] 只与f[i-1]相关,因此只⽤使⽤⼀个⼀维数组[0~v]来存储前⼀个状态。
那么如何来实现呢?第⼀个问题:状态转移假设dp数组存储了上⼀个状态,那么应该有:dp[i] = max(dp[i] , dp[i-v[i]]+w[i])max函数⾥⾯的dp[i]代表的是上⼀个状态的值。
动态规划法求01背包问题
动态规划法求01背包问题思路状态表⽰:f[i][j]表⽰前i个物品在容量为j的背包下的最⼤价值v[i]表⽰第i个物品的价值,w[i]表⽰第i个物品的重量状态转换:对于第i个物品如果当前背包不可以装下这个物品,那么当前的f[i][j] = f[i - 1][j],也就是上⼀个状态的最⼤价值如果当前背包可以装下这个物品,那么当前的f[i][j] = f[i - 1][j - v[i]] + w[i]和f[i - 1][j]取较⼤的那⼀个,第⼀个是考虑把第i个物品装⼊背包,那么背包物品的价值就是前i-1个物品装⼊容量为j-w[i]再加上第i个物品v[i]的价值,第⼆个是不把当前物品装⼊背包的价值,两个取⼤的那⼀个作为最优解代码详解:1. 0/1背包问题#include<iostream>using namespace std;const int N = 1e3 + 10;int f[N], w[N], v[N];int main(){int n, c;cin >> n >> c;for(int i = 1; i <= n; i ++ )cin >> v[i] >> w[i];for(int i = 1; i <= n; i ++)for(int j = c; j >= 0 && j >= v[i]; j --)f[j] = max(f[j], f[j - v[i]] + w[i]);cout << f[c] << endl;return 0;}}完全背包问题(每个物品可以使⽤⽆数次f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i])完全背包问题本来应该是在背包问题上再加⼀个循环的,但是可以推导出下⾯这个样⼦f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i])为什么呢,只是把0/1背包问题的f[i - 1][j - v[i] + w[i])的i-1换成了i就可以了?从头来说:完全背包问题,对于第i个物品,假设剩下的背包容量还可以装n个这个物品,那么⼀共就有n+1中决策⽅案,还有⼀种⽅案就是⼀个都不选那么对于第i个物品:它的最⼤价值就是f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]), f[i - 1][j - 2 * v[i]] + 2 * w[i]....)⼀直到n为⽌(①式)令 j = j - v[i] 那么f[i][j - v[i]] = max(f[i - 1][j - v[i]], f[i - 1][j - 2 * v[i]] + w[i].....)(②式)然后发现⼆式中的右边⽐较像⼀式中的第⼆项到最后⼀项,就是每⼀项都少了⼀个w[i]把⼆式带⼊⼀式,那么⼀式就是 f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i]), 就是下⾯这个状态⽅程啦 ;#include<iostream>using namespace std;const int N = 1e3 + 10;int f[N][N], w[N], v[N];int main(){int n, c;cin >> n >> c;for(int i = 1; i <= n; i ++)cin >> v[i] >> w[i];for(int i = 1; i <= n; i ++ ){for(int j = 1; j <= c; j ++ ){f[i][j] = f[i - 1][j];if(j >= v[i])f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i]);}}cout << f[n][c] << endl;return 0;}完全背包优化:#include<iostream>using namespace std;const int N = 1e3 + 10;int f[N], w[N], v[N];int main(){int n, c;cin >> n >> c;for(int i = 1; i <= n; i ++)cin >> v[i] >> w[i]; for(int i = 1; i <= n; i ++ )for(int j = v[i]; j <= c; j ++ )//从前往后更新 f[j] = max(f[j], f[j - v[i]] + w[i]);cout << f[c] << endl;return 0;}。
二维背包问题经典例题
二维背包问题经典例题
二维背包问题是一个经典的动态规划问题,下面是一个具体的例题:
题目描述:
有N件物品和一个容量是V的背包,背包能承受的最大重量是M。
每件物品只能用一次。
体积是vi,重量是mi,价值是wi。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。
输出最大价值。
输入格式:
第一行两个整数,N,V,M,用空格隔开,分别表示物品件数、背包容积和背包可承受的最大重量。
接下来有N行,每行三个整数vi,mi,wi,用空格隔开,分别表示第i件物品的体积、重量和价值。
输出格式:
输出一个整数,表示最大价值。
输入样例:
4 5 6
1 2 32
4 4 43
4 5 54
5 6 6
输出样例:8
思路分析:
二维背包问题可以通过建立一个三维数组f[i][j][k]来解决,其中f[i][j][k]表示在前i个物品中选出体积不超过j、质量不超过k的物品的最大价值。
状态转移方程为f[i][j][k]=max(f[i-1][j][k], f[i-1][j-v][k-m]+w),其中v表示第i个物品的体
积,m表示第i个物品的质量,w表示第i个物品的价值。
由于数组是三维的,因此可以使用滚动数组来优化空间复杂度。
具体实现时,可以将j和k的枚举顺序改为从大到小,以防止需要用到的状态被覆盖。
动态规划-01背包问题
动态规划——01背包问题01背包问题,是用来介绍动态规划算法最经典的例子,网上关于01背包问题的讲解也很多,我写这篇文章力争做到用最简单的方式,最少的公式把01背包问题讲解透彻。
01背包的状态转换方程 f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ), f[i-1,j] }f[i,j]表示在前i件物品中选择若干件放在承重为j 的背包中,可以取得的最大价值。
Pi表示第i件物品的价值。
决策:为了背包中物品总价值最大化,第i件物品应该放入背包中吗?题目描述:有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?只要你能通过找规律手工填写出上面这张表就算理解了01背包的动态规划算法。
首先要明确这张表是至底向上,从左到右生成的。
为了叙述方便,用e2单元格表示e行2列的单元格,这个单元格的意义是用来表示只有物品e 时,有个承重为2的背包,那么这个背包的最大价值是0,因为e物品的重量是4,背包装不了。
对于d2单元格,表示只有物品e,d时,承重为2的背包,所能装入的最大价值,仍然是0,因为物品e,d都不是这个背包能装的。
同理,c2=0,b2=3,a2=6。
对于承重为8的背包,a8=15,是怎么得出的呢?根据01背包的状态转换方程,需要考察两个值,一个是f[i-1,j],对于这个例子来说就是b8的值9,另一个是f[i-1,j-Wi]+Pi;在这里, f[i-1,j]表示我有一个承重为8的背包,当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值f[i-1,j-Wi]表示我有一个承重为6的背包(等于当前背包承重减去物品a的重量),当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值f[i-1,j-Wi]就是指单元格b6,值为9,Pi指的是a物品的价值,即6由于f[i-1,j-Wi]+Pi = 9 + 6 = 15 大于f[i-1,j] = 9,所以物品a应该放入承重为8的背包以下是actionscript3 的代码[java]view plain copy1.public function get01PackageAnswer(bagItems:Array,bagSize:int):Array2.{3. var bagMatrix:Array=[];4. var i:int;5. var item:PackageItem;6.for(i=0;i<bagItems.length;i++)7. {8. bagMatrix[i] = [0];9. }10.for(i=1;i<=bagSize;i++)11. {12.for(var j:int=0;j<bagItems.length;j++)13. {14. item = bagItems[j] as PackageItem;15.if(item.weight > i)16. {17.//i背包转不下item18.if(j==0)19. {20. bagMatrix[j][i] = 0;21. }22.else23. {24. bagMatrix[j][i]=bagMatrix[j-1][i];25. }26. }27.else28. {29.//将item装入背包后的价值总和30. var itemInBag:int;31.if(j==0)32. {33. bagMatrix[j][i] = item.value;34.continue;35. }36.else37. {38. itemInBag = bagMatrix[j-1][i-item.weight]+item.value;39. }40. bagMatrix[j][i] = (bagMatrix[j-1][i] > itemInBag ? bagMatrix[j-1][i] : itemInBag)41. }42. }43. }44.//find answer45. var answers:Array=[];46. var curSize:int = bagSize;47.for(i=bagItems.length-1;i>=0;i--)48. {49. item = bagItems[i] as PackageItem;50.if(curSize==0)51. {52.break;53. }54.if(i==0 && curSize > 0)55. {56. answers.push();57.break;58. }59.if(bagMatrix[i][curSize]-bagMatrix[i-1][curSize-item.weight]==item.value)60. {61. answers.push();62. curSize -= item.weight;63. }64. }65.return answers;66.}PackageItem类[java]view plain copy1.public class PackageItem2.{3.public var name:String;4.public var weight:int;5.public var value:int;6.public function PackageItem(name:String,weight:int,value:int)7. { = name;9.this.weight = weight;10.this.value = value;11. }12.}测试代码[java]view plain copy1.var nameArr:Array=['a','b','c','d','e'];2.var weightArr:Array=[2,2,6,5,4];3.var valueArr:Array=[6,3,5,4,6];4.var bagItems:Array=[];5.for(var i:int=0;i<nameArr.length;i++)6.{7. var bagItem:PackageItem = new PackageItem(nameArr[i],weightArr[i],valueArr[i]);8. bagItems[i]=bagItem;9.}10.var arr:Array = ac.get01PackageAnswer(bagItems,10);出师表两汉:诸葛亮先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。
python背包问题例题
python背包问题例题背包问题是一个经典的组合优化问题,其目标是在给定的一组物品中选择一些物品放入背包,使得背包中物品的总价值最大,同时限制背包的总重量不超过一定值。
下面给出一个简单的例题来说明背包问题:假设有以下物品:物品1:重量2,价值3物品2:重量3,价值4物品3:重量4,价值5物品4:重量5,价值6现在有一个背包,其最大承重为10。
问如何选择物品放入背包,使得背包中物品的总价值最大?针对这个问题,我们可以使用动态规划的方法来解决。
定义一个二维数组dp,其中dp[i][j]表示将前i个物品放入承重为j的背包中所能达到的最大价值。
首先,考虑边界情况:当i=0或j=0时,dp[i][j]均为0,表示没有物品可选或者背包承重为0,此时背包中的总价值为0。
然后,我们可以通过以下递推关系来更新dp数组:若第i个物品的重量wi小于等于j,则可以选择将物品i放入背包中,此时背包中的总价值为dp[i-1][j-wi]+vi,即考虑前i-1个物品放入承重为j-wi的背包中所能达到的最大价值,再加上第i个物品的价值vi。
若第i个物品的重量wi大于j,则无法选择将物品i放入背包中,此时背包中的总价值仍然为dp[i-1][j]。
最终,dp中的最后一个元素dp[n][m](n为物品个数,m为背包的最大承重)即为所求的最优解。
对于给定的例题,可以得到以下dp数组:0 0 0 0 0 0 0 0 0 0 00 0 3 3 3 3 3 3 3 3 30 0 3 4 4 7 7 7 7 7 70 0 3 4 5 7 8 9 9 12 120 0 3 4 5 7 8 9 10 12 13其中,dp[4][10]为最终的最大价值,即13。
根据dp数组的构造过程,我们还可以知道选择的物品为1、3、4,其总重量为11,总价值为13。
这就是背包问题的一个简单例题的解答过程。
在实际应用中,背包问题可以有更多的约束条件和变种形式,需要根据具体情况选择合适的算法和策略来解决。
动态规划方案解决算法背包问题实验报告含源代码
动态规划方案解决算法背包问题实验报告含嘿,大家好!今天我来给大家分享一个相当有趣的编程问题——背包问题。
这可是算法领域里的经典难题,也是体现动态规划思想的好例子。
我会用我10年的方案写作经验,给大家带来一份详细的实验报告,附带哦!让我简单介绍一下背包问题。
假设你是一个盗贼,要盗取一个博物馆里的宝贝。
博物馆里有n个宝贝,每个宝贝都有它的价值v和重量w。
你有一个承重为W的背包,你希望放入背包的宝贝总价值最大,但总重量不能超过背包的承重。
这个问题,就是我们要解决的背包问题。
一、算法思路1.创建一个二维数组dp,dp[i][j]表示前i个宝贝放入一个承重为j的背包中,能达到的最大价值。
2.初始化dp数组,dp[0][j]=0,因为如果没有宝贝,那么无论背包承重多少,价值都是0。
3.遍历每个宝贝,对于每个宝贝,我们有两种选择:放入背包或者不放入背包。
4.如果不放入背包,那么dp[i][j]=dp[i-1][j],即前i-1个宝贝放入一个承重为j的背包中,能达到的最大价值。
5.如果放入背包,那么dp[i][j]=dp[i-1][j-w[i]]+v[i],即前i-1个宝贝放入一个承重为j-w[i]的背包中,加上当前宝贝的价值。
6.dp[i][j]取两种情况的最大值。
二、defknapsack(W,weights,values,n):dp=[[0for_inrange(W+1)]for_inrange(n+1)]foriinrange(1,n+1):forjinrange(1,W+1):ifj>=weights[i-1]:dp[i][j]=max(dp[i-1][j],dp[i-1][j-weights[i-1]]+values[i -1])else:dp[i][j]=dp[i-1][j]returndp[n][W]测试数据W=10weights=[2,3,4,5]values=[3,4,5,6]n=len(values)输出结果max_value=knapsack(W,weights,values,n)print("最大价值为:",max_value)三、实验结果分析通过上面的代码,我们可以得到最大价值为15。
5.5动态规划求解01背包问题
xn-2,…,x1将依次推导得出
例2的解向量推导
S0={(0,0)}
S1={(0,0),(1,2)}
S2={(0,0),(1,2), (2,3),(3,5)}
● Si的构造
记S1i 是fi-1(X-wi)+pi的所有序偶的集合,则
S1i {( P,W ) | (P pi ,W wi ) S i1}
其中,Si-1是fi-1的所有序偶的集合
Si的构造:由Si-1和 S1i 按照支配规则合并而成。
支配规则:如果Si-1和S1i 之一有序偶(Pj,Wj),另一有(Pk,Wk),
5.5动态规划求解 0/1背包问题
1.问题描述 背包容量M,n个物品,分别具有效益值P1…Pn,物
品重量w1…wn,从n个物品中,选择若干物品放入 背包,物品要么整件放入背包,要么不放入。怎 样决策可以使装入背包的物品总效益值最大?
形式化描述:
目标函数:
约束条件:
max pixi
1i j
wixi M
1in
xi
0或1,
pi
0, wi
0,1
i
n
0/1背包问题:KNAP(1,n,M)
❖ 0/1背包问题:M=6,N=3,W=(3,3,4),P=(3,3,5) ❖ 贪心法:p3/w3 > p1/w1 > p2/w2 ❖ 贪心解 ∑P=5(0,0,1) ❖ 最优解是:∑P=6(1,1,0)
❖ 贪心法求解0/1背包问题不一定得到最优解! ❖ 动态规划求解的问题必须满足最优化原理
C语言动态规划之背包问题详解
C语⾔动态规划之背包问题详解01背包问题给定n种物品,和⼀个容量为C的背包,物品i的重量是w[i],其价值为v[i]。
问如何选择装⼊背包的物品,使得装⼊背包中的总价值最⼤?(⾯对每个武平,只能有选择拿取或者不拿两种选择,不能选择装⼊某物品的⼀部分,也不能装⼊物品多次)声明⼀个数组f[n][c]的⼆维数组,f[i][j]表⽰在⾯对第i件物品,且背包容量为j时所能获得的最⼤价值。
根据题⽬要求进⾏打表查找相关的边界和规律根据打表列写相关的状态转移⽅程⽤程序实现状态转移⽅程真题演练:⼀个旅⾏者有⼀个最多能装M公⽄的背包,现在有n件物品,它们的重量分别是W1、W2、W3、W4、…、Wn。
它们的价值分别是C1、C3、C2、…、Cn,求旅⾏者能获得最⼤价值。
输⼊描述:第⼀⾏:两个整数,M(背包容量,M<= 200)和N(物品数量,N<=30);第2…N+1⾏:每⾏两个整数Wi,Ci,表⽰每个物品的质量与价值。
输出描述:仅⼀⾏,⼀个数,表⽰最⼤总价值样例:输⼊:10 42 13 34 57 9输出:12解题步骤定义⼀个数组dp[i][j]表⽰容量为j时,拿第i个物品时所能获取的最⼤价值。
按照题⽬要求进⾏打表,列出对应的dp表。
W[i](质量)V[i](价值)01234567891000000000000210011111111133001334444444500135568899790013556991012对于⼀个动态规划问题设置下标时最好从0开始,因为动态规划经常会和上⼀个状态有关系!从上⾯的dp表可以看出来对于⼀个物品我们拿还是不难需要进⾏两步来判断。
第⼀步:判断背包当前的容量j是否⼤于物品当前的质量,如果物品的质量⼤于背包的容量那么就舍弃。
第⼆步:如果背包可以装下这个物品,就需要判断装下该物品获取的最⼤价值是不是⼤于不装下这个物品所获取的最⼤价值,如果⼤于那么就把东西装下!根据这样的思想我们可以得到状态转移⽅程:如果单签背包的容量可以装下物品:dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);如果当前背包的容量装不下该物品:dp[i][j]=dp[i-1][j];#include <stdio.h>int max(const int a,const int b){return a>b ? a:b;}int main(){int w[35]={0},v[35]={0},dp[35][210]={0};int n,m;scanf("%d %d",&m,&n);int i,j;for(i=1;i<=n;i++){scanf("%d %d",&w[i],&v[i]);}for(i=1;i<=n;i++){for(j=1;j<=m;j++){if(j>=w[i])//如果当前背包的容量⼤于商品的质量{dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);//判断是否应该拿下}else//⼤于背包的当前容量{dp[i][j]=dp[i-1][j];}}}for(int k=0;k<=n;k++){for(int l=0;l<=m;l++){printf("%d ",dp[k][l]);}printf("\n");}printf("%d\n",dp[n][m]);}通过运⾏以上程序可以看到最终的输出dp表和我们的预期是相符合的!但是并没有结束,动态规划有⼀个后⽆效性原则(当前状态只与前⼀个状态有关)。
物流系统动态规划算法经典例题matlab
物流系统动态规划算法经典例题matlab
1、生产计算问题
工厂生产某种产品,每单位(千件)的成本为1 (千元),每次开工的固定成本为3(千元),工厂每季度的最大生产能力为6 (千件)。
经调查,市场对该产品的需求量第一、二、三、四季度分别为2, 3,2,4 (千件)。
如果工厂在第一、二季度将全年的需求都生产出来,自然可以降低成本(少付固定成本费),但是对于第三、四季度才能上市的产品需付存储费,每季每千件的存储费为0.5 (千元)。
还规定年初和年末这种产品均无库存。
试制定-一个生产计划,即安排每个季度的产量,使一年的总费用(生产成本和存储费)最少。
2、背包问题
有编号分别为a,b,c,d,e 的五种金银物品,它们的重量分别是2,2,6,5,4,它们的价值分别为6,3,5,4,6,现有一个承重为10的背包,如何让背包带走金银物品的价值总和最大?。
博物馆大盗问题的动态规划(背包问题)
博物馆⼤盗问题的动态规划(背包问题)博物馆⼤盗问题⼤盗潜⼊博物馆,⾯前有5件宝物,分别有重量和价值,⼤盗的背包仅能负重20公⽄,请问如何选择宝物,总价值最⾼?item weight value1232343484585610m(i, W) 表⽰前i(1<=i<=5)个宝物中,组合不超过W(1<=W<=20) 重量,得到的最⼤价值。
第 i 件宝物重量 Wi > 背包承重 W, 那么m(i, W) = m(i-1, W);第 i 件宝物重量 Wi <= 背包承重 W,如果第 i 件宝物太重,加不到背包中,那么前 i 件宝物价值等于前 i-1 件宝物价值,即 m(i, W) = m(i-1, W);如果第 i 件宝物可以加⼊到背包中,那么前 i 件宝物价值等于前 i-1 件宝物价值加上第 i 件宝物价值Wi,即 m(i, W) = m(i-1, W-Wi)+vi。
因此,m(i, W) 应该是m(i-1, W) 和m(i-1, W-Wi)+vi 两者最⼤值,即m(i, W) = max{m(i-1, W), m(i-1, W-Wi)+vi}以m(5, 5)为例,m(5,5) = m(4,5) = max(m(3,5), m(3,0)+8), 动态规划表格如下:动态规划解法代码# ⽤⼀个列表来保存宝物的重量w和价值vtr = [None, {'w':2, 'v':3}, {'w':3, 'v':4},{'w':4, 'v':8}, {'w':5, 'v':8}, {'w':9, 'v':10}]# 设置背包最⼤承重max_w = 20# 初始化⼆维表格m[(i, w)],将表格中所有价值均初始化为0# 表⽰前i个宝物中,最⼤重量w的组合,所得到的最⼤价值# 当i或w为0时,价值为0m = {(i, w):0 for i in range(len(tr))for w in range(max_w + 1)}# 逐个填写⼆维表格# 外层循环为 i个宝物,[1,6)的循环# 内层循环为重量w,[1, max_w+1)的循环for i in range(1, len(tr)):for w in range(1, max_w + 1):if tr[i]['w'] > w:# 装不下第i个宝物,即不装第i个宝物m[(i, w)] = m[(i-1, w)]else:# 装得下第i个宝物时,在不装第i个宝物与装第i个宝物这两种情况下,取最⼤价值m[(i, w)] = max(m[(i-1, w)],m[(i-1, w-tr[i]['w'])] + tr[i]['v'])# 输出结果print(m[(len(tr)-1, max_w)])递归解法# ⽤⼀个字典来保存宝物的重量w和价值vtr = {(2, 3), (3, 4), (4, 8), (5, 8), (9, 10)}# 设置背包最⼤承重max_w = 20# 初始化记忆化表格m# key是(宝物组合,最⼤重量),value是最⼤价值m = {}def thief(tr, w):if tr == set() or w == 0: # 基本结束条件m[(tuple(tr), w)] = 0return 0elif (tuple(tr), w) in m:return m[(tuple(tr), w)]else:vmax = 0for t in tr:if t[0] <= w:# 逐个从集合中去掉某个宝物t,递归调⽤ # 选出所有价值中的最⼤值v = thief(tr-{t}, w-t[0]) + t[1] # 调⽤⾃⾝ vmax = max(vmax, v)m[(tuple(tr), w)] = vmaxreturn vmax# 输出结果print(thief(tr, max_w))。
动态规划应用案例
动态规划应用案例动态规划是一种解决复杂问题的优化算法。
它通过将问题拆分成多个子问题,并记录每个子问题的解,以避免重复计算,从而提高算法的效率。
在实际应用中,动态规划被广泛用于解决各种问题,包括最优化问题、路径搜索问题、序列问题等。
本文将介绍几个动态规划的应用案例,以展示其在实际问题中的强大能力。
案例一:背包问题背包问题是动态规划中经典的一个例子。
假设有一个背包,容量为V,现有n个物品,每个物品的重量为wi,价值为vi。
要求在不超过背包容量的前提下,选取一些物品放入背包,使得背包中的物品总价值最大。
这个问题可以用动态规划来解决。
首先定义一个二维数组dp,其中dp[i][j]表示在前i个物品中选择一些物品,使得它们的总重量不超过j时的最大总价值。
然后,可以得到如下的状态转移方程:dp[i][j] = max(dp[i-1][j], dp[i-1][j-wi] + vi)最后,根据状态转移方程,可以循环计算出dp[n][V]的值,即背包中物品总价值的最大值,从而解决了背包问题。
案例二:最长递增子序列最长递增子序列是指在一个序列中,选取一些数字,使得这些数字按照顺序排列,且长度最长。
动态规划也可以应用于解决最长递增子序列问题。
假设有一个序列nums,长度为n。
定义一个一维数组dp,其中dp[i]表示以nums[i]为结尾的最长递增子序列的长度。
然后,可以得到如下的状态转移方程:dp[i] = max(dp[j] + 1),其中j < i且nums[j] < nums[i]最后,循环计算出dp数组中的最大值,即为最长递增子序列的长度。
案例三:最大子数组和最大子数组和问题是指在一个数组中,选取一段连续的子数组,使得子数组的和最大。
动态规划也可以用于解决最大子数组和问题。
假设有一个数组nums,长度为n。
定义一个一维数组dp,其中dp[i]表示以nums[i]为结尾的连续子数组的最大和。
然后,可以得到如下的状态转移方程:dp[i] = max(dp[i-1] + nums[i], nums[i])最后,循环计算出dp数组中的最大值,即为最大子数组的和。
DP-资源背包动态规划
带条件的背包问题(1)
• 有N件物品; • 第i件物品Wi公斤; • 第i件物品价值Ci元; • 第i件物品可能带0~2个附件; • 若装载附件,必须装载主件,反之没有约束; • 现有一辆载重M公斤的卡车; • 问选取装载哪些物品,使得卡车运送的总价值最
大?
分析
• 假设只有主件的情况 ,显然与经典背包问题完 全相同!
动态规划
• 可以按每个物品进行规划,同样每种物品有选和 不选两种选择
• 设F(i,j)表示前i件物品载重为j的最大效益,则有
F(i 1, j w[i]) C[i],第i种物品装载 F(i, j) MaxF(i 1, j),第i种物品不装载
• 1<=i<=N, 0<=j<=N • 初值:F(0,j)=0 • F(N,M)即答案 • 显然时间复杂度为O(NM)
,与数据相关
总结
• 对于资源类动态规划问题,我们可以看出,问题描述必须 有一个基本要素:资源,有时这种资源可能是金钱、空间 或者时间,问题就是要对这些资源如何分配,一种基本的 想法是将资源应用于前i个阶段,然后考虑第i个阶段和前i1个阶段之间的关系。
• 设前i个点的消耗j的资源得到的最优值,研究前i-1个点消 耗的资源的最优值,利用第i个点决策转移,如下图。
主程序如下
for i:=1 to m do f[0,i]:=0; //初始化
for i:=1 to n do f[i,0]:=0;
for i:=1 to n do
// 动态规划,递推求f
for j:=1 to m do
begin
动态规划基础之背包问题
动态规划基础之背包问题01背包问题是最经典的背包问题,没有之⼀。
关于背包问题,我举过⼀个例⼦,有⼀天,阿⾥巴巴背着⼀个背包来到了⼭洞⾥,⾯对⼤量的⾦银财宝,他的背包却容量有限,他要如何选择呢?假如说这些财宝可以⽆限分割,例如是⾦粉,银粉,铜粉,他只要贪⼼地先放⾦,再放银,最后放铜,直到装满,那么如果这些财宝各有体积且⽆法切割,很显然,贪⼼⽆法解决这个问题,那么就只能使⽤动态规划了。
举个例题Bone CollectorTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 128982 Accepted Submission(s): 51264Problem DescriptionMany years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?InputThe first line contain a integer T , the number of cases.Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.OutputOne integer per line representing the maximum of the total value (this number will be less than 231).Sample Input1 5 10 12345 5 4 3 2 1Sample Output14本题为01背包裸题,其状态转移⽅程为dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]),dp[i][j]表⽰遍历到第i个物品,使⽤了j的容量时得到的最⼤价值,其值由是否将这个物品放⼊决定。
动态规划之01背包问题
动态规划之01背包问题01背包问题问题描述:给定 n 件物品,物品的重量为 w[i],物品的价值为 c[i]。
现挑选物品放⼊背包中,假定背包能承受的最⼤重量为 V,问应该如何选择装⼊背包中的物品,使得装⼊背包中物品的总价值最⼤?针对这个问题,本⼈理解了多次,也了看各种题解,尝试各种办法总还觉得抽象;或者说,看了多次以后,只是把题解的状态转移⽅程记住了⽽已,并没有真正的“掌握”其背后的逻辑。
直到我看了,在此感谢作者并记录于此。
01背包问题之另⼀种风格的描述:假设你是⼀个⼩偷,背着⼀个可装下4磅东西的背包,你可以偷窃的物品如下:为了让偷窃的商品价值最⾼,你该选择哪些商品?暴⼒解法最简单的算法是:尝试各种可能的商品组合,并找出价值最⾼的组合。
这样显然是可⾏的,但是速度⾮常慢。
在只有3件商品的情况下,你需要计算8个不同的集合;当有4件商品的时候,你需要计算16个不同的集合。
每增加⼀件商品,需要计算的集合数都将翻倍!对于每⼀件商品,都有选或不选两种可能,即这种算法的运⾏时间是O(2ⁿ)。
动态规划解决这样问题的答案就是使⽤动态规划!下⾯来看看动态规划的⼯作原理。
动态规划先解决⼦问题,再逐步解决⼤问题。
对于背包问题,你先解决⼩背包(⼦背包)问题,再逐步解决原来的问题。
⽐较有趣的⼀句话是:每个动态规划都从⼀个⽹格开始。
(所以学会⽹格的推导⾄关重要,⽽有些题解之所以写的不好,就是因为没有给出⽹格的推导过程,或者说,没有说清楚为什么要”这样“设计⽹格。
本⽂恰是解决了我这⽅⾯长久以来的困惑!)背包问题的⽹格如下:⽹格的各⾏表⽰商品,各列代表不同容量(1~4磅)的背包。
所有这些列你都需要,因为它们将帮助你计算⼦背包的价值。
⽹格最初是空的。
你将填充其中的每个单元格,⽹格填满后,就找到了问题的答案!1. 吉他⾏后⾯会列出计算这个⽹格中单元格值得公式,但现在我们先来⼀步⼀步做。
⾸先来看第⼀⾏。
这是吉他⾏,意味着你将尝试将吉他装⼊背包。
背包问题(Knapsackproblem)采用动态规划求解
背包问题(Knapsackproblem)采⽤动态规划求解问题说明:假设有⼀个背包的负重最多可达8公⽄,⽽希望在背包中装⼊负重范围内可得之总价物品,假设是⽔果好了,⽔果的编号、单价与重量如下所⽰:李⼦4KGNT$45001苹果5KGNT$57002橘⼦2KGNT$22503草莓1KGNT$1100解法背包问题是关于最佳化的问题,要解最佳化问题可以使⽤「动态规划」(Dynamicprogramming),从空集合开始,每增加⼀个元素就先求出该阶段的最佳解,直到所有的元素加⼊⾄集合中,最后得到的就是最佳解。
下⾯我们看下代码:/*问题:假设有⼀个背包的负重最多可达8公⽄,⽽希望在背包中装⼊负重范围内可得之总价物品算法说明:采⽤动态规划,在当前阶段求解出最好的解,如此反复⽇期:2013/8/18张威*/#include <iostream>#include <time.h>using namespace std;#define MAXSIZE 8//定义全局变量char name[5][5] = {"李⼦","苹果","橘⼦","草莓","甜⽠"};//⽔果名称int wight[5] = {4,5,2,1,6};//单个⽔果所占⽄数int price[5] = {4500,5700,2250,1100,6700};//单个⽔果的价值int perkg_price[5];//每⽄⽔果的价钱int perkg_num[5] = {0,1,2,3,4};void GetNmae(int num){for (int i = 0;i <= 4;i++){cout<<name[num][i];}}void GetBestAnswer(int currentwigh){//判断递归终⽌条件if (currentwigh >= MAXSIZE){cout<<"包裹已经满了,⽆法再装进东西"<<endl;}else{//check⽤来表证到底剩下来的物品⾥⾯还有没有能装进去背包⾥的bool check = true;int i = 0;for (;i <= 4;i++){//若是没有进⼊到这个条件内,说明剩下来的物品的重量都超过了背包剩余重量,到此结束.否则i就代表当前所能选中的最优解if (wight[perkg_num[i]] <= MAXSIZE-currentwigh){check = false;break;}}if (check == true){cout<<"已经装不进去任何⽔果了"<<endl;}else{//得到最优解,并且将当前重量增加,进⼊下⼀次递归currentwigh += wight[perkg_num[i]];cout<<"购买了";GetNmae(perkg_num[i]);cout<<endl;GetBestAnswer(currentwigh);}}}int main(){//计算出每⽄⽔果的价钱,便于动态规划时求出当前最佳解for (int i = 0;i <= 4;i++){perkg_price[i] = price[i] / wight[i];}//对perkg_num进⾏排序,同时保证单价和perkg_num之间的⼀⼀对应关系.即两个数组要同时变化//采⽤的是冒泡排序,在元素进⾏交换时perkg_num和perkg_price同时变化for (int i = 0;i <= 3;i++){for (int j = i;j <= 3;j++){if (perkg_price[j] < perkg_price[j+1]){int temp1 = perkg_price[j];int temp2 = perkg_num[j];perkg_price[j] = perkg_price[j+1];perkg_price[j+1] = temp1;perkg_num[j] = perkg_num[j+1];perkg_num[j+1] = temp2;}}}//开始计算求解GetBestAnswer(0);return0;}背包问题在这⾥,算法的主要思想有两个:1.通过冒泡排序得到⼀个单价表,并将物品的ID与之配对起来.这样我们在每次的递归中通过ID找到物品的相应属性,筛选出当前步骤的最优解出来2.通过递归,传递当前的重量,得到还剩余的重量,根据前⾯的单价表,筛选出可选的最优解,然后将重量变化进⼊下⼀次递归.这是最⼤空间为8的运⾏结果: 这是最⼤空间为29的运⾏结果:下⾯附上指导书上⾯的代码:#include <stdio.h>#include <stdlib.h>#define LIMIT 8// 重量限制#define N 5// 物品种类#define MIN 1// 最⼩重量struct body {char name[20];int size;int price;};背包负重12345678value110225335450570680795905item32301323背包负重12345678value110225335450570680795905item32301323typedef struct body object;int main(void) {int item[LIMIT+1] = {0};int value[LIMIT+1] = {0};int newvalue, i, s, p;object a[] = {{"李⼦", 4, 4500}, {"苹果", 5, 5700},{"橘⼦", 2, 2250},{"草莓", 1, 1100},{"甜⽠", 6, 6700}};for(i = 0; i < N;i++) {for(s = a[i].size; s <= LIMIT;s++) { p = s - a[i].size;newvalue = value[p] + a[i].price;if(newvalue > value[s]) {// 找到阶段最佳解value[s] = newvalue;item[s] = i;}}}printf("物品\t价格\n");for(i = LIMIT;i >= MIN;i = i - a[item[i]].size) {printf("%s\t%d\n",a[item[i]].name, a[item[i]].price);}printf("合计\t%d\n", value[LIMIT]);return0;}Javaclass Fruit {private String name;private int size;private int price;public Fruit(String name,int size, int price){ = name;this.size = size;this.price = price;}public String getName(){return name;}public int getPrice(){return price;}public int getSize() {return size;}}public class Knapsack {public static void main(String[] args){final int MAX = 8;final int MIN = 1;int[] item = new int[MAX+1];int[] value = new int[MAX+1];Fruit fruits[] = {new Fruit("李⼦", 4, 4500),new Fruit("苹果", 5, 5700),new Fruit("橘⼦", 2, 2250),new Fruit("草莓", 1, 1100),new Fruit("甜⽠", 6, 6700)};for(int i = 0; i < fruits.length;i++) {for(int s = fruits[i].getSize(); s <= MAX;s++){int p = s - fruits[i].getSize();int newvalue = value[p] +fruits[i].getPrice();if(newvalue > value[s]) {// 找到阶段最佳解value[s] = newvalue;item[s] = i;}}}System.out.println("物品\t价格");for(int i = MAX;i >= MIN;i = i - fruits[item[i]].getSize()) {System.out.println(fruits[item[i]].getName()+"\t" + fruits[item[i]].getPrice());}System.out.println("合计\t" + value[MAX]);}}指导书上⾯的代码我居然没想到使⽤结构体,失策失策,都没⽤什么⾼级点的数据结构,看起来貌似很复杂的样⼦.明天再看。
动态规划之背包问题01——Java实现
动态规划之背包问题01——Java实现背包问题具体例⼦:假设现有容量10kg的背包,另外有3个物品,分别为a1,a2,a3。
物品a1重量为3kg,价值为4;物品a2重量为4kg,价值为5;物品a3重量为5kg,价值为6。
将哪些物品放⼊背包可使得背包中的总价值最⼤?⾸先想到的,⼀般是穷举法,⼀个⼀个地试,对于数⽬⼩的例⼦适⽤,如果容量增⼤,物品增多,这种⽅法就⽆⽤武之地了。
其次,可以先把价值最⼤的物体放⼊,这已经是贪婪算法的雏形了。
如果不添加某些特定条件,结果未必可⾏。
最后,就是动态规划的思路了。
先将原始问题⼀般化,欲求背包能够获得的总价值,即欲求前i个物体放⼊容量为m(kg)背包的最⼤价值c[i][m]——使⽤⼀个数组来存储最⼤价值,当m取10,i取3时,即原始问题了。
⽽前i个物体放⼊容量为m(kg)的背包,⼜可以转化成前(i-1)个物体放⼊背包的问题。
下⾯使⽤数学表达式描述它们两者之间的具体关系。
表达式中各个符号的具体含义。
w[i] : 第i个物体的重量; p[i] : 第i个物体的价值; c[i][m] :前i个物体放⼊容量为m的背包的最⼤价值; c[i-1][m] :前i-1个物体放⼊容量为m的背包的最⼤价值; c[i-1][m-w[i]] :前i-1个物体放⼊容量为m-w[i]的背包的最⼤价值; 由此可得: c[i][m]=max{c[i-1][m-w[i]]+pi , c[i-1][m]}(下图将给出更具体的解释) 根据上式,对物体个数及背包重量进⾏递推,列出⼀个表格(见下表),表格来⾃(),当逐步推出表中每个值的⼤⼩,那个最⼤价值就求出来了。
推导过程中,注意⼀点,最好逐⾏⽽⾮逐列开始推导,先从编号为1的那⼀⾏,推出所有c[1][m]的值,再推编号为2的那⾏c[2] [m]的⼤⼩。
这样便于理解。
思路厘清后,开始编程序,Java代码如下所⽰:public class BackPack {public static void main(String[] args) {int m = 10;int n = 3;int w[] = {3, 4, 5};int p[] = {4, 5, 6};int c[][] = BackPack_Solution(m, n, w, p);for (int i = 1; i <=n; i++) {for (int j = 1; j <=m; j++) {System.out.print(c[i][j]+"\t");if(j==m){System.out.println();}}}//printPack(c, w, m, n);}/*** @param m 表⽰背包的最⼤容量* @param n 表⽰商品个数* @param w 表⽰商品重量数组* @param p 表⽰商品价值数组*/public static int[][] BackPack_Solution(int m, int n, int[] w, int[] p) {//c[i][v]表⽰前i件物品恰放⼊⼀个重量为m的背包可以获得的最⼤价值int c[][] = new int[n + 1][m + 1];for (int i = 0; i < n + 1; i++)c[i][0] = 0;for (int j = 0; j < m + 1; j++)c[0][j] = 0;for (int i = 1; i < n + 1; i++) {for (int j = 1; j < m + 1; j++) {//当物品为i件重量为j时,如果第i件的重量(w[i-1])⼩于重量j时,c[i][j]为下列两种情况之⼀://(1)物品i不放⼊背包中,所以c[i][j]为c[i-1][j]的值//(2)物品i放⼊背包中,则背包剩余重量为j-w[i-1],所以c[i][j]为c[i-1][j-w[i-1]]的值加上当前物品i的价值if (w[i - 1] <= j) {if (c[i - 1][j] < (c[i - 1][j - w[i - 1]] + p[i - 1]))c[i][j] = c[i - 1][j - w[i - 1]] + p[i - 1];elsec[i][j] = c[i - 1][j];} elsec[i][j] = c[i - 1][j];}}return c;}运⾏结果为:0 0 4 4 4 4 4 4 4 40 0 4 5 5 5 9 9 9 90 0 4 5 6 6 9 10 11 11Process finished with exit code 0。
案例4:背包问题
案例:背包问题有一个徒步旅行者,已知他能承受的旅行背包的重量不超过a (kg )。
设有n 种物品可供他选择装入背包,这n 种物品分别编号为1,2,…,n 。
其中第i 种物品每件的重量为a i (kg ),其使用价值(指一件第i 种物品对旅行者来说所带来的好处的一种数量指标)为c i (i =1,2,…,n )。
问这位旅行者应如何选择携带这n 种物品的件数,使得总价值最大?⏹ 分析:这是一个组合最优化问题,易将此问题归结为一个线性整数规划问题。
⏹ 建立线性规划模型【建立线性规划模型】设旅行者选择携带第i 种物品的件数为i x ,不难看出,背包问题可以归结为如下的线性规划问题:11 max s.t. 01,2,ni ii n i i i i z c x a x ax i n===≤≥=∑∑且整,,⏹ 建立动态规划模型 【建立动态规划模型】设把可装入背包的物品种类分为n 个阶段。
在第i 阶段先装入前i 种物品(i =1,2,…,n )。
在第i 阶段开始时,把旅行者背包中允许装入前i 种物品的总重量作为状态变量,设为y 。
装入每种物品的件数x i (i =1,2,…,n )为各阶段的决策变量。
变量说明:设()k f y 等于当背包中允许装入物品的总重量不超过y 和只允许装入前k 种物品采用最优策略时的最大使用价值。
(k =1,2,…,n )。
则11()max (1,2,,)k i i i k k i i i a x y f y c x k n ==≤==∑∑并且当k =n ,y =a 时,有11()max n i i i nn i i i a x a f a c x ==≤=∑∑ 显然()n f a 也就是上述线性规划模型的最优解。
把上式转化为递归方程: (属于前向算法){}1111010()max ()max () i k k y x a k k k k k k y x a f y c x f y c x f y a x ⎢⎥≤≤⎢⎥⎣⎦-⎢⎥≤≤⎢⎥⎣⎦=⎧⎪⎪⎨=+-⎪⎪⎩其中k x 为非负整数。
01背包问题例题
01背包问题例题01背包问题是一个经典的动态规划问题,下面是一个具体的例子:假设有一个容量为5kg的背包,现有若干物品可供选择,每件物品的重量和价值如下:物品A:重量3kg,价值20元物品B:重量2kg,价值10元物品C:重量1kg,价值5元我们需要选择一些物品放入背包中,使得背包中的物品总价值最大。
根据01背包问题的解决方案,我们可以采用动态规划的方法来解决这个问题。
首先,我们创建一个二维数组dp,其中dp[i][j]表示在前i个物品中选择,总重量不超过jkg的最大价值。
接下来,我们按照物品的重量和价值进行排序。
然后,从第一个物品开始,依次计算每个物品放入背包和不放入背包的价值,取二者中的较大值作为dp[i][j]的值。
最终,dp[n][c]的值就是所求的最大价值,其中n是物品的数量,c是背包的容量。
具体的计算过程如下:物品A:放入背包的价值为20元,不放入背包的价值为0元,所以dp[1][3]=20。
物品B:放入背包的价值为10元,不放入背包的价值为0元,所以dp[1][2]=10。
物品C:放入背包的价值为5元,不放入背包的价值为0元,所以dp[1][1]=5。
物品A:放入背包的价值为20+5=25元(取出物品C后重量为1kg),不放入背包的价值为dp[1][2]=10元,所以dp[2][3]=25。
物品B:放入背包的价值为10+5=15元(取出物品C后重量为2kg),不放入背包的价值为dp[1][2]=10元,所以dp[2][2]=15。
物品C:放入背包的价值为5元,不放入背包的价值为0元,所以dp[2][1]=5。
物品A:放入背包的价值为20+10=30元(取出物品B后重量为3kg),不放入背包的价值为dp[2][2]=15元,所以dp[3][3]=30。
物品B:放入背包的价值为10+5=15元(取出物品C后重量为2kg),不放入背包的价值为dp[2][2]=15元,所以dp[3][2]=15。
Python基于动态规划算法解决01背包问题实例
Python基于动态规划算法解决01背包问题实例本⽂实例讲述了Python基于动态规划算法解决01背包问题。
分享给⼤家供⼤家参考,具体如下:在01背包问题中,在选择是否要把⼀个物品加到背包中,必须把该物品加进去的⼦问题的解与不取该物品的⼦问题的解进⾏⽐较,这种⽅式形成的问题导致了许多重叠⼦问题,使⽤动态规划来解决。
n=5是物品的数量,c=10是书包能承受的重量,w=[2,2,6,5,4]是每个物品的重量,v=[6,3,5,4,6]是每个物品的价值,先把递归的定义写出来:然后⾃底向上实现,代码如下:def bag(n,c,w,v):res=[[-1 for j in range(c+1)] for i in range(n+1)]for j in range(c+1):res[0][j]=0for i in range(1,n+1):for j in range(1,c+1):res[i][j]=res[i-1][j]if j>=w[i-1] and res[i][j]<res[i-1][j-w[i-1]]+v[i-1]:res[i][j]=res[i-1][j-w[i-1]]+v[i-1]return resdef show(n,c,w,res):print('最⼤价值为:',res[n][c])x=[False for i in range(n)]j=cfor i in range(1,n+1):if res[i][j]>res[i-1][j]:x[i-1]=Truej-=w[i-1]print('选择的物品为:')for i in range(n):if x[i]:print('第',i,'个,',end='')print('')if __name__=='__main__':n=5c=10w=[2,2,6,5,4]v=[6,3,5,4,6]res=bag(n,c,w,v)show(n,c,w,res)输出结果如下:更多关于Python相关内容感兴趣的读者可查看本站专题:《》、《》、《》、《》、《》及《》希望本⽂所述对⼤家Python程序设计有所帮助。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
动态规划经典案例详解之背包问题【摘要】本文主要从动态规划经典案例——背包问题的动态规划设计思路出发,结合具体实例,对动态规划在程序设计中的典型应用以及衍生拓展进行详细分析。
【关键字】动态规划信息学奥赛0/1背包问题动态规划并非一个算法,而是一种解题的思路,其核心思想是通过使用大量的存储空间把中间结果记录下来,大大减少重复计算的时间,从而提高的程序的执行效率,因为信息学奥林匹克复赛题目的解决程序一般是有时间限制的,对于某些用搜索必然耗费大量时间的题目,动态规划几乎是唯一的选择。
但是动态规划并没有一个简单的模型可以套用,对于每个不同的题目都有对应的不同规划思路,我们只能通过对一些动态规划经典案例的学习来训练自己的动态规划思维能力,从而以不变应万变,应付各种复杂的程序设计,本文通过对动态规划经典案例之一的背包问题进行详细阐述,旨在让学生了解动态规划和搜索的不同设计思路以及动态规划的优越性。
【原型例题】从n个物品中选取装入背包的物品,每件物品i的重量为wi,价值为pi。
求使物品价值最高的选取方法。
【输入文件】第一行一个数c,为背包容量。
第二行一个数n,为物品数量第三行n个数,以空格间隔,为n个物品的重量第四行n个数,以空格间隔,为n个物品的价值【输出文件】能取得的最大价值。
【分析】初看这类问题,第一个想到的会是贪心,但是贪心法却无法保证一定能得到最优解,看以下实例:贪心准则1:从剩余的物品中,选出可以装入背包的价值最大的物品,利用这种规则,价值最大的物品首先被装入(假设有足够容量),然后是下一个价值最大的物品,如此继续下去。
这种策略不能保证得到最优解。
例如,考虑n=2,w=[100,10,10],p=[20,15,15],c=105。
当利用价值贪婪准则时,获得的解为x=[1,0,0],这种方案的总价值为20。
而最优解为[0,1,1],其总价值为30。
贪心准则2:从剩下的物品中选择可装入背包的重量最小的物品。
虽然这种规则对于前面的例子能产生最优解,但在一般情况下则不一定能得到最优解。
考虑n=2,w=[10,20], p=[5,100],c=25。
当利用重量贪婪策略时,获得的解为x=[1,0],比最优解[0,1]要差。
贪心准则3:价值密度pi/wi贪婪算法,这种选择准则为:从剩余物品中选择可装入包的pi/wi值最大的物品,但是这种策略也不能保证得到最优解。
利用此策略解n=3,w=[20,15,15],p=[40,25,25],c=30时的得到的就不是最优解。
由以上的三种贪心策略可知,本题如果采用贪心方法求解,则完全取决于输入的数据,不管采用哪种方法都不能保证完全正确。
既然贪心不能解决,那么搜索行不行呢?我们可以深度搜索每种取物方案,然后依次对比得到的最终结果,取最大值即可。
这个思路是正确的,结果也是可期的,但是时间代价是阶乘级的,当物品数量很多(N>10就已经需要很长时间了)时,所耗费的时间代价是巨大的,对于奥赛要求一秒钟内出解就根本不可能了,于是我们不得不想另外的思路,【新思路】要使物品价值最高,即p1*x1+p2*x1+...+pi*xi(其1<=i<=n,x取0或1,取1表示选取物品i)取得最大值。
在该问题中需要决定x1..xn的值。
假设按i=1,2,...,n的次序来确定xi的值。
如果置x1=0,则问题转变为相对于其余物品(即物品2,3,.,n),背包容量仍为c的背包问题。
若置x1=1,问题就变为关于最大背包容量为c-w1的问题。
现设r={c,c-w1}为剩余的背包容量。
在第一次决策之后,剩下的问题便是考虑背包容量为r时的决策。
不管x1是0或是1,[x2,.,xn]必须是第一次决策之后的一个最优方案。
也就是说在此问题中,最优决策序列由最优决策子序列组成。
这样就满足了动态规划的程序设计条件。
【动态规划思路】阶段i:在前i件物品中,选取若干件物品放入背包中;状态:在前i件物品中,选取若干件物品放入所剩空间为c的背包中的所能获得的最大价值;决策:第i件物品放或者不放;由此可以写出动态转移方程:用f[i,j]表示在前i件物品中选择若干件放在所剩空间为j的背包里所能获得的最大价值f[i,j]=max{f[i-1,j-wi]+pi(j>=wi),f[i-1,j]}这样,就可以自底向上地得出在前n件物品中取出若干件放进背包能获得的最大价值,也就是f[n,c]【算法伪代码】for i:=0to c do{i=0也就是没有物品时清零}f[0,i]:=0;for i:=1to n do{枚举n件物品}for j:=0to c do{枚举所有的装入情况}beginf[i,j]:=f[i-1,j];{先让本次装入结果等于上次结果}if(j>=w[i])and(f[i-1,j-w[i]]+p[i]>f[i,j]){如果能装第i件物品}then f[i,j]:=f[i-1,j-w[i]]+p[i];{且装入后价值变大则装入}end;writeln(f[n,c]);【输入文件】104514340102530【输出结果】下面列出所有的f[i,j]0000404040404040101010104050505050501010102540505050657510103040405055708080分析次结果,可以很清楚的了解整个程序的执行过程,最后的80就是本题的答案。
【实例1:开心的金明(NOIP2006普及组真题)】金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。
更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。
今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的N元。
于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。
他还从因特网上查到了每件物品的价格(都是整数元)。
他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1...jk,则所求的总和为:v[j1]*w[j1]+..+v[jk]*w[jk]请你帮助金明设计一个满足要求的购物单。
【输入文件】输入的第1行,为两个正整数,用一个空格隔开:N m(其中N(<30000)表示总钱数,m(<25)为希望购买物品的个数。
)从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有2个非负整数v p(其中v表示该物品的价格(v≤10000),p表示该物品的重要度(1~5))【输出文件】输出只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)。
【分析】掌握了背包原型题目,此题可以迎刃而解,只要把总钱数N元看成是背包容量,物品的价格看成是物品的重量,重要度看成价值即可直接套用背包原型程序。
【实例2:金明的预算方案(NOIP2006提高组真题)】【题目描述】金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。
更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。
今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:主件附件电脑打印机,扫描仪书柜图书书桌台灯,文具工作椅无如果要买归类为附件的物品,必须先买该附件所属的主件。
每个主件可以有0个、1个或2个附件。
附件不再有从属于自己的附件。
金明想买的东西很多,肯定会超过妈妈限定的N元。
于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。
他还从因特网上查到了每件物品的价格(都是10元的整数倍)。
他希望在不超过N 元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,……,jk,则所求的总和为:v[j1]*w[j1]+v[j2]*w[j2]+…+v[jk]*w[jk]。
(其中*为乘号)请你帮助金明设计一个满足要求的购物单。
【输入格式】输入文件的第1行,为两个正整数,用一个空格隔开:N m其中N(<32000)表示总钱数,m(<60)为希望购买物品的个数。
)从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有3个非负整数v p q(其中v表示该物品的价格(v<10000),p表示该物品的重要度(1~5),q表示该物品是主件还是附件。
如果q=0,表示该物品为主件,如果q>0,表示该物品为附件,q是所属主件的编号)【输出格式】输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<200000)。
【输入样例】100058002040051300514003050020【输出样例】2200【分析】本题跟上题非常类似,可以确认用背包问题可以解决,但是难度却大大高于上题,这里提供两个思路。
简单方案:对每一个物品做两种决策,取与不取。
如果取,满足两个条件:1.要么它是主件,要么它所属的主件已经在包里了。
2.放进去后的重要度与价格的成绩的总和要比没放进时的大。
这两个条件缺一不可的。
于是得到如下的动规方程:f[i,j]:=f[i-1,j];if(i为主件or i的主件在包中)and(f[i,j]<f[i,j-v]+v*w)then f[i,j]:=f[i,j-v]+v*w;这个方案看似简单,其实有个非常复杂的问题,就是后一个条件“i的主件在包中”的判断,因为动态规划有个固有的弱点,就是很难知道整个中间过程,所以这个判断其实写起来是非常麻烦的。
下面提供一个更好的方案:改进方案:细细的看题目,还一个很重要的条件我们还没用:“每个主件可以有0个,1个或2个附件”。
也就是说对于一套物品(包含主件,所有的附件),我们称为一个属类,对一个属类的物品的购买方法,有以下5种:1.一个都不买2.主件3.主件+附件14.主件+附件25.主件+附件1+附件2这五种购买方法也是唯一的五种方法,也就是说对一属类的物品,我们只有上述的5种购买方法。
于是我们很自然的就会想到把物品按物品的属类捆在一起考虑。
这样我们把物品的属类作为dp的状态。
可以得到如下的dp方程:f[i,j]=max{f[i-1,j];第1种情况f[i-1,j-v[i,0]]+v[i,0]*w[i,0];第2种情况f[i-1,j-v[i,0]-v[i,1]]+v[i,0]*w[i,0]+v[i,1]*w[i,1];第3种情况f[i-1,j-v[i,0]-v[i,2]]+v[i,0]*w[i,0]+v[i,2]*w[i,2];第4种情况f[i-1,j-v[i,0]-v[i,1]-v[i,2]]+v[i,0]*w[i,0]+v[i,1]*w[i,1]+v[i,2]*w[i,2];}第5种情况这种方法的DP效率大大提高,不过需要对输入数据进行重新处理,使之按属类重新编号。