如图所C,如果我们按行存储q些数据Q? apple append and antiy banana band 我们需?+6+3+5+6+4=29 B 的空间? 但是字典树只需?0 B 的空间? q在数据量更大的时候能起到更好的效果?/font>
有NU物品和一个容量ؓV的背包。第iU物品最多有n[i]件可用,每g费用是c[i]Qh值是w[i]。求解将哪些物品装入背包可ɘq些物品的费用d不超q背包容量,且h值d最大?/p>
q题目和完全背包问题很类伹{基本的方程只需完全背包问题的方程略微一改即可,因ؓ对于WiU物品有n[i]+1U策略:?Ӟ??#8230;…取n[i]件。of[i][v]表示前iU物品恰攑օ一个容量ؓv的背包的最大权|则有状态{ULE:
f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}
复杂度是O(V*Σn[i])?/p>
另一U好惛_写的基本Ҏ是{化ؓ01背包求解Q把WiU物品换成n[i]?1背包中的物品Q则得到了物品数?#931;n[i]?1背包问题Q直接求解,复杂度仍然是O(V*Σn[i])?/p>
但是我们期望它转化?1背包问题之后能够像完全背包一样降低复杂度。仍然考虑二进制的思想Q我们考虑把第iU物品换成若qg物品Q得原问题中第iU物品可取的每种{略——?..n[i]?#8212;—均能{h于取若干件代换以后的物品。另外,取超qn[i]件的{略必不能出现?/p>
Ҏ是:第iU物品分成若qg物品Q其中每件物品有一个系敎ͼqg物品的费用和价值均是原来的费用和hg以这个系数。ɘq些pL分别?,2,4,...,2^(k-1),n[i]-2^k+1Q且k是满n[i]-2^k+1>0的最大整数。例如,如果n[i]?3Q就这U物品分成系数分别ؓ1,2,4,6的四件物品?/p>
分成的这几g物品的系数和为n[i]Q表明不可能取多于n[i]件的WiU物品。另外这U方法也能保证对?..n[i]间的每一个整敎ͼ均可以用若干个系数的和表C,q个证明可以?..2^k-1?^k..n[i]两段来分别讨论得出,q不难,希望你自己思考尝试一下?/p>
q样将WiU物品分成了O(log n[i])U物品,原问题转化Z复杂度ؓ<math>O(V*Σlog n[i])?1背包问题Q是很大的改q?/p>
下面l出O(log amount)旉处理一件多重背包中物品的过E,其中amount表示物品的数量:
procedure MultiplePack(cost,weight,amount)
if cost*amount>=V
CompletePack(cost,weight)
return
integer k=1
while k<amount
ZeroOnePack(k*cost,k*weight)
amount=amount-k
k=k*2
ZeroOnePack(amount*cost,amount*weight)
希望你仔l体会这个伪代码Q如果不太理解的话,不妨译成程序代码以后,单步执行几次Q或者头脑加U笔模拟一下,也许׃慢慢理解了?/p>
多重背包问题同样有O(VN)的算法。这个算法基于基本算法的状态{ULE,但应用单调队列的Ҏ使每个状态的值可以以均摊O(1)的时间求解。由于用单调队列优化的DP已超ZNOIP的范_故本文不再展开讲解。我最初了解到q个Ҏ是在楼天成的“男h八题”qȝ片上?/p>
q里我们看到了将一个算法的复杂度由O(V*Σn[i])改进到O(V*Σlog n[i])的过E,q知道了存在应用出NOIP范围的知识的O(VN)法。希望你特别注意“拆分物品”的思想和方法,自己证明一下它的正性,q将完整的程序代码写出来?/p>
有NU物品和一个容量ؓV的背包,每种物品都有无限件可用。第iU物品的费用是c[i]Qh值是w[i]。求解将哪些物品装入背包可ɘq些物品的费用d不超q背包容量,且h值d最大?/p>
q个问题非常cM?a >01背包问题Q所不同的是每种物品有无限g。也是从每U物品的角度考虑Q与它相关的{略已ƈ非取或不取两U,而是有取0件、取1件、取2?#8230;…{很多种。如果仍然按照解01背包时的思\Qof[i][v]表示前iU物品恰攑օ一个容量ؓv的背包的最大权倹{仍然可以按照每U物品不同的{略写出状态{ULE,像这P
f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}
q跟01背包问题一hO(VN)个状态需要求解,但求解每个状态的旉已经不是常数了,求解状态f[i][v]的时间是O(v/c[i])Qȝ复杂度可以认为是O(V*Σ(V/c[i]))Q是比较大的?/p>
?1背包问题的基本思\加以改进Q得Cq样一个清晰的Ҏ。这说明01背包问题的方E的是很重要,可以推及其它cd的背包问题。但我们q是试图改进q个复杂度?/p>
完全背包问题有一个很单有效的优化Q是q样的:若两件物品i、j满c[i]<=c[j]且w[i]>=w[j]Q则物品jLQ不用考虑。这个优化的正确性显ӞM情况下都可将价值小费用高得j换成物美价廉的iQ得到至不会更差的Ҏ。对于随机生成的数据Q这个方法往往会大大减物品的件数Q从而加快速度。然而这个ƈ不能改善最坏情늚复杂度,因ؓ有可能特别设计的数据可以一件物品也M掉?/p>
q个优化可以单的O(N^2)地实玎ͼ一般都可以承受。另外,针对背包问题而言Q比较不错的一U方法是Q首先将费用大于V的物品去掉,然后使用cM计数排序的做法,计算用相同的物品中h值最高的是哪个,可以O(V+N)地完成这个优化。这个不太重要的q程׃l出伪代码了Q希望你能独立思考写Z代码或程序?/p>
既然01背包问题是最基本的背包问题,那么我们可以考虑把完全背包问题{化ؓ01背包问题来解。最单的x是,考虑到第iU物品最多选V/c[i]Ӟ于是可以把第iU物品{化ؓV/c[i]件费用及价值均不变的物品,然后求解q个01背包问题。这样完全没有改q基本思\的时间复杂度Q但q毕竟给了我们将完全背包问题转化?1背包问题的思\Q将一U物品拆成多件物品?/p>
更高效的转化Ҏ是:把第iU物品拆成费用ؓc[i]*2^k、hgؓw[i]*2^k的若qg物品Q其中k满c[i]*2^k<=V
。这是二q制的思想Q因Z最优策略选几件第iU物品,d以表C成若干?^k件物品的和。这h每种物品拆成O(log V/c[i])件物品,是一个很大的改进?/p>
但我们有更优的O(VN)的算法?/p>
q个法使用一l数l,先看伪代码:
for i=1..N
for v=0..V
f[v]=max{f[v],f[v-cost]+weight}
你会发现Q这个伪代码?a >P01的伪代码只有v的@环次序不同而已。ؓ什么这样一改就可行呢?首先xZ么P01中要按照v=V..0的逆序来@环。这是因保证Wiơ@环中的状态f[i][v]是由状态f[i-1][v-c[i]]递推而来。换句话_q正是ؓ了保证每件物品只选一ơ,保证在考虑“选入Wi件物?#8221;qg{略Ӟ依据的是一个绝无已l选入Wi件物品的子结果f[i-1][v-c[i]]。而现在完全背包的特点恰是每种物品可选无限gQ所以在考虑“加选一件第iU物?#8221;q种{略Ӟ却正需要一个可能已选入WiU物品的子结果f[i][v-c[i]]Q所以就可以q且必须采用v=0..V的顺序@环。这是q个单的E序Z成立的道理?/p>
值得一提的是,上面的伪代码中两层for循环的次序可以颠倒。这个结论有可能会带来算法时间常C的优化?/p>
q个法也可以以另外的思\得出。例如,基本思\中求解f[i][v-c[i]]的状态{ULE显式地写出来,代入原方E中Q会发现该方E可以等价地变Ş成这UŞ式:
f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]}
这个方E用一l数l实玎ͼ便得C上面的伪代码?/p>
最后抽象出处理一件完全背包类物品的过E伪代码Q?/p>
procedure CompletePack(cost,weight)
for v=cost..V
f[v]=max{f[v],f[v-c[i]]+w[i]}
完全背包问题也是一个相当基的背包问题,它有两个状态{ULE,分别?#8220;基本思\”以及“O(VN)的算?#8220;的小节中l出。希望你能够对这两个状态{ULE都仔细C会,不仅CQ也要弄明白它们是怎么得出来的Q最好能够自己想一U得到这些方E的Ҏ。事实上Q对每一道动态规划题目都思考其方程的意义以及如何得来,是加深对动态规划的理解、提高动态规划功力的好方法?/p>
有N件物品和一个容量ؓV的背包。第i件物品的费用是c[i]Qh值是w[i]。求解将哪些物品装入背包可价值d最大?/p>
q是最基础的背包问题,特点是:每种物品仅有一Ӟ可以选择放或不放?/p>
用子问题定义状态:即f[i][v]表示前i件物品恰攑օ一个容量ؓv的背包可以获得的最大h倹{则其状态{ULE便是:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
q个方程非常重要Q基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要它详细解释一下:“前i件物品放入容量ؓv的背包中”q个子问题,若只考虑Wi件物品的{略Q放或不放)Q那么就可以转化Z个只牉|前i-1件物品的问题。如果不攄i件物品,那么问题p{化ؓ“前i-1件物品放入容量ؓv的背包中”Qhgؓf[i-1][v]Q如果放Wi件物品,那么问题p{化ؓ“前i-1件物品放入剩下的定w为v-c[i]的背包中”Q此时能获得的最大h值就是f[i-1][v-c[i]]再加上通过攑օWi件物品获得的价值w[i]?/p>
以上Ҏ的时间和I间复杂度均为O(VN)Q其中时间复杂度应该已经不能再优化了Q但I间复杂度却可以优化到O?/p>
先考虑上面讲的基本思\如何实现Q肯定是有一个主循环i=1..NQ每ơ算出来二维数组f[i][0..V]的所有倹{那么,如果只用一个数lf[0..V]Q能不能保证Wiơ@环结束后f[v]中表C的是我们定义的状态f[i][v]呢?f[i][v]是由f[i-1][v]和f[i-1][v-c[i]]两个子问题递推而来Q能否保证在推f[i][v]Ӟ也即在第iơ主循环中推f[v]Ӟ能够得到f[i-1][v]和f[i-1][v-c[i]]的值呢Q事实上Q这要求在每ơ主循环中我们以v=V..0的顺序推f[v]Q这h能保证推f[v]时f[v-c[i]]保存的是状态f[i-1][v-c[i]]的倹{伪代码如下Q?/p>
for i=1..N
for v=V..0
f[v]=max{f[v],f[v-c[i]]+w[i]};
其中的f[v]=max{f[v],f[v-c[i]]}一句恰q当于我们的{ULE?code>f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]}Q因为现在的f[v-c[i]]q当于原来的f[i-1][v-c[i]]。如果将v的@环顺序从上面的逆序Ҏ序的话Q那么则成了f[i][v]由f[i][v-c[i]]推知Q与本题意不W,但它却是另一个重要的背包问题P02最L解决ҎQ故学习只用一l数l解01背包问题是十分必要的?/p>
事实上,使用一l数l解01背包的程序在后面会被多次用到Q所以这里抽象出一个处理一?1背包中的物品q程Q以后的代码中直接调用不加说明?/p>
q程ZeroOnePackQ表C处理一?1背包中的物品Q两个参数cost、weight分别表明qg物品的费用和价倹{?/p>
procedure ZeroOnePack(cost,weight)
for v=V..cost
f[v]=max{f[v],f[v-cost]+weight}
注意q个q程里的处理与前面给出的伪代码有所不同。前面的CZE序写成v=V..0是ؓ了在E序中体现每个状态都按照方程求解了,避免不必要的思维复杂度。而这里既然已l抽象成看作黑箱的过E了Q就可以加入优化。费用ؓcost的物品不会媄响状态f[0..cost-1]Q这是显然的?/p>
有了q个q程以后Q?1背包问题的伪代码可以这样写Q?/p>
for i=1..N
ZeroOnePack(c[i],w[i]);
我们看到的求最优解的背包问题题目中Q事实上有两U不太相同的问法。有的题目要?#8220;恰好装满背包”时的最优解Q有的题目则q没有要求必L背包装满。一U区别这两种问法的实现方法是在初始化的时候有所不同?/p>
如果是第一U问法,要求恰好装满背包Q那么在初始化时除了f[0]?其它f[1..V]均设?∞Q这样就可以保证最l得到的f[N]是一U恰好装满背包的最优解?/p>
如果q没有要求必L背包装满Q而是只希望h格尽量大Q初始化时应该将f[0..V]全部设ؓ0?/p>
Z么呢Q可以这L解:初始化的f数组事实上就是在没有M物品可以攑օ背包时的合法状态。如果要求背包恰好装满,那么此时只有定w?的背包可能被价gؓ0的nothing“恰好装满”Q其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。如果背包ƈ非必被装满Q那么Q何容量的背包都有一个合法解“什么都不装”Q这个解的hgؓ0Q所以初始时状态的g全部ؓ0了?/p>
q个技巧完全可以推q到其它cd的背包问题,后面也就不再对进行状态{UM前的初始化进行讲解?/p>
前面的伪代码中有 for v=V..1Q可以将q个循环的下限进行改q?/p>
׃只需要最后f[v]的|倒推前一个物品,其实只要知道f[v-w[n]]卛_。以此类推,对以Wj个背包,其实只需要知道到f[v-sum{w[j..n]}]卛_Q即代码中的
for i=1..N
for v=V..0
可以Ҏ
for i=1..n
bound=max{V-sum{w[i..n]},c[i]}
for v=V..bound
q对于V比较大时是有用的?/p>
01背包问题是最基本的背包问题,它包含了背包问题中设计状态、方E的最基本思想Q另外,别的cd的背包问题往往也可以{换成01背包问题求解。故一定要仔细体会上面基本思\的得出方法,状态{ULE的意义Q以及最后怎样优化的空间复杂度?/p>