最短路[Dijkstra和堆优化的Dijkstra][Bellman-Ford和SPFA][Floyd最短路](更新中)
文章目錄
- 第一類:單源最短路
- 一 所有邊權都是正數(Dijkstra)
- 樸素版Dijkstra(稠密圖)
- 堆優化版Dijkstra(稀疏圖)
- 二 存在負權邊(BF和SPFA)
- 第二類:多源匯最短路(Floyd)
常見的最短路問題,假定邊為m,頂點為n。
第一類:單源最短路
求一個點到其他所有點的最短距離,比如:從1到n的最短路
單源最短路再分兩種:
一 所有邊權都是正數(Dijkstra)
兩種算法:
樸素Dijkstra算法,時間復雜度O(n2)O(n^2)O(n2),和邊的數量無關,適合于稠密圖(邊數很多,m和n2n^2n2是一個規模)。
堆優化的Dijstra算法。 時間復雜度O(mlogn)O(mlogn)O(mlogn),適合于稀疏圖。
其中,n是圖中點的數量,m是圖中邊的數量
Dijkstra算法的思想是基于貪心的,每次選擇最短的路。
樸素Dijkstra算法步驟:
集合S表示已確定最短距離的點
1. dist[1]=0,dist[i]= INF //初始化 2. for i: 1~nt ?不在S中的距離最近的點S ? t用t更新其他點的距離樸素版Dijkstra(稠密圖)
Acwing849. Dijkstra求最短路 I(稠密圖)
給定一個n個點m條邊的有向圖,圖中可能存在重邊和自環,所有邊權均為正值。
請你求出1號點到n號點的最短距離,如果無法從1號點走到n號點,則輸出-1。
輸入格式
第一行包含整數n和m。
接下來m行每行包含三個整數x,y,z,表示存在一條從點x到點y的有向邊,邊長為z。
輸出格式
輸出一個整數,表示1號點到n號點的最短距離。
如果路徑不存在,則輸出-1。
數據范圍
1≤n≤500,
1≤m≤105,
圖中涉及邊長均不超過10000。
輸入樣例:
3 3
1 2 2
2 3 1
1 3 4
輸出樣例:
3
ac代碼
#include<bits/stdc++.h> using namespace std; const int N=510;int m , n; int g[N][N];bool st[N]; //該點是否確定最短路 int dist[N];//原點到各點的距離 int dijkstra(){memset(dist,0x3f3f3f,sizeof(dist)); //初始化距離無窮大dist[1] =0 ; //1號點初始化為0//迭代n次 for(int i=0;i<n;i++){//找最小值int t=-1; //找到當前最小的值 用t來存,t=-1表示沒找到 for(int j=1;j<=n;j++){if(!st[j]&&( t==-1 || dist[t] >dist[j] ))t=j;} //加到集合S中st[t]=true;//用t更新其他點的距離for(int j=1;j<=n;j++)dist[j]= min(dist[j],dist[t]+g[t][j]); } //如果不連通返回-1if(dist[n]>=0xffff) return -1;return dist[n]; }int main(){cin>>n>>m;memset(g,0x3f,sizeof g);while(m--){int a,b,c;cin>>a>>b>>c;g[a][b]=min(g[a][b],c); //去除重邊 } int t=dijkstra();cout<<t<<endl;}堆優化版Dijkstra(稀疏圖)
acwing850. Dijkstra求最短路 II
給定一個n個點m條邊的有向圖,圖中可能存在重邊和自環,所有邊權均為非負值。
請你求出1號點到n號點的最短距離,如果無法從1號點走到n號點,則輸出-1。
輸入格式
第一行包含整數n和m。
接下來m行每行包含三個整數x,y,z,表示存在一條從點x到點y的有向邊,邊長為z。
輸出格式
輸出一個整數,表示1號點到n號點的最短距離。
如果路徑不存在,則輸出-1。
數據范圍
1≤n,m≤1.5×1051≤n,m≤1.5×10^51≤n,m≤1.5×105,
圖中涉及邊長均不小于0,且不超過10000。
輸入樣例:
3 3
1 2 2
2 3 1
1 3 4
輸出樣例:
3
分析:
這題是稀疏圖,使用鄰接表來存儲。
ac代碼
#include<bits/stdc++.h> using namespace std; const int Mn=1e9;const int N=1000000;typedef pair<int ,int > PII;bool st[N]; //是否確定最短路 int dist[N]; //最短距離 int n ,m; //使用鄰接表來存儲圖 int h[N]; // 頭節點數組 int ne[N]; //next指針指向的地址,這里是結點b的地址 int e[N]; //next指針指向的具體的值,這里是結點b int w[N]; // a到b的邊的權重 int idx; //索引//加邊 a后面加b,權重是c void add(int a,int b,int c){e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++; }int dijkstra(){memset(dist,0x3f,sizeof dist);dist[1]=0;priority_queue<PII,vector<PII> ,greater<PII>> heap; heap.push({0,1}); //距離為0,編號為1 之所以距離放前面,是因為heap按照第一關鍵字排序while(heap.size()){//找最小點auto t=heap.top();heap.pop();int ver =t.second, distance = t.first; //ver是編號,distance 是距離if(st[ver]) continue;//找到最小值st[ver]=true; //用當前點更新其他點//遍歷當前 點ver的相鄰的點for(int i=h[ver];i!=-1;i = ne[i]){int j=e[i]; //點if(dist[j]> distance + w[i]){dist[j]= distance +w[i];heap.push({dist[j],j});}}}if(dist[n]>=Mn) return -1;return dist[n];}int main(){cin>>n>>m;memset(h ,-1 ,sizeof h); //頭結點數組初始化為-1while(m--){int a , b, c;cin>>a>>b>>c;add(a,b,c); //鄰接表}int t=dijkstra();cout<<t<<endl;}二 存在負權邊(BF和SPFA)
兩種算法:假定邊為m,頂點為n
Bellman-Ford算法 ,時間復雜度O(nm)O(nm)O(nm),適合于邊數不超過k條(此處k為限定的邊數)。
算法思路:
而spfa算法呢,在國際上通常稱為”隊列優化的Bellman-Ford算法“。它是對Bellman-Ford算法的優化,一般而言,我們刷題用的多的還是spfa。
SPFA算法 一般情況下比Bellman-Ford快,時間復雜度O(m)O(m)O(m),最壞O(nm)O(nm)O(nm),適合于負權邊
spfa算法流程:
851. spfa求最短路
來源:acwing
spfa算法模板代碼
#include<bits/stdc++.h> #define x first #define y second using namespace std; const int N = 1e5 + 10; int n, m; int h[N],ne[N],w[N],e[N], idx; typedef pair<int,int> PII;int dist[N]; bool st[N]; // st數組存的是當前這個點是否在隊列中void add(int a, int b, int c){e[idx] = b, w[idx] = c, ne[idx] = h[a] ,h[a] = idx ++; }int spfa(){memset(dist, 0x3f, sizeof dist);dist[1] = 0;queue<int> q;q.push(1); // 把1號點放進隊列st[1] = true; while(q.size()){int t = q.front();q.pop();st[t] = false; // 從隊列中出來for(int i = h[t]; i != -1; i = ne[i]){int j = e[i];if(dist[j] > dist[t] + w[i] ){dist[j] = dist[t] + w[i];if(!st[j]){ // 如果不在隊列中,則加入到隊列中q.push(j);st[j] = true;}}}}if(dist[n] == 0x3f3f3f3f) return -1;return dist[n];}int main(){cin >> n >> m;memset(h, -1, sizeof h);while(m --){int a, b, c;cin >> a >> b >> c;add(a, b ,c );}int t = spfa();if( t == -1) puts("impossible");else cout << t << endl;}補充手寫隊列的spfa寫法:
int spfa(){memset(dist, 0x3f, sizeof dist);int hh = 0, tt = 0;q[0] = 1;dist[1] = 0;st[1] = true;while(hh <= tt){auto t = q[ hh ++];st[t] = false;for(int i = h[t]; ~i; i = ne[i]){int j = e[i];if(dist[j] > dist[t] + w[i]){dist[j] = dist[t] + w[i]; if(!st[j]){q[ ++ tt] = j;st[j] = true;}}}}if(dist[n] == 0x3f3f3f3f) return -1;return dist[n]; }第二類:多源匯最短路(Floyd)
源點:起點;匯點:終點;
假定邊為m,頂點為n
任選兩個點,從其中一個點到另外一個點的最短距離。即起點和終點不確定。很多不同起點到其他點的最短路
算法 :Floyd 算法, 時間復雜度O(n3)O(n^3)O(n3)
考察側重點:建圖。
可以用有向圖的算法直接解決無向圖的問題。
總結
以上是生活随笔為你收集整理的最短路[Dijkstra和堆优化的Dijkstra][Bellman-Ford和SPFA][Floyd最短路](更新中)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PAT甲级1126 Eulerian P
- 下一篇: 小白也能看懂的git入门实操[狂神聊gi