动态规划经典案例详解(背包问题)
- 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{枚举所有的装入情况}
begin
f[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]);
【输入文件】
10
4
5143
40102530
【输出结果】下面列出所有的f[i,j]
0000404040404040
10101010405050505050
10101025405050506575
10103040405055708080
分析次结果,可以很清楚的了解整个程序的执行过程,最后的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))
【输出文件】
输出只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值