UTF8gbsn
Introduction
先来看看问题,加入我们有一个价格表
length(i) 1 2 3 4 5 6 7 8 9 10
price( p i p_i pi) 1 5 8 9 10 17 17 20 24 30
那么,我们现在有一根刚才长度为
n
n
n,你是一个加工商,需要把长度为
n
n
n
的刚才切割为
(
1
,
2
,
.
.
.
,
10
)
(1,2,...,10)
(1,2,...,10)长度卖出去。问怎么才能卖出最高的价格。
Recursive
alg
我们先来看看一个递归解法。也就是把问题分治,
当一个刚才被切割为2部分的时候。我们可以分别计算这两部分的最大收益,然后得出这种分法的最大收益。递归而来,最终可以得到最终的收益。
CUT-ROD(p, n)
if n == 0
return 0
q = - infinity
for i = 1 to n
q = max(q, p[i]+CUT-ROD(p, n-i))
return q
complexity
因为是一个递归算法我们用调用的次数来衡量算法的复杂度。那么首先,我们先来看看
-
T ( 0 ) = 1 T(0) = 1 T(0)=1
-
T ( 1 ) = 2 T(1) = 2 T(1)=2
-
T ( 2 ) = 4 T(2) = 4 T(2)=4
-
T ( n ) = 2 n T(n) = 2^n T(n)=2n
这个可以用归纳法求出来,也就是说
T
(
n
−
1
)
=
2
n
−
1
,
T
(
0
)
=
1
,
T
(
1
)
=
2
→
T
(
n
)
=
2
n
T(n-1)=2^{n-1},T(0)=1,T(1)=2\rightarrow T(n) = 2^n
T(n−1)=2n−1,T(0)=1,T(1)=2→T(n)=2n
2
n
=
1
+
2
n
−
1
+
2
n
−
2
+
.
.
.
+
2
3
+
2
2
+
2
1
+
1
2^n=1+2^{n-1}+2^{n-2}+...+2^3+2^2+2^1+1
2n=1+2n−1+2n−2+...+23+22+21+1
所以,调用是指数级别的的增长。比如当 n = 40 n=40 n=40时,程序就需要运行数分钟之久。所以,递归解法不是一个可取的算法。
dynamic programming
我们仔细来分析一下,问题。对于上面提到的递归算法,实际上重复计算了很多东西。比如,当 n = 5 n=5 n=5的时候。
-
第一次循环中
( 1 , C U T − R O D ( p , 4 ) ) , ( 2 , C U T − R O D ( p , 3 ) ) , ( 3 , C U T − R O D ( p , 2 ) ) , . . . , ( 5 , C U T − R O D ( p , 0 ) ) (1, CUT-ROD(p, 4)),(2, CUT-ROD(p, 3)),(3, CUT-ROD(p, 2)),...,(5, CUT-ROD(p, 0)) (1,CUT−ROD(p,4)),(2,CUT−ROD(p,3)),(3,CUT−ROD(p,2)),...,(5,CUT−ROD(p,0)) -
把 ( 1 , C U T − R O D ( p , 4 ) ) (1, CUT-ROD(p, 4)) (1,CUT−ROD(p,4))展开
( 1 , C U T − R O D ( p , 3 ) ) , ( 2 , C U T − R O D ( p , 2 ) ) , . . . , ( 4 , C U T − R O D ( p , 0 ) ) (1, CUT-ROD(p, 3)),(2, CUT-ROD(p, 2)),...,(4, CUT-ROD(p, 0)) (1,CUT−ROD(p,3)),(2,CUT−ROD(p,2)),...,(4,CUT−ROD(p,0))
可以看到,第一步中第二项和第二步中的第一项的
CUT-ROD是一模一样的。但是因为是递归。实际上我们对这个函数计算了两次。而动态规划的核心就是如何把已经计算的东西缓存起来,让计算更高效。
top-down
自顶向下的方法。
CUT-ROD(p, n)
let r[0..n] be a new array
for i = 0 to n
r[i] = - Infinity
return CUT-ROD-AUX(p, n, r)
这里引入了一个辅助函数
CUT-ROD-AUX(p, n, r)
if r[n] > = 0
return r[n]
if n == 0
q = 0
else q = - Infinity
for i = 1 to n
q = max(q, p[i] + CUT-ROD-AUX(p, n-i, r))
r[n] = q
return q
bottom-up
更为简单 CUT-ROD(p, n)
let r[0..n] be a new array
r[0] = 0
for j = 1 to n
q = - Infinity
for i = 1 to j
q = max(q, p[i]+r[j-1])
r[j] = q
return r[n]
complexity
由于我们使用了两层循环所以复杂度为 Θ ( n 2 ) \Theta(n^2) Θ(n2)
Reconstruct a solution
EX-CUT-ROD(p, n)
let r[0..n] and s[0..n] be new arrays
r[0] = 0
for j = 1 to n
q = - Infinity
for i = 1 to j
if q<p[i]+r[j-i]
q = p[i]+r[j-i]
s[j] = i
r[j] = q
return r and s
PRINT-CUT-ROD(p,n)
(r,s) = EX-CUT-ROD(p, n)
while n > 0
print s[n]
n = n - s[n]
The End
至此,动态规划解决钢管切割问题,就完成了。