最短路徑問題
+ 最小生成樹
y2sung
講者介紹
- 宋元堯
- WLSH5701
- 交大電機 -> 交大資工
- sungyuanyao@gmail.com
- deror1869107 / Deror Sung / y2sung
最短路徑
從起點到終點、權重最小的路徑。
Data Structure
- Adjacency Matrix
- Adjacency List
- 家安昨天應該講過了
Adjacency Matrix
const int INF = 0x7fffffff;
const int MAX = 1000 + 5;
int w[MAX][MAX];
存取權重速度較快,記憶體使用量較大(空間複雜度O(V^2))
適合場合:Vertices數較少之時候
1000個點僅佔4MB
10000個點就會佔400MB
Adjacency List
#include <iostream>
#include <vector>
struct Edge {
int b, w; //b是另一個端點 w是權重
Edge(int b, int w):b(b), w(w){}
Edge(){}
};
const int MAX = 100000 + 5;
vector<Edge> e[MAX];
int main()
{
int n;
cin >> n;
int a, b, w;
for(int i = 0; i < n; ++i){
cin >> a >> b >> w;
e[a].push_back(Edge(b, w));
e[b].push_back(Edge(a, w));//雙向
}
}
記憶體使用量較小(空間複雜度O(E))
適合場合:Edge數遠小於V^2時
Floyd-Warshall
Floyd-Warshall
核心概念:DP
設 D(i, j, k) 為從 i 到 j 的只以 ( 1.. k ) 集合中的節點為中間節點的最短路徑的長度。
D(i, j, k) = min(D(i, j, k - 1), D(i, k, k - 1) + D(k, j, k - 1))
(不經過k點) (經過k點)
全局最短路徑問題
複雜度:O(V^3)
適用Vertices數<=100
Code簡單
Floyd-Warshall
for(int k = 0; k < n; ++k){
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j){
if(w[i][k] + w[k][j] < w[i][j]){
w[i][j] = w[i][k] + w[k][j];
}
}
}
}
Bellman-Ford
Bellman-Ford
令w[a][b]是a點到b點的距離(即是邊的權重)。 令d[a]是起點到a點的最短路徑長度,起點設為零,其他點都設為無限大。
重複下面這件事V-1次:
窮舉邊ab:d[a] + w[a][b] < d[b]
以邊ab來修正起點到b點的最短路徑:d[b] = d[a] + w[a][b](relaxation)
複雜度:adjacency matrix : O(V^3) adjacency list : O(VE)
Bellman-Ford
const int MAX = 100000, INF = 1e9;
vector<Edge> e[MAX];
int d[MAX], parent[MAX];
void bellman_ford(int source, int V)
{
for(int i = 0; i < V; ++i) d[i] = INF;
d[source] = 0;
parent[source] = source;
for(int i = 0; i < V - 1; ++i){
for(int a = 0; a < V; ++a){
for(auto edge : e[a]){
int b = edge.b;
if(d[a] != INF && edge.w != INF){
if(d[a] + edge.w < d[b]){
d[b] = d[a] + edge.w;
parent[b] = a;
}
}
}
}
}
}
Dijkstra
Dijkstra
發音很重要,j不發音
重複下面這件事V次,以將所有點加入到最短路徑樹:
尋找一個目前未走訪過而且離起點最近的點
令剛剛加入的點為a點,找一個與a點相鄰的點b,把d[a]+w[a][b]存入到d[b]當中,儘可能紀錄越小
(relaxation)
複雜度:adjacency matrix or list 無 priority_queue : O(V^2)
adjacency list + priority_queue O(E + VlogV) 快!
Dijkstra
Dijkstra
#include <vector>
#include <queue>
struct Node {
int b, d;
Node(int b, int d):b(b), d(d){}
Node(){}
};
bool operator<(const Node& n1, const Node& n2) {return n1.d > n2.d;}
const int MAX = 100000 + 5, INF = 1e9;
vector<Edge> e;
int d[MAX], parent[MAX];
bool visit[MAX];
void dijkstra(int source, int V)
{
for(int i = 0; i < MAX; ++i) visit[i] = false;
for(int i = 0; i < MAX; ++i) d[i] = INF;
priority_queue<Node> pq;
d[source] = 0;
parent[source] = source;
pq.push(Node(source, d[source]));
for(int i = 0; i < V; ++i){
int a = -1;
while(!pq.empty() && visit[a = pq.top()]) pq.pop();
if(a == -1) break;
visit[a] = true;
for(auto edge : e[a]){
int b = edge.b;
if(!visit[b] && d[a] + edge.w < d[b]){
d[b] = d[a] + edge.w;
parent[b] = a;
pq.push(Node(b, d[b]));
}
}
}
}
Minimal Spanning Tree
Minimal Spanning Tree
Spanning Tree:
一個無向圖G = (V,E),其中|V| = N而且|E| ≥ N-1。
用圖形中的N-1個邊建立一棵可以連接所有頂點的樹。
Minimal (Cost) Spanning Tree:
所有生成樹中,權重和最小的就是MST
Prim Algorithm
Prim Algorithm
基於Dijkstra的框架
S={起點}
將距離S最近的點加入S
直到所有點都已在S中
Adjacency Matrix: O(V^2)、Fibonacci Heap: O(E+VlogV)
適用時機:稠密圖
Prim Algorithm
#include <vector>
#include <queue>
struct Edge {
int a, b, w;
Edge(int a, int b, int w):a(a), b(b), w(w){}
Edge(){}
};
bool cmp(const Edge& lhs, const Edge& rhs)
{
return lhs.w < rhs.w;
}
const int MAX = 100000 + 5, INF = 1e9;
vector<Edge> e[MAX], mst;
int d[MAX], parent[MAX];
bool visit[MAX];
int prim(int V)
{
for(int i = 0; i < MAX; ++i) visit[i] = false;
for(int i = 0; i < MAX; ++i) d[i] = INF;
priority_queue<int> pq;
d[0] = 0; // zero-based
parent[0] = 0;
pq.push(0);
for(int i = 0; i < V - 1; ++i){
int a = -1;
while(!pq.empty() && visit[a = pq.top()]) pq.pop();
if(a == -1) break;
visit[a] = true;
for(auto edge : e[a]){
int b = edge.b;
if(!visit[b] && edge.w < d[b]){
d[b] = edge.w;
parent[b] = a;
pq.push(b);
mst.push_back(edge);
}
}
}
}
Kruskal Algorithm
Kruskal Algorithm
應用Disjoint Set
將所有邊由小到大排序
連接不在同一塊的兩端點
直到所有點都已連在一起
O(ElogV)
適用時機:稀疏圖
Kruskal Algorithm
補充Disjoint Set
Disjoint Set「互斥集」的意思是一堆集合們,大家擁有的元素都不相同,也就是說這些集合們之間都沒有交集。
初始化:每個人自己和自己一組
操作:
find : 找出一個人在哪一團
union : 將兩個不同團體合併為一團
Kruskal Algorithm
續Disjoint Set
int d[MAX + 1];
void init()
{
for(int i = 1; i <= MAX; ++i) {
d[i] = i;
}
}
int find(int n)
{
return (d[n] == n) ? d[n] : d[n] = find(d[n]);
}
bool Union(int a, int b)
{
int x = find(a);
int y = find(b);
if(x != y) d[x] = y;
return x != y; // for Kruskal
}
Kruskal Algorithm
續Disjoint Set
#include <vector>
#include <algorithm>
struct Edge {
int a, b, w;
Edge(int a, int b, int w):a(a), b(b), w(w){}
Edge(){}
};
bool cmp(const Edge& lhs, const Edge& rhs)
{
return lhs.w < rhs.w;
}
vector<E> v, mst;
void kruskal()
{
init(); // initialize disjoint set
sort(v.begin, v.end, cmp); // sort edges by cost
for(auto e: v){
if(Union(e.a, e.b)){
mst.push_back(e); // put selected edge in MST
}
}
}
2016武陵電資推廣暑訓(Shortest Path + MST)
By deror1869107
2016武陵電資推廣暑訓(Shortest Path + MST)
- 178