< left; mso-layout-grid-align: none" align=left>1.3.2 0/1背包问题<p></p></P>< 20pt; TEXT-ALIGN: left; mso-char-indent-count: 2.0; mso-char-indent-size: 10.0pt; mso-layout-grid-align: none" align=left>在0 / 1背包问题中,需对容量为<I>c </I>的背包进行装载。从<I>n </I>个物品中选取装入背包的物品,每件物品<I>i </I>的重量为<I>w</I><I>i </I>,价值为<I>p</I><I>i </I>。对于可行的背包装载,背包中物品的总重量不能超过背包的容量,最佳装载是指所装入的物品价值最高,即<I>n </I>å<I>i</I>=1<I>p</I><I>i </I><I>x</I><I>i </I>取得最大值。约束条件为<I>n </I>å<I>i </I>=1<I>w</I><I>i </I><I>x</I><I>i</I>≤<I>c </I>和<I>x</I><I>i</I>Î[ 0 , 1 ] ( 1≤<I>i</I>≤<I>n</I>)。<p></p></P>< 20pt; TEXT-ALIGN: left; mso-char-indent-count: 2.0; mso-char-indent-size: 10.0pt; mso-layout-grid-align: none" align=left>在这个表达式中,需求出<I>x</I><I>t </I>的值。<I>x</I><I>i </I>= 1表示物品<I>i </I>装入背包中,<I>x</I><I>i </I>=0 表示物品<I>i </I>不装入背包。0 / 1背包问题是一个一般化的货箱装载问题,即每个货箱所获得的价值不同。货箱装载问题转化为背包问题的形式为:船作为背包,货箱作为可装入背包的物品。<p></p></P><P 20.1pt; TEXT-ALIGN: left; mso-char-indent-count: 2.0; mso-char-indent-size: 10.05pt; mso-layout-grid-align: none" align=left><B>例</B><B>1-8 </B> 在杂货店比赛中你获得了第一名,奖品是一车免费杂货。店中有<I>n </I>种不同的货物。规则规定从每种货物中最多只能拿一件,车子的容量为<I>c</I>,物品<I>i </I>需占用<I>w</I><I>i </I>的空间,价值为<I>p</I><I>i </I>。你的目标是使车中装载的物品价值最大。当然,所装货物不能超过车的容量,且同一种物品不得拿走多件。这个问题可仿照0 / 1背包问题进行建模,其中车对应于背包,货物对应于物品。<p></p></P><P 20pt; TEXT-ALIGN: left; mso-char-indent-count: 2.0; mso-char-indent-size: 10.0pt; mso-layout-grid-align: none" align=left>0 / 1背包问题有好几种贪婪策略,每个贪婪策略都采用多步过程来完成背包的装入。在每一步过程中利用贪婪准则选择一个物品装入背包。一种贪婪准则为:从剩余的物品中,选出可以装入背包的价值最大的物品,利用这种规则,价值最大的物品首先被装入(假设有足够容量),然后是下一个价值最大的物品,如此继续下去。这种策略不能保证得到最优解。例如,考虑<I>n</I>=2, <I>w</I>=[100,10,10], <I>p =</I>[20,15,15], <I>c </I>= 1 0 5。当利用价值贪婪准则时,获得的解为<I>x</I>= [ 1 , 0 , 0 ],这种方案的总价值为2 0。而最优解为[ 0 , 1 , 1 ],其总价值为3 0。<p></p></P><P 20pt; TEXT-ALIGN: left; mso-char-indent-count: 2.0; mso-char-indent-size: 10.0pt; mso-layout-grid-align: none" align=left>另一种方案是重量贪婪准则是:从剩下的物品中选择可装入背包的重量最小的物品。虽然这种规则对于前面的例子能产生最优解,但在一般情况下则不一定能得到最优解。考虑<I>n</I>= 2 ,<I>w</I>=[10,20], <I>p</I>=[5,100], <I>c</I>= 2 5。当利用重量贪婪策略时,获得的解为<I>x </I>=[1,0], 比最优解[ 0 , 1 ]要差。<p></p></P><P 20pt; TEXT-ALIGN: left; mso-char-indent-count: 2.0; mso-char-indent-size: 10.0pt; mso-layout-grid-align: none" align=left>还可以利用另一方案,价值密度<I>p</I><I>i </I>/<I>w</I><I>i </I>贪婪算法,这种选择准则为:从剩余物品中选择可<p></p></P><P left; mso-layout-grid-align: none" align=left>装入包的<I>p</I><I>i </I>/<I>w</I><I>i </I>值最大的物品,这种策略也不能保证得到最优解。利用此策略试解<I>n</I>= 3 ,<I>w</I>=[20,15,15], <I>p</I>=[40,25,25], <I>c</I>=30 时的最优解。<p></p></P><P 20pt; TEXT-ALIGN: left; mso-char-indent-count: 2.0; mso-char-indent-size: 10.0pt; mso-layout-grid-align: none" align=left>我们不必因所考察的几个贪婪算法都不能保证得到最优解而沮丧, 0 / 1背包问题是一个N P-复杂问题。对于这类问题,也许根本就不可能找到具有多项式时间的算法。虽然按<I>p</I><I>i </I>/<I>w</I><I>i </I>非递(增)减的次序装入物品不能保证得到最优解,但它是一个直觉上近似的解。我们希望它是一个好的启发式算法,且大多数时候能很好地接近最后算法。在6 0 0个随机产生的背包问题中,用这种启发式贪婪算法来解有2 3 9题为最优解。有5 8 3个例子与最优解相差1 0 %,所有6 0 0个答案与最优解之差全在2 5 %以内。该算法能在O (<I>n</I>l o g<I>n</I>)时间内获得如此好的性能。我们也许会问,是否存在一个<I>x </I>(<I>x</I><1 0 0 ),使得贪婪启发法的结果与最优值相差在<I>x</I>%以内。答案是否定的。为说明这一点,考虑例子<I>n </I>=2, <I>w </I>= [ 1 ,<I>y</I>], <I>p</I>= [ 1 0 , 9<I>y</I>], 和<I>c</I>= <I>y</I>。贪婪算法结果为<I>x</I>=[1,0], 这种方案的值为1 0。对于<I>y</I>≥1 0 / 9,最优解的值为9 y。因此,贪婪算法的值与最优解的差对最优解的比例为( ( 9<I>y </I>- 1 0)/9<I>y</I>* 1 0 0 ) %,对于大的<I>y</I>,这个值趋近于1 0 0 %。但是可以建立贪婪启发式方法来提供解,使解的结果与最优解的值之差在最优值的<I>x</I>% (<I>x</I><100) 之内。首先将最多<I>k </I>件物品放入背包,如果这<I>k </I>件物品重量大于<I>c</I>,则放弃它。否则,剩余的容量用来考虑将剩余物品按<I>p</I><I>i </I>/<I>w</I><I>i </I>递减的顺序装入。通过考虑由启发法产生的解法中最多为<I>k </I>件物品的所有可能的子集来得到最优解。<p></p></P><P 20.1pt; TEXT-ALIGN: left; mso-char-indent-count: 2.0; mso-char-indent-size: 10.05pt; mso-layout-grid-align: none" align=left><B>例</B><B>13-9 </B>考虑<I>n </I>=4, <I>w</I>=[2,4,6,7], <I>p</I>=[6,10,12,13], <I>c </I>= 11。当<I>k</I>= 0时,背包按物品价值密度非递减顺序装入,首先将物品1放入背包,然后是物品2,背包剩下的容量为5个单元,剩下的物品没有一个合适的,因此解为<I>x </I>= [ 1 , 1 , 0 , 0 ]。此解获得的价值为1 6。<p></p></P><P 20pt; TEXT-ALIGN: left; mso-char-indent-count: 2.0; mso-char-indent-size: 10.0pt; mso-layout-grid-align: none" align=left>现在考虑<I>k </I>= 1时的贪婪启发法。最初的子集为{ 1 } , { 2 } , { 3 } , { 4 }。子集{ 1 } , { 2 }产生与<I>k</I>= 0时相同的结果,考虑子集{ 3 },置<I>x</I><I>3 </I>为1。此时还剩5个单位的容量,按价值密度非递增顺序来考虑如何利用这5个单位的容量。首先考虑物品1,它适合,因此取<I>x</I>1 为1,这时仅剩下3个单位容量了,且剩余物品没有能够加入背包中的物品。通过子集{ 3 }开始求解得结果为<I>x </I>= [ 1 , 0 , 1 , 0 ],获得的价值为1 8。若从子集{ 4 }开始,产生的解为<I>x </I>= [ 1 , 0 , 0 , 1 ],获得的价值为1 9。考虑子集大小为0和1时获得的最优解为[ 1 , 0 , 0 , 1 ]。这个解是通过<I>k</I>= 1的贪婪启发式算法得到的。<p></p></P><P 20pt; TEXT-ALIGN: left; mso-char-indent-count: 2.0; mso-char-indent-size: 10.0pt; mso-layout-grid-align: none" align=left>若<I>k</I>= 2,除了考虑<I>k</I>< 2的子集,还必需考虑子集{ 1 , 2 } , { 1 , 3 } , { 1 , 4 } , { 2 , 3 } , { 2 , 4 }和{ 3 , 4 }。首先从最后一个子集开始,它是不可行的,故将其抛弃,剩下的子集经求解分别得到如下结果:[ 1 , 1 , 0 , 0 ] , [ 1 , 0 , 1 , 0 ] , [ 1 , 0 , 0 , 1 ] , [ 0 , 1 , 1 , 0 ]和[ 0 , 1 , 0 , 1 ],这些结果中最后一个价值为2 3,它的值比<I>k</I>= 0和<I>k</I>= 1时获得的解要高,这个答案即为启发式方法产生的结果。<p></p></P><P 20pt; TEXT-ALIGN: left; mso-char-indent-count: 2.0; mso-char-indent-size: 10.0pt; mso-layout-grid-align: none" align=left>这种修改后的贪婪启发方法称为k阶优化方法(<I>k - o p t i m a l</I>)。也就是,若从答案中取出<I>k </I>件物品,并放入另外<I>k </I>件,获得的结果不会比原来的好,而且用这种方式获得的值在最优值的( 1 0 0 / (<I>k </I>+ 1 ) ) %以内。当<I>k</I>= 1时,保证最终结果在最佳值的5 0 %以内;当<I>k</I>= 2时,则在3 3 . 3 3 %以内等等,这种启发式方法的执行时间随<I>k </I>的增大而增加,需要测试的子集数目为O (<I>n</I><I>k </I>),每一个子集所需时间为O (<I>n</I>),因此当<I>k </I>>0时总的时间开销为O (<I>n</I><I>k</I>+1 )。实际观察到的性能要好得多。<p></p></P><P left; mso-layout-grid-align: none" align=left> <p></p></P> |