WiwiHo
Topological sort
若從節點 \(u\) 能到達節點 \(v\)
在拓樸排序中 \(u\) 就要出現在 \(v\) 之前
不斷把入度 0 的節點拔掉
DFS 的出點順序反過來就是拓樸排序
把 DP 的每個狀態視為節點
若 \(u\) 是 \(v\) 的轉移來源
就連有向邊 \((u,v)\)
圖會是 DAG
反過來
DAG 上當然可以 DP
在負環上一直繞
路徑長就會一直變短
所以有負環就不存在最短路徑
最短路徑必是簡單路徑
Relaxation
對邊 \((u,v)\) 鬆弛:
用到 \(u\) 的最短路徑更新到 \(v\) 的最短路徑
對點 \(v\) 鬆弛:
對 \(v\) 的所有臨邊或出邊鬆弛
還記得 BFS 嗎?
邊帶權會造成
直接把點丟在 queue 尾端
會無法滿足距離遞增
那就用 priority queue?
一開始所有 \(dis_v\) 都是未確定的
當確定一個 \(dis_v\) 時,就鬆弛 \(v\)
如果權重都不是負的
已知最小的未確定的 \(dis_v\) 肯定不能再變小了
就確定它
vector<vector<pair<int, int>>> g; //鄰接串列,pair 是 (邊權, 邊到達的點)
//別忘了改成從小到大排序,pair 是 (dis_v, v 的編號)
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
vector<int> dis; //先初始化好 dis[s]=0,其他 dis[v]=很大的數字
while(!pq.empty()){
int now = pq.top().second;
int d = pq.top().first;
pq.pop();
if(d != dis[now]) continue;
for(pair<int, int> i : g[now]){
if(d + i.first < dis[i.second]){
dis[i.second] = d + i.first;
pq.push(make_pair(d + i.first, dis[i.second]));
}
}
}
時間複雜度:\(O((|V|+|E|) \log |E|)\)
全點對最短路徑?
做 \(|V|\) 次 Dijkstra's algorithm?
速度快但不能做負權邊
\(dis_{i,j}=\min\{dis_{i,k}+dis_{k,j}| k \neq i,j\}\)
轉移順序?
有狀態互為轉移來源
\(dis_{k,i,j}=\)
由前 \(k\) 個節點和 \(i\)、\(j\) 為兩端點
構成的最短路徑長
\(dis_{k,i,j}=\min(dis_{k-1,i,k}+dis_{k-1,k,j}, w_{i,j}, dis_{k-1,i,j})\)
時間複雜度:\(O(|V|^3)\)
有負權的單點源最短路徑?
Floyd-Warshall 可以做但浪費時間
最短路徑最多只有 \(|V|-1\) 條邊
\(\Rightarrow\) 把所有點都鬆弛 \(|V|-1\) 次
如果還能鬆弛就是有負環
如果有節點在某一輪的距離沒有變
那下一輪就沒有必要再鬆弛一次
\(\Rightarrow\) 每一輪記錄哪些節點距離改變
下一輪只鬆弛它們
另外記錄每個節點被鬆弛成功次數
超過 \(|V|-1\) 就是有負環
Disjoint-set union-find
用來維護一些不相交的集合
支援:
將每個元素視為一個節點
集合看作一棵樹
讓集合中的其中一個點當根節點
把其中一棵樹的根
變成另一棵的根的子節點
維護每個節點的父節點
遞迴找根
\(\Rightarrow\) 可以判斷兩個點是否在同一集合
樹成鏈時
多次查詢同一個節點很浪費時間
\(\Rightarrow\) 查詢完就把節點變成根節點的子節點
把深度大的樹接到深度小的樹下
合併完深度會比反過來接大
\(\Rightarrow\) 維護深度
Lowest common ancestor
如果 \(u\) 是 \(v\) 的祖先
那麼 \(u\) 就是 LCA
否則
令 \(w\) 為 \(u\) 的祖先中
深度最小且不為 \(v\) 的祖先的
令 \(x\) 為 \(w\) 和 \(u\) 之間的邊數
\(u\) 往上跳 \(2^k\) 個節點到達的點記為 \(anc(u, k)\)
不斷找最大的 \(k\)
使得 \(anc(u, k)\) 不是 \(v\) 的祖先
把 \(u\) 往上跳 \(2^k\) 個節點
最後答案是 \(anc(u, 0)\)
\(anc(u,k)=anc(anc(u,k-1),k-1)\)
子孫會比祖先晚入點、早出點
\(\Rightarrow\) 記錄每個節點的入出點順序