給定一個圖有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
開一個二維陣列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)
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
(弗洛伊德演算法)
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]);
}
無向加權圖中求任意兩點最小距離
不停找到目前離起點最近的點
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
(貝爾曼-福特演算法)
像倒水一樣,從起點散開
時間複雜度為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]);
}
無向無加權圖中求兩點最小距離
用遞迴把每條路徑都走一次
時間複雜度為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);
}
}無向無加權圖中走過每個點