浅谈“背包问题”的灵活应用

众所周知“背包问题”是动态规划中的基础问题,也是必修问题。对于基本的“背包问题”我想大家应该都有所掌握,但是对于初学者而言能否将“背包问题”的求解思想灵活应用于题目当中成为真正掌握“背包问题”的关键。下面我为大家介绍一些灵活应用“背包问题”的方法。
  一、“背包问题”的基本结构
  1.01背包。(01背包是所有“背包问题”的基础,也可以说所有的“背包问题”都是从01背包演变而来的)。
  2.完全背包问题(这类“背包问题”是可以解决对于同一件物品可以选无数次的题目)
  3.多重背包(每一件物品拥有一个可选件数的固定上限)
  4.分组背包问题(可以解决对于某一物品不能和其它一些物品同时选取的问题)
  以上是“背包问题”的几种基本类型,相信大家都有所掌握,在此就不再赘述,不懂的朋友可以阅读DD的《背包九讲》。里面有详细的讲解。
  二、求最小价值
  此类“背包问题”之所以单独罗列是因为对于初学者来说这类问题还是不太好理解的,并且在DD的《背包九讲》中并没有详细介绍而是一笔带过了,这类问题是指对于一个体积为V的背包,有N件物品,求选取哪几样物品可以使背出的价值最小,当然在这里价值不是负数,否则就失去了这道题的实际意义。对于这类问题其实只需要搞清楚一点,那就是这里的背包是一定要装满的。因为,对于价值大于等于0而言,如果不是要求必须将背包装满,那么,什么也不装价值为0一定是最优解。如果这样,那么本题同样失去了意义。这类题目的正确解法就是将数组F(即状态转移数组)初始化成maxlongint即可。原因是根据状态转移方程对于当前的体积V减去当前的这件物品的体积(即放入该物品之前的背包体积V1)所对应的最小价值F[v-w[i]]在不是被填充满的情况下,是一定不能装入该物品的。具体在这里为大家引入一道例题:
  最小乘车费用
  问题描述 
  假设某条街上每一公里就有一个公共汽车站,并且乘车费用如下表:
  公里数   1    2   3   4   5   6   7   8   9   10
  费用  12 21 31 40 49 58 69 79 90 101
  而任意一辆汽车从不行驶超过10公里。某人想行驶n公里,假设他可以任意次换车,请你帮他找到一种乘车方案,使得总费用最小
  注意:10公里的费用比1公里小的情况是允许的。 
  输入格式 
  共两行,第一行为10个不超过200的整数,依次表示行驶1~10公里的费用,相邻两数间用一个空格隔开;第二行为某人想要行驶的公里数。 
  输出格式 
  仅一行,包含一个整数,表示行使这么远所需要的最小费用。 
  样例输入 
  12 21 31 40 49 58 69 79 90 101 
  15 
  样例输出 
  147
  这是来自RQNOJ的一道题目,对于这道题目而言我们就可以轻松的将其转化成球最小值的“背包问题”从而轻松求解。
  状态转移过程如下:
   For i:= 1 to 10 do
   For j:=v downto i do
    If f[j]>f[j-i]+w[i]
     then f[j]:=f[j-i]+w[i];
  三、特别的二维背包
  这类问题在实际应用过程中其实并不常见,但是我认为它也是必须掌握的,首先通过一个例题引入该问题:
  多多看DVD(加强版)
  问题描述 
  多多进幼儿园了,今天报名了。只有今晚可以好好放松一下了(以后上了学后会很忙)。她的叔叔决定给她买一些动画片DVD晚上看。可是爷爷规定他们只能在一定的时间段L看完。(因为叔叔还要搞NOIP不能太早陪多多看碟,而多多每天很早就困了所以只能在一定的时间段里看碟)。多多列出一张表要叔叔给她买N张DVD碟,大多都是多多爱看的动画片(福音战士,机器猫,火影忍者,樱桃小丸子……)。这N张碟编号为(1,2,3……N)。多多给每张碟都打了分Mi(Mi>0),打分越高的碟说明多多越爱看。每张碟有播放的时间Ti。多多想在今晚爷爷规定的时间里看的碟总分最高。(必须把要看的碟看完,也就是说一张碟不能只看一半)。显然叔叔在买碟时没必要把N张全买了,只买要看的就OK了,这样节省资金啊。而且多多让叔叔惯的特别任性只要她看到有几张就一定会看完。
  可是出现了一个奇怪的问题,买碟的地方只买给顾客M(M
  聪明的你帮帮多多的叔叔吧。
  输入格式 
  输入文件有三行
  第一行:两个数空格隔开的正整数,N,M,L(分别表示叔叔给多多买的碟的数量,商店要买给叔叔的碟的数量,爷爷规定的看碟的时间段)。
  第二行到第N行,每行两个数:T,M,给出多多列表中DVD碟的信息。
  输出格式 
  单独输出一行,表示多多今晚看的碟的总分。
  如果商店卖给叔叔的M张碟无法在爷爷规定的时间看完输出0;
  样例输入 
  3 2 10
  1 1 100
  1 2
  9 1
  样例输出 
  3
  这道题目也同样来自RQNOJ,乍一看这道题目就是一个普通的二维背包,但是相信大家在仔细审题后会发现,这道题目有一个特殊之处,它的特殊之处就来自于对于两种费用,一种需要必须装满,而另一种却无需装满,对于平时我们所接触的多维背包题目是要求所有体积都必须装满或者都无需装满的。那么应该如何解决这类问题呢,通过上面求最小价值的分析我们可以得出一个结论:对于必须装满的背包是需要将数组初始化的,在这里要注意的是当求最大值时需要初始化成-maxlongint而求最小值时需要初始化成maxlongint,然后再将必须装满的某一维或多维体积所对应的体积为0的数组部分初始化成0,具体为什么相信大家通过上述分析都应该有所领悟。下面是对于上述例题的具体代码希望对大家理解起来有所帮助:
  for i:=1 to m do
   for j:=0 to t do
   f[i,j]:=-maxlongint;
  for i:=1 to n do
   for j:=m downto 1 do
     for k:=t downto c[i] do
      if f[j-1,k-c[i]]+w[i]>f[j,k]
       then f[j,k]:=f[j-1,k-c[i]]+w[i];
  四、泛化体积和价值的概念
  灵活应用“背包问题”求解的关键在于掌握如何泛化体积和价值的概念,并非当题目中明确指出“对于一些物品它们都拥有一个价值和一个体积………”时才意识到应该用背包求解,其实对于很多题目在泛化价值和体积的概念之后都是可以用“背包问题”的求解方法求解的。泛化体积和价值的概念简单的说就是将题目中所给的信息进行一定的转换使之成为“背包问题”中的必要条件从而用“背包问题”的求解方法求解。首先还是引入一道例题:
  邮票问题
  问题描述
  给定一个信封,最多只允许粘贴N(N≤100)张邮票,我们现在有M(M≤100)种邮票,面值分别为:X1,X2……Xm(Xi≤255为正整数),并假设各种邮票都有足够多张。
  要求计算所能获得的邮资最大范围。即求最大值MAX,使1~MAX之间的每一个邮资都能得到。
  例如:N=4,有2种邮票,面值分别为1分,4分,于是可以得到1~10分和12分,13分,16分邮资,由于得不到11分和15分,所以邮资的最大范围MAX=10
  输入文件
  第一行为最多粘贴的邮票张数N。第二行为邮票种数M。以下M行各有一个数字表示邮票的面值。
  输出文件
  仅一个数,最大的MAX的值。
  输入样例
  4
  2
  1
  4
  输出样例
  10
  这是一道来自USACO的经典题目,解法也有很多,在这里就为大家介绍一种推广性较强的背包解法,通过题目的描述我们可以知道题目要求是求可以组成的连续的最大面值,这就有别于一般的“背包问题”,不可以简单的将可以贴的邮票数目作为背包体积,因为这样并不能保证所能取得的最大价值是可连续得到的,但是通过泛化价值和体积的概念我们可以巧妙地将可以贴出的面值作为背包的体积,把邮票的面额作为物品的体积,然后当做“必须装满的背包问题”求解即可。具体做法请看核心代码:
  for i:=1 to m do
  read(w[i]);
  for i:=1 to total do
  f[i]:=maxn;
  for j:=1 to total do
  begin
   for i:=w[i] to m do
  if (f[j]>f[j-w[i]]+1)and(f[j-w[i]]+1<=n)
   then f[j]:=f[j-w[i]]+1;
   if f[j]=maxn
     then begin
         writeln(j-1);
        exit;
         end;
  end;
  这道题目只是一个泛化体积和价值的简单例子,下面再为大家介绍一种巧妙地用法,这种用法在很多题目中都有所涉及。
  拔河比赛
  问题描述
  superwyh的学校要举行拔河比赛,为了在赛前锻炼大家,老师决定把班里所有人分为两拨,进行拔河。为了避免其中一方的实力过强,老师决定以体重来划分队伍,尽量保持两个队伍的体重差最少,因为老师对结果没兴趣,所以只告诉老师最小的体重差是多少就行了。这个受苦受累的任务就交给superwyh了,因为这两天superwyh的后背间谍sjh闹肚子了,所以只好superwyh亲自去调查每个人的体重,但是仅仅知道体重依然难以确定到底如何分配队伍,请各位oier帮助superwyh出出主意。
  输入格式
  第一行为人数(1≤n≤100),从第二行开始是每个人的体重(0≤m≤100)。
  输出格式
  最小体重差。
  样例输入
  4
  10
  23
  41
  12
  样例输出
  4
  这道题是RQNOJ上一次模拟赛的题目,简单的说就是从所有人中挑选出一部分人使这些人的重量尽量接近总重的1/2即可求解,于是我们可以将所有人总体重的1/2作为背包的体积,每个人的重量既作为重量又作为价值,然后用简单的01背包求解即可。具体代码如下:
read(n);
  for i:=1 to n do
   begin
   read(a[i]);
   total:=total+a[i];
   end;
  for i:=1 to n do
   for j:=total div 2 downto a[i] do
   if f[j]
     then f[j]:=f[j-a[i]]+a[i];
  writeln(total-2*f[total div 2]);
  通过这两个题目我们可以看出通过泛化体积和价值可以将很多题目转换成“背包问题”求解,从而使问题变的简单,这也恰恰体现了在“背包问题”中泛化价值和体积的概念是非常重要的。
  小结:希望通过本文大家能对“背包问题”有新的领悟,真正的掌握“背包问题”的解法,更重要的是领悟这种思想,灵活运用“背包问题”的求解方法,使其为自己提供更多的便利。
  最后为大家留一道练习题(希望大家能用“背包问题”进行求解):
  积木城堡
  问题描述 
  Description   XC的儿子小XC最喜欢玩的游戏是用积木垒漂亮的城堡。城堡是用一些立方体的积木垒成的,城堡的每一层是一块积木。小XC是一个比他爸爸XC还聪明的孩子,他发现垒城堡的时候,如果下面的积木比上面的积木大,那么城堡便不容易倒。所以他在垒城堡的时候总是遵循这样的规则。 
  小XC想把自己垒的城堡送给幼儿园里漂亮的女孩子们,这样可以增加他的好感度。为了公平起见,他决定送给每个女孩子一样高的城堡,这样可以避免女孩子们为了获得更漂亮的城堡而引起争执。可是他发现自己在垒城堡的时候并没有预先考虑到这一点。所以他现在要改造城堡。由于没有多余的积木了,他灵机一动,想出了一个巧妙的改造方案。他决定从每一个城堡中挪去一些积木,使得最终每座城堡都一样高。为了使他的城堡更雄伟,他觉得应该使最后的城堡都尽可能的高。 
  任务: 
  请你帮助小XC编一个程序,根据他垒的所有城堡的信息,决定应该移去哪些积木才能获得最佳的效果。 
  输入格式
  第一行是一个整数N(N≤100),表示一共有几座城堡。以下N行每行是一系列非负整数,用一个空格分隔,按从下往上的顺序依次给出一座城堡中所有积木的棱长。用-1结束。一座城堡中的积木不超过100块,每块积木的棱长不超过100。 
  输出格式 
  一个整数,表示最后城堡的最大可能的高度。如果找不到合适的方案,则输出0。 
  样例输入
  2 
  2 1 -1 
  3 2 1 –-1 
  样例输出 
  3
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值