題目描述
如題,給出一個有向圖,請輸出從某一點出發到所有點的最短路徑長度。
輸入輸出格式
輸入格式:
第一行包含三個整數N、M、S,分別表示點的個數、有向邊的個數、出發點的編號。
接下來M行每行包含三個整數Fi、Gi、Wi,分別表示第i條有向邊的出發點、目標點和長度。
輸出格式:
一行,包含N個用空格分隔的整數,其中第i個整數表示從點S出發到點i的最短路徑長度(若S=i則最短路徑長度為0,若從點S無法到達點i,則最短路徑長度為2147483647)
輸入輸出樣例
輸入樣例#1:
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
輸出樣例#1:
0 2 4 3
1.SPFA的描述
SPFA(Shortest Path Faster Algorithm)(隊列優化)算法是求單源最短路徑的一種算法,它還有一個重要的功能是判負環(在差分約束系統中會得以體現),在Bellman-ford算法的基礎上加上一個隊列優化,減少了冗余的松弛操作,是一種高效的最短路算法。SPFA算法由段凡丁于1994年西安交通大學提出。
2.SPFA算法過程
算法過程分為兩步
1.初始化
2.松弛操作
a)初始化過程
將未特別說明的數據設置為正無窮(int最大值2147483647)。
i=j時,它到它自己的邊權為0,無向邊的數據直接更新為正無窮,在后面的代碼實現時,我們可以進行對比更新。
b)松弛操作
初始化源點數據,設置一個dis數組,inq數組,q隊列。
dis用于存最小路徑,inq判斷當點是否在隊列內,q用于存待松弛的邊。
第一個壓入q隊列的數據為源點1。
進入核心循環代碼。
取出隊列頂數據,為待松弛的邊。
之后進行遍歷,spfa的核心就是過一點的距離+該點長度小于末點的dis值,就更新末點的dis。
接著繼續判斷沒有訪問過的邊,加入待松弛隊列。
最后直接輸出dis數組數據即為最小路徑。
代碼實現
#include<vector>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
#include<list>
using namespace std;
const int maxn = 500000 + 10;
struct nodes {
int u, to, w;
};//to表示末點,w表示長度
int dis[10000 + 20];
typedef struct nodes node;//方便后面的定義。。
list<node> v[10000 + 20];//邊集,list和vector都可以。。
int n, m, s;
int inq[10000 + 20]; //用inq來表示點是否在queue中,判重和記憶。。
queue<int> q;//不解釋了、、
int main()
{
cin >> n >> m >> s;
for (int i = 0; i < m; i++) {
int x, y, z;
cin >> x >> y >> z;
v[x].push_back({ x,y,z }); //用list來讀入邊集
}
for (int i = 0; i < 10000 + 20; i++) dis[i] = 2147483647; //初始化的時候 dis全部是2147483647,而是dis[s]=0;這個在輸出的時候有用。。
dis[s] = 0;
q.push(s);
inq[s] = 1; //用來判斷點是否在隊列里面,,類似記憶。。
while (!q.empty()) {
int cur = q.front(); q.pop();//取出待松弛的邊
inq[cur] = 0;
for (list<node>::iterator it = v[cur].begin(); it != v[cur].end(); it++) {//遍歷
if (dis[cur] + it->w < dis[it->to]) {//這個是松弛操作的核心。。如果過一點的距離+該點長度小于末點的dis值,就更新末點的dis
dis[it->to] = dis[cur] + it->w;
if (!inq[it->to]) {//判斷是否in queue
q.push(it->to);//松弛之后,加入queue,等待接下來的松弛。。
}
}
}
}
for (int i = 1; i <= n; i++) {
cout << dis[i] << " ";
}
return 0;
}