1. 程式人生 > 實用技巧 >C++ STL入門

C++ STL入門

求一個圖的最短路徑。

一、多源最短路

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             {
 7
if(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>
 3
using 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 }
View Code

二、單源最短路

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