动态规划算法的思想
优化子结构:
一个问题的优化解包含了子问题的优化解
重叠子问题:
在问题的求解过程中,很多子问题的解将被多次使用
大部分的时候都是在填表(但是填表也是一门艺术啊)说实话,在老师讲之前,我没怎么接触过算法,但是这道题跟我很大的启发,而且这种题
课上的例题
1.最长公共子序列
输入:
X
=
(
x
1
,
x
2
,
.
.
.
,
x
m
)
,
Y
=
(
y
1
,
y
2
,
.
.
.
y
n
)
X = (x_1,x_2,...,x_m),Y = (y_1,y_2,...y_n)
X=(x1,x2,...,xm),Y=(y1,y2,...yn)
输出:
Z
=
X
Z = X
Z=X与
Y
Y
Y的最长公共子序列
优化子结构(就是我们在现有的条件下怎样往下找下一次的):
设
X
=
(
x
1
,
.
.
.
,
x
m
)
、
Y
=
(
y
1
,
.
.
.
,
y
n
)
X=(x_1, ..., x_m)、Y=(y_1, ..., y_n)
X=(x1,...,xm)、Y=(y1,...,yn)是两个序列,
Z
=
(
z
1
,
.
.
.
,
z
k
)
Z=(z_1, ..., z_k)
Z=(z1,...,zk)是
X
X
X与
Y
Y
Y的
L
C
S
LCS
LCS,我们有:
⑴ 如果
x
m
=
y
n
x_m=y_n
xm=yn, 则
z
k
=
x
m
=
y
n
,
Z
k
−
1
z_k=x_m=y_n, Z_{k-1}
zk=xm=yn,Zk−1是
X
m
−
1
X_{m-1}
Xm−1和
Y
n
−
1
Y_{n-1}
Yn−1的
L
C
S
LCS
LCS,即,
L
C
S
X
Y
=
L
C
S
X
m
−
1
Y
n
−
1
+
<
x
m
=
y
n
>
LCS_{XY} = LCS_{X_{m-1}Y_{n-1}}+ <x_m=y_n>
LCSXY=LCSXm−1Yn−1+<xm=yn>.
⑵ 如果
x
m
≠
y
n
x_m\ne y_n
xm=yn,且
z
k
≠
x
m
z_k\ne x_m
zk=xm,则
Z
Z
Z是
X
m
−
1
X_{m-1}
Xm−1和
Y
Y
Y的
L
C
S
LCS
LCS,即
L
C
S
X
Y
=
L
C
S
X
m
−
1
Y
LCS_{XY}= LCS_{X_{m-1}Y}
LCSXY=LCSXm−1Y
⑶ 如果
x
m
≠
y
n
x_m\ne y_n
xm=yn,且
z
k
≠
y
n
z_k\ne y_n
zk=yn,则
Z
Z
Z是
X
X
X与
Y
n
−
1
Y_{n-1}
Yn−1的
L
C
S
LCS
LCS,即
L
C
S
X
Y
=
L
C
S
X
Y
n
−
1
LCS_{XY}= LCS_{XY_{n-1}}
LCSXY=LCSXYn−1
主要思想就是,我们从后往前遍历两个串,如果当字符串中的 x m = y n x_m=y_n xm=yn时,那么最长公共子序列中肯定就有这个字母了,我们就在 L C S X m − 1 Y n − 1 LCS_{X_{m-1}Y_{n-1}} LCSXm−1Yn−1寻找就好了;当 x m ≠ y n x_m\ne y_n xm=yn,且 z k ≠ x m z_k\ne x_m zk=xm的时候,就说明 x m x_m xm不是最长公共子序列中的字母,我们在 L C S X m − 1 Y LCS_{X_{m-1}Y} LCSXm−1Y找就好了;当 x m ≠ y n x_m\ne y_n xm=yn,且 z k ≠ y n z_k\ne y_n zk=yn,就说明 y n y_n yn不是最长公共子序列中的字母,我们在 L C S X Y n − 1 LCS_{XY_{n-1}} LCSXYn−1找就好了;
不用动态规划的时候我们会这样做:
S
i
m
p
l
e
L
C
S
(
X
,
Y
)
SimpleLCS(X,Y)
SimpleLCS(X,Y)
(
1
)
I
f
m
=
0
或
n
=
0
;
T
h
e
n
输
出
空
串
,
算
法
结
束
;
(1) If\quad m=0 或 n=0 ;Then 输出空串,算法结束;
(1)Ifm=0或n=0;Then输出空串,算法结束;
(
2
)
I
f
x
n
=
y
m
;
T
h
e
n
(2) If \quad x_n=y_m; Then
(2)Ifxn=ym;Then
(
3
)
输
出
S
i
m
p
l
e
L
C
S
(
X
,
Y
)
+
<
x
n
>
;
(3) \quad 输出SimpleLCS(X,Y)+<x_n>;
(3)输出SimpleLCS(X,Y)+<xn>;
(
4
)
E
l
s
e
(4) Else
(4)Else
(
5
)
Z
1
←
S
i
m
p
l
e
L
C
S
(
X
n
−
1
,
Y
)
;
(5) \quad Z1\leftarrow SimpleLCS(X_{n-1},Y);
(5)Z1←SimpleLCS(Xn−1,Y);
(
6
)
Z
2
←
S
i
m
p
l
e
L
C
S
(
X
,
Y
m
−
1
)
;
(6) \quad Z2\leftarrow SimpleLCS(X,Y_{m-1});
(6)Z2←SimpleLCS(X,Ym−1);
(
7
)
输
出
Z
1
,
Z
2
中
较
长
者
;
(7) 输出Z1,Z2中较长者;
(7)输出Z1,Z2中较长者;
当时我们发现,我们这么做会重复的计算很多东西,比如在计算
L
C
S
X
m
Y
n
LCS_{X_mY_n}
LCSXmYn我们会计算
L
C
S
X
m
−
1
Y
n
−
1
,
L
C
S
X
m
−
1
Y
n
,
L
C
S
X
m
Y
n
−
1
LCS_{X_{m-1}Y_{n-1}}, LCS_{X_{m-1}Y_n},LCS_{X_mY_{n-1}}
LCSXm−1Yn−1,LCSXm−1Yn,LCSXmYn−1然后在计算
L
C
S
X
m
−
1
Y
n
−
1
LCS_{X_{m-1}Y_{n-1}}
LCSXm−1Yn−1的时候,我们会计算
L
C
S
X
m
−
2
Y
n
−
2
,
L
C
S
X
m
−
2
Y
n
−
1
,
L
C
S
X
m
−
1
Y
n
−
2
LCS_{X_{m-2}Y_{n-2}}, LCS_{X_{m-2}Y_{n-1}}, LCS_{X_{m-1}Y_{n-2}}
LCSXm−2Yn−2,LCSXm−2Yn−1,LCSXm−1Yn−2而在计算
L
C
S
X
m
−
1
Y
n
LCS_{X_{m-1}Y_n}
LCSXm−1Yn时,我们会计算
L
C
S
X
m
−
2
Y
n
−
1
,
L
C
S
X
m
−
2
Y
n
,
L
C
S
X
m
−
1
Y
n
−
1
LCS_{X_{m-2}Y_{n-1}} ,LCS_{X_{m-2}Y_n} ,LCS_{X_{m-1}Y_{n-1}}
LCSXm−2Yn−1,LCSXm−2Yn,LCSXm−1Yn−1,而在计算
L
C
S
X
m
Y
n
−
1
LCS{X_mY_{n-1}}
LCSXmYn−1时,我们会计算
L
C
S
X
m
−
1
Y
n
−
2
,
L
C
S
X
m
−
1
Y
n
−
1
,
L
C
S
X
m
Y
n
−
2
LCS_{X_{m-1}Y_{n-2}}, LCS_{X_{m-1}Y_{n-1}}, LCS_{X_mY_{n-2}}
LCSXm−1Yn−2,LCSXm−1Yn−1,LCSXmYn−2;这时我们就会发现有重叠子问题了。为了避免重复的计算,我们就需要设计一个动态规划的算法。
我们根据我们发现的优化子结构能够总结出来下面的公式:
(
1
)
C
[
i
,
j
]
=
0
i
f
i
=
0
或
j
=
0
(
2
)
C
[
i
,
j
]
=
C
[
i
−
1
,
j
−
1
]
+
1
i
f
i
,
j
>
0
a
n
d
x
i
=
y
j
(
3
)
C
[
i
,
j
]
=
M
a
x
(
C
[
i
,
j
−
1
]
,
C
[
i
−
1
,
j
]
)
i
f
i
,
j
>
0
a
n
d
x
i
≠
y
j
(1)C[i, j] = 0 \qquad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad if\quad i=0 或 j=0 \\ (2)C[i, j] = C[i-1, j-1] + 1 \quad\quad\quad\quad\quad if\quad i, j>0 \quad and\quad xi = yj \\ (3)C[i, j] = Max(C[i, j-1], C[i-1, j]) \quad if\quad i, j>0\quad and\quad xi\ne yj
(1)C[i,j]=0ifi=0或j=0(2)C[i,j]=C[i−1,j−1]+1ifi,j>0andxi=yj(3)C[i,j]=Max(C[i,j−1],C[i−1,j])ifi,j>0andxi=yj
这样我们就能像填表一样完整这个问题了。
我们先初始化第一行和第一列成0,然后对于任意的
C
[
i
,
j
]
C[i, j]
C[i,j],其中
i
,
j
≠
0
i,j\ne 0
i,j=0我们看
C
[
i
−
1
,
j
−
1
]
C[i-1, j-1]
C[i−1,j−1],
C
[
i
−
1
,
j
]
C[i-1, j]
C[i−1,j],
C
[
i
,
j
−
1
]
C[i, j-1]
C[i,j−1],我们按照上面的三个公式计算
C
[
i
,
j
]
C[i, j]
C[i,j]
L C S − l e n g t h ( X , Y ) ( 1 ) m ← l e n g t h ( X ) ; n ← l e n g t h ( Y ) ; ( 2 ) F o r i ← 0 T o m D o C [ i , 0 ] ← 0 ; ( 3 ) F o r j ← 0 T o n D o C [ 0 , j ] ← 0 ; ( 4 ) F o r i ← 1 T o m D o ( 5 ) F o r j ← 1 T o n D o ( 6 ) I f x i = y j ( 7 ) T h e n C [ i , j ] ← C [ i − 1 , j − 1 ] + 1 ; B [ i , j ] ← “ ↖ ” ; ( 8 ) E l s e I f C [ i − 1 , j ] ← C [ i , j − 1 ] T h e n ( 9 ) C [ i , j ] ← C [ i − 1 , j ] ; B [ i , j ] ← “ ↑ ” ; ( 10 ) E l s e C [ i , j ] ← C [ i , j − 1 ] ; B [ i , j ] ← “ ← ” ; ( 11 ) R e t u r n C a n d B LCS-length(X, Y) \\ (1)m\leftarrow length(X);n\leftarrow length(Y);\\ (2)For\quad i\leftarrow 0\quad To\quad m\quad Do\quad C[i,0]\leftarrow 0;\\ (3)For\quad j\leftarrow 0\quad To\quad n\quad Do\quad C[0,j]\leftarrow 0;\\ (4)For\quad i\leftarrow 1\quad To\quad m\quad Do\\ (5)\quad For\quad j\leftarrow 1\quad To\quad n\quad Do\\ (6)\quad\quad If\quad x_i = y_j\\ (7)\quad\quad Then\quad C[i,j]\leftarrow C[i-1,j-1]+1;B[i,j]\leftarrow “↖”;\\ (8)\quad\quad Else\quad If\quad C[i-1,j]\leftarrow C[i,j-1]\quad Then\\ (9)\quad\quad C[i,j]\leftarrow C[i-1,j]; B[i,j]\leftarrow“↑”;\\ (10)\quad\quad Else\quad C[i,j]\leftarrow C[i,j-1]; B[i,j]\leftarrow“←”;\\ (11)Return\quad C\quad and\quad B LCS−length(X,Y)(1)m←length(X);n←length(Y);(2)Fori←0TomDoC[i,0]←0;(3)Forj←0TonDoC[0,j]←0;(4)Fori←1TomDo(5)Forj←1TonDo(6)Ifxi=yj(7)ThenC[i,j]←C[i−1,j−1]+1;B[i,j]←“↖”;(8)ElseIfC[i−1,j]←C[i,j−1]Then(9)C[i,j]←C[i−1,j];B[i,j]←“↑”;(10)ElseC[i,j]←C[i,j−1];B[i,j]←“←”;(11)ReturnCandB
其中的 C C C数组是存储最长公共子序列的, B B B数组是存储最长公共子序列的查找方法的,时间复杂度 T ( n ) = O ( m n ) T(n)=O(mn) T(n)=O(mn)
2.矩阵链乘法
输入:
<
A
1
,
A
2
,
.
.
.
,
A
n
>
,
A
i
<A_1, A_2, ..., A_n>, A_i
<A1,A2,...,An>,Ai是矩阵
输出:计算
A
1
,
A
2
,
.
.
,
A
n
A_1,A_2,..,A_n
A1,A2,..,An的最小代价方法
这个问题主要是我们知道若 A A A是 p × q p\times q p×q矩阵, B B B是 q × r q\times r q×r矩阵,则 A × B A\times B A×B的代价是 O ( p q r ) O(pqr) O(pqr),在做矩阵的乘法的时候,乘法的顺序跟代价密切相关,而且最小的代价可能跟最坏的代价在数量级上差好几个0。
这道题的子问题的重叠性很显然,比如:在计算 A 1 × A 2 × A 3 × A 4 A_1\times A_2\times A_3\times A_4 A1×A2×A3×A4的时候,我们可以有三种加括号的方式 A 1 × ( A 2 × A 3 × A 4 ) A_1\times(A_2\times A_3\times A_4) A1×(A2×A3×A4)或者 ( A 1 × A 2 ) × ( A 3 × A 4 ) (A_1\times A_2)\times (A_3\times A_4) (A1×A2)×(A3×A4)或者 ( A 1 × A 2 × A 3 ) × A 4 (A_1\times A_2\times A_3)\times A_4 (A1×A2×A3)×A4,然后接着往下面计算的时候,我们就会发现重叠的子问题有 A 1 × A 2 A_1\times A_2 A1×A2、 A 2 × A 3 和 A_2\times A_3和 A2×A3和A_3\times A_4$我们很容易的就能找到。
优化子结构:若计算 A 1... n A_{1...n} A1...n的优化顺序在 k k k处断开矩阵链, 即 A 1... n = A 1... k × A k + 1... n A_{1...n}=A_{1...k}\times A_{k+1...n} A1...n=A1...k×Ak+1...n,则在 A 1... n A_{1...n} A1...n的优化顺序中,对应于子问题 A 1... k A_{1...k} A1...k的解必须是 A 1... k A_{1...k} A1...k的优化解,对应于子问题 A k + 1... n A_{k+1...n} Ak+1...n的解必须是 A k + 1... n A_{k+1...n} Ak+1...n的优化解(可以用反证法证明)
所以,我们就能得到求解的方程:
(
1
)
m
[
i
,
j
]
=
0
i
f
i
=
j
(
2
)
m
[
i
,
j
]
=
m
i
n
i
≤
k
<
j
{
m
[
i
,
k
]
+
m
[
k
+
1
,
j
]
+
p
i
−
1
p
k
p
j
}
i
f
i
<
j
(1)m[i, j]=0\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad if\quad i=j\\ (2)m[i, j]= min_{i\le k<j}\{ m[i, k]+m[k+1, j]+p_{i-1}p_kp_j \}\quad if\quad i<j
(1)m[i,j]=0ifi=j(2)m[i,j]=mini≤k<j{m[i,k]+m[k+1,j]+pi−1pkpj}ifi<j
我们只需要先把对角线的数值初始化好了,然后对于每一个
m
[
i
,
j
]
m[i, j]
m[i,j],利用
m
[
i
,
k
]
m[i, k]
m[i,k]和
m
[
k
+
1
,
j
]
m[k+1, j]
m[k+1,j]计算就好了。
M a t r i x C h a i n O r d e r ( p ) ( 1 ) n = l e n g t h ( p ) − 1 ; ( 2 ) F O R i = 1 T O n D O ( 3 ) m [ i , i ] = 0 ; ( 4 ) F O R l = 2 T O n D O ( 5 ) F O R i = 1 T O n − l + 1 D O ( 6 ) j = i + l − 1 ; ( 7 ) m [ i , j ] = ∞ ; ( 8 ) F O R k ← i T o j − 1 D O ( 9 ) q = m [ i , k ] + m [ k + 1 , j ] + p i − 1 p k p j ( 10 ) I F q < m [ i , j ] T H E N m [ i , j ] = q ; ( 11 ) R e t u r n m . MatrixChainOrder(p)\\ (1)n=length(p)-1;\\ (2)FOR\quad i=1\quad TO\quad n\quad DO\\ (3)\quad m[i, i]=0;\\ (4)FOR\quad l=2\quad TO\quad n\quad DO\\ (5)\quad FOR\quad i=1\quad TO\quad n-l+1\quad DO\\ (6)\quad\quad\quad j=i+l-1;\\ (7)\quad\quad\quad m[i, j]=∞;\\ (8)\quad\quad\quad FOR\quad k\leftarrow i\quad To\quad j-1\quad DO\\ (9)\quad\quad\quad\quad\quad q = m[i, k]+m[k+1, j]+p_{i-1}p_kp_j\\ (10)\quad\quad\quad\quad\quad IF\quad q<m[i, j]\quad THEN\quad m[i,j]=q;\\ (11)Return\quad m. MatrixChainOrder(p)(1)n=length(p)−1;(2)FORi=1TOnDO(3)m[i,i]=0;(4)FORl=2TOnDO(5)FORi=1TOn−l+1DO(6)j=i+l−1;(7)m[i,j]=∞;(8)FORk←iToj−1DO(9)q=m[i,k]+m[k+1,j]+pi−1pkpj(10)IFq<m[i,j]THENm[i,j]=q;(11)Returnm.
时间复杂度: T ( n ) = O ( n 3 ) T(n)=O(n^3) T(n)=O(n3)
3.0/1 背包问题
输入:
C
>
0
,
w
i
>
0
,
v
i
>
0
,
1
≤
i
≤
n
C>0, w_i>0, v_i>0, 1\le i\le n
C>0,wi>0,vi>0,1≤i≤n
输出:
(
x
1
,
x
2
,
…
,
x
n
)
,
x
i
∈
{
0
,
1
}
,
满
足
∑
1
≤
i
≤
n
w
i
x
i
≤
C
,
求
m
a
x
∑
1
≤
i
≤
n
v
i
x
i
(x_1, x_2, …, x_n), x_i\in\{0, 1\}, 满足\sum_{1\le i\le n}w_ix_i\le C,求max\sum_{1\le i\le n}v_ix_i
(x1,x2,…,xn),xi∈{0,1},满足∑1≤i≤nwixi≤C,求max∑1≤i≤nvixi
优化子结构:如果 ( y 1 , y 2 , … , y n ) (y_1, y_2, …, y_n) (y1,y2,…,yn)是0-1背包问题的优化解,则 ( y 2 , … , y n ) (y_2, …, y_n) (y2,…,yn)是如下子问题的优化解: m a x ∑ 2 ≤ i ≤ n v i x i ; ∑ 2 ≤ i ≤ n w i x i ≤ C – w 1 y 1 ; x i ∈ { 0 , 1 } , 2 ≤ i ≤ n ; max\sum_{2\le i\le n}v_ix_i;\\ \sum_{2\le i\le n}w_ix_i\le C – w_1y_1;\\ x_i\in \{0, 1\}, 2\le i\le n; max∑2≤i≤nvixi;∑2≤i≤nwixi≤C–w1y1;xi∈{0,1},2≤i≤n;
重叠子问题也是显然的。
m
(
i
,
j
)
m(i, j)
m(i,j):背包容量为
j
j
j, 可选物品为
x
i
,
x
i
+
1
,
…
,
x
n
x_i, x_{i+1}, …, x_n
xi,xi+1,…,xn时,问题的最优解的代价是
m
(
i
,
j
)
m(i, j)
m(i,j).
递归方程:
(
1
)
m
(
i
,
j
)
=
m
(
i
+
1
,
j
)
0
≤
j
<
w
i
(
2
)
m
(
i
,
j
)
=
m
a
x
{
m
(
i
+
1
,
j
)
,
m
(
i
+
1
,
j
−
w
i
)
+
v
i
}
j
≥
w
i
(
3
)
m
(
n
,
j
)
=
0
0
≤
j
<
w
n
(
4
)
m
(
n
,
j
)
=
v
n
j
≥
w
n
(1)m(i, j) = m(i+1, j)\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad 0\le j < w_i\\ (2)m(i, j) = max\{m(i+1, j), m(i+1, j-w_i)+v_i\}\quad j \ge w_i\\ (3)m(n, j) = 0\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad 0 \le j <w_n\\ (4)m(n, j) = v_n\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad j \ge w_n
(1)m(i,j)=m(i+1,j)0≤j<wi(2)m(i,j)=max{m(i+1,j),m(i+1,j−wi)+vi}j≥wi(3)m(n,j)=00≤j<wn(4)m(n,j)=vnj≥wn
我们先初始化 m m m的第 n n n行,前 w n w_n wn个赋值成0,后面的赋值成 v n v_n vn,当 0 ≤ j < w i 0\le j < w_i 0≤j<wi的时候,就说明这一次没有空间装这个 w w w的物品了,就直接 m ( i , j ) = m ( i + 1 , j ) m(i, j) = m(i+1, j) m(i,j)=m(i+1,j),然后当 j ≥ w i j\ge w_i j≥wi时,说明这个里面能装这个物品,所以就是分成装这个物品或者不装两种情况,最后取最大就好了。
( 1 ) F o r j = 0 T o m i n ( w n − 1 , C ) D o m [ n , j ] = 0 ; ( 2 ) F o r j = w n T o C D o m [ n , j ] = v n ; ( 3 ) F o r i = n − 1 T o 2 D o ( 4 ) F o r j = 0 T o m i n ( w i − 1 , C ) D o ( 5 ) m [ i , j ] = m [ i + 1 , j ] ; ( 6 ) F o r j = w i T o C D o ( 7 ) m [ i , j ] = m a x { m [ i + 1 , j ] , m [ i + 1 , j − w i ] + v i } ; ( 8 ) I f C < w 1 T h e n m [ 1 , C ] = m [ 2 , C ] ; ( 9 ) E l s e m [ 1 , C ] = m a x { m [ 2 , C ] , m [ 2 , C − w 1 ] + v 1 } ; (1)For\quad j=0\quad To\quad min(w_n-1, C)\quad Do\quad m[n, j] = 0;\\ (2)For\quad j=w_n\quad To\quad C\quad Do\quad m[n, j] = v_n;\\ (3)For\quad i=n-1\quad To\quad 2\quad Do\\ (4)\quad For\quad j=0\quad To\quad min(w_i-1, C)\quad Do\\ (5)\quad\quad\quad m[i, j] = m[i+1, j];\\ (6)\quad For\quad j=w_i\quad To\quad C\quad Do\\ (7)\quad\quad\quad m[i, j]=max\{m[i+1, j], m[i+1, j-w_i]+v_i\};\\ (8)If\quad C<w_1\quad Then\quad m[1, C]=m[2, C];\\ (9)Else\quad m[1, C]=max\{m[2, C], m[2, C-w_1]+v_1\}; (1)Forj=0Tomin(wn−1,C)Dom[n,j]=0;(2)Forj=wnToCDom[n,j]=vn;(3)Fori=n−1To2Do(4)Forj=0Tomin(wi−1,C)Do(5)m[i,j]=m[i+1,j];(6)Forj=wiToCDo(7)m[i,j]=max{m[i+1,j],m[i+1,j−wi]+vi};(8)IfC<w1Thenm[1,C]=m[2,C];(9)Elsem[1,C]=max{m[2,C],m[2,C−w1]+v1};
时间复杂度: T ( n ) = O ( n ) T(n)=O(n) T(n)=O(n)
4.最优二叉搜索树
二叉搜索树
T
T
T:
结点:
K
=
k
1
,
k
2
,
…
,
k
n
K={k_1, k_2, …, k_n}
K=k1,k2,…,kn;
D
=
d
0
,
d
1
,
…
,
d
n
D={d_0, d_1, …, d_n}
D=d0,d1,…,dn
d
i
d_i
di对应区间
(
k
i
,
k
i
+
1
)
(k_i, k_{i+1})
(ki,ki+1) ;
d
0
d_0
d0对应区间
(
−
∞
,
k
1
)
(-\infin, k_1)
(−∞,k1) ;
d
n
d_n
dn对应区间
(
k
n
,
+
∞
)
(k_n,+\infin)
(kn,+∞)
附加信息:
搜索
k
i
k_i
ki的概率为
p
i
p_i
pi;搜索
d
i
d_i
di的概率为
q
i
q_i
qi;
∑
i
=
1
n
p
i
+
∑
j
=
1
n
q
j
=
1
\sum_{i=1}^{n}p_i+\sum_{j=1}^{n}q_j=1
∑i=1npi+∑j=1nqj=1
搜索树的期望代价
E
(
T
)
=
∑
i
=
1
n
(
D
E
P
T
(
k
i
)
+
1
)
p
i
+
∑
j
=
0
n
(
D
E
P
T
(
d
j
)
+
1
)
q
j
E(T)=\sum_{i=1}^{n}(DEP_T(k_i)+1)p_i+\sum_{j=0}^{n}(DEP_T(d_j)+1)q_j
E(T)=∑i=1n(DEPT(ki)+1)pi+∑j=0n(DEPT(dj)+1)qj
输入:
K
=
{
k
1
,
k
2
,
…
,
k
n
}
,
k
1
<
k
2
<
…
<
k
n
,
P
=
{
p
1
,
p
2
,
…
,
p
n
}
K=\{k_1, k_2, …, k_n\}, k_1< k_2< …< k_n,P=\{p_1, p_2, …, p_n\}
K={k1,k2,…,kn},k1<k2<…<kn,P={p1,p2,…,pn},
p
i
p_i
pi为搜索
k
i
k_i
ki的概率;
Q
=
{
q
0
,
q
1
,
…
,
q
n
}
Q=\{q_0, q_1, …, q_n\}
Q={q0,q1,…,qn},
q
i
q_i
qi为搜索值
d
i
d_i
di的概率。
输出:构造
K
K
K的二叉搜索树
T
T
T, 使得
E
(
T
)
=
∑
i
=
1
n
(
D
E
P
T
(
k
i
)
+
1
)
p
i
+
∑
j
=
0
n
(
D
E
P
T
(
d
j
)
+
1
)
q
j
E(T)=\sum_{i=1}^{n}(DEP_T(k_i)+1)p_i+\sum_{j=0}^{n}(DEP_T(d_j)+1)q_j
E(T)=∑i=1n(DEPT(ki)+1)pi+∑j=0n(DEPT(dj)+1)qj最小。
划分子问题:
K
=
{
k
i
,
k
i
+
1
,
…
,
k
j
}
K=\{k_i, k_{i+1}, …, k_j\}
K={ki,ki+1,…,kj}的优化解的根必为
K
K
K中某个
k
r
k_r
kr如果
r
=
i
r=i
r=i, 左子树
{
k
i
,
…
,
k
i
−
1
}
\{k_i, …, k_{i-1}\}
{ki,…,ki−1}仅包含
d
i
−
1
d_{i-1}
di−1;如果
r
=
j
r=j
r=j, 右子树${k_{r+1}, …, k_j$}仅包含
d
j
d_j
dj
优化子结构:如果优化二叉搜索树
T
T
T具有包含关键字集合
{
k
i
,
k
i
+
1
,
…
,
k
j
}
\{k_i, k_{i+1}, …, k_j\}
{ki,ki+1,…,kj}的子树
T
’
T’
T’, 则
T
’
T’
T’ 是关于关键字集合
{
k
i
,
k
i
+
1
,
…
,
k
j
}
\{k_i, k_{i+1}, …, k_j\}
{ki,ki+1,…,kj}的子问题的优化解.(反证法即可证明)
令
E
(
i
,
j
)
E(i, j)
E(i,j)为
{
k
i
,
…
,
k
j
}
\{k_i, …, k_j\}
{ki,…,kj}的优化解
T
i
j
T_{ij}
Tij的期望搜索代价
当
j
=
i
−
1
j=i-1
j=i−1时,
T
i
j
T_{ij}
Tij中只有叶结点
d
i
−
1
,
E
(
i
,
i
−
1
)
=
q
i
−
1
d_{i-1}, E(i, i-1)=q_{i-1}
di−1,E(i,i−1)=qi−1
当
j
≥
i
j\ge i
j≥i时, 选择一个
k
r
∈
{
k
i
,
…
,
k
j
}
k_r\in\{k_i, …, k_j\}
kr∈{ki,…,kj}
当把左右优化子树放进Tij时, 每个结点的深度增加1
E
(
i
,
j
)
=
P
r
+
E
(
i
,
r
−
1
)
+
W
(
i
,
r
−
1
)
+
E
(
r
+
1
,
j
)
+
W
(
r
+
1
,
j
)
E(i, j)=P_r + E(i, r-1)+ W(i, r-1)+ E(r+1, j)+ W(r+1, j)
E(i,j)=Pr+E(i,r−1)+W(i,r−1)+E(r+1,j)+W(r+1,j)
W
(
i
,
r
−
1
)
=
E
(
L
T
+
1
)
−
E
(
L
T
)
=
∑
l
=
i
r
−
1
p
l
+
∑
l
=
i
−
1
r
−
1
q
l
W(i,r-1)=E(LT+1)-E(LT)=\sum_{l=i}^{r-1}p_l+\sum_{l=i-1}^{r-1}q_l
W(i,r−1)=E(LT+1)−E(LT)=∑l=ir−1pl+∑l=i−1r−1ql
同理
W
(
r
+
1
,
j
)
=
∑
l
=
r
+
1
j
p
l
+
∑
l
=
r
j
q
l
W(r+1,j)=\sum_{l=r+1}^{j}p_l+\sum_{l=r}^{j}q_l
W(r+1,j)=∑l=r+1jpl+∑l=rjql
W
(
i
,
j
)
=
W
(
i
,
r
−
1
)
+
W
(
r
+
1
,
j
)
+
p
r
=
∑
l
=
i
j
p
l
+
∑
l
=
i
−
1
j
q
l
=
W
(
i
,
j
−
1
)
+
p
j
+
q
j
W(i, j)=W(i, r-1) + W(r+1, j) + p_r=\sum_{l=i}^{j}p_l+\sum_{l=i-1}^{j}q_l=W(i,j-1)+p_j+q_j
W(i,j)=W(i,r−1)+W(r+1,j)+pr=∑l=ijpl+∑l=i−1jql=W(i,j−1)+pj+qj
W
(
i
,
i
−
1
)
=
q
i
−
1
W(i, i-1)=q_{i-1}
W(i,i−1)=qi−1
⇒
E
(
i
,
j
)
=
E
(
i
,
r
−
1
)
+
E
(
r
+
1
,
j
)
+
W
(
i
,
j
)
\Rightarrow E(i, j) = E(i, r-1) + E(r+1, j) + W(i, j)
⇒E(i,j)=E(i,r−1)+E(r+1,j)+W(i,j)
(
1
)
E
(
i
,
j
)
=
q
i
−
1
I
f
j
=
i
−
1
(1)E(i, j)=q_{i-1}\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad If\quad j=i-1
(1)E(i,j)=qi−1Ifj=i−1
(
2
)
E
(
i
,
j
)
=
m
i
n
i
≤
r
≤
j
{
E
(
i
,
r
−
1
)
+
E
(
r
+
1
,
j
)
+
W
(
i
,
j
)
}
I
f
j
≥
i
(2)E(i, j)=min_{i\le r\le j} \{E(i,r-1)+E(r+1,j)+W(i, j)\}\quad If\quad j\ge i
(2)E(i,j)=mini≤r≤j{E(i,r−1)+E(r+1,j)+W(i,j)}Ifj≥i
(
3
)
W
(
i
,
i
−
1
)
=
q
i
−
1
(3)W(i, i-1) = q_{i-1}
(3)W(i,i−1)=qi−1
(
4
)
W
(
i
,
j
)
=
W
(
i
,
j
−
1
)
+
p
j
+
q
j
(4)W(i, j) = W(i, j-1) + p_j + q_j
(4)W(i,j)=W(i,j−1)+pj+qj
不知不觉又变成了一个填表题
O p t i m a l − B S T ( p , q , n ) ( 1 ) F o r i = 1 T o n + 1 D o ( 2 ) E ( i , i − 1 ) = q i − 1 ; ( 3 ) W ( i , i − 1 ) = q i − 1 ; ( 4 ) F o r l = 1 T o n D o ( 5 ) F o r i = 1 T o n − l + 1 D o ( 6 ) j = i + l − 1 ; ( 7 ) E ( i , j ) = ∞ ; ( 8 ) W ( i , j ) = W ( i , j − 1 ) + p j + q j ; ( 9 ) F o r r = i T o j D o ( 10 ) t = E ( i , r − 1 ) + E ( r + 1 , j ) + W ( i , j ) ; ( 11 ) I f t < E ( i , j ) ( 12 ) T h e n E ( i , j ) = t ; R o o t ( i , j ) = r ; ( 13 ) R e t u r n E a n d R o o t Optimal-BST(p, q, n)\\ (1)For\quad i=1\quad To\quad n+1\quad Do\\ (2)\quad\quad E(i, i-1) = qi-1;\\ (3)\quad\quad W(i, i-1) = qi-1;\\ (4)For\quad l=1\quad To\quad n\quad Do\\ (5)\quad\quad For\quad i=1\quad To\quad n-l+1\quad Do\\ (6)\quad\quad\quad\quad j=i+l-1;\\ (7)\quad\quad\quad\quad E(i, j)=\infin;\\ (8)\quad\quad\quad\quad W(i, j)=W(i, j-1)+pj+qj;\\ (9)\quad\quad\quad\quad For\quad r=i\quad To\quad j\quad Do\\ (10)\quad\quad\quad\quad\quad\quad t=E(i, r-1)+E(r+1, j)+W(i, j);\\ (11)\quad\quad\quad\quad\quad\quad If\quad t<E(i, j)\\ (12)\quad\quad\quad\quad\quad\quad Then\quad E(i, j)=t; Root(i, j)=r;\\ (13)Return\quad E\quad and\quad Root Optimal−BST(p,q,n)(1)Fori=1Ton+1Do(2)E(i,i−1)=qi−1;(3)W(i,i−1)=qi−1;(4)Forl=1TonDo(5)Fori=1Ton−l+1Do(6)j=i+l−1;(7)E(i,j)=∞;(8)W(i,j)=W(i,j−1)+pj+qj;(9)Forr=iTojDo(10)t=E(i,r−1)+E(r+1,j)+W(i,j);(11)Ift<E(i,j)(12)ThenE(i,j)=t;Root(i,j)=r;(13)ReturnEandRoot
时间复杂度: T ( n ) = O ( n 3 ) T(n)=O(n^3) T(n)=O(n3)
课后习题
1.考虑三个字符串 X , Y , Z X,Y,Z X,Y,Z 的最长公共子序列 L C S ( X , Y , Z ) LCS(X,Y,Z) LCS(X,Y,Z)。设计动态规划算法计算 X , Y , Z X,Y,Z X,Y,Z的最长公共子序列,分析算法的时间复杂度。
解:刚开始看着道题的时候就感觉跟前面的最长公共子序列的求解方法几乎一样,就是把上面的讨论在增多一点,最后得出的一个递推公式是:
c
[
i
,
j
,
k
]
=
{
0
i
f
i
=
0
o
r
j
=
0
o
r
k
=
0
c
[
i
−
1
,
j
−
1
,
k
−
1
]
+
1
i
f
x
i
=
y
j
=
z
k
m
a
x
(
c
[
i
−
1
,
j
,
k
]
,
c
[
i
,
j
−
1
,
k
]
,
c
[
i
,
j
,
k
−
1
]
)
o
t
h
e
r
w
i
s
e
c[i,j,k]=\left\{ \begin{aligned} 0\quad ifi=0orj=0ork=0\\ c[i-1,j-1,k-1]+1\quad ifx_i=y_j=z_k\\ max(c[i-1,j,k],c[i,j-1,k],c[i,j,k-1])\quad otherwise \end{aligned} \right.
c[i,j,k]=⎩⎪⎨⎪⎧0ifi=0orj=0ork=0c[i−1,j−1,k−1]+1ifxi=yj=zkmax(c[i−1,j,k],c[i,j−1,k],c[i,j,k−1])otherwise
初始化的时候,我们将
x
,
y
,
z
x,y,z
x,y,z轴上的点都初始化成0。然后我们用一个三重循环,利用上面的公式填表就好了。最后的时间复杂度是
T
(
n
)
=
O
(
X
l
e
n
g
t
h
Y
l
e
n
g
t
h
Z
l
e
n
g
t
h
)
T(n)=O(X_{length}Y_{length}Z_{length})
T(n)=O(XlengthYlengthZlength)
2.输入数组 A [ 0 : n ] A[0:n] A[0:n] 和正实数 d d d, 试设计一个动态规划算法输出 A [ 0 : n ] A[0:n] A[0:n]的一个最长子序列, 使得子序列中相继元素之差的绝对值不超过 d d d。分析算法的时间复杂度。
解:我们以 A [ i ] A[i] A[i]为最后一个数字的最长子序列的长度是 L [ i ] = m a x ( 1 , m a x 1 ≤ k < i , ∣ A [ k ] − A [ i ] ∣ ≤ d ( L [ k ] + 1 ) ) , 1 ≤ i ≤ n L[i]=max(1,max_{1\le k<i,|A[k]-A[i]|\le d}(L[k]+1)),1\le i\le n L[i]=max(1,max1≤k<i,∣A[k]−A[i]∣≤d(L[k]+1)),1≤i≤n我们先把数组 L L L初始化成1,然后我们对于每一个 A [ i ] A[i] A[i],我们查找前面的每一个满足条件 ∣ A [ k ] − A [ i ] ∣ ≤ d |A[k]-A[i]|\le d ∣A[k]−A[i]∣≤d的 A [ k ] A[k] A[k],然后如果没有的话,我们就直接判断下有一个 A [ i + 1 ] A[i+1] A[i+1],如果有的话,就将 L [ i ] L[i] L[i]改变成最大的 L [ k ] + 1 L[k]+1 L[k]+1。时间复杂度 T ( n ) = O ( n 2 ) T(n)=O(n^2) T(n)=O(n2)
3.给定一个整数序列 a 1 , . . . , a n a_1 ,...,a_n a1,...,an 。相邻两个整数可以合并, 合并两个整数的代价是这两个整数之和。通过不断合并最终可以将整个序列合并成一个整数, 整个过程的总代价是每次合并操作代价之和。试设计一个动态规划算法给出 a 1 , . . . , a n a_1 ,...,a_n a1,...,an 的一个合并方案使得该方案的总代价最大。
解:从 a [ i : j ] a[i:j] a[i:j]这个范围内进行判断的时候,如果 i = j i=j i=j,那么就不需要合并, m [ i : j ] = 0 m[i:j]=0 m[i:j]=0;否则,我们就对每一个分点 k k k,计算最大的 m [ i : k ] + m [ k + 1 : j ] m[i:k]+m[k+1:j] m[i:k]+m[k+1:j],最后的递推公式就是 m [ i : j ] = m a x i ≤ k < j ( m [ i : k ] + m [ k + 1 : j ] ) + ∑ l = i j a [ l ] m[i:j]=max_{i\le k<j}(m[i:k]+m[k+1:j])+\sum_{l=i}^{j}a[l] m[i:j]=maxi≤k<j(m[i:k]+m[k+1:j])+∑l=ija[l]。这个最后就是填一个 n 2 / 2 n^2/2 n2/2的表格,但是每填一个数字可能需要比较 n n n次,时间复杂度是: T ( n ) = O ( n 3 ) T(n)=O(n^3) T(n)=O(n3)
4.输入表达式 a 1 O 1 a 2 O 2 ⋅ ⋅ ⋅ O n − 1 a n a_1O_1 a_2 O_2 ···O_{n−1}a_n a1O1a2O2⋅⋅⋅On−1an, 其中 a i a_i ai 是整数 ( 1 ≤ i ≤ n ) , O j ∈ { + , − , × } ( 1 ≤ i ≤ n − 1 ) (1≤i≤n), O_j∈\{+,−,×\} (1≤i≤ n−1) (1≤i≤n),Oj∈{+,−,×}(1≤i≤n−1)。设计一个动态规划算法, 输出一个带括号的表达式 (不改变操作数和操作符的次序), 使得表达式的值达到最大, 分析算法的时间复杂性。
解:这里面的符号有三种
{
+
,
−
,
×
}
\{+,−,×\}
{+,−,×},我们知道
m
a
x
+
m
a
x
=
m
a
x
max+max=max
max+max=max,但是
m
a
x
−
m
a
x
?
=
m
a
x
max-max?=max
max−max?=max,但是我们知道
m
a
x
−
m
i
n
=
m
a
x
max-min=max
max−min=max,所以这里面我们应该同时计算两个值
m
a
x
max
max和
m
i
n
min
min,
m
a
x
[
i
:
j
]
=
m
a
x
i
≤
k
<
j
{
(
m
a
x
[
i
:
k
]
+
m
a
x
[
k
+
1
:
j
]
)
,
(
m
a
x
[
i
:
k
]
−
m
i
n
[
k
+
1
:
j
]
)
,
m
a
x
(
m
a
x
[
i
:
k
]
×
m
a
x
[
k
+
1
:
j
]
,
m
i
n
[
i
:
k
]
×
m
a
x
[
k
+
1
:
j
]
,
m
a
x
[
i
:
k
]
×
m
i
n
[
k
+
1
:
j
]
,
m
i
n
[
i
:
k
]
×
m
i
n
[
k
+
1
:
j
]
)
}
max[i:j]=max_{i\le k<j}\{(max[i:k]+max[k+1:j]),(max[i:k]-min[k+1:j]),max(max[i:k]\times max[k+1:j],min[i:k]\times max[k+1:j],max[i:k]\times min[k+1:j],min[i:k]\times min[k+1:j])\}
max[i:j]=maxi≤k<j{(max[i:k]+max[k+1:j]),(max[i:k]−min[k+1:j]),max(max[i:k]×max[k+1:j],min[i:k]×max[k+1:j],max[i:k]×min[k+1:j],min[i:k]×min[k+1:j])}
m
i
n
[
i
:
j
]
=
m
i
n
i
≤
k
<
j
{
(
m
i
n
[
i
:
k
]
+
m
i
n
[
k
+
1
:
j
]
)
,
(
m
i
n
[
i
:
k
]
−
m
a
x
[
k
+
1
:
j
]
)
,
m
i
n
(
m
a
x
[
i
:
k
]
×
m
a
x
[
k
+
1
:
j
]
,
m
i
n
[
i
:
k
]
×
m
a
x
[
k
+
1
:
j
]
,
m
a
x
[
i
:
k
]
×
m
i
n
[
k
+
1
:
j
]
,
m
i
n
[
i
:
k
]
×
m
i
n
[
k
+
1
:
j
]
)
}
min[i:j]=min_{i\le k<j}\{(min[i:k]+min[k+1:j]),(min[i:k]-max[k+1:j]),min(max[i:k]\times max[k+1:j],min[i:k]\times max[k+1:j],max[i:k]\times min[k+1:j],min[i:k]\times min[k+1:j])\}
min[i:j]=mini≤k<j{(min[i:k]+min[k+1:j]),(min[i:k]−max[k+1:j]),min(max[i:k]×max[k+1:j],min[i:k]×max[k+1:j],max[i:k]×min[k+1:j],min[i:k]×min[k+1:j])},我们必须要用这两个数组进行计算,初始化的时候,我们将
m
a
x
max
max和
m
i
n
min
min这两个数组的对角线都初始化成
a
[
i
]
a[i]
a[i]就好了,时间复杂度是:
T
(
n
)
=
O
(
n
3
)
T(n)=O(n^3)
T(n)=O(n3)
5.设平面上有一个 m × n m × n m×n 的网格, 将左下角的网格点标记为 ( 0 , 0 ) (0,0) (0,0),而右上角的网格点标记为 ( m , n ) (m,n) (m,n)。某人想从 ( 0 , 0 ) (0,0) (0,0) 出发沿网格线行进到达 ( m , n ) (m,n) (m,n),但是在网格点 ( i , j ) (i,j) (i,j) 处他只能向上行进或者向右行进, 向上行进的代价为 a i j ( a m j = + ∞ ) a_{ij} (a_{mj} = +∞) aij(amj=+∞), 向右行进的代价是 b i j ( b i n = + ∞ ) b_{ij} (b_{in} = +∞) bij(bin=+∞)。试设计一个动态规划算法, 在这个网格中为该旅行者寻找一条代价最小的旅行路线。
解:由于只能从左下角移动到右上角,所以我们就能列出方程 C [ i , j ] = m i n ( C [ i + 1 , j ] + a [ i , j ] , C [ i , j − 1 ] + b [ i , j − 1 ] ) C[i,j]=min(C[i+1,j]+a[i,j],C[i,j-1]+b[i,j-1]) C[i,j]=min(C[i+1,j]+a[i,j],C[i,j−1]+b[i,j−1]) C [ i , j ] C[i,j] C[i,j]为从 [ m , 0 ] [m,0] [m,0]移动到 [ i , j ] [i,j] [i,j]的代价,我们初始化的时候只需要初始化最后一行和第一列,然后用这个式子计算就好了,时间复杂度是: T ( n ) = O ( m n ) T(n)=O(mn) T(n)=O(mn)
6.给定一个 n × n n × n n×n 的矩阵 A A A, 矩阵中的元素只取 0 或者 1。设计一个动态规划算法, 求解得到 A A A 中元素全是 1 的子方阵使其阶数达到最大值。
解:我们先将数组 A A A复制一份成 B B B,最后 B B B输出的就是以 A [ i , j ] A[i,j] A[i,j],为右下角的全1方阵的阶数。当 A [ i , j ] A[i,j] A[i,j]为1的时候,显然 A [ i , j ] A[i,j] A[i,j]不能形成一个全是1的方阵,所以当 A [ i , j ] A[i,j] A[i,j]是1的时候,我们至少能得到一个一阶的全1方阵, B [ i , j ] = m i n ( B [ i − 1 , j ] , B [ i , j − 1 ] , B [ i − 1 , j − 1 ] ) + 1 B[i,j]=min(B[i-1,j],B[i,j-1],B[i-1,j-1])+1 B[i,j]=min(B[i−1,j],B[i,j−1],B[i−1,j−1])+1,最后我们重新扫描一遍数组 B B B然后找到最大的数字就好了。时间复杂度是: T ( n ) = O ( n 2 ) T(n)=O(n^2) T(n)=O(n2)
7.输入平面上 n n n 个点, 点与点之间的距离定义为欧几里得距离。试设计一个动态规划算法输出一条先从左到右再从右到左的一条最短路径, 使得每个输入点恰好被访问一次。
解:我们求从点 i i i到点 j j j的最短路径 ( i < j ) (i<j) (i<j),那么就是先从点 i i i到点 n n n,然后再从点 n n n到点 j j j。当 i + 1 < j i+1<j i+1<j的时候,只能够先从点 i i i到点 i + 1 i+1 i+1,然后再从点 i + 1 i+1 i+1到点 j j j;当 i + 1 = j i+1=j i+1=j的时候,只能够先从点 i i i到任意一个点 k k k,然后再从点 k k k到点 j j j。所以最后的方程就是 c o s t [ i : i + 1 ] = m i n i + 1 < k ≤ n ( c o s t [ i + 1 , k ] + d i s t a n c e ( i , k ) ) cost[i:i+1]=min_{i+1<k\le n}(cost[i+1,k]+distance(i,k)) cost[i:i+1]=mini+1<k≤n(cost[i+1,k]+distance(i,k))我们最后只需要扫描一遍数组找到最小的 c o s t [ 1 : k ] cost[1:k] cost[1:k]就好了。时间复杂度是: T ( n ) = O ( n 3 ) T(n)=O(n^3) T(n)=O(n3)
8.输入一棵加权树 T T T, 其中每条边的权值均是实数, 试设计一个动态规划算法输出权值最大的子树.
解:我们假设 C [ i , j ] C[i,j] C[i,j]是树的第 i i i层第 j j j个节点,我们最下面的一层初始化成0,然后对于每一个节点 C [ i , j ] = ∑ k ∈ T r e e i j ( C [ i + 1 , k ] + B [ i , k ] ) C[i,j]=\sum_{k\in Tree_{ij}}(C[i+1,k]+B[i,k]) C[i,j]=∑k∈Treeij(C[i+1,k]+B[i,k]),这个就是填一个 n n n个节点的的图,最后我们再遍历一遍 C C C这个树,然后找到最大值就好了。时间复杂度是 T ( n ) = O ( n ) T(n)=O(n) T(n)=O(n)
参考:哈工大算法设计与分析PPT