求最短路的幾個方法及優化!!!!
看到的結論:
Dijkstra:適用於權值為非負的圖的單源最短路徑,用斐波那契堆的複雜度O(E+VlgV)
BellmanFord:適用於權值有負值的圖的單源最短路徑,並且能夠檢測負圈,複雜度O(VE)
SPFA:適用於權值有負值,且沒有負圈的圖的單源最短路徑,論文中的複雜度O(kE),k為每個節點進入Queue的次數,且k一般<=2,但此處的複雜度證明是有問題的,其實SPFA的最壞情況應該是O(VE).
Floyd:每對節點之間的最短路徑。
先給出結論:
(1)當權值為非負時,用Dijkstra。
(2)當權值有負值,且沒有負圈,則用SPFA,SPFA能檢測負圈,但是不能輸出負圈。
(3)當權值有負值,而且可能存在負圈,則用BellmanFord,能夠檢測並輸出負圈。
(4)SPFA檢測負環:當存在一個點入隊大於等於V次,則有負環,後面有證明。
此段引用於:
--------------我----------------------是---------------分---------------割------------------------線-------------------------------------
我本來還想整理總結一下各種求最短路的方法呢,一搜發現別人弄的簡直完美,貼幾個地址地址:
1:floyd演算法https://www.cnblogs.com/wangyuliang/p/9216365.html
下面的演算法有的用前向星鄰接表實現(模擬vector),如果不知道的看我上個貼:
2:Dijkstra演算法https://www.bilibili.com/video/av36886088 這個視訊看了就能理解原理了。
3:Bellman-Ford演算法,這個我個人覺得用不到,因為改進後的SPFA效率比這個高,都用SPFA了。
https://blog.csdn.net/liangzhaoyang1/article/details/51344742
4:SPFA:其實是3的佇列優化,就如我說的,都是用SPFA而不是3。
https://blog.csdn.net/qq_40061421/article/details/82054784
最後來個程式碼總結
求最短路的四種方法模板:https://blog.csdn.net/qq_41117236/article/details/80517605
所以為什麼我還要總結呢,因為搜了很多,幾乎沒見過用優先佇列優化的。
寫個題
貼個簡單的題:
圖結構練習——最短路徑
Time Limit: 1000 ms Memory Limit: 65536 KiB
Problem Description
給定一個帶權無向圖,求節點1到節點n的最短路徑。
Input
輸入包含多組資料,格式如下。
第一行包括兩個整數n m,代表節點個數和邊的個數。(n<=100)
剩下m行每行3個正整數a b c,代表節點a和節點b之間有一條邊,權值為c。
Output
每組輸出佔一行,僅輸出從1到n的最短路徑權值。(保證最短路徑存在)
Sample Input
3 2
1 2 1
1 3 1
1 0
Sample Output
1
0
Dijkstra優化
這個是用前向星模擬vector,然後優先佇列優化。
#include <bits/stdc++.h>
#define inf 0x3f3f3f
using namespace std;
int m, n;
int vis[111], dis[111];
struct node
{
int x, y; x是距離,y是編號。
};
int Next[111];
struct qq
{
int u, v, w, next;
} p[111111];
bool operator < (node a, node b){ //過載函式,表示在優先佇列裡排序是怎麼排的,是按照x值的從小到大排的。
return a.x > b.x; //從小到大,這個定義是>,是相反的。
}
void betterdis(int st);
int main()
{
while(cin>> n >> m)
{
memset(dis, inf, sizeof(dis));
memset(Next, 0, sizeof(Next));
memset(vis, 0, sizeof(vis));
memset(p, 0, sizeof(p));
int to = 0;
for(int i = 1; i <= m; i++)
{
int u, v, w;
cin >> u >> v >> w;
p[++to] = (qq){u, v, w, Next[u]};
Next[u] = to;
p[++to] = (qq){v, u, w, Next[v]};
Next[v] = to; //無向圖
}
betterdis(1); 算的是從1到其他點的距離。
cout << dis[n] << endl;
}
return 0;
}
void betterdis(int st)
{
priority_queue<node> q;
dis[st] = 0;
q.push((node){0,st});
while(q.size())
{
node now = q.top();
q.pop();
if(vis[now.y]) continue;
vis[now.y] = 1;
for(int i = Next[now.y]; i != 0; i = p[i].next)
{
int u = p[i].u, v = p[i].v, w = p[i].w;
if(!vis[v] && dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
q.push((node){dis[v], v});
}
}
}
}
--------------------------------------------------分割線----------------------------
直接用vector的優先佇列優化:
#include<bits/stdc++.h>
using namespace std;
#define N 11111
#define inf 0x3f3f3f
struct node
{
int di, num; //distance距離的前兩個字母di,num是編號。
};
bool operator <(node a, node b)
{
return a.di > b.di;
}
int dis[N], vis[N], m, n;
vector<node> Map[N];
void add(int u, int v, int w)
{
Map[u].push_back((node){w, v});
}
void bettergls(int u, int e);
int main()
{
while(cin >> n >> m)
{
memset(dis, inf, sizeof(dis));
memset(vis, 0, sizeof(vis));
for(int i = 0; i <= n; i++) Map[i].clear();
for(int i = 1; i <= m; i++)
{
int u, v, w;
cin >> u >> v >> w;
add(u, v, w);
add(v, u, w);
}
bettergls(1, n); 從某點到某點
}
}
void bettergls(int u, int e)
{
priority_queue<node> q;
dis[u] = 0;
q.push((node){0,u});
while(q.size())
{
node Now = q.top();
q.pop();
if(vis[Now.num] == 1) continue;
vis[Now.num] = 1;
int len = Map[Now.num].size();
for(int i = 0; i < len; i++)
{
int di = Map[Now.num][i].di, num = Map[Now.num][i].num;
if(!vis[num] && dis[num] > dis[Now.num] + di)
{
dis[num] = dis[Now.num] + di;
q.push((node){dis[num], num});
}
}
}
cout << dis[e] << endl;
}