【常用算法总结——最短路径四种方法】
以下轉自https://blog.csdn.net/weixin_42060896/article/details/82216379
例題:HDU 2544
最短路
Time Limit: 5000/1000 MS (Java/Others) ? ?Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 89730 ? ?Accepted Submission(s): 38892
Problem Description
在每年的校賽里,所有進入決賽的同學都會獲得一件很漂亮的t-shirt。但是每當我們的工作人員把上百件的衣服從商店運回到賽場的時候,卻是非常累的!所以現在他們想要尋找最短的從商店到賽場的路線,你可以幫助他們嗎?
?
Input
輸入包括多組數據。每組數據第一行是兩個整數N、M(N<=100,M<=10000),N表示成都的大街上有幾個路口,標號為1的路口是商店所在地,標號為N的路口是賽場所在地,M則表示在成都有幾條路。N=M=0表示輸入結束。接下來M行,每行包括3個整數A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A與路口B之間有一條路,我們的工作人員需要C分鐘的時間走過這條路。
輸入保證至少存在1條商店到賽場的路線。
?
Output
對于每組輸入,輸出一行,表示工作人員從商店走到賽場的最短時間
?
Sample Input
2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0
?
Sample Output
3
2
?
1),深度或廣度優先搜索算法(解決單源最短路徑)
從起始結點開始訪問所有的深度遍歷路徑或廣度優先路徑,則到達終點結點的路徑有多條,取其中路徑權值最短的一條則為最短路徑。
給定一個帶權有向圖G=(V,E),其中每條邊的權是一個實數。另外,還給定V中的一個頂點,稱為
源。
現在要計算從源到其他所有各頂點的最短路徑長度。這里的長度就是指路上各邊權之和。這個問題通
常稱為單源最短路徑 [1] 問題。
從起始結點開始訪問所有的深度遍歷路徑或廣度優先路徑,則到達終點結點的路徑有多條,取其中路
徑權值最短的一條則為最短路徑
下面是核心代碼:
1 //題意:求1->n的最短路徑 2 #include<iostream> 3 #include<string.h> 4 #define inf 99999999 5 using namespace std; 6 int dis[111][111]; 7 bool vis[111]; 8 int n,cnt;//n為節點數,cnt為最短長度 9 void init(int x){ 10 for(int i=0;i<=n;i++){ 11 for(int j=0;j<=n;j++) 12 dis[i][j]=inf; 13 dis[i][i]=0; 14 vis[i]=0; 15 } 16 } 17 void dfs(int st,int dst) 18 { 19 if(dst>cnt)return ;//距離大于最短路徑,無需遍歷 20 if(st==n){//到達終點 21 cnt=cnt>dst?dst:cnt; 22 return; 23 } 24 for(int i=1;i<=n;i++) 25 { 26 if(!vis[i]&&dis[st][i]!=inf&&dis[st][i]){ 27 vis[i]=1; 28 dfs(i,dst+dis[st][i]); 29 vis[i]=0; 30 } 31 } 32 } 33 int main() 34 { 35 int m; 36 while(~scanf("%d%d",&n,&m)&&n&&m) 37 { 38 int x,y,len; 39 cnt=inf; 40 init(n); 41 while(m--){ 42 scanf("%d%d%d",&x,&y,&len); 43 dis[x][y]=min(dis[x][y],len);//兩點之間距離重復輸入取小距離 44 dis[y][x]=dis[x][y]; 45 } 46 vis[1]=1; 47 dfs(1,0); 48 printf("%d\n",cnt); 49 } 50 return 0; 51 } 52 Sample Input 2 53 5 14 54 2 2 262 55 5 3 403 56 4 2 456 57 1 5 289 58 3 1 1000 59 2 4 217 60 2 5 536 61 2 5 415 62 2 4 880 63 3 1 179 64 3 4 972 65 5 3 2 66 1 3 491 67 4 1 872 68 0 0 69 Sample Output 2 70 1812),弗洛伊德算法(解決多源最短路徑):時間復雜度O(n^3),空間復雜度O(n^2)
基本思想:最開始只允許經過1號頂點進行中轉,接下來只允許經過1號和2號頂點進行中轉......允許經過1~n號所有頂點進行中轉,來不斷動態更新任意兩點之間的最短路程。即求從i號頂點到j號頂點只經過前k號點的最短路程。
1 //題意:求1->n的最短路徑 2 #include<iostream> 3 #include<string.h> 4 #define inf 99999999 5 using namespace std; 6 int n,dis[111][111]; 7 void init(){ 8 for(int i=0;i<=n;i++){ 9 for(int j=0;j<=n;j++) 10 dis[i][j]=inf; 11 dis[i][i]=0; 12 } 13 } 14 int main() 15 { 16 int m; 17 while(~scanf("%d%d",&n,&m)&&n&&m) 18 { 19 init(); 20 while(m--){ 21 int x,y,len; 22 scanf("%d%d%d",&x,&y,&len); 23 dis[x][y]=min(dis[x][y],len); 24 dis[y][x]=dis[x][y]; 25 } 26 for(int k=1;k<=n;k++)//要經過的點 27 for(int i=1;i<=n;i++) 28 for(int j=1;j<=n;j++) 29 dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); 30 printf("%d\n",dis[1][n]);//可以選任意兩點之間的距離 31 } 32 return 0; 33 } 34 Sample Input 2 35 5 14 36 2 2 262 37 5 3 403 38 4 2 456 39 1 5 289 40 3 1 1000 41 2 4 217 42 2 5 536 43 2 5 415 44 2 4 880 45 3 1 179 46 3 4 972 47 5 3 2 48 1 3 491 49 4 1 872 50 0 0 51 Sample Output 2 52 1813),迪杰斯特拉算法(解決單源最短路徑)
基本思想:每次找到離源點(如1號結點)最近的一個頂點,然后以該頂點為中心進行擴展,最終得到源點到其余所有點的最短路徑。
基本步驟:1.開容器v,儲存子節點、距離、花費;2、開數組dis記錄起始點到各點距離;3、進行n-1次松弛操作(先找出未標記點中離起始點最近的點,標記該點,然后求出該點子節點到起始點的最短距離(優先)與最短花費);4、輸出到終點的最短距離與花費;
1 //題意:求兩點之間最短路徑 2 #include<iostream> 3 #include<string.h> 4 #include<vector> 5 #include<algorithm> 6 #define N 999999999 7 using namespace std; 8 struct node{ 9 int er,len,cost; 10 }; 11 vector<node>v[1111]; 12 int main() 13 { 14 int n,m; 15 while(~scanf("%d%d",&n,&m)&&n&&m) 16 { 17 int dis[1111],spend[1111]; 18 bool vis[1111]; 19 node tmp; 20 int x,y; 21 for(int i=0;i<1111;i++) 22 v[i].clear(); 23 while(m--){ 24 scanf("%d%d%d%d",&x,&y,&tmp.len,&tmp.cost); 25 tmp.er=x; 26 v[y].push_back(tmp); 27 tmp.er=y; 28 v[x].push_back(tmp); 29 } 30 scanf("%d%d",&x,&y);//起點和終點 31 for(int i=1;i<=n;i++){ 32 vis[i]=0; 33 dis[i]=spend[i]=N; 34 } 35 for(int i=0;i<v[x].size();i++){ 36 dis[v[x][i].er]=v[x][i].len; 37 spend[v[x][i].er]=v[x][i].cost; 38 } 39 vis[x]=1; 40 for(int k=1;k<=n-1;k++) 41 { 42 int id,mi=N; 43 for(int i=1;i<=n;i++){ 44 if(!vis[i]&&dis[i]<mi){//查詢并記錄離x最近的點 45 id=i;mi=dis[i]; 46 } 47 } 48 vis[id]=1;//標記過的點已經是最短 49 for(int i=0;i<v[id].size();i++) 50 { 51 int vv=v[id][i].er; 52 if(!vis[vv]&&dis[vv]>dis[id]+v[id][i].len)//未標記、直接距離大于通過id點的距離 53 dis[vv]=dis[id]+v[id][i].len, 54 spend[vv]=spend[id]+v[id][i].cost; 55 else if(!vis[vv]&&dis[vv]==dis[id]+v[id][i].len&&spend[vv]>spend[vv]+v[id][i].cost)//未標記、距離相等找花費更小的 56 spend[vv]=spend[id]+v[id][i].cost; 57 } 58 } 59 printf("%d %d\n",dis[y],spend[y]); 60 } 61 return 0; 62 } 63 /* 64 3 2 65 1 2 5 6 66 2 3 4 5 67 1 3 68 3 2 69 1 3 5 6 70 2 1 3 5 71 3 2 72 9 11 73 8 11 74 */4),Bellman-Ford算法(解決負權邊,解決單源最短路徑,前幾種方法不能求含負權邊的圖)::時間復雜度O(nm),空間復雜度O(m)
主要思想:對所有的邊進行n-1輪松弛操作,因為在一個含有n個頂點的圖中,任意兩點之間的最短路徑最多包含n-1邊。換句話說,第1輪在對所有的邊進行松弛后,得到的是從1號頂點只能經過一條邊到達其余各定點的最短路徑長度。第2輪在對所有的邊進行松弛后,得到的是從1號頂點只能經過兩條邊到達其余各定點的最短路徑長度,......
以下是圖示:
此外,Bellman_Ford還可以檢測一個圖是否含有負權回路:POJ1860
1 /* 2 題意:有多種匯幣,匯幣之間可以交換,這需要手續費,當你用100A幣 3 交換B幣時,A到B的匯率是29.75,手續費是0.39,那么你可以得到 4 (100 - 0.39) * 29.75 = 2963.3975 B幣。問s幣的金額經過交換最終 5 得到的s幣金額數能否增加 6 貨幣的交換是可以重復多次的,所以我們需要找出是否存在 7 正權回路,且最后得到的s金額是增加的 8 怎么找正權回路呢?(正權回路:在這一回路上,頂點的權值能不斷增加即能一直進行松弛) 9 分析: 10 反向利用Bellman-Ford算法 11 單源最短路徑算法,因為題目可能存在負邊,所以用Bellman Ford算法, 12 原始Bellman Ford可以用來求負環,這題需要改進一下用來求正環 13 一種貨幣就是圖上的一個點 14 一個“兌換點”就是圖上兩種貨幣之間的一個兌換環,相當于“兌換方式”M的個數,是雙邊 15 唯一值得注意的是權值,當擁有貨幣A的數量為V時,A到A的權值為K,即沒有兌換 16 而A到B的權值為(V-Cab)*Rab 17 本題是“求最大路徑”,之所以被歸類為“求最小路徑”是因為本題題恰恰 18 與bellman-Ford算法的松弛條件相反,求的是能無限松弛的最大正權路徑, 19 但是依然能夠利用bellman-Ford的思想去解題。 20 因此初始化d(S)=V 而源點到其他店的距離(權值)初始化為無窮小(0), 21 當s到其他某點的距離能不斷變大時,說明存在最大路徑 22 */ 23 #include<iostream> 24 #include<string.h> 25 #include<algorithm> 26 using namespace std; 27 struct node 28 { 29 int x,y; 30 double r,c; 31 }num[222]; 32 int n,m,s,ans; 33 double v; 34 void add(int x,int y,double r,double c) 35 { 36 num[ans].x=x; 37 num[ans].y=y; 38 num[ans].r=r; 39 num[ans].c=c; 40 ans++; 41 } 42 bool bellon() 43 { 44 double dis[111]; 45 for(int i=0;i<=n;i++) 46 dis[i]=0; 47 dis[s]=v; 48 for(int j=1;j<n;j++) 49 { 50 bool flag=0; 51 for(int i=0;i<ans;i++){ 52 if(dis[num[i].y]<(dis[num[i].x]-num[i].c)*num[i].r) 53 dis[num[i].y]=(dis[num[i].x]-num[i].c)*num[i].r,flag=1; 54 } 55 if(!flag) 56 return 0; 57 } 58 for(int i=0;i<ans;i++) 59 if(dis[num[i].y]<(dis[num[i].x]-num[i].c)*num[i].r) 60 return 1; 61 return 0; 62 } 63 int main() 64 { 65 int a,b; 66 ans=0; 67 double ra,rb,ca,cb; 68 scanf("%d%d%d%lf",&n,&m,&s,&v); 69 while(m--) 70 { 71 scanf("%d%d%lf%lf%lf%lf",&a,&b,&ra,&ca,&rb,&cb); 72 add(a,b,ra,ca); 73 add(b,a,rb,cb); 74 } 75 if(bellon()) 76 printf("YES\n"); 77 else 78 printf("NO\n"); 79 return 0; 80 }?
轉載于:https://www.cnblogs.com/hualian/p/11171370.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的【常用算法总结——最短路径四种方法】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jq封装插件
- 下一篇: 强大的DataGrid组件[1]【转】