Knapsack Problem
- 问题定义
- 输入: n n n件物品,每个物品包含两个数据——价值 v i v_i vi,和大小 w i w_i wi,以及目标背包的大小 W W W
- 输出:一个自己 S ⊆ { 1 , 2 , … , n } S \sube \{1, 2, \dots, n\} S⊆{1,2,…,n},最大化 ∑ i ∈ S v i \sum_{i \in S} v_i ∑i∈Svi,要求 ∑ i ∈ S w i ≤ W \sum_{i \in S} w_i \le W ∑i∈Swi≤W
- 最优子结构
- 如果 n ∉ S n \notin S n∈/S, S S S必然为前 n − 1 n - 1 n−1个物品的最优解
- 如果 n ∈ S n \in S n∈S,那么 S − n S - n S−n为 W − w n W - w_n W−wn的背包的最优解
- v i , x = max { v i − 1 , x , v i − 1 , x − w i + v i } v_{i, x} = \max \{v_{i-1, x}, v_{i-1, x- w_i} + v_i\} vi,x=max{vi−1,x,vi−1,x−wi+vi}
- 实现
- 二维数组,从左到右不断更新(两重循环): A [ i , x ] = max { A [ i − 1 , x ] , A [ i − 1 , x − w i ] + v i } A[i, x] = \max \{A[i - 1, x], A[i - 1, x - w_i] + v_i\} A[i,x]=max{A[i−1,x],A[i−1,x−wi]+vi}
- 时间复杂度 θ ( n W ) \theta(nW) θ(nW)
- 结果重建
- 最佳价值: A [ n , W ] A[n, W] A[n,W]
- 结果重建
- 如果 A [ i , x ] = A [ i − 1 , x ] A[i, x] = A[i - 1, x] A[i,x]=A[i−1,x],那么此时不包含第 i i i个物体,从 A [ i − 1 , x ] A[i - 1, x] A[i−1,x]继续考察
- 如果 A [ i , x ] = A [ i − 1 , x − w i ] + v i A[i, x] = A[i - 1, x - w_i] + v_i A[i,x]=A[i−1,x−wi]+vi,那么此时包含第 i i i个物体,从 A [ i − 1 , x − w i ] A[i - 1, x -w_i] A[i−1,x−wi]继续考察
Sequence Alignment
- 问题定义
- 输入: X = x 1 , … , x m X = x_1, \dots, x_m X=x1,…,xm, Y = y 1 , … , y n Y = y_1, \dots, y_n Y=y1,…,yn,插入空行罚分 α g a p \alpha_{gap} αgap,匹配罚分 α a b \alpha_{ab} αab
- 输出:对齐结果,最少罚分
- 最优子结构
- 考虑当前对齐情形的两个序列的最后一个位置
- x m , y n x_m, y_n xm,yn匹配
- x m , _ x_m, \_ xm,_匹配
- _ , y n \_, y_n _,yn匹配
- 令
X
′
=
X
−
x
m
X^\prime = X - x_m
X′=X−xm,
Y
′
=
Y
−
y
n
Y^\prime = Y - y_n
Y′=Y−yn
- 第一种情形, X ′ X^\prime X′和 Y ′ Y^\prime Y′的对齐结果将是最优的
- 第二种情形, X ′ X^\prime X′和 Y Y Y的对齐结果将是最优的
- 第三种情形, X X X和 Y ′ Y^\prime Y′的对齐结果将是最优的
- 考虑当前对齐情形的两个序列的最后一个位置
- 算法实现
- 记 X i X_i Xi表示 X X X的前 i i i个字符, Y j Y_j Yj同理
- 记 P i j P_{ij} Pij为和 X i X_i Xi与 Y j Y_j Yj的对齐罚分综合, P i j = { α x i y j + P i − 1 , j − i α g a p + P i − 1 , j α g a p + P i , j − 1 P_{ij} = \begin{cases}\alpha_{x_i y_j} &+& P_{i - 1, j - i} \\ \alpha_{gap} &+& P_{i - 1, j} \\ \alpha_{gap} &+& P_{i, j - 1} \end{cases} Pij=⎩⎪⎨⎪⎧αxiyjαgapαgap+++Pi−1,j−iPi−1,jPi,j−1
- 初始化: A [ i , 0 ] = A [ 0 , i ] = i ⋅ α g a p A[i, 0] = A[0, i] = i \cdot \alpha_{gap} A[i,0]=A[0,i]=i⋅αgap
- 两重循环,考察两个序列长度
- A [ i , j ] = min { A [ i − 1 , j − 1 ] + α x i , y j , A [ i − 1 , j ] + α g a p , A [ i , j − 1 ] + α g a p } A[i, j] = \min \{A[i - 1, j - 1] + \alpha_{x_i, y_j}, A[i - 1, j] + \alpha_{gap}, A[i, j - 1] + \alpha_{gap}\} A[i,j]=min{A[i−1,j−1]+αxi,yj,A[i−1,j]+αgap,A[i,j−1]+αgap}
- 解重建
- 向前回溯
- 类似地,比较三种情形的罚分情形,取最佳,并继续考察对应位置
- 任意一个序列到头,则直接加入对应数量的gap
- 时间复杂度 O ( m + n ) O(m + n) O(m+n)
Optimal Binary Search Tree
- 问题定义
- 最佳的二叉搜索树:对于任意一个给定键值分布,搜索时间复杂度保持在最优
- 在查询概率已知的情形下,平衡二叉树不一定最优,可以比 O ( log n ) O(\log n) O(logn)更好
- 给定对应键值的被查询概率分别为 p i p_i pi,键值按编号排大小
- 最小查询时间 C ( T ) = ∑ i p i ⋅ d e p t h i C(T) = \sum_i p_i \cdot depth_i C(T)=∑ipi⋅depthi
- 最优子结构
- 给定根节点 r r r,左子树要保证对 { 1 , … , r − 1 } \{1, \dots, r - 1\} {1,…,r−1}最优,右子树保证对 { r + 1 , … , n } \{r + 1, \dots, n\} {r+1,…,n}最优
- 子结构性质: C ( T ) = ∑ k p k + C ( T 1 ) + C ( T 2 ) C(T) = \sum_k p_k + C(T_1) + C(T_2) C(T)=∑kpk+C(T1)+C(T2)
- 计算项目:最优连续区间 { i , i + 1 , … , j − 1 , j } \{i, i + 1, \dots, j - 1, j\} {i,i+1,…,j−1,j},类似于上述问题丢掉后缀,这里要分别丢掉前缀和后缀
- 算法
- 记 C i j C_{ij} Cij为区间 { i , i + 1 , … , j − 1 , j } \{i, i + 1, \dots, j - 1, j\} {i,i+1,…,j−1,j}的加权代价
- C i j = min r { ∑ k p k + C i , r − 1 + C r + 1 , j } C_{ij} = \min_r \{\sum_k p_k + C_{i, r -1} + C_{r + 1, j}\} Cij=minr{∑kpk+Ci,r−1+Cr+1,j}
- 边界约定: C x y = 0 C_{xy} = 0 Cxy=0,如果 x > y x \gt y x>y
- 两重循环(
s
和i
): A [ i , i + s ] = min r { ∑ k p k + A [ i , r − 1 ] + A [ r + 1. i + s ] } A[i, i + s] = \min_r \{\sum_k p_k + A[i, r - 1] + A[r + 1. i + s]\} A[i,i+s]=minr{∑kpk+A[i,r−1]+A[r+1.i+s]} - 最优代价: A [ 1 , n ] A[1, n] A[1,n]
- 实现过程中实际只计算了一个上三角矩阵
- 运行时间复杂度: O ( n 3 ) O(n^3) O(n3)( n 2 n^2 n2个子问题,每个问题的计算是线性时间)