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