Outline
- 動態規划原理
- 編號動態規划:最大不下降子序列
- 划分動態規划:矩陣鏈乘、凸多邊形三角剖分
- 數軸動態規划:0-1背包
- 前綴動態規划:最長公共子序列
- 樹形動態規划:最優二分搜索樹
Notes
## 動態規划原理
- 基本思想:問題的最優解如果可以由子問題的最優解推導得到,則可以先求解子問題的最優解,在構造原問題的最優解;若子問題有較多的重復出現,則可以自底向上從最終子問題向原問題逐步求解。
- 使用條件:可分為多個相關子問題,子問題的解被重復使用
- Optimal substructure(優化子結構):
- 一個問題的優化解包含了子問題的優化解
- 縮小子問題集合,只需那些優化問題中包含的子問題,降低實現復雜性
- 我們可以自下而上的
- Subteties(重疊子問題):在問題的求解過程中,很多子問題的解將被多次使用。
- Optimal substructure(優化子結構):
- 動態規划算法的設計步驟:
- 分析優化解的結構
- 遞歸地定義最優解的代價
- 自底向上地計算優化解的代價保存之,並獲取構造最優解的信息
- 根據構造最優解的信息構造優化解
- 動態規划特點:
- 把原始問題划分成一系列子問題;
- 求解每個子問題僅一次,並將其結果保存在一個表中,以后用到時直接存取,不重復計算,節省計算時間
- 自底向上地計算。
- 整體問題最優解取決於子問題的最優解(狀態轉移方程)(將子問題稱為狀態,最終狀態的求解歸結為其他狀態的求解)
## 編號動態規划:最大不下降子序列
本類的狀態是基礎的基礎,大部分的動態規划都要用到它,成為一個維。
-
最長不下降子序列定義:從序列中選出若干個數組成一個新的序列,不改變他們的隊伍的順序,要求新的序列里xi≤xi+1≤xi+1.....舉個例子{4,6,5,7,3},最長不下降子序列就是{4,6,7}。
- 子問題的表示:令dp[i]表示以第i個元素結尾的前i個元素構成的最長不下降子序列的長度
- 優化子結構:若最長不下降子序列包括ak,則必有一個解包含a1,a2…ak-1的最長不下降子序列,dp[i]表示為前i個元素的序列的最長不下降子序列
- 方程: dp[i] = max{dp[j] | 0<j<i , aj≥ai} + 1
- 偽代碼:
輸入a[1,...,n] 輸出:最長子序列

時間復雜度:O(n^2)
## 划分動態規划
【矩陣鏈乘】
- 優化子結構:若計算A1~n的優化順序在k處斷開矩陣鏈, 即A1~n=A1~k × Ak+1~n,則在A1~n的優化順序中,對應於子問題A1~k的解必須是A1-k的優化解,對應於子問題Ak+1~n的解必須是Ak+1~n的優化解
- 子問題重疊性:

- 方程:
假設:m[i, j] = 計算Ai~j的最小乘法數; A1 ... AkAk+1 .... An 是優化解(k實際上是不可預知)


- 偽代碼:
輸入:<A1, A2, ..., An>, Ai是矩陣 輸出:計算A1 x A2 x ... x An的最小代價方法 Matrix-Chain-Order(p) n=length(p)-1; FOR i=1 TO n DO m[i, i]=0; FOR l=2 TO n DO /* 計算地l對角線*/ FOR i=1 TO n-l+1 DO j=i+l-1; m[i, j]= ∞; FOR k←i To j←1 DO /* 計算m[i,j] */ q=m[i, k]+m[k+1, j]+ pi-1pkpj IF q<m[i, j] THEN
m[i,j]=q; s[i,j]=k; Return m and s.
Print-Optimal-Parens(s, i, j) //構建最優解,輸出A1-n的優化計算順序 IF j=i THEN Print “A”i; ELSE Print “(” Print-Optimal-Parens(s, i, s[i, j]) Print-Optimal-Parens(s, s[i, j]+1, j) Print “)”
- 算法復雜度
- 計算代價的時間:三層循環 O(n3)
- 構建最優解的時間: O(n)
- 總時間復雜度:O(n3)
- 空間復雜度
- 使用數組m和s
- 需要空間O(n3)
【三角剖分】
- 優化子結構:將多邊形P划分為不相交三角形的弦的集合
- 優化問題:

- 方程:設t[i,j] = <vi-1,vi,.....,vj>的優化三角剖分代價

## 數軸動態規划:0-1背包
- 問題描述:給定n種物品和一個背包,物品i的重量是wi,價值vi,背包容量為C,問如何選擇裝入背包的物品,使裝入背包中的物品的總價值最大?對於每種物品總能選擇完全裝入或不裝入,一個物品最多裝入一次。
- 等價整數規划問題:

- Naive的方法:每個物品只有兩種選擇:不裝或裝,n個物品共2n個裝取方案,每個裝取方案的計算代價為n,總計算代價為O(n2n)
- 問題的划分:

- 定義代價矩陣m與方程:
- 定義m(i, j) :背包容量為j,可選物品為xi,xi+1…xn時,問題的最優解代價時m[i,j]
- m(n, j) = 0, 0 ≤ j <wn
- m(n, j) = vn, j ≥wn
- m(i, j) = m(i+1, j), 0≤ j< wi
- m(i, j) = max{m(i+1, j), m(i+1, j-wi)+vi}, j ≥ wi
- 優化子結構和自底向上的代價


- 偽代碼
輸入:C>0, wi>0, vi>0, 1≤ i≤n
輸出:(x1, x2, …, xn), xi∈{0, 1}, 滿足 ∑1≤i≤nwi xi ≤C, ∑1≤i≤nvi xi 最大
For j=0 To min(wn-1, C) Do m[n, j] = 0; For j=wn To C Do m[n, j] = vn; For i=n-1 To 2 Do For j=0 To min(wi -1, C) Do m[i, j] = m[i+1, j]; For j=wi To C Do m[i, j]=max{m[i+1, j], m[i+1, j-wi]+vi}; If C<w1 Then m[1, C]=m[2, C]; Else m[1, C]=max{m[2, C], m[2, C-w1]+v1};
m(1, C)是最優解代價值,相應解計算如下: //構造優化解 If m(1, C) = m(2, C) Then x1 = 0; Else x1 = 1; 如果x1=0, 由m(2, C)繼續構造最優解; 如果x1=1, 由m(2, C-w1)繼續構造最優解.
- 時間復雜度:
- 計算代價的時間為O(Cn)
- 構造最優解的時間:O(Cn)
- 總時間復雜度為:O(Cn)
- 空間復雜度:
- 使用數組m,需要空間O(Cn)
## 前綴動態規划:最長公共子序列(LCS)
- 問題描述:Z是序列X與Y的公共子序列如果Z是X的子序列也是Y的子序列。
- Naive方法:
- 枚舉X的每個子序列Z
- 檢查Z是否為Y的子序列
- T(n)=O(n2m)
- 優化子結構:
- 設X=(x1, ..., xm)、Y=(y1, ..., yn)是兩個序列, LCSXY=(z1, ..., zk)是X與Y的LCS,我們有:
- 如果xm=yn, 則zk=xm=yn, LCSXY = LCSXm-1Yn-1 + <xm=yn>, LCSXm-1Yn-1是Xm-1和Yn-1的LCS.
- 如果xm≠yn,且zk≠xm,則LCSXY是Xm-1和Y的LCS,即 LCSXY = LCSXm-1Y
- 如果xm≠yn,且zk≠yn,則LCSXY是X與Yn-1的LCS,即 LCSXY = LCSXYn-1

- 子問題重疊性

- 方程:

- 自底向上計算:

- 偽代碼

輸入:X = (x1,x2,...,xm),Y = (y1,y2,...yn) 輸出:Z = X與Y的最長公共子序列 C[0:m,0:n]: C[i,j]是Xi與Yj的LCS的長度 B[1:m,1:n]: B[i,j]是指針,指向計算C[i,j]時所選擇的子問題的優化解所對應的C表的表項 LCS-length(X, Y) m←length(X);n←length(Y); For i←0 To m Do C[i,0]←0; For j←0 To n Do C[0,j]←0; For i←1 To m Do For j←1 To n Do If xi = yj Then C[i,j]←C[i-1,j-1]+1;B[i,j]←“↖”; Else If C[i-1,j]≥C[i,j-1] Then C[i,j]←C[i-1,j]; B[i,j]←“↑”; Else C[i,j]←C[i,j-1]; B[i,j]←“←”; Return C and B. Print-LCS(B, X, i, j) IF i=0 or j=0 THEN Return; IF B[i, j]=“↖” THEN Print-LCS(B, X, i-1, j-1); Print xi; ELSE If B[i, j]=“↑” THEN Print-LCS(B, X, i-1, j); ELSE Print-LCS(B, X, i, j-1). Print-LCS(B, X, length(X), length(Y)) 可打印出X與Y的LCS。
- 時間復雜度:
- 計算代價的時間:O(mn)
- 構造最優解的時間:O(m+n)
- 總時間復雜度為: O(mn)
- 空間復雜度:
- 使用數組C和B,需要空間O(mn)
## 樹形動態規划
