給定一個n個頂點,m條邊的有向圖(其中某些邊權可能為負,但保證沒有負環)。請你計算從1號點到其他點的最短路(頂點從1到n編號)。
第一行兩個整數n, m。
接下來的m行,每行有三個整數u, v, l,表示u到v有一條長度為l的邊。
1 2 -1
2 3 -1
3 1 2
-2
對於10%的數據,n = 2,m = 2。
對於30%的數據,n <= 5,m <= 10。
對於100%的數據,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保證從任意頂點都能到達其他所有頂點。
#include<cstdio> #include<iostream> #include<cstring> using namespace std; const int MAXN = 20001; int floyd[MAXN][MAXN]; int main(){ int m, n; memset(floyd, 2, sizeof(floyd)); cin >> m >> n; for (int i = 1; i <= m; i++){ int from, to, value; cin >> from >> to >> value; floyd[from][to] = value; } for (int j = 1; j <= n; j++) for (int k = 1; k <= n; k++){ if (floyd[1][k] + floyd[k][j] < floyd[1][j]) floyd[1][j] = floyd[1][k] + floyd[k][j]; } for (int m = 2; m <= n; m++) cout << floyd[1][m] << endl; return 0; }
上網查了一下發現SPFA算法,利用隊列優化了一下。
SPFA(Shortest Path Faster Algorithm)(隊列優化)算法是求單源最短路徑的一種算法,它還有一個重要的功能是判負環(在差分約束系統中會得以體現),在Bellman-ford算法的基礎上加上一個隊列優化,減少了冗余的松弛操作,是一種高效的最短路算法。
算法大致思路:
s表示源點
利用dist[x]表示從源點s到x的最短距離
用Q隊列來保存需要處理的結點
用inQueue[x]保存點x是否在隊列中
初始化:dist[]數組全部賦值為無窮大,比如INT_MAX(一定要足夠大, 我一開始就是給小了所以有些數據錯了)
dist[s] = 0
開始算法:隊列+松弛操作
讀取Q隊首元素並出隊(記得把inQueue[Q.top()]置為false)
對與隊首結點相連的所有點v進行松弛操作(如果源點通過隊首結點再到結點v的距離比源點直接到v的距離要短,就更新dist[v],並且如果inQueue[v] == false 即V當前不在隊列中,則v入隊,當隊列Q為空時,判斷結束)
代碼如下:
#include<cstdio> #include<iostream> #include<cstring> #include<queue> using namespace std; const int MAXN = 20001; const int MAXL = 200001; const int INF = INT_MAX; int dist[MAXN]; //dist[x]表示從源點到x所需的最短距離,初始為INF int head[MAXN]; int M; //邊的索引 bool inQueue[MAXN]; queue<int> Q; //隊列Q用來存放可松弛周圍結點的結點 struct Edge{ int value; int to; int next; }edge[MAXL]; //采用鏈式前向星存儲邊集 //構建邊集合 void add(int from, int to, int value){ edge[M].to = to; edge[M].next = head[from]; edge[M].value = value; head[from] = M++; } //SPFA算法 void SPFA(int start){ dist[start] = 0; //源點到自己的距離為0 Q.push(start); inQueue[start] = true; while (!Q.empty()){ int temp = Q.front(); //取隊頭元素 Q.pop(); for (int j = head[temp]; j != -1; j = edge[j].next){ int toNode = edge[j].to; if (dist[toNode] > dist[temp] + edge[j].value){ //本題保證無負環,否則需要利用一個數組判斷j是否入隊超過n次 dist[toNode] = dist[temp] + edge[j].value; if (!inQueue[toNode]){ Q.push(toNode); inQueue[toNode] = true; } } } inQueue[temp] = false; } } int main(){ memset(head, -1, sizeof(head)); int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++){ //初始化 dist[i] = INF; inQueue[i] = false; } for (int p = 1; p <= m; p++){ int from, to, value; scanf("%d%d%d", &from, &to, &value); //用cin速度好像要慢一倍= = add(from, to, value); } SPFA(1); for (int x = 2; x <= n; x++){ printf("%d\n", dist[x]); } return 0; }