C++ STL入門
阿新 • • 發佈:2020-09-08
求一個圖的最短路徑。
一、多源最短路
1.Floyed-Wallsahll演算法
借用動態規劃的思想,是列舉中轉點,其中核心語句的原型是mp[i][k][k-1]+mp[k][j][k-1]<=mp[i][j][k],只不過因為最後的k,k-1沒什麼用,就壓縮成了二維。
程式碼:
1 for(int k=1;k<=n;k++)//k迴圈一定要在外面 2 { 3 for(int i=1;i<=n;i++) 4 { 5 for(int j=1;j<=n;j++) 6 { 7if(mp[i][j]>mp[i][k]+mp[k][j]) 8 { 9 mp[i][j]=mp[i][k]+mp[k][j];//核心演算法語句 10 } 11 } 12 } 13 }
藉助Floyed演算法,加入path[][]陣列,可以進行路徑的儲存,最終實現路徑輸出。程式碼如下:
1 #include<iostream> 2 #include<cstdio> 3View Codeusing namespace std; 4 const int maxn=1000; 5 const int inf=0x3f3f3f3f; 6 int path[maxn][maxn]; 7 int mp[maxn][maxn]; 8 int n,m; 9 void Floyed()//Floyed演算法核心語句 10 { 11 int i,j,k; 12 for(i=1;i<=n;i++) 13 { 14 for(j=1;j<=n;j++) 15 { 16 if(mp[i][j]<inf&&i!=j)17 path[i][j]=j;//記錄能到達的路徑 18 else 19 path[i][j]=-1; 20 } 21 } 22 for(k=1;k<=n;k++) 23 { 24 for(i=1;i<=n;i++) 25 { 26 for(j=1;j<=n;j++) 27 { 28 if(mp[i][j]>mp[i][k]+mp[k][j]) 29 { 30 mp[i][j]=mp[i][k]+mp[k][j]; 31 path[i][j]=path[i][k];//更新路徑 32 } 33 } 34 } 35 } 36 } 37 void Output(int start,int end)//最短路輸出 38 { 39 int next=start; 40 printf("%d",start); 41 while(next!=end) 42 { 43 printf("-->"); 44 printf("%d",path[next][end]); 45 next=path[next][end]; 46 } 47 cout<<endl; 48 } 49 int main() 50 { 51 cin>>n>>m; 52 for(int i=1;i<=n;i++) 53 { 54 for(int j=1;j<=n;j++) 55 { 56 if(i==j) 57 mp[i][j]=0; 58 else 59 mp[i][j]=inf; 60 } 61 } 62 int a,b,c; 63 for(int i=1;i<=m;i++) 64 { 65 scanf("%d%d%d",&a,&b,&c); 66 mp[a][b]=c; 67 mp[b][a]=c; 68 } 69 Floyed(); 70 int Qa,Qb; 71 while(cin>>Qa>>Qb) 72 { 73 cout<<mp[Qa][Qb]<<endl; 74 Output(Qa,Qb); 75 } 76 return 0; 77 }
二、單源最短路
1.Dijkstra演算法
藉助貪心演算法的思想。
主要用來計算一個節點到其他所有點的最短路徑。以起點為中心點向外拓展。
該演算法的要求:圖中不存在負權邊。
其中典型的操作鬆弛。首先用dis[]陣列記錄源點到其他所有點的距離並標記正無窮,然後從源點開始,比如dis[3]表示1到3的距離,此時比較dis[3]和dis[2]+mp[2][3],即如果此時1到3的距離通過點2鬆弛如果更小,那麼更新,其他依次操作,直到所有點都更新完成。
程式碼:
1 int book[50],min,u,v,i,j; 2 for(i=1;i<=n;i++)//把距離改為源點到所有可直接連通點的距離 3 { 4 dis[i]=map[1][i]; 5 } 6 for(i=1;i<=n;i++)//標記 7 { 8 book[i]=0; 9 } 10 book[1]=1;//1已經遍歷過 11 for(i=1;i<=n-1;i++)//進行n-1次操作 12 {//操作是尋找距離1號最近的頂點 13 min=din; 14 for(j=1;j<=n;j++)//尋找可以更新的點 15 { 16 if(book[j]==0&&dis[j]<min) 17 { 18 min=dis[j]; 19 u=j; 20 } 21 } 22 book[u]=1; 23 for(v=1;v<=n;v++) 24 { 25 if(map[u][v]<din) 26 { 27 if(dis[v]>dis[u]+map[u][v]) 28 { 29 dis[v]=dis[u]+map[u][v]; 30 } 31 } 32 } 33 }
2.Bellman_Ford演算法
採用“鬆弛”的策略,從原點到每個點的方案作對比,逐步更新,更新n(頂點)-1次後,一定全部更新完畢,再次判斷是否可更新,如果還可更新,則一定存在負權迴路。
程式碼如下:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int inf=0x3f3f3f3f; 5 const int maxn=1e5; 6 struct Edge 7 { 8 int u,v,w; 9 }; 10 Edge e[maxn]; 11 int dis[maxn],pre[maxn]; 12 int n,m,s;//n為點數,m為通路條數,s為源點 13 14 bool Bellman_Ford() 15 { 16 for(int i=1;i<=n;i++)//初始化源點到其他各個點的距離 17 { 18 if(i==s) 19 dis[i]=0; 20 else 21 dis[i]=inf; 22 } 23 for(int i=1;i<=n-1;i++)//核心程式碼,“鬆弛”操作 24 { 25 for(int j=1;j<=m;j++) 26 { 27 if(dis[e[j].v]>dis[e[j].u]+e[j].w) 28 { 29 dis[e[j].v]=dis[e[j].u]+e[j].w; 30 pre[e[j].v]=e[j].u;//前驅路線更新 31 } 32 } 33 } 34 bool flag=true;//判斷是否含有負權迴路的標誌 35 for(int i=1;i<=m;i++) 36 { 37 if(dis[e[i].v]>dis[e[i].u]+e[i].w) 38 { 39 flag=false; 40 break; 41 } 42 } 43 return flag; 44 } 45 void Output_path(int s)//路徑輸出的函式 46 { 47 while(s!=pre[s]) 48 { 49 printf("%d",s); 50 printf("-->"); 51 s=pre[s]; 52 } 53 if(s==pre[s]) 54 cout<<s<<endl; 55 } 56 int main() 57 { 58 cin>>n>>m; 59 cin>>s; 60 for(int i=1;i<=n;i++) 61 { 62 scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); 63 } 64 pre[s]=s; 65 bool flag; 66 flag=Bellman_Ford(); 67 if(flag) 68 { 69 for(int i=1;i<=n;i++) 70 { 71 printf("%d\n",dis[i]); 72 Output_path(i); 73 } 74 } 75 else 76 printf("圖中含有負權迴路。\n"); 77 return 0; 78 }View Code