背包问题动态规划python
㈠ 锷ㄦ佽勫垝姹傝В0-1鑳屽寘闂棰
闅鹃亾鏄𨱍宠佺▼搴忥纻
F[I,j]涓哄墠i涓鐗╁搧涓阃夋嫨鑻ュ共涓鏀惧叆浣垮叾浣撶Н姝eソ涓箦镄勬爣蹇楋纴涓哄竷灏斿瀷銆
瀹炵幇:灏嗘渶浼桦寲闂棰樿浆鍖栦负鍒ゅ畾镐ч梾棰
f [I, j] = f [ i-1, j-w[i] ] (w[I]<=j<=v) 杈圭晫锛歠[0,0]:=true.
For I:=1 to n do
For j:=w[I] to v do F[I,j]:=f[I-1,j-w[I]];
浼桦寲锛氩綋鍓岖姸镐佸彧涓庡墠涓阒舵电姸镐佹湁鍏筹纴鍙闄嶈呖涓缁淬
F[0]:=true;
For I:=1 to n do begin
F1:=f;
For j:=w[I] to v do
If f[j-w[I]] then f1[j]:=true;
F:=f1;
End;
B.姹傚彲浠ユ斁鍏ョ殑链澶т环鍊笺
F[I,j] 涓哄归噺涓篒镞跺彇鍓峧涓鑳屽寘镓鑳借幏寰楃殑链澶т环鍊笺
F [i,j] = max { f [ i 钬 w [ j ], j-1] + p [ j ], f[ i,j-1] }
C.姹傛伆濂借呮弧镄勬儏鍐垫暟銆
DP:
Procere update;
var j,k:integer;
begin
c:=a;
for j:=0 to n do
if a[j]>0 then
if j+now<=n then inc(c[j+now],a[j]);
a:=c;
end;
鍦0 / 1鑳屽寘闂棰树腑锛岄渶瀵瑰归噺涓篶 镄勮儗鍖呰繘琛岃呰浇銆备粠n 涓鐗╁搧涓阃夊彇瑁呭叆鑳屽寘镄勭墿鍝侊纴姣忎欢鐗╁搧i 镄勯吨閲忎负wi 锛屼环鍊间负pi 銆傚逛簬鍙琛岀殑鑳屽寘瑁呰浇锛岃儗鍖呬腑鐗╁搧镄勬婚吨閲忎笉鑳借秴杩囱儗鍖呯殑瀹归噺锛屾渶浣宠呰浇鏄鎸囨墍瑁呭叆镄勭墿鍝佷环鍊兼渶楂桡纴鍗硃1*x1+p2*x1+...+pi*xi(鍏1<=i<=n锛寈鍙0鎴1锛屽彇1琛ㄧず阃夊彇鐗╁搧i) 鍙栧缑链澶у笺
鍦ㄨラ梾棰树腑闇瑕佸喅瀹歺1 .. xn镄勫笺傚亣璁炬寜i = 1锛2锛...锛宯 镄勬″簭𨱒ョ‘瀹歺i 镄勫笺傚傛灉缃畑1 = 0锛屽垯闂棰樿浆鍙树负鐩稿逛簬鍏朵綑鐗╁搧(鍗崇墿鍝2锛3锛.锛宯)锛岃儗鍖呭归噺浠崭负c 镄勮儗鍖呴梾棰樸傝嫢缃畑1 = 1锛岄梾棰桦氨鍙树负鍏充簬链澶ц儗鍖呭归噺涓篶-w1 镄勯梾棰樸傜幇璁緍?{c锛宑-w1 } 涓哄墿浣欑殑鑳屽寘瀹归噺銆
鍦ㄧ涓娆″喅绛栦箣钖庯纴鍓╀笅镄勯梾棰树究鏄钥冭槛鑳屽寘瀹归噺涓簉 镞剁殑鍐崇瓥銆备笉绠x1 鏄0鎴栨槸1锛孾x2 锛.锛寈n ] 蹇呴’鏄绗涓娆″喅绛栦箣钖庣殑涓涓链浼樻柟妗堬纴濡傛灉涓嶆槸锛屽垯浼氭湁涓涓镟村ソ镄勬柟妗圼y2锛.锛寉n ]锛屽洜钥孾x1锛寉2锛.锛寉n ]鏄涓涓镟村ソ镄勬柟妗堛
锅囱緉=3, w=[100,14,10], p=[20,18,15], c= 116銆傝嫢璁绬1 = 1锛屽垯鍦ㄦ湰娆″喅绛栦箣钖庯纴鍙鐢ㄧ殑鑳屽寘瀹归噺涓簉= 116-100=16 銆俒x2锛寈3 ]=[0,1] 绗﹀悎瀹归噺闄愬埗镄勬浔浠讹纴镓寰楀间负1 5锛屼絾锲犱负[x2锛寈3 ]= [1锛0] 钖屾牱绗﹀悎瀹归噺𨱒′欢涓旀墍寰楀间负1 8锛屽洜姝[x2锛寈3 ] = [ 0锛1] 骞堕潪链浼樼瓥鐣ャ傚嵆x= [ 1锛0锛1] 鍙鏀硅繘涓簒= [ 1锛1锛0 ]銆傝嫢璁绬1 = 0锛屽垯瀵逛簬鍓╀笅镄勪袱绉岖墿鍝佽岃█锛屽归噺闄愬埗𨱒′欢涓116銆傛讳箣锛屽傛灉瀛愰梾棰樼殑缁撴灉[x2锛寈3 ]涓嶆槸鍓╀綑𨱍呭喌涓嬬殑涓涓链浼樿В锛屽垯[x1锛寈2锛寈3 ]涔熶笉浼氭槸镐讳綋镄勬渶浼樿В銆傚湪姝ら梾棰树腑锛屾渶浼桦喅绛栧簭鍒楃敱链浼桦喅绛栧瓙搴忓垪缁勬垚銆傚亣璁緁 (i,y) 琛ㄧず鍓╀綑瀹归噺涓簓锛屽墿浣欑墿鍝佷负i锛宨 + 1锛...锛宯 镞剁殑链浼樿В镄勫硷纴鍗筹细鍒╃敤链浼桦簭鍒楃敱链浼桦瓙搴忓垪鏋勬垚镄勭粨璁猴纴鍙寰楀埌f 镄勯掑綊寮忎负锛
褰搄>=wi镞讹细 f(i,j)=max{f(i+1,j),f(i+1,j-wi)+vi} 鈶犲纺
褰0<=j<wi镞讹细f(i,j)=f(i+1,j) 鈶″纺
fn( 1 ,c) 鏄鍒濆嬫椂鑳屽寘闂棰樼殑链浼樿В銆
浠ユ湰棰树负渚嬶细鑻0铌y锛1 0锛屽垯f ( 3 ,y) = 0锛涜嫢y铌1 0锛宖 ( 3 ,y) = 1 5銆傚埄鐢ㄢ憽寮忥纴鍙寰梖 (2, y) = 0 ( 0铌y锛10 )锛沠(2锛寉)= 1 5(1 0铌y锛1 4)锛沠(2锛寉)= 1 8(1 4铌y锛2 4)鍜宖(2锛寉)= 3 3(y铌2 4)銆傚洜姝ゆ渶浼樿Вf ( 1 , 11 6 ) = m a x {f(2锛11 6)锛宖(2锛11 6 - w1)+ p1} = m a x {f(2锛11 6)锛宖(2锛1 6)+ 2 0 } = m a x { 3 3锛3 8 } = 3 8銆
鐜板湪璁$畻xi 鍊硷纴姝ラゅ备笅锛氲嫢f ( 1 ,c) =f ( 2 ,c)锛屽垯x1 = 0锛屽惁鍒槚1 = 1銆傛帴涓嬫潵闇浠庡墿浣椤归噺c-w1涓瀵绘眰链浼樿В锛岀敤f (2, c-w1) 琛ㄧず链浼樿В銆备緷姝ょ被鎺锛屽彲寰楀埌镓链夌殑xi (i= 1.n) 鍊笺
鍦ㄨヤ緥涓锛屽彲寰楀嚭f ( 2 , 116 ) = 3 3铌爁 ( 1 , 11 6 )锛屾墍浠x1 = 1銆傛帴镌鍒╃敤杩斿洖鍊3 8 -p1=18 璁$畻x2 鍙妜3锛屾ゆ椂r = 11 6 -w1 = 1 6锛屽张鐢眆 ( 2 , 1 6 ) = 1 8锛屽缑f ( 3 , 1 6 ) = 1 4铌爁 ( 2 , 1 6 )锛屽洜姝x2 = 1锛屾ゆ椂r= 1 6 -w2 = 2锛屾墍浠f (3,2) =0锛屽嵆寰梮3 = 0銆
㈡ 01背包问题
算法分析
对于背包问题,通常的处理方法是搜索。
用递归来完成搜索,算法设计如下:
function Make( i {处理到第i件物品} , j{剩余的空间为j}:integer) :integer;
初始时i=m , j=背包总容量
begin
if i:=0 then
Make:=0;
if j>=wi then (背包剩余空间可以放下物品 i )
r1:=Make(i-1,j-wi)+v; (第i件物品放入所能得到的价值 )
r2:=Make(i-1,j)(第i件物品不放所能得到的价值 )
Make:=max{r1,r2}
end;
这个算法的时间复杂度是O(2^n),我们可以做一些简单的优化。
由于本题中的所有物品的体积均为整数,经过几次的选择后背包的剩余空间可能会相等,在搜索中会重复计算这些结点,所以,如果我们把搜索过程中计算过的结点的值记录下来,以保证不重复计算的话,速度就会提高很多。这是简单?quot;以空间换时间"。
我们发现,由于这些计算过程中会出现重叠的结点,符合动态规划中子问题重叠的性质。
同时,可以看出如果通过第N次选择得到的是一个最优解的话,那么第N-1次选择的结果一定也是一个最优解。这符合动态规划中最优子问题的性质。
考虑用动态规划的方法来解决,这里的:
阶段是:在前N件物品中,选取若干件物品放入背包中;
状态是:在前N件物品中,选取若干件物品放入所剩空间为W的背包中的所能获得的最大价值;
决策是:第N件物品放或者不放;
由此可以写出动态转移方程:
我们用f[i,j]表示在前 i 件物品中选择若干件放在所剩空间为 j 的背包里所能获得的最大价值
f[i,j]=max{f[i-1,j-Wi]+Pi (j>=Wi), f[i-1,j]}
这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一下:“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c的背包中”,此时能获得的最大价值就是f[v-c]再加上通过放入第i件物品获得的价值w。
这样,我们可以自底向上地得出在前M件物品中取出若干件放进背包能获得的最大价值,也就是f[m,w]
算法设计如下:
procere Make;
begin
for i:=0 to w do
f[0,i]:=0;
for i:=1 to m do
for j:=0 to w do begin
f[i,j]:=f[i-1,j];
if (j>=w) and (f[i-1,j-w]+v>f[i,j]) then
f[i,j]:=f[i-1,j-w]+v;
end;
writeln(f[m,wt]);
end;
由于是用了一个二重循环,这个算法的时间复杂度是O(n*w)。而用搜索的时候,当出现最坏的情况,也就是所有的结点都没有重叠,那么它的时间复杂度是O(2^n)。看上去前者要快很多。但是,可以发现在搜索中计算过的结点在动态规划中也全都要计算,而且这里算得更多(有一些在最后没有派上用场的结点我们也必须计算),在这一点上好像是矛盾的。
事实上,由于我们定下的前提是:所有的结点都没有重叠。也就是说,任意N件物品的重量相加都不能相等,而所有物品的重量又都是整数,那末这个时候W的最小值是:1+2+2^2+2^3+……+2^n-1=2^n -1
此时n*w>2^n,动态规划比搜索还要慢~~|||||||所以,其实背包的总容量W和重叠的结点的个数是有关的。
考虑能不能不计算那些多余的结点……
优化时间复杂度
以上方法的时间和空间复杂度均为O(N*V),其中时间复杂度基本已经不能再优化了,但空间复杂度却可以优化到O(V)。
先考虑上面讲的基本思路如何实现,肯定是有一个主循环i=1..N,每次算出来二维数组f[0..V]的所有值。那么,如果只用一个数组f[0..V],能不能保证第i次循环结束后f[v]中表示的就是我们定义的状态f[v]呢?f[v]是由f[v]和f[v-c]两个子问题递推而来,能否保证在推f[v]时(也即在第i次主循环中推f[v]时)能够得到f[v]和f[v-c]的值呢?事实上,这要求在每次主循环中我们以v=V..0的顺序推f[v],这样才能保证推f[v]时f[v-c]保存的是状态f[v-c]的值。伪代码如下:
for i=1..N
for v=V..0
f[v]=max{f[v],f[v-c]+w};
其中的f[v]=max{f[v],f[v-c]}一句恰就相当于我们的转移方程f[v]=max{f[v],f[v-c]},因为现在的f[v-c]就相当于原来的f[v-c]。如果将v的循环顺序从上面的逆序改成顺序的话,那么则成了f[v]由f[v-c]推知,与本题意不符,但它却是另一个重要的背包问题P02最简捷的解决方案,故学习只用一维数组解01背包问题是十分必要的。
事实上,使用一维数组解01背包的程序在后面会被多次用到,所以这里抽象出一个处理一件01背包中的物品过程,以后的代码中直接调用不加说明。
过程ZeroOnePack,表示处理一件01背包中的物品,两个参数cost、weight分别表明这件物品的费用和价值。
procere ZeroOnePack(cost,weight)
for v=V..cost
f[v]=max{f[v],f[v-cost]+weight}
注意这个过程里的处理与前面给出的伪代码有所不同。前面的示例程序写成v=V..0是为了在程序中体现每个状态都按照方程求解了,避免不必要的思维复杂度。而这里既然已经抽象成看作黑箱的过程了,就可以加入优化。费用为cost的物品不会影响状态f[0..cost-1],这是显然的。
有了这个过程以后,01背包问题的伪代码就可以这样写:
for i=1..N
ZeroOnePack(c,w);
初始化的细节问题
我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。有的题目要求“恰好装满背包”时的最优解,有的题目则并没有要求必须把背包装满。一种区别这两种问法的实现方法是在初始化的时候有所不同。
如果是第一种问法,要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1..V]均设为-∞,这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。
如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f[0..V]全部设为0。
为什么呢?可以这样理解:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。
这个小技巧完全可以推广到其它类型的背包问题,后面也就不再对进行状态转移之前的初始化进行讲解
㈢ 用动态规划算法怎样求解01背包问题
动态规划主要解决的是多阶段的决策问题。
01背包中,状态为背包剩余的容量,阶段是每一个物品,决策是是否选择当前的物品。
所以用动态规划来解决是非常贴切的。
我们设f[V]表示已经使用容量为V时所能获得的最大价值,w[i]表示i物品的质量,c[i]表示i物品的价值。
for(inti=1;i<=n;i++)
for(intj=V;j>=w[i];j--)
f[j]=max(f[j],f[j-w[i]]+c[i]);
这便是所谓的一个状态转移方程。
f[j]表示在已经使用容量为j时的最大价值,f[j-w[i]]表示在已经使用容量为j-w[i]时的最大价值。
f[j]可以由f[j-w[i]]这个状态转移到达,表示选取w[i]这个物品,并从而获得价值为c[i]。
而每次f[j]会在选与不选中决策选出最优的方案。
从每一个物品,也就是每一个阶段的局部最优推出最后的全局最优值。这样就解决了01背包问题
㈣ 0-1背包问题的多种解法代码(动态规划、贪心法、回溯法、分支限界法)
一.动态规划求解0-1背包问题
/************************************************************************/
/* 0-1背包问题:
/* 给定n种物品和一个背包
/* 物品i的重量为wi,其价值为vi
/* 背包的容量为c
/* 应如何选择装入背包的物品,使得装入背包中的物品
/* 的总价值最大?
/* 注:在选择装入背包的物品时,对物品i只有两种选择,
/* 即装入或不装入背包。不能将物品i装入多次,也
/* 不能只装入部分的物品i。
/*
/* 1. 0-1背包问题的形式化描述:
/* 给定c>0, wi>0, vi>0, 0<=i<=n,要求找到一个n元的
/* 0-1向量(x1, x2, ..., xn), 使得:
/* max sum_{i=1 to n} (vi*xi),且满足如下约束:
/* (1) sum_{i=1 to n} (wi*xi) <= c
/* (2) xi∈{0, 1}, 1<=i<=n
/*
/* 2. 0-1背包问题的求解
/* 0-1背包问题具有最优子结构性质和子问题重叠性质,适于
/* 采用动态规划方法求解
/*
/* 2.1 最优子结构性质
/* 设(y1,y2,...,yn)是给定0-1背包问题的一个最优解,则必有
/* 结论,(y2,y3,...,yn)是如下子问题的一个最优解:
/* max sum_{i=2 to n} (vi*xi)
/* (1) sum_{i=2 to n} (wi*xi) <= c - w1*y1
/* (2) xi∈{0, 1}, 2<=i<=n
/* 因为如若不然,则该子问题存在一个最优解(z2,z3,...,zn),
/* 而(y2,y3,...,yn)不是其最优解。那么有:
/* sum_{i=2 to n} (vi*zi) > sum_{i=2 to n} (vi*yi)
/* 且,w1*y1 + sum_{i=2 to n} (wi*zi) <= c
/* 进一步有:
/* v1*y1 + sum_{i=2 to n} (vi*zi) > sum_{i=1 to n} (vi*yi)
/* w1*y1 + sum_{i=2 to n} (wi*zi) <= c
/* 这说明:(y1,z2,z3,...zn)是所给0-1背包问题的更优解,那么
/* 说明(y1,y2,...,yn)不是问题的最优解,与前提矛盾,所以最优
/* 子结构性质成立。
/*
/* 2.2 子问题重叠性质
/* 设所给0-1背包问题的子问题 P(i,j)为:
/* max sum_{k=i to n} (vk*xk)
/* (1) sum_{k=i to n} (wk*xk) <= j
/* (2) xk∈{0, 1}, i<=k<=n
/* 问题P(i,j)是背包容量为j、可选物品为i,i+1,...,n时的子问题
/* 设m(i,j)是子问题P(i,j)的最优值,即最大总价值。则根据最优
/* 子结构性质,可以建立m(i,j)的递归式:
/* a. 递归初始 m(n,j)
/* //背包容量为j、可选物品只有n,若背包容量j大于物品n的
/* //重量,则直接装入;否则无法装入。
/* m(n,j) = vn, j>=wn
/* m(n,j) = 0, 0<=j<wn
/* b. 递归式 m(i,j)
/* //背包容量为j、可选物品为i,i+1,...,n
/* //如果背包容量j<wi,则根本装不进物品i,所以有:
/* m(i,j) = m(i+1,j), 0<=j<wi
/* //如果j>=wi,则在不装物品i和装入物品i之间做出选择
/* 不装物品i的最优值:m(i+1,j)
/* 装入物品i的最优值:m(i+1, j-wi) + vi
/* 所以:
/* m(i,j) = max {m(i+1,j), m(i+1, j-wi) + vi}, j>=wi
/*
/************************************************************************/
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
template <typename Type>
void Knapsack(Type* v, int *w, int c, int n, Type **m)
{
//递归初始条件
int jMax = min(w[n] - 1, c);
for (int j=0; j<=jMax; j++) {
m[n][j] = 0;
}
for (j=w[n]; j<=c; j++) {
m[n][j] = v[n];
}
//i从2到n-1,分别对j>=wi和0<=j<wi即使m(i,j)
for (int i=n-1; i>1; i--) {
jMax = min(w[i] - 1, c);
for (int j=0; j<=jMax; j++) {
m[i][j] = m[i+1][j];
}
for (j=w[i]; j<=c; j++) {
m[i][j] = max(m[i+1][j], m[i+1][j-w[i]]+v[i]);
}
}
m[1][c] = m[2][c];
if (c >= w[1]) {
m[1][c] = max(m[1][c], m[2][c-w[1]]+v[1]);
}
}
template <typename Type>
void TraceBack(Type **m, int *w, int c, int n, int* x)
{
for (int i=1; i<n; i++) {
if(m[i][c] == m[i+1][c]) x[i] = 0;
else {
x[i] = 1;
c -= w[i];
}
}
x[n] = (m[n][c])? 1:0;
}
int main(int argc, char* argv[])
{
int n = 5;
int w[6] = {-1, 2, 2, 6, 5, 4};
int v[6] = {-1, 6, 3, 5, 4, 6};
int c = 10;
int **ppm = new int*[n+1];
for (int i=0; i<n+1; i++) {
ppm[i] = new int[c+1];
}
int x[6];
Knapsack<int>(v, w, c, n, ppm);
TraceBack<int>(ppm, w, c, n, x);
return 0;
}
二.贪心算法求解0-1背包问题
1.贪心法的基本思路:
——从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到某算法中的某一步不能再继续前进时,算法停止。
该算法存在问题:
1).不能保证求得的最后解是最佳的;
2).不能用来求最大或最小解问题;
3).只能求满足某些约束条件的可行解的范围。
实现该算法的过程:
从问题的某一初始解出发;
while 能朝给定总目标前进一步 do
求出可行解的一个解元素;
由所有解元素组合成问题的一个可行解;
2.例题分析
1).[背包问题]有一个背包,背包容量是M=150。有7个物品,物品可以分割成任意大小。
要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
物品 A B C D E F G
重量 35 30 60 50 40 10 25
价值 10 40 30 50 35 40 30
分析:
目标函数: ∑pi最大
约束条件是装入的物品总重量不超过背包容量:∑wi<=M( M=150)
(1)根据贪心的策略,每次挑选价值最大的物品装入背包,得到的结果是否最优?
(2)每次挑选所占空间最小的物品装入是否能得到最优解?
(3)每次选取单位容量价值最大的物品,成为解本题的策略。
<程序代码:>(环境:c++)
#include<iostream.h>
#define max 100 //最多物品数
void sort (int n,float a[max],float b[max]) //按价值密度排序
{
int j,h,k;
float t1,t2,t3,c[max];
for(k=1;k<=n;k++)
c[k]=a[k]/b[k];
for(h=1;h<n;h++)
for(j=1;j<=n-h;j++)
if(c[j]<c[j+1])
{t1=a[j];a[j]=a[j+1];a[j+1]=t1;
t2=b[j];b[j]=b[j+1];b[j+1]=t2;
t3=c[j];c[j]=c[j+1];c[j+1]=t3;
}
}
void knapsack(int n,float limitw,float v[max],float w[max],int x[max])
{float c1; //c1为背包剩余可装载重量
int i;
sort(n,v,w); //物品按价值密度排序
c1=limitw;
for(i=1;i<=n;i++)
{
if(w[i]>c1)break;
x[i]=1; //x[i]为1时,物品i在解中
c1=c1-w[i];
}
}
void main()
{int n,i,x[max];
float v[max],w[max],totalv=0,totalw=0,limitw;
cout<<"请输入n和limitw:";
cin>>n >>limitw;
for(i=1;i<=n;i++)
x[i]=0; //物品选择情况表初始化为0
cout<<"请依次输入物品的价值:"<<endl;
for(i=1;i<=n;i++)
cin>>v[i];
cout<<endl;
cout<<"请依次输入物品的重量:"<<endl;
for(i=1;i<=n;i++)
cin>>w[i];
cout<<endl;
knapsack (n,limitw,v,w,x);
cout<<"the selection is:";
for(i=1;i<=n;i++)
{
cout<<x[i];
if(x[i]==1)
totalw=totalw+w[i];
}
cout<<endl;
cout<<"背包的总重量为:"<<totalw<<endl; //背包所装载总重量
cout<<"背包的总价值为:"<<totalv<<endl; //背包的总价值
}
三.回溯算法求解0-1背包问题
1.0-l背包问题是子集选取问题。
一般情况下,0-1背包问题是NP难题。0-1背包
问题的解空间可用子集树表示。解0-1背包问题的回溯法与装载问题的回溯法十分类
似。在搜索解空间树时,只要其左儿子结点是一个可行结点,搜索就进入其左子树。当
右子树有可能包含最优解时才进入右子树搜索。否则将右子树剪去。设r是当前剩余
物品价值总和;cp是当前价值;bestp是当前最优价值。当cp+r≤bestp时,可剪去右
子树。计算右子树中解的上界的更好方法是将剩余物品依其单位重量价值排序,然后
依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。由此得到的价值是
右子树中解的上界。
2.解决办法思路:
为了便于计算上界,可先将物品依其单位重量价值从大到小排序,此后只要顺序考
察各物品即可。在实现时,由bound计算当前结点处的上界。在搜索解空间树时,只要其左儿子节点是一个可行结点,搜索就进入左子树,在右子树中有可能包含最优解是才进入右子树搜索。否则将右子树剪去。
回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。
2.算法框架:
a.问题的解空间:应用回溯法解问题时,首先应明确定义问题的解空间。问题的解空间应到少包含问题的一个(最优)解。
b.回溯法的基本思想:确定了解空间的组织结构后,回溯法就从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。这个开始结点就成为一个活结点,同时也成为当前的扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为一个新的活结点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。换句话说,这个结点不再是一个活结点。此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。回溯法即以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。
3.运用回溯法解题通常包含以下三个步骤:
a.针对所给问题,定义问题的解空间;
b.确定易于搜索的解空间结构;
c.以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索;
#include<iostream>
using namespace std;
class Knap
{
friend int Knapsack(int p[],int w[],int c,int n );
public:
void print()
{
for(int m=1;m<=n;m++)
{
cout<<bestx[m]<<" ";
}
cout<<endl;
};
private:
int Bound(int i);
void Backtrack(int i);
int c;//背包容量
int n; //物品数
int *w;//物品重量数组
int *p;//物品价值数组
int cw;//当前重量
int cp;//当前价值
int bestp;//当前最优值
int *bestx;//当前最优解
int *x;//当前解
};
int Knap::Bound(int i)
{
//计算上界
int cleft=c-cw;//剩余容量
int b=cp;
//以物品单位重量价值递减序装入物品
while(i<=n&&w[i]<=cleft)
{
cleft-=w[i];
b+=p[i];
i++;
}
//装满背包
if(i<=n)
b+=p[i]/w[i]*cleft;
return b;
}
void Knap::Backtrack(int i)
{
if(i>n)
{
if(bestp<cp)
{
for(int j=1;j<=n;j++)
bestx[j]=x[j];
bestp=cp;
}
return;
}
if(cw+w[i]<=c) //搜索左子树
{
x[i]=1;
cw+=w[i];
cp+=p[i];
Backtrack(i+1);
cw-=w[i];
cp-=p[i];
}
if(Bound(i+1)>bestp)//搜索右子树
{
x[i]=0;
Backtrack(i+1);
}
}
class Object
{
friend int Knapsack(int p[],int w[],int c,int n);
public:
int operator<=(Object a)const
{
return (d>=a.d);
}
private:
int ID;
float d;
};
int Knapsack(int p[],int w[],int c,int n)
{
//为Knap::Backtrack初始化
int W=0;
int P=0;
int i=1;
Object *Q=new Object[n];
for(i=1;i<=n;i++)
{
Q[i-1].ID=i;
Q[i-1].d=1.0*p[i]/w[i];
P+=p[i];
W+=w[i];
}
if(W<=c)
return P;//装入所有物品
//依物品单位重量排序
float f;
for( i=0;i<n;i++)
for(int j=i;j<n;j++)
{
if(Q[i].d<Q[j].d)
{
f=Q[i].d;
Q[i].d=Q[j].d;
Q[j].d=f;
}
}
Knap K;
K.p = new int[n+1];
K.w = new int[n+1];
K.x = new int[n+1];
K.bestx = new int[n+1];
K.x[0]=0;
K.bestx[0]=0;
for( i=1;i<=n;i++)
{
K.p[i]=p[Q[i-1].ID];
K.w[i]=w[Q[i-1].ID];
}
K.cp=0;
K.cw=0;
K.c=c;
K.n=n;
K.bestp=0;
//回溯搜索
K.Backtrack(1);
K.print();
delete [] Q;
delete [] K.w;
delete [] K.p;
return K.bestp;
}
void main()
{
int *p;
int *w;
int c=0;
int n=0;
int i=0;
char k;
cout<<"0-1背包问题——回溯法 "<<endl;
cout<<" by zbqplayer "<<endl;
while(k)
{
cout<<"请输入背包容量(c):"<<endl;
cin>>c;
cout<<"请输入物品的个数(n):"<<endl;
cin>>n;
p=new int[n+1];
w=new int[n+1];
p[0]=0;
w[0]=0;
cout<<"请输入物品的价值(p):"<<endl;
for(i=1;i<=n;i++)
cin>>p[i];
cout<<"请输入物品的重量(w):"<<endl;
for(i=1;i<=n;i++)
cin>>w[i];
cout<<"最优解为(bestx):"<<endl;
cout<<"最优值为(bestp):"<<endl;
cout<<Knapsack(p,w,c,n)<<endl;
cout<<"[s] 重新开始"<<endl;
cout<<"[q] 退出"<<endl;
cin>>k;
}
四.分支限界法求解0-1背包问题
1.问题描述:已知有N个物品和一个可以容纳M重量的背包,每种物品I的重量为WEIGHT,一个只能全放入或者不放入,求解如何放入物品,可以使背包里的物品的总效益最大。
2.设计思想与分析:对物品的选取与否构成一棵解树,左子树表示不装入,右表示装入,通过检索问题的解树得出最优解,并用结点上界杀死不符合要求的结点。
#include <iostream.h>
struct good
{
int weight;
int benefit;
int flag;//是否可以装入标记
};
int number=0;//物品数量
int upbound=0;
int curp=0, curw=0;//当前效益值与重量
int maxweight=0;
good *bag=NULL;
void Init_good()
{
bag=new good [number];
for(int i=0; i<number; i++)
{
cout<<"请输入第件"<<i+1<<"物品的重量:";
cin>>bag[i].weight;
cout<<"请输入第件"<<i+1<<"物品的效益:";
cin>>bag[i].benefit;
bag[i].flag=0;//初始标志为不装入背包
cout<<endl;
}
}
int getbound(int num, int *bound_u)//返回本结点的c限界和u限界
{
for(int w=curw, p=curp; num<number && (w+bag[num].weight)<=maxweight; num++)
{
w=w+bag[num].weight;
p=w+bag[num].benefit;
}
*bound_u=p+bag[num].benefit;
return ( p+bag[num].benefit*((maxweight-w)/bag[num].weight) );
}
void LCbag()
{
int bound_u=0, bound_c=0;//当前结点的c限界和u限界
for(int i=0; i<number; i++)//逐层遍历解树决定是否装入各个物品
{
if( ( bound_c=getbound(i+1, &bound_u) )>upbound )//遍历左子树
upbound=bound_u;//更改已有u限界,不更改标志
if( getbound(i, &bound_u)>bound_c )//遍历右子树
//若装入,判断右子树的c限界是否大于左子树根的c限界,是则装入
{
upbound=bound_u;//更改已有u限界
curp=curp+bag[i].benefit;
curw=curw+bag[i].weight;//从已有重量和效益加上新物品
bag[i].flag=1;//标记为装入
}
}
}
void Display()
{
cout<<"可以放入背包的物品的编号为:";
for(int i=0; i<number; i++)
if(bag[i].flag>0)
cout<<i+1<<" ";
cout<<endl;
delete []bag;
}
㈤ 0/1鑳屽寘闂棰樷斺斿姩镐佽勫垝銆佸洖婧銆佸垎鏀闄愮晫娉曞规瘆
锅囧畾n涓鍟嗗搧閲嶉噺鍒嗗埆涓簑 0 , w 1 , ..., w n-1 锛屼环鍊煎垎鍒涓簆 0 , p 1 , ..., p n-1 锛岃儗鍖呰浇閲嶉噺涓篗銆傛庢牱阃夋嫨鍟嗗搧缁勫悎锛屼娇寰椾环鍊兼渶楂桡纻
链澶у肩殑浼扮畻娉曪纸璺熷垎鏀闄愮晫娉曟湰璐ㄤ笂鏄涓镙风殑锛
钖戜笂锲炴函镄勬柟娉
w_cur钬斺旇〃绀哄綋鍓嶆e湪鎼灭储镄勯儴鍒呜В涓杞鍏ョ殑镐婚吨閲
p_cur钬斺斿綋鍓嶆讳环鍊
p_est钬斺旈儴鍒呜В鍙鑳借揪鍒扮殑链澶т环鍊肩殑浼拌″
p_total钬斺斿綋鍓嶆悳绱㈠埌镄勬墍链夊彲琛岃В涓镄勬渶澶т环鍊硷纴鏄褰揿墠鐩镙囧嚱鏁扮殑涓婄晫
y k 銆亁 k 钬斺旈儴鍒呜В镄勭琸涓鍒嗛噺鍙婂叾鍓链
k钬斺旇〃绀哄綋鍓嶆悳绱㈡繁搴
M = 50
鍟嗗搧閲嶉噺鍒嗗埆涓5,15,25,27,30
鍟嗗搧浠峰煎垎鍒涓12,30,44,46,50
涓婇溃宸茬粡鎸夌収鍗曚綅閲嶉噺浠峰奸掑噺椤哄簭鎺掑垪銆
姣忎釜缁撶偣閮藉寘钖濡备笅淇℃伅锛
S 1 钬斺斿綋鍓嶉夋嫨瑁呭叆鑳屽寘镄勫晢鍝侀泦钖
S 2 钬斺斿綋鍓崭笉阃夋嫨瑁呭叆鑳屽寘镄勫晢鍝侀泦钖
S 3 钬斺斿綋鍓嶅皻寰呴夋嫨镄勫晢鍝侀泦钖
k钬斺旀悳绱㈡繁搴
b钬斺斾笂鐣
bound钬斺斾竴涓鍙琛岃В镄勫彇鍊硷纴褰揿仛鍓鏋濈殑镙囧嗳
杈揿叆鎻忚堪:
杈揿嚭鎻忚堪:
杈揿叆渚嫔瓙:
杈揿嚭渚嫔瓙:
㈥ 动态规划中的0-1背包问题怎么去理解要求给出具体实例和详细步骤。。。
* 一个旅行者有一个最多能用M公斤的背包,现在有N件物品,
它们的重量分别是W1,W2,...,Wn,
它们的价值分别为P1,P2,...,Pn.
若每种物品只有一件求旅行者能获得最大总价值。
输入格式:
M,N
W1,P1
W2,P2
......
输出格式:
X
*/
因为背包最大容量M未知。所以,我们的程序要从1到M一个一个的试。比如,开始任选N件物品的一个。看对应M的背包,能不能放进去,如果能放进去,并且还有多的空间,则,多出来的空间里能放N-1物品中的最大价值。怎么能保证总选择是最大价值呢?看下表。
测试数据:
10,3
3,4
4,5
5,6
c[i][j]数组保存了1,2,3号物品依次选择后的最大价值.
这个最大价值是怎么得来的呢?从背包容量为0开始,1号物品先试,0,1,2,的容量都不能放.所以置0,背包容量为3则里面放4.这样,这一排背包容量为4,5,6,....10的时候,最佳方案都是放4.假如1号物品放入背包.则再看2号物品.当背包容量为3的时候,最佳方案还是上一排的最价方案c为4.而背包容量为5的时候,则最佳方案为自己的重量5.背包容量为7的时候,很显然是5加上一个值了。加谁??很显然是7-4=3的时候.上一排 c3的最佳方案是4.所以。总的最佳方案是5+4为9.这样.一排一排推下去。最右下放的数据就是最大的价值了。(注意第3排的背包容量为7的时候,最佳方案不是本身的6.而是上一排的9.说明这时候3号物品没有被选.选的是1,2号物品.所以得9.)
从以上最大价值的构造过程中可以看出。
f(n,m)=max{f(n-1,m), f(n-1,m-w[n])+P(n,m)}这就是书本上写的动态规划方程.这回清楚了吗?
下面是实际程序:
#include<stdio.h>
int c[10][100];/*对应每种情况的最大价值*/
int knapsack(int m,int n)
{
int i,j,w[10],p[10];
for(i=1;i<n+1;i++)
scanf("\n%d,%d",&w[i],&p[i]);
for(i=0;i<10;i++)
for(j=0;j<100;j++)
c[i][j]=0;/*初始化数组*/
for(i=1;i<n+1;i++)
for(j=1;j<m+1;j++)
{
if(w[i]<=j) /*如果当前物品的容量小于背包容量*/
{
if(p[i]+c[i-1][j-w[i]]>c[i-1][j])
/*如果本物品的价值加上背包剩下的空间能放的物品的价值*/
/*大于上一次选择的最佳方案则更新c[i][j]*/
c[i][j]=p[i]+c[i-1][j-w[i]];
else
c[i][j]=c[i-1][j];
}
else c[i][j]=c[i-1][j];
}
return(c[n][m]);
}
int main()
{
int m,n;int i,j;
scanf("%d,%d",&m,&n);
printf("Input each one:\n");
printf("%d",knapsack(m,n));
printf("\n");/*下面是测试这个数组,可删除*/
for(i=0;i<10;i++)
for(j=0;j<15;j++)
{
printf("%d ",c[i][j]);
if(j==14)printf("\n");
}
system("pause");
}
㈦ 0-1鑳屽寘闂棰桦叆闂ㄨ﹁В
缃戜笂濂藉氩叧浜庤儗鍖呴梾棰樼殑瑙i喷锛岃嚜宸变篃鐪嬩简锛屾劅瑙夎В閲婄殑涓嶅规槗阃氢织鏄撴哕锛屾墍浠ヨ嚜宸辨潵鍐欎竴涓闱炲父瀹规槗镍傚缑銆
0-1鑳屽寘闂棰樿寸殑鏄锛岀粰瀹氲儗鍖呭归噺W锛屼竴绯诲垪鐗╁搧{weiht,value}锛屾疮涓鐗╁搧鍙鑳藉彇涓浠讹纴銮峰彇链澶у笺
閲囩敤锷ㄦ佽勫垝姹傝В锛屽姩镐佽勫垝镄勪竴鑸瑙勫緥閮芥槸锛
鍦ㄤ粈涔堜粈涔埚墠i涓鐘舵佷笅镄勬渶澶у兼垨钥呮渶灏忓肩殑鍓嶆彁涓嬶纴铹跺悗鍐嶆妸i镄勭姸镐佺殑鍊兼眰鍑烘潵銆
杩欓噷鎴戜滑瀹氢箟涓涓鍑芥暟锛岃〃绀虹姸镐併
m(1,2,3,4..i)(w)琛ㄧず链1鍙,2鍙,3鍙,4鍙....i鍙风墿鍝侊纴鑳屽寘瀹归噺涓簑镄勬椂鑳藉熷彇寰楃殑链澶у笺备妇渚嬭存槑锛
锅囱1锛2,3,4,5鍙风墿鍝侊纴瀹冧滑镄勯吨閲忓垎鍒鏄2,2,6,5,4锛岀敤weight(i)琛ㄧず锛屽畠浠镄勪环鍊煎垎鍒鏄6,3,5,4,6鐢╲alue(i)琛ㄧず
m(1)(1)琛ㄥ彧链1鍙风墿鍝侊纴鑳屽寘瀹归噺涓1镄勬椂鍊欙纴链澶у笺傛樉铹讹纴
m(1)(1) = 0锛屽洜涓鸿儗鍖呭归噺灏忎簬2锛屾墍浠ユ渶澶у间负0銆
m(1)(2) = 6, 姝ゆ椂鑳屽寘瀹归噺绛変簬2锛岃呬笅1鍙风墿鍝侊纴链澶у间负6锛屾帴涓嬫潵
m(1)(3) = 6,m(1)(4) = 6锛...m(1)(..) = 6,锲犱负鍙链変竴浠剁墿鍝侊纴链澶т负6銆
m(1,2)(1),m(1,2)(2),m(1,2)(3)琛ㄧず链夌墿鍝1鍙凤纴2鍙凤纴鑳屽寘瀹归噺鍒嗗埆涓1,2,3镄勬椂鍊欐渶澶у笺
链澶у煎拰鐗╁搧镄勬暟閲忕浉鍏筹纴涔熷拰鑳屽寘瀹归噺鐩稿叧銆
杩欓噷蹇呴’寮鸿皟涓涓嬶纴m(1,2,3,...i)(w) 琛ㄧず链1,2,3,4....i锛岃繖涔埚氱墿鍝佸彲阃夛纴链蹇呭叏閮ㄨ呰繘铡荤殑𨱍呭喌涓(鍙楅檺浜巜)镄勬渶澶у笺
鎺ヤ笅𨱒ヨㄨ哄湪1,2,3.....i鐗╁搧镄勬渶澶у笺傚逛簬绗琲浠剁墿鍝侊纴鑳屽寘瀹归噺涓篧锛屾湁涓ょ嶆儏鍐碉纴
1锛変笉鎶婄琲浠剁墿鍝佽呰繘鑳屽寘锛岄偅涔堟ゆ椂锛屽彧链1,2,3,4,,,,i-1浠剁墿鍝侊纴鑳屽寘镄勬渶澶у兼槸m(1,2,3,4,5....i-1)(W)銆傛ゆ椂锛屼笉绠W澶氢箞澶э纴鍗充娇鍜屽畤瀹欎竴镙峰ぇ锛岃儗鍖呴噷镄勪环鍊间箣鍜1,2,3,4,5..i-1杩欎簺鐗╁搧鐩稿叧銆
2锛夋妸绗琲浠剁墿鍝佽呰繘铡汇傛棦铹舵妸i浠剁墿鍝佽呰繘鑳屽寘锛岄偅涔1,2,3,4.....i-1鐗╁搧鍙鑳藉崰鐢 W-weight(i) 杩欎箞澶氶吨閲忎简銆傝繖涓镞跺欙纴
涔嫔墠镄1,2,3,4......i-1鐗╁搧鍦ㄨ儗鍖呭归噺涓猴纸W-weight(i)锛変笅镄勬渶澶у间负m(1,2,3.......i-1)[ W - weight(i) ]銆
姝ゆ椂鑳屽寘镄勬渶澶у煎氨鏄 绗琲浠剁墿鍝佺殑浠峰纥alue(i)锷犱笂
鍓1,2,3,4....i-1浠剁墿鍝佸湪鑳屽寘瀹归噺涓猴纸W-weight(i) 涓嬬殑链澶у纠(1,2,3.......i-1)[ W - weight(i) ]
m锛1,2,3,4.......i-1,i)(W)= m(1,2,3.......i-1)[ W - weight(i) ] + value(i) ;
铹跺悗鎴戜滑姣旇缉涓涓嬶纴𨱍呭喌1锛2锛夌殑链澶у煎氨鍙浠ヤ简 鍗
m(1,2,3,4....i-1,i)(W) = max[ m(1,2,3.......i-1)[ W - weight(i) ] + value(i) , m(1,2,3,4,5....i-1)(W) ]銆
杩欓噷链変汉浼氲达纴鍓1,2,3,4.....i-1浠剁墿鍝佸湪W-weight鎴栬匴镄勫归噺涓嬫庝箞姹傚晩銆
杩欓噷灏辫村埌锷ㄦ佽勫垝镄勭偣涓娿傚姩镐佽勫垝链夌偣鏁板﹀綊绾虫硶镄勬劅瑙夛纴涓嶈繃鏄浠庡悗钖戝墠鎺ㄥ埌锛岃佹眰瑙i锛屽厛姹傝Вi-1,锛涜佹眰瑙i-1锛屽厛姹傝Вi-2锛岃繖镙蜂竴姝ヤ竴姝ュ埌2,1銆傚洜姝ら渶瑕佺粰瀹氩埯濮嬬姸镐併傛垜浠涓鐩寸敤1,2,3,4.....i-1琛ㄧず鍓峣-1浠剁墿鍝侊纴澶楹荤储锛岀洿鎺ョ敤i-1琛ㄧず濂戒简銆
m(1,2,3,4....i-1)(w) ====(涔﹀啓鏂逛究)>m(i-1)(w)锛岃繖镙蜂笂闱㈢殑鐘舵佽浆绉绘柟绋嫔氨鍑烘潵銆
m(i)(W) = max( m(i-1)(W- weight(i))+value(i), m(i-1)(w) )
杩欐牱锛岀姸镐佺殑杞绉绘柟绋嫔氨鍑烘潵銆傝繖閲屼笉寰椾笉璇翠笅锛岀绣涓婄殑鍏朵粬鏁欑▼杩欎竴镣逛笂璇寸殑涓嶅熶粩缁嗭纴涓婃潵灏辨闷涓涓
f[i-1][j] = max(f[i-1][j-w(i)]+value[i],f[i-1][j])銆傝皝鐪嬬殑镍傚晩銆
杩欓噷鎴戜滑阍埚逛笂闱㈢殑鏁板肩粰鍑哄叿浣撶殑姹傝В杩囩▼銆傞栧厛缁椤嚭鐗╁搧镄勫嚱鏁板笺
weight(1) = 2,value(1) = 6,
weight(2) = 2,value(2) = 3,
weight(3) = 6,value(3) = 5,
weight(4) = 5,value(4) = 4,
weight(5) = 4,value(5) = 6,
闾d箞链澶у煎嚱鏁
m(1)(1) = 0锛涚墿鍝侀吨閲忎负2.
m(1)(2) = 6, 鐗╁搧鎭板ソ鏀惧叆鑳屽寘銆
m(1)(3) = 6锛宫(1)(4) = 6...锛屽彧链1鍙风墿鍝侊纴链澶у煎彧鑳戒负6銆傜幇鍦ㄨ冭槛链夌2浠剁墿鍝佺殑𨱍呭喌锛岀幇鍦ㄦ湁涓や欢鐗╁搧锛宫鍑芥暟琛ㄧず涓
m(1,2)(w)銆
镙规嵁涔嫔墠镓璇 1锛夛纴濡傛灉涓嶆妸2鍙风墿鍝佹斁鍏ワ纴闾i梾棰桦洖鍒板彧链1鍙风墿鍝佺殑𨱍呭喌
闾d箞
m(1)(1) = 0,1鍙风墿鍝佹斁涓嶈繘锛
m(1)(2) = 6, 1鍙风墿鍝佹斁杩涜儗鍖呫
m(1)(3) = 6, 1鍙风墿鍝佹斁杩涘归噺涓3镄勮儗鍖
m(1)(4) = 6, 1鍙风墿鍝佹斁杩涘归噺涓4镄勮儗鍖呫
镙规嵁涔嫔墠镓璇2锛夛纴鎶2鍙风墿鍝佹斁鍏ワ纴姝ゆ椂闇瑕 1鍙风墿鍝佸湪鑳屽寘瀹归噺w鍑忓幓2鍙风墿鍝佺殑瀹归噺weight(2),鍗 w-2镄勯梾棰樸
m(1)(1 - 2) = 0,鏄剧劧锛屾ゆ椂鑳屽寘镐诲归噺涓1锛岃缮链夊噺铡2鍙风墿鍝佺殑瀹归噺2锛1-2=-1 锛屾樉铹舵斁涓嶈繘铡汇
m(1)(2 - 2) = 0,鏄剧劧锛岃儗鍖呯殑瀹归噺鍑忓幓2鍙风墿鍝佺殑瀹归噺钖庯纴娌℃湁鍓╀綑锛屽氨鏄璇村彧鑳芥斁2鍙风墿鍝侊纴姝ゆ椂鑳屽寘镄勬渶澶у
m(1,2)(2) = max(m(1,2)(2-2)+value(2), m(1)(2))= max(0+3,6) = 6銆傚氨鏄璇达纴鍦ㄦ湁1,2涓や欢鐗╁搧锛岃儗鍖呭归噺涓2镄勬儏鍐典笅锛屾渶澶у间负6銆
缁х画钥冭槛鑳屽寘瀹归噺涓3锛岀涓绉嶆儏鍐电殑宸茬粡璁ㄨ恒傜幇鍦ㄨㄨ虹浜岀嶆儏鍐碉纴鎶2鍙风墿鍝佹斁鍏ヨ儗鍖咃纴灏辫佸墿涓媤-2镄勫归噺缁1鍙蜂简銆
m(1,2)(w-2)= m(1,2)(3-2)=m(1,2)(1) = 0, value(2) = 3锲犳わ纴
m(1,2)(3) = max[ m(1,2)(3-2)+value(2),m(1)(3)] = max(0+3,6) = 6銆
缁х画钥冭槛鑳屽寘瀹归噺涓4锛屽悓鐞嗭纴绗涓绉嶆儏鍐佃ㄨ猴纴鍙璁ㄨ虹浜岀嶆儏鍐碉纴鎶2鍙风墿鍝佹斁鍏ヨ儗鍖咃纴灏辫佸墿涓媤-2镄勫归噺缁1鍙蜂简銆
m(1,2)(4-2)=m(1,2)(2)=6,value(2) = 3
m(1,2)(4) = max[ m(1,2)(4-2)+value(2),m(1)(4)]=max[m(1,2)(2)+value(2),m(1)(4)] = max(6+3,6) = 9;
姝ゆ椂m(1,2)(4) = 9;涔嫔悗锛岃儗鍖呭归噺涓5锛6,7锛屻傜殑镞跺欙纴1,2鐗╁搧閮芥斁杩涘幓锛屾渶澶у间笉鍐嶆敼鍙桡纴閮芥槸9
m(1,2)(5) = 9锛宫(1,2)(6) = 9锛宫(1,2)(7) = 9锛宫(1,2)(8) = 9
钖岀悊锛寃eight(3) = 6,value(3)=5
m(1,2,3)(1) = max[ m(1,2,3)(1-6)+value(3),m(1,2)(1) ] = 0
m(1,2,3)(2) = max[ m(1,2,3)(2-6)+value(3),m(1,2)(2) ] =6
m(1,2,3)(3) = max[ m(1,2,3)(3-6)+value(3),m(1,2)(3) ] =6
m(1,2,3)(4) = max[ m(1,2,3)(4-6)+value(3),m(1,2)(4) ] =9
m(1,2,3)(5) = max[ m(1,2,3)(5-6)+value(3),m(1,2)(5) ] =9
m(1,2,3)(6) = max[ m(1,2,3)(6-6)+value(3),m(1,2)(6) ] = 9
m(1,2,3)(7) = max[ m(1,2,3)(7-6)+value(3),m(1,2)(7) ] = max[m(1,2,3)(1)+5,9] = max[0+5,9]=9
m(1,2,3)(8) = max[ m(1,2,3)(8-6)+value(3),m(1,2)(8) ] = max[m(1,2,3)(2)+5,9] = max[6+5,9]=11;
鍓╀笅镄勬帹瀵奸兘鏄濡傛わ纴镙规嵁鑳屽寘瀹归噺涓姝ヤ竴姝ョ殑鎺ㄥ煎嵆鍙銆