###### tags: `演算法`
# 演算法 期末整理 (done)
## 1. (DP)多階圖最小成本路徑 ==似乎不考(?)==
![](https://i.imgur.com/P33raTt.png)
`Input:` $具n個頂點的k階多階圖G(V,E), 其中V=⋃_{i=1..k} P_i, P_i \cap P_j = \varnothing for \ i \neq j, \\
P_1 = \{s\}, P_k = \{t\}, <x,y> \in E \rightarrow (x \in P_i \wedge y \in P_{i+1}), <x,y>的權重為w[x,y]$
`Output:`$path[1..k], d[s], 其中path[1..k]紀錄第1階(節點1)到第k階(節點n)的最小成本路徑, \\ d[s]紀錄最小成本路徑總成本$
```cpp=
d[t] = 0; d[x] = INF, for x != t; // 陣列d[x]儲存節點x到節點t的最小距離
for i = k-1 to 1 do // 由第k-1階到第1階
for every node x in P_i do
for every edge (x, y) in E do
if (d[x] > w[x,y] + d[y]) do
d[x] = w[x,y] + d[y]
next[x] = y // 代表在最短路徑中節點x的下節點為y
path[1] = s; path[k] = t; // path[j]表示路徑中第j階的節點
for j = 2 to k-1 do
path[j] = next[path[j-1]];
return path[s], d[s]
```
## 2. (DP)矩陣鏈乘積
`Input:` $陣列 p[1...n], 代表矩陣 A[i] 的維度為 p[i-1] * p[i]$
`Output:`$最小乘法次數m[1,n], 矩陣乘法分割位置s$
```cpp=
MATRIX_CHAIN_ORDER(p)
n = length[p]–1 // 矩陣數
for i = 1 to n
m[i,i] = 0
for l = 2 to n // level 2 to n
for i = 1 to n–l+1
j = i+l–1
m[i,j] = INF
for k = i to j–1
q = m[i,k] + m[k+1,j] + p[i-1]*p[k]*p[j]
if q < m[i,j]
m[i,j] = q
s[i,j] = k
return m[1,n], s
```
Example:
`p = [30, 35, 15, 5, 10, 20, 25]`
![`矩陣圖`](https://i.imgur.com/pc9vuMU.png)
```cpp=
PRINT_OPTIMAL_PARENS(s, i, j)
if i == j
print "A_i"
else
print "("
PRINT_OPTIMAL_PARENS(s, i, s[i,j])
PRINT_OPTIMAL_PARENS(s, s[i,j]+1, j)
print ")"
```
## 3. (DP)最小編輯成本 ==似乎不考(?)==
* 定義:給定字串A,B,利用以下操作將A改成B的最低成本
* 操作1(Cost1): 在字串A刪除一個字元
* 操作2(Cost2): 在字串A插入一個字元
* 操作3(Cost3): 在字串A將一個字元換成另一個字元
* c[i, j] 中儲存將 A 的子字串 $a_1a_2...a_m$ 轉為 B 的子字串 $b_1b_2...b_n$ 的最小成本,
其中 $0 \leq i \leq m$ 且 $0 \leq j \leq n$
```cpp=
// 邊界條件
for i = 0 to m
c[i,0] = i * Cost1
for j = 0 to n
c[0,j] = j * Cost2
// 遞迴關係
if a_i == b_j
c[i,j] = c[i-1,j-1]
else
c[i,j] = min(c[i-1,j] + Cost1,
c[i,j-1] + Cost2,
c[i-1,j-1]+ Cost3 )
// 答案
return c[m,n]
```
## 4. (DP)最佳二元搜尋樹 (類似2.)
`Input:` $陣列 p[i = 1...n], 代表已排序序列K=<k_1, ..., k_n>被搜尋機率, \\
陣列 q[i = 0...n], 代表搜尋目標 d_i 不在K中的機率, 其中 k_i < d_i < k_{i+1}$
`Output:`$最佳二元搜尋樹的期望搜索成本e[1,n], 各子樹樹根root$
```cpp=
OPTIMAL_BST(p,q,n)
for i = 1 to n+1
e[i,i–1] = q[i-1]
w[i,i–1] = q[i-1]
for l = 1 to n // level 1 to n
for i = 1 to n–l+1
j = i+l–1
e[i, j] = INF
w[i, j] = w[i, j–1] + p[j] + q[j] // 第i~j項機率總和
for r = i to j
t = e[i,r–1] + e[r+1,j] + w[i,j]
if t < e[i,j]
e[i,j] = t
root[i,j] = r
return e[1,n], root
```
Example:
`p = [0.15, 0.10, 0.05, 0.10, 0.20]`
`q = [0.05, 0.10, 0.05, 0.05, 0.05, 0.10]`
![`矩陣圖`](https://i.imgur.com/2iE2Sdz.png)
## 5. (DP)子集合加總 (類似0/1背包)
![子集合加總問題](https://i.imgur.com/b5DipRa.png)
![子集合加總演算法](https://i.imgur.com/TueF5bk.png)
## 6. Tree Searching
### 6-1. 廣度優先搜尋(BFS)
```=
設定起始節點p為解答空間樹的root,並建立一個只包含p的queue。
檢查queue的第一個元素是否為目標節點。若是,則輸出目標節點所對應的解答並停止。
移除queue的第一個節點,將其未被拜訪過的子節點一一加入queue。
若queue為空,則回報無解答並停止。否則,跳至步驟2。
```
[`BFS舉例`](https://i.imgur.com/NTgGQ90.png)
### 6-2. 深度優先搜尋(DFS)
```=
設定起始節點p為解答空間樹的root,並建立一個只包含p的stack。
檢查stack的頂端元素是否為目標節點。若是,則輸出目標節點所對應的解答並停止。
移除stack的頂端節點。將其未被拜訪過的子節點一一加入stack。
若stack為空,則回報無解答並停止。否則,跳至步驟2。
```
[`DFS舉例1`](https://i.imgur.com/EXRyZsU.png)
[`DFS舉例2`](https://i.imgur.com/uPgfzS4.png)
### 6-3. 登山搜尋(Hill-climbing Search) = DFS + 評估函數
```=
設定起始節點p為解答空間樹的root,並建立一個只包含p的stack。
檢查stack的頂端元素是否為目標節點。若是,則輸出目標節點所對應的解答並停止。
移除stack的頂端節點。將其未被拜訪過的子節點[由評估函數所算的優先度低的先加入]stack。
若stack為空,則回報無解答並停止。否則,跳至步驟2。
```
[`HCS舉例`](https://i.imgur.com/rYlEJXj.png)
### 6-4. 最佳優先搜尋(Best-Frist Search) = BFS + DFS + 評估函數
```=
設定起始節點p為解答空間樹的root,並建立一個只包含p的heap。
檢查heap中優先度最高的元素是否為目標節點。若是,則輸出目標節點所對應的解答並停止。
移除heap優先度最高的節點。將其未被拜訪過的子節點[由評估函數所算的優先度]加入heap中。
若heap為空,則回報無解答並停止。否則,跳至步驟2。
```
[`Best-Frist Search舉例`](https://i.imgur.com/SajCveP.png)
### 6-5. 實例
* 漢米爾頓迴路 **[NPC問題]**
![](https://i.imgur.com/m6t7WJn.png)
* [解答空間樹_完整](https://i.imgur.com/O0rBzD2.png)
* [解答空間樹_廣度優先](https://i.imgur.com/E9AxpcT.png)
* [解答空間樹_深度優先](https://i.imgur.com/B7lJLT1.png)
* 旅行銷售員問題:求出「邊加權總合最小或長度最短」的漢米爾頓迴路 [**NP-hard問題**]
* 尤拉迴路問題
* 尤拉路徑 or 尤拉鏈:無向圖上經過每個邊恰好一次的路徑。 **[P問題]**
* 尤拉迴路:無向圖上經過每個邊恰好一次的迴路。 **[P問題]**
## 7. 回溯演算法 #我覺得根本就是DFS = =
* 八皇后問題
* 皇后棋子:可以從八個方向攻擊對方的棋子。
* 如何在一個8×8的西洋棋棋盤上放置8個不會互相攻擊的皇后棋子。
```cpp=
PutQueen(i,n) // 以放有i個皇后,總共要放n個皇后
j = 0 // j代表行(column)編號, j = 0 to n-1
while j < n
if i列j行可放置皇后棋子
q[i] = j // 編號為i的皇后放在第i列第j行上
if i == n-1 // n個皇后棋子均已放置妥當
return q
else
PutQueen(i+1,n)
j++
return Failure
```
[`圖解,讓你看看什麼是回朔ㄏㄏ,根本就只是stack退回去啊= =`](https://i.imgur.com/q1ezr34.png)
## 8. 分支定界演算法
* 重點:
* 找出好的機制在搜尋解答的過程中,擴展解答空間樹產生分支。
* 找出好的機制設定**上界**與**下界**,使許多分支**終止搜尋**。
* [旅行推銷員問題之分支(重點)](https://i.imgur.com/Hv9gHLd.png),其他請看PPT。
## 9. A*演算法 = 最佳優先搜尋 + 「特殊」評估函式
* 可被視為一個分支定界演算法的特例,當第一個可行解被走訪時,所有在堆積(優先佇列)中的節點都被終止。
* 評估函式:$f(n) = g(n) + h(n)$
* $g(n):$ 從root至節點n的==當前成本==。
* $h(n):$ 從節點n到目標節點的==評估的未來成本==。
* $h^*(n):$ 從節點n到目標節點的==**實際**的未來成本==。
* $f^*(n):$ 從root至節點n再到目標節點的==**實際**成本==。
* $h(n) \leq h^*(n)$
* 證明:
1. $f(n) = g(n)+h(n) \leq g(n)+h^*(n) = f^*(n)$
2. 令 $x$ 為目標節點。可得
$f^*(x) = f(x)+h(x) = f(x)+0 = f(x)$
3. 假設 $x$ 不是最佳節點,則必存在一個已存在heap中的,但是未被選擇的節點 $y$,使得
$f^*(y) < f^*(x)$
4. 因我們使用最佳優先搜尋法,故可得 $f(x) \leq f(y)$
5. 由不等式(1)(2)(4)可得
$f^*(x) = f(x) \leq f(y) \leq f^*(y)$
$\rightarrow f^*(x) \leq f^*(y)$,與(3)矛盾,故得證 $x$ 為最佳節點。
* [解多階圖最短路徑問題](https://imgur.com/a/f74Jc)(from PPT)
## 10. 最大流演算法
```c=
FORD_FULKERSON(G,s,t)
for each edge (u,v) in E of G
f[u,v] = 0
f[v,u] = 0
while there exists a path p from s to t in the residual network G_f
c_f(p) = min(c_f[u,v]: (u,v) in p) // 流量最小邊的值
for each edge (u,v) in p
f[u,v] = f[u,v] + c_f(p)
f[v,u] = f[v,u] - c_f(p)
c[u,v] = c[u,v] - c_f(p)
c[v,u] = c[v,u] + c_f(p)
```
[`Ford-Fulkerson演算法 圖解`](https://i.imgur.com/Og6yRzO.png)
[`Edmons-Karp演算法`](https://i.imgur.com/QKWGUig.png)` = Ford-Fulkerson演算法 + BFS`
[`多起點終點 => 單起點&終點`](https://i.imgur.com/KzaOkHS.png)
## 11. 最小成本最大流演算法
* 最小成本最大流問題為
* 先找出由源點到匯點的最大流
* 再找出達成此最大流的最小成本
* [\[複習\] Bellman-Ford最短路徑演算法](https://i.imgur.com/k6TCfdY.png)
```cpp=
Min_Cost_Max_Flow(G)
先不看成本參數找出最大流f
加入成本參數計算f的成本c
while true
計算G_f
用Bellman-Ford在G_f上找負成本循環
if 無負成本循環
return f, c
else
在循環中環繞以減低總成本直到循環中的一個邊的容量用盡為止
```
## 12. [匈牙利演算法](https://zh.wikipedia.org/wiki/%E5%8C%88%E7%89%99%E5%88%A9%E7%AE%97%E6%B3%95)
解 ==**最大權重完美二分匹配**== $\leftrightarrow$ 解 ==**最大化問題**==
[二分匹配(Bipartite Matching)](http://www.csie.ntnu.edu.tw/~u91029/Matching.html#2)
用匈牙利演算法解決最大化問題,先將各個元素換成以最大元素減去其本身。
接著套用標準的匈牙利演算法:
```cpp=
藉由列的減法變轉陣列:每一列皆減去該列中的最小元素。
計算覆蓋元素0的最少直線數。如果直線數與陣列的大小相同,跳到步驟6。
藉由行的減法變轉陣列:每一行皆減去該行中的最小元素。
計算覆蓋元素0的最少直線數。如果直線數與陣列的大小相同,跳到步驟6。
找出未被覆蓋的元素中最小者,將所有未覆蓋元素中減去此元素值,並增加這個元素值到被兩條直線覆蓋的元素中,回到步驟4。
只從元素0去找出極大二分配對,並套用此配對到原始陣列中以找出最小成本。
```
## 13. 問題變轉(Problem Reduction)
* $A \propto B:$ 問題 $A$ 變轉為(reduces to)問題 $B$。
* 若且唯若$A$可藉由任何能解決$B$的演算法而獲得解決。
* ![](https://i.imgur.com/Mg6v9Ug.png)
* $LowerBound(B) = T(ALG_B)$
* $LowerBound(A) \leq T(ALG_A) \leq T(tr_1) + T(ALG_B) + T(tr_2)$
* if $T(tr_1) + T(tr_2) \leq L(A) \\
\rightarrow L(A) \leq T(ALG_A) \leq T(ALG_B) = L(B) \\
\rightarrow 假設A的下界是已知的,若A \propto B,且變轉中的轉形時間複雜度比A的問題下界還低,\\
則A的下界亦是B的下界。$
* [凸包問題(The convex hull problem)](https://i.imgur.com/RAVq46g.png)
* [凸包(convex hull)問題的下界](https://i.imgur.com/IvL9lCu.png)
* $\because「排序問題」\propto「凸包問題」,且「排序問題」的下界為 \Omega(nlogn)$
$\therefore「凸包問題」的下界亦為 \Omega(nlogn)$
## 14. NP-Completeness理論 #幹拎德這是三小鬼東西= =
* ![](https://i.imgur.com/N5daReA.png)
* P:此類問題,可用 ==**確定性**多項式(時間)== 演算法解決。
* NP:此類==決策問題==,可用 ==**非確定性**多項式(時間)== 演算法解決。
* NP-hard:每個NP問題皆可 ==多項式時間**變轉**== 為此類問題。
* NP-complete (NPC):此類問題 ==屬於**NP-hard**也屬於**NP**==。
* ==確定性==演算法:需滿足以下條件
* 0或多個輸入
* 1個以上的輸出
* 有限性(finiteness):步驟數有限而且步驟執行會終止
* 明確性(definiteness):每個步驟必須明確且不含糊(unambiguous)
* 有效性(effectiveness):每個步驟必須是有效可行的(feasible)
* ==非確定性==演算法
* 包含兩個階段:
1. 猜測(guess) / 選擇(choice)
2. 檢查(check):檢查後使演算法「==成功/不成功==」結束
* 假設:NP演算法永遠會在第一階段非確定性地(事先無法確定地知道結果)做出對的猜測或選擇使演算法成功結束。
* 實務面:能執行NP演算法的機器目前==並不存在==
* 理論面:若問題$A$能以NP演算法解決,則$A \in$ **NP**
* 三個特別敘述:
1. `Success`:演算法成功結束
2. `Failure`:演算法不成功結束
3. `Choice(S)`:從集合S裡任意地選出一個元素,$O(1)$
* 假設: `Choice(S)`是一個非確定性敘述,但它一定會挑中一個可以讓演算法成功結束的元素(除非這種元素不存在)。
* 實例:[非確定漢米爾頓迴路演算法](https://i.imgur.com/KRN8Qpw.png) $\rightarrow 「漢米爾頓迴路問題」\in$ **NP**
* 問題的 ==決策版本== vs ==原始版本==
* 例如:旅行銷售員(TSP)問題
* 原始版本$O$:給定圖找最短的旅途(tour)或巡環(cycle)
* 決策版本$D$:給定圖是否具有一個旅途的總長度$\leq$常數c
* 答案只會是:是 or 否
* $D \propto O$
* ==**NPC證明**== 方法
* 欲證明 $Q \in$ **NPC**:
1. 證明 $Q \in$ **NP**:寫一個可解 $Q$ 的NP演算法
2. 證明 $\exists \ Q' \in$ **NPC** s.t. $Q' \propto Q$
* 存在一個函數$f(x)$為polynomial-time computable
* $f(x)$使得對所有 $x$ 而言,$x\in L_1$ iff $f(x) \in L_2$。
($L_1$ 為 $Q’$ 問題的解集合;$L_2$ 為 $Q$ 問題的解集合)
* 已知的**NPC**問題:**SAT**問題(滿足問題,**Sat**isfiability Problem):
* 給一個布林函數$E(x),x=\{x_1,...,x_n\}$,若存在一組輸入當作 $x$ s.t. $E(x)=True$,則回傳`Success`;否則回傳`Failure`。
* $E(x)$ 為[CNF](https://zh.wikipedia.org/wiki/%E5%90%88%E5%8F%96%E8%8C%83%E5%BC%8F),即數位電路中的product of sum,例:$(x_1 \lor x_2) \land (x_4 \lor \lnot x_5)$
* Cook’s Theorem:證明**SAT**問題 $\in$ **NPC**,[詳細](https://en.wikipedia.org/wiki/Cook%E2%80%93Levin_theorem#Proof)就別看了,很難也不重要Orz
1. 所有**NP**問題 $\propto$ **SAT**問題
2. **SAT** $\in$ **P** $\leftrightarrow$ **NP** = **P**,但目前無法證明,也似乎不成立
* [舉例:證明3SAT問題為NPC](https://imgur.com/a/cn0oX)
* 已知的**NPC**問題:
1. SAT問題
2. 3SAT問題:如 $E(x) = (x_1 \lor x_2 \lor \lnot x_4) \land (\lnot x_3 \lor x_4 \lor \lnot x_5)$,變數三個一組
3. [集合覆蓋問題](https://zh.wikipedia.org/wiki/%E9%9B%86%E5%90%88%E8%A6%86%E7%9B%96%E9%97%AE%E9%A2%98)
4. 0/1背包問題
5. 旅行推銷員問題
6. [分割問題](https://i.imgur.com/0d4fBV7.png)
7. [著色問題](https://zh.wikipedia.org/wiki/%E5%9B%BE%E7%9D%80%E8%89%B2%E9%97%AE%E9%A2%98)
8. 完全圖或[分團問題](https://zh.wikipedia.org/wiki/%E5%88%86%E5%9C%98%E5%95%8F%E9%A1%8C)
9. [點覆蓋問題](https://zh.wikipedia.org/wiki/%E8%A6%86%E7%9B%96_(%E5%9B%BE%E8%AE%BA))
10. [支配集合問題](https://i.imgur.com/WxhKW95.png)
* 參考:from 課程網站
* [Recipe, Cook and Carp](http://staff.csie.ncu.edu.tw/jrjiang/alg2017Fall/Recipe,%20Cook%20and%20Carp.pdf)
* [NPC理論測驗及解答](http://staff.csie.ncu.edu.tw/jrjiang/alg2017Fall/AlgQuiz%28ANS%292013-11-26.pdf)