图上最短路(Dijkstra, spfa)

单源最短路径

题目描述

如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入输出格式

输入格式:

第一行包含三个整数N、M、S,分别表示点的个数、有向边的个数、出发点的编号。

接下来M行每行包含三个整数Fi、Gi、Wi,分别表示第i条有向边的出发点、目标点和长度。

 

输出格式:

一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为2147483647)

输入输出样例

输入样例

 

输出样例

 

单源最短路(Single-Source Shortest Paths, SSSP)

先上Dijkstra算法

Dikstra 算法适用于边权为正的情况。主要想法是:将一个点走一步能到达的所有结点都放进队列里,并从队列里选择源点到该点路径最短的结点出队。这样就保证出队的结点一定是源点到该点的最短路。那么就能确定放进队列的每一个结点不仅要有该结点的编号,也要有源点到该结点的距离,所以可以用结构体来实现。队列中出队的必须是最小的,那么就可以用优先队列实现。所以开一个结构体优先队列。

 1 #include<cstdio>
 2 #include<cmath>
 3 #include<iostream>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<vector>
 8 using namespace std;
 9 const int maxn = 1e4 + 5;
10 const int INF = 2147483647;
11 struct Grap
12 {
13     int num, cost;    //num点编号,cost到该点距离 
14     bool operator < (const Grap& other)const    
15     /*优先队列出队的原本是最大的,而我们期望的是最小的,所
16     以重载小于号,不仅要兼容结构体,还要使逻辑相反 */ 
17     {
18         return cost > other.cost;
19     }
20 }; 
21 vector<int>v[maxn];
22 vector<int>c[maxn];
23 int n, m, k, vis[maxn], dis[maxn];
24 void dijkstra(int k)
25 {
26     for(int i = 0; i < maxn; ++i)    
27         dis[i] = INF;
28     /*初始化设为无穷,同时也代表了源点无法到达的点
29     的最短路径长度就是无穷*/
30     memset(vis, 0, sizeof(vis));
31     priority_queue<Grap>q;
32     q.push((Grap){k, 0});
33     dis[k] = 0;
34     while(!q.empty())
35     {
36         Grap now = q.top(); q.pop();
37         int node = now.num;
38         if(vis[node]) continue;
39         /*如果该点已经出队,那到这个点的路径长度一定是最短路。为了防止
40         结点的重复扩展,如果发现新取出来的结点曾经被取出来过,应该直接
41         把它扔掉,所以开一个数组记录。 */
42         vis[node] = 1;
43         for(int i = 0; i < v[node].size(); ++i)
44         {
45             if(dis[v[node][i]] > dis[node] + c[node][i])
46             {
47                 dis[v[node][i]] = dis[node] + c[node][i];
48                 
49                 q.push((Grap){v[node][i], dis[v[node][i]]});
50             }
51         }
52     }
53 }
54 int main()
55 {
56     scanf("%d%d%d", &n, &m, &k);
57     for(int i = 0; i < m; ++i)
58     {
59         int a, b, cost; scanf("%d%d%d", &a, &b, &cost);
60         v[a].push_back(b); c[a].push_back(cost);    //用vector建图 
61     }
62     dijkstra(k);
63     for(int i = 1; i <= n; ++i) printf("%d%s",dis[i], i == n ? "\n" : " ");
64     return 0;
65 }

再上一个spfa算法

spfa算法不仅可以求最短路,也可以判断一个图中存不存在负圈。在这个算法中,一个结点可能多次入队(出队),最多入队 n 次(结点个数次),而当超过 n 次时,就证明一定存在负圈。

 1 #include <cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<queue>
 5 #include<vector>
 6 #include<cstring>
 7 #include<algorithm>
 8 using namespace std;
 9 vector<int>v[10005], c[10005];
10 const int INF = 2147483647;
11 int dis[10005];
12 bool vis[10005];
13 void spfa(int a)    
14 {
15     for(int i = 0; i < 10005; ++i) dis[i] = INF;
16     dis[a] = 0;
17     memset(vis, 0, sizeof(vis));
18     queue<int>q;q.push(a);
19     while(!q.empty())
20     {
21         int now = q.front();q.pop();
22         vis[now] = 0;    //出队后去除标记 
23         for(int i = 0; i < (int)v[now].size(); ++i)
24         {
25             if(dis[now] + c[now][i] < dis[v[now][i]])
26             {
27                 dis[v[now][i]] = dis[now] + c[now][i];
28                 if(!vis[v[now][i]])
29                     {q.push(v[now][i]); vis[v[now][i]] = 1;}
30             }
31         }
32     }
33 }
34 int main()
35 {
36     int n, m, s;
37     scanf("%d%d%d", &n, &m, &s);
38     for(int i = 0; i < m; ++i)
39     {
40         int f, g, w;
41         scanf("%d%d%d", &f, &g, &w);
42         v[f].push_back(g);c[f].push_back(w);    
43     }
44     spfa(s);
45     for (int i = 1; i <= n; i++) printf("%d%c", dis[i], i == n ? '\n' : ' ');
46 }
原文地址:https://www.cnblogs.com/mrclr/p/8150866.html