最短路径

Date:2019-06-18 14:14:31

单源最短路径

  • 即求图中某一顶点到其他各顶点的最短路径

Dijskra算法

  • 权值非负

伪码描述

 1 //G为图,全局变量;数组d为源点到达各个点的最短路径长度;s为初始顶点
 2 Dijskra (G, d[], s)
 3 {
 4     初始化;
 5     for(i<=n)
 6     {
 7         u = 使d[u]最小的还未被访问的定点的编号
 8         vis[u] = 1;
 9         for(从u出发能到达的所有顶点v)
10         {
11             if(vis[v]==0 && 以u为中介点使得s到v的距离比d[v]更近)
12                 优化d[v];
13                 令v的前驱pre[v]=u来记录最短路径
14         }
15     }
16 }

邻接矩阵实现

 1 const int M = 1e3, INF=1x3fffffff;
 2 int n, grap[M][M], vis[M]={0}, d[M];
 3 
 4 void Dijskra(int s) //s为起点
 5 {
 6     fill(d, d+M, INF);  //数组整体赋值
 7     fill(vis, vis+M, 0);
 8     d[s] = 0;   //s到自身为0
 9     for(int i=1; i<=n; i++)
10     {
11         int u=0, Min=INF;
12         for(int j=1; j<=n; j++) //寻找未访问的结点中最小的结点u
13             if(vis[j]==0 && d[j]<Min)
14             {                   //第一次执行该循环后,u = s;
15                 u = j;
16                 Min = d[j];
17             }
18         if(u == 0)  return ;    //图不连通
19         vis[u] = 1;
20         for(int v=1; v<=n; v++)
21         {
22             if(vis[v]==0 && grap[u][v]!=INF && d[u]+grap[u][v]<d[v])
23                 d[v] = d[u] + grap[u][v];
24         }
25     }
26 }

邻接表实现

 1 struct node
 2 {
 3     int v, w;
 4 };
 5 
 6 vector<node> adj[M];
 7 int d[M], vis[M], n;
 8 
 9 void Dijskra(int s)
10 {
11     fill(d, d+M, INF);
12     fill(vis,vis+M,0);
13     d[s] = 0;
14     for(int i=1; i<=n; i++)
15     {
16         int u=0, Min=INF;
17         for(int j=1; j<=n; j++)
18             if(vis[j]==0 && d[j]<Min)
19             {
20                 u = j;
21                 Min = d[j];
22             }
23         if(u == 0)  return;
24         vis[u] = 1;
25         for(int j=1; j<=n; j++)
26         {
27             int v = adj[u][j].v;
28             if(vis[v]==0 && d[u]+adj[u][j].w<d[v])
29                 d[v] = d[u]+adj[u][j].w;
30         }
31     }
32 }

多条最短路径求解第二尺度最优路径

 1 //Q1: 求代价最小的最短路径
 2 int cost[M][M]; //cost[u][v]表示u到v的代价
 3 int c[M]        //c[u]表示s到u的最少代价
 4 
 5 fill(c,c+M,INF);
 6 c[s]=0;
 7 for(int v=1; v<=n; v++)
 8 {
 9     if(vis[v]==0 && grap[u][v]!=INF)
10     {
11         if(d[u]+grap[u][v]<d[v])
12         {
13             d[v] = d[u] + grap[u][v];
14             c[c] = c[u] + cost[u][v];
15         }
16         else if(d[u]+grap[u][v]==d[v] && c[u]+cost[u][v]<c[v])
17             c[v] = c[u] + cost[u][v];
18     }
19 }
20 
21 //Q2:求点权最大的最短路径
22 int weight[M];
23 int w[M];
24 
25 fill(w,w+M, 0);
26 w[s]=weight[s];
27 for(int v=1; v<=n; v++)
28 {
29     if(vis[v]==0 && grap[u][v]!=INF)
30     {
31         if(d[u]+grap[u][v] < d[v])
32         {
33             d[v] = d[u] + grap[u][v];
34             w[v] = w[u] + weight[v];
35         }
36         else if(d[u]+grap[u][v]==d[v] && w[u]+weight[v]>w[v])
37             w[v] = w[u] + weight[v];
38     }
39 }
40 
41 //Q3:求最短路径的条数
42 int num[M];
43 
44 fill(num, num+M, 0);
45 num[s] = 1;
46 for(int v=1; v<=n; v++)
47 {
48     if(d[u]+grap[u][v] < d[v])
49     {
50         d[v] = d[u] + grap[u][v];
51         num[v] = num[u];
52     }
53     else if(d[u]+grap[u][v]==d[v])
54         num[v] += num[u];
55 }

算法实现

 1 /*
 2 Sample Input:
 3 //v, e, s
 4 6 8 0
 5 0 1 1
 6 0 3 4
 7 0 4 4
 8 1 3 2
 9 2 5 1
10 3 2 2
11 3 4 3
12 4 5 3
13 Sample Output:
14 0 1 5 3 4 6
15 */
16 
17 #include<cstdio>
18 #include<algorithm>
19 using namespace std;
20 const int M=1e3, INF=1e9;
21 int n,m,s,grap[M][M],vis[M],d[M],path[M];
22 
23 void Dijskra(int s)
24 {
25     fill(d, d+M, INF);
26     fill(vis,vis+M,0);
27     for(int i=0; i<n; i++)
28         path[i] = i;
29     d[s]=0;
30     for(int i=0; i<n; i++)
31     {
32         int u=-1, Min=INF;
33         for(int j=0; j<n; j++)
34         {
35             if(vis[j]==0 && d[j]<Min)
36             {
37                 u = j;
38                 Min = d[j];
39             }
40         }
41         if(u==-1)   return;
42         vis[u] = 1;
43         for(int v=0; v<n; v++)
44         {
45             if(vis[v]==0 && grap[u][v]!=INF && d[u]+grap[u][v]<d[v])
46             {
47                 d[v] = d[u] + grap[u][v];
48                 path[v] = u;
49             }
50         }
51     }
52 }
53 
54 void DFS(int s, int v)  //输出最短路径
55 {
56     if(s == v)
57     {
58         printf("%d ", s);
59         return;
60     }
61     DFS(s,path[v]);
62     printf("%d ", v);
63 }
64 
65 int main()
66 {
67 #ifdef    ONLINE_JUDGE
68 #else
69     freopen("Test.txt", "r", stdin);
70 #endif
71 
72     scanf("%d%d%d", &n,&m,&s);
73     fill(grap[0], grap[0]+M*M, INF);
74 
75     int u,v,w;
76     for(int i=0; i<m; i++)
77     {
78         scanf("%d%d%d", &u,&v,&w);
79         grap[u][v]=w;
80         grap[v][u]=w;
81     }
82     Dijskra(s);
83     //输出最短距离
84     for(int i=0; i<n; i++)
85         printf("%d ", d[i]);
86     printf("\n");
87     //输出最短路径
88     DFS(s,5);
89     printf("\n");
90 
91     return 0;
92 }

 

Dijskra算法+DFS

  • 含有多个标准尺度的最短路径
 1 //Dijskra:记录所有的最短路径
 2 vector<int> pre[M];
 3 
 4 void Dijskra(int s)
 5 {
 6     fill(d,d+M,INF);
 7     d[s] = 0;
 8     for(int i=0; i<n; i++)
 9     {
10         int u=-1, Min=INF;
11         for(int j=0; j<n; j++)
12             if(vis[j]==0 && d[j]<Min)
13             {
14                 u = j;
15                 Min = d[j];
16             }
17         if(u == -1) return;
18         vis[u] = 1;
19         for(int v=0; v<n; v++)
20         {
21             if(vis[v]==0 && grap[u][v]!=INF)
22             {
23                 if(d[u]+grap[u][v] < d[v])
24                 {
25                     d[v] = d[u] + grap[u][v];
26                     pre[v].clear();
27                     pre[v].push_back(u);
28                 }
29                 else if(d[u]+grap[u][v] == d[v])
30                     pre[v].push_back(u);
31             }
32         }
33     }
34 }
35 
36 //DFS:从所有的最短路径中找出第二标尺最优的路径
37 int optValue=0,cnt=0;   //最优值
38 vector<int> pre, optPath, tempPath;    //path为逆序,即v->s
39 
40 void DFS(int v)
41 {
42     if(v == s)      //s为pre递归树的叶子结点,也即图的起点
43     {
44         tempPath.push_back(s);
45 
46         int value=0;
47         //计算边权之和
48         for(int i=tempPath.size()-1; i>0; i--)
49         {
50             int id = tempPath[i];   idNext=tempPath[i-1];
51             value += v[id][idNext];
52         }
53         //计算点权之和
54         for(int i=tempPath.size()-1; i>=0; i--)
55         {
56             int id = tempPath[i];
57             value += w[id];
58         }
59         //计算路径条数
60         cnt++;
61 
62         //选择最优路径
63         if(value > optValue)
64         {
65             optValue = value;
66             optPath = tempPath;
67         }
68         tempPath.pop_back();
69         return ;
70     }
71 
72     tempPath.push_back(v);
73     for(int i=0; i<pre[v].size(); i++)
74         DFS(pre[v][i]);
75     tempPath.pop_back();
76 }

 

Bellman-Ford算法

  • 处理带有负权值的单源最短路径

邻接表实现

 1 //邻接表法
 2 int n,d[M],w[M][M];
 3 vector<int> grap[M];
 4 
 5 //判断有无负环并且给出最短路径
 6 bool Bellman(int s)
 7 {
 8     fill(d,d+M,INF);
 9     d[s] = 0;
10     for(int i=0; i<n-1; i++)
11     {
12         for(int u=0; u<n; u++)
13         {
14             for(int j=0; j<grap[u].size(); j++)
15             {
16                 int v = grap[u][j];
17                 if(d[u]+w[u][v] < d[v])
18                     d[v] = d[u] + w[u][v];
19             }
20         }
21     }
22 
23     //判断负环
24     for(int u=0; u<n; u++)
25     {
26         for(int j=0; j<grap[u].size(); j++)
27         {
28             int v = grap[u][j];
29             if(d[u]+w[u][v] < d[v])
30                 return false;
31         }
32     }
33     return true;
34 }

算法实现

 1 /*
 2 Sample Input:
 3 5 6 0 2
 4 1 2 1 5 3
 5 0 1 1
 6 0 2 2
 7 0 3 1
 8 1 2 1
 9 2 4 1
10 3 4 1
11 Sample Output:
12 2 4
13 */
14 
15 #include<cstdio>
16 #include<cstring>
17 #include<vector>
18 #include<set>
19 #include<algorithm>
20 using namespace std;
21 const int M=1e3,INF=1e9;
22 struct node
23 {
24     int v, dis;
25     node(int _v, int _dis) : v(_v), dis(_dis) {}
26 };
27 vector<node> adj[M];
28 int n,weight[M];
29 int d[M],w[M],t[M];
30 set<int> pre[M];
31 
32 void Bellman(int s)
33 {
34     fill(d, d+M, INF);
35     fill(t, t+M, 0);
36     fill(w, w+M, 0);
37     d[s] = 0;
38     w[s] = weight[s];
39     t[s] = 1;
40     for(int i=0; i<n-1; i++)
41     {
42         bool isLoose = true;
43         for(int u=0; u<n; u++)
44         {
45             for(int j=0; j<adj[u].size(); j++)
46             {
47                 int v = adj[u][j].v;
48                 int dis = adj[u][j].dis;
49                 if(d[u]+dis < d[v])
50                 {
51                     d[v] = d[u] + dis;
52                     w[v] = w[u] + weight[v];
53                     t[v] = t[u];
54                     pre[v].clear();
55                     pre[v].insert(u);
56                     isLoose = false;
57                 }
58                 else if(d[u]+dis == d[v])
59                 {
60                     if(w[u]+weight[v] > w[v])
61                         w[v] = w[u] + weight[v];
62                     pre[v].insert(u);           //BF算法会多次访问同一个结点,使用set可以去重
63                     t[v]=0;
64                     set<int>::iterator it;
65                     for(it=pre[v].begin(); it!=pre[v].end(); it++)
66                         t[v] += t[*it];     //重新计算最短路径条数
67                 }
68             }
69         }
70         if(isLoose)
71             break;  //不再松弛时退出
72     }
73 }
74 
75 int main()
76 {
77 #ifdef ONLINE
78 #else
79     freopen("Test.txt", "r", stdin);
80 #endif // ONLINE
81 
82     int m,s,v;
83     scanf("%d%d%d%d", &n,&m,&s,&v);
84     for(int i=0; i<n; i++)
85         scanf("%d", &weight[i]);
86     int v1,v2,c;
87     for(int i=0; i<m; i++)
88     {
89         scanf("%d%d%d", &v1,&v2,&c);
90         adj[v1].push_back(node(v2,c));
91         adj[v2].push_back(node(v1,c));
92     }
93     Bellman(s);
94     printf("%d %d\n", t[v], w[v]);
95 
96     return 0;
97 }

 

 

全源最短路径

  • 即求每对顶点间的最短路径

Floyd算法

  • 不能含有带负权值的回路

算法实现

 1 /*
 2 6 8
 3 0 1 1
 4 0 3 4
 5 0 4 4
 6 1 3 2
 7 2 5 1
 8 3 2 2
 9 3 4 3
10 4 5 3
11 */
12 #include<cstdio>
13 #include<algorithm>
14 using namespace std;
15 const int M=220,INF=1e9;
16 int n,m,dis[M][M];
17 
18 void Floyd()
19 {
20     for(int k=0; k<n; k++)
21         for(int i=0; i<n; i++)
22             for(int j=0; j<n; j++)
23                 if(dis[i][k]!=INF && dis[k][j]!=INF && dis[i][k]+dis[k][j]<dis[i][j])
24                     dis[i][j] = dis[i][k] + dis[k][j];
25 }
26 
27 int main()
28 {
29     freopen("Test.txt", "r", stdin);
30 
31     int u,v,w;
32     fill(dis[0],dis[0]+M*M,INF);
33     scanf("%d%d", &n,&m);
34     for(int i=0; i<n; i++)
35         dis[i][i]=0;
36     for(int i=0; i<m; i++)
37     {
38         scanf("%d%d%d",&u,&v,&w);
39         dis[u][v] = w;
40     }
41     Floyd();
42     for(int i=0; i<n; i++)
43     {
44         for(int j=0; j<n; j++)
45             printf("%d ", dis[i][j]);
46         printf("\n");
47     }
48 
49     return 0;
50 }

 

posted @ 2019-06-18 14:54  林東雨  阅读(889)  评论(0编辑  收藏  举报