最短距離
給定一個圖有n個節點m條邊和每條邊的邊權
詢問i~j的最短距離為多少
1
2
3
4
無向圖
邊沒有通行方向限制
1
2
3
2
3
6
無加權圖
加權圖
2
1
3
4
5
6
{1, 2, 3}為一連通圖塊
{4, 5, 6}為一連通圖塊
有向圖
每條邊有固定通行方向
2
5
1
6
4
3
1
2
3
5
4
2
1
-7
1
2
無加權圖
加權圖
混和圖
有向圖和無向圖結合
1
2
3
5
4
6
7
8
9
10
11
12
1.鄰接矩陣
開一個二維陣列v[ n ][ n ]
v[ i ][ j ]代表i~j邊的長度
空間複雜度為O(n²)
圖的存取
1
3
2
4
二維陣列v[ 4 ][ 4 ]
v[ 1 ][ 2 ] = 4
v[ 2 ][ 3 ] = 2
v[ 1 ][ 4 ] = 8
4
2
8
程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int v[10005][10005];
int main(){
int n, m, st, en, l;
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++){
scanf("%d%d%d", &st, &en, &l);
v[st][en] = l;
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
printf("%d ", v[i][j]);
}
printf("\n");
}
}
開一個存放pair的vector v[n]
在pair存放連接到的點j和~j的距離
空間複雜度為O(n + m) = O(n)
圖的存取
2.鄰接串列
vector v[ 4 ]
v[ 1 ] = {{2, 4}, {4, 8}}
v[ 2 ] = {{3, 2}}
1
3
2
4
4
2
8
程式碼
#include <bits/stdc++.h>
using namespace std;
vector <pair <int, int> > v[10005];
int main(){
int n, m;
cin >> n >> m;
for(int i = 0; i < m; i++){
int a, b, w;
cin >> a >> b >> w;
v[a].push_back({b, w});
}
for(int i = 1; i <= n; i++){
cout << i << " : ";
for(auto j : v[i]){
cout << j.first << ", " << j.second << " ";
}
cout << '\n';
}
}
假設點i、j和起點的距離是dis[ i ]、dis[ j ]
如果dis[ i ]+i~j的距離<dis[ j ]
那麼就可以直接把dis[ j ]設為dis[ i ]+i~j
鬆弛
1
2
3
4
2
10
Floyd-Warshall
(弗洛伊德演算法)
用途
- 計算任意兩點間的最小距離
- 在n較小的時候使用
作法
- 枚舉3個點a、b、c
- 把b看成a、c點的中點
- 鬆弛a、c
1
2
3
4
5
6
3
5
4
2
1
2
4
時間複雜度為O(n^3)
程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 1e9
int dis[10005][10005];
int main(){
int n, m, st, en, l;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(i == j)
dis[i][j] = 0;
else
dis[i][j] = maxn;
}
}
for(int i = 0; i < m; i++){
scanf("%d%d%d", &st, &en, &l);
dis[st][en] = l;
dis[en][st] = l;
}
for(int k = 1; k <= n; k++){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(dis[i][k] + dis[k][j] < dis[i][j])
dis[i][j] = dis[i][k] + dis[k][j];
}
}
}
scanf("%d%d", &st, &en);
printf("%d", dis[st][en]);
}
無向加權圖中求任意兩點最小距離
Dijsktra
(戴克斯特拉演算法)
不停找到目前離起點最近的點
用途
- 計算某一點到另一點的最短距離
- 有向圖和無向圖皆可用
作法
- 尋找距離起點最近且沒走到過的可連通點
- 登記點為已走到過
- 添加可連通點並對點鬆弛
1
2
3
4
5
6
3
5
4
2
1
2
4
時間複雜度為O(nlog(n))
特性
無法處理邊權為負數
1
2
3
5
4
-3
程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 1e9
vector <pair <int, int> > v[100005];
priority_queue <pair <int, int> > q;
int dis[100005];
bool u[100005] = {false};
void dijkstra(int n, int st){
for(int i = 1; i <= n; i++){
dis[i] = maxn;
}
dis[st] = 0;
q.push({0, st});
while(!q.empty()){
int lowind = q.top().second;
q.pop();
if(u[lowind])
continue;
u[lowind] = true;
for(auto i : v[lowind]){
int newdis = dis[lowind] + i.second;
if(newdis < dis[i.first]){
dis[i.first] = newdis;
q.push({-dis[i.first], i.first});
}
}
}
}
int main(){
int n, m, st, en, l;
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++){
scanf("%d%d%d", &st, &en, &l);
v[st].push_back({en, l});
v[en].push_back({st, l});
}
scanf("%d%d", &st, &en);
dijkstra(n, st);
printf("%d", dis[en]);
}
無向加權圖中求兩點最小距離
例題
額外補充
Bellman-Ford
(貝爾曼-福特演算法)
- 能處理邊權為負數的圖
- 能判斷圖中有沒有負環
BFS
(廣度優先搜尋)
像倒水一樣,從起點散開
用途
- 計算某一點可走到的點
- 在無加權圖中找到兩點最短距離
作法
- 尋找當前可連通點
- 登記點為已走到過
- 更新距離並添加可連通點
時間複雜度為O(m)
1
2
3
4
5
7
6
程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int dis[100005];
vector <int> v[100005];
bool u[100005] = {false};
queue <int> q;
void bfs(int st){
q.push(st);
u[st] = true;
dis[st] = 0;
while(!q.empty()){
int tmp = q.front();
q.pop();
for(auto i : v[tmp]){
if(!u[i]){
dis[i] = dis[tmp] + 1;
u[i] = true;
q.push(i);
}
}
}
}
int main(){
int n, m, st, en;
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++){
scanf("%d%d", &st, &en);
v[st].push_back(en);
v[en].push_back(st);
}
scanf("%d%d", &st, &en);
bfs(st);
printf("%d", dis[en]);
}
無向無加權圖中求兩點最小距離
例題
DFS
(深度優先搜尋)
用遞迴把每條路徑都走一次
用途
- 計算某一點可走到的點
- 計算連通區塊
- 圖論常用到
作法
- 找到當前可連通點
- 再從可連通點找下一個點
- 直到找不到再return
時間複雜度為O(m)
1
2
4
5
6
7
8
3
程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
vector <int> v[100005];
bool u[100005] = {false};
void dfs(int n){
printf("%d ", n);
u[n] = true;
for(auto i : v[n]){
if(!u[i]){
dfs(i);
printf("\n");
}
}
return;
}
int main() {
int n, m, st, en;
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++){
scanf("%d%d", &st, &en);
v[st].push_back(en);
v[en].push_back(st);
}
for(int i = 1; i <= n; i++){
if(!u[i])
dfs(i);
}
}無向無加權圖中走過每個點
例題
最短距離
By patrickh
最短距離
- 129