Loading

最短路径-Dijkstra+Floyd+Spfa

Dijkstra算法:

Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。注意该算法要求图中不存在负权边。

问题描述:在无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点 V0 到其余各点的最短路径。(单源最短路径)

算法的基本思想是:每次找到离源点(上面例子的源点就是 1 号顶点)最近的一个顶点,然后以该顶点为中心进行扩展,最终得到源点到其余所有点的最短路径。基本步骤如下:

  • 将所有的顶点分为两部分:已知最短路程的顶点集合 P 和未知最短路径的顶点集合 Q。最开始,已知最短路径的顶点集合 P 中只有源点一个顶点。我们这里用一个vis[i]数组来记录哪些点在集合 P 中。例如对于某个顶点 i,如果vis[i]为 1 则表示这个顶点在集合 P 中,如果 vis [i]为 0 则表示这个顶点在集合 Q 中。
  • 设置源点 s 到自己的最短路径为 0 即 dis=0。若存在源点有能直接到达的顶点 i,则把 dist[ i ]设为 e[s][i]。同时把所有其它(源点不能直接到达的)顶点的最短路径为设为 ∞。
  • 在集合 Q 的所有顶点中选择一个离源点 s 最近的顶点 u(即 dist[u]最小)加入到集合 P。并考察所有以点 u 为起点的边,对每一条边进行松弛操作。例如存在一条从 u 到 v 的边,那么可以通过将边 u->v 添加到尾部来拓展一条从 s 到 v 的路径,这条路径的长度是 dist[u]+e[u][v]。如果这个值比目前已知的 dist[v]的值要小,我们可以用新值来替代当前 dist[v]中的值。
  • 重复第 3 步,如果集合 Q 为空,算法结束。最终 dist 数组中的值就是源点到所有顶点的最短路径。

Floyd算法:

Floyd算法又称为插点法,理解起来也很方便,复杂度为o(n3),如要从结点1到结点n,可以由1直通n,再逐渐向其中插入其他结点作为中转点,不断更新在插入结点后的最短路径。

代码也很简洁,四行的算法。

 

 for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    for(int k=1;k<=n;k++)
    e[j][k]=min(e[j][i]+e[i][k],e[j][k]);

 

 Spfa:

SPFA的思路比较简单,网上的说法也比较统一,NOCOW和百度百科上都有。这里在网上找到讲的比较通俗易懂的:

*SPFA(Shortest Path Faster Algorithm) *是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算。 算法大致流程是用一个队列来进行维护。 初始时将源加入队列。 每次从队列中取出一个元素, 并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队。 直到队列为空时算法结束。 它可以在O(kE)的时间复杂度内求出源点到其他所有点的最短路径,可以处理负边。

SPFA 在形式上和BFS非常类似,不同的是BFS中一个点出了队列就不可能重新进入队列,但是SPFA中 一个点可能在出队列之后再次被放入队列,也就是一个点改进过其它的点之后,过了一段时间可能本 身被改进,于是再次用来改进其它的点,这样反复迭代下去。

判断有无负环:如果某个点进入队列的次数超过V次则存在负环(SPFA无法处理带负环的图)。

 其实吧...Spfa老被卡...最好还是用dijkstra吧

HDU-3790 最短路径(模板题)

Dijkstra版:

这个算法还能利用堆和优先队列进行优化

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 typedef unsigned long long ull;
 5 #define INF 0X3f3f3f3f
 6 const ll MAXN = 1e3 + 7;
 7 const ll mod = 1e9 + 7;
 8 //权值为正
 9 int n, m;
10 int vis[MAXN];
11 int dist[MAXN];
12 int a[MAXN][MAXN];
13 void Dijkstra(int x,int y)
14 {
15     for (int i = 1; i <= n; i++)
16     {
17         dist[i] = a[x][i];
18         vis[i] = 0;
19     }
20     vis[x] = 1;
21     int p;
22     for (int i = 1; i <= n; i++)
23     {
24         int minn = INF;
25         for (int j = 1; j <= n; j++)
26         {
27             if (!vis[j] && dist[j] < minn)
28             {   
29                 minn = dist[j];
30                 p = j;
31             }
32         }
33         vis[p] = 1;
34         for (int j = 1; j <= n; j++)
35             if (!vis[j] && dist[p] + a[p][j] < dist[j])
36                 dist[j] = dist[p] + a[p][j];//更新这个找到的距离最小的点所连的点的距离 
37     }
38 }
39 int main()
40 {
41     ios::sync_with_stdio(false);
42     while (cin >> n >> m && n && m)
43     {
44         memset(a,INF,sizeof(a));
45         for (int i = 0; i < m; i++)
46         {
47             int x, y, len;
48             cin >> x >> y >> len;
49             a[x][y] = len;
50             a[y][x] = len; //无向图
51         }
52         Dijkstra(1,n);
53         cout << dist[n] << endl;
54     }
55 }
Dijkstra邻接矩阵版本
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 typedef unsigned long long ull;
 5 #define INF 0x3f3f3f3f
 6 const ll MAXN = 1e3 + 7;
 7 const ll MOD = 1e9 + 7;
 8 const double pi = acos(-1);
 9 struct node
10 {
11     int v, c;
12     node(int a = 0, int b = 0) { v = a, c = b; }
13     bool operator<(const node &a) const
14     {
15         if (c == a.c)
16             return v < a.v;
17         else
18             return c > a.c;
19     }
20 };
21 struct Edge
22 {
23     int v, cost;
24     Edge(int _v = 0, int _cost = 0) { v = _v, cost = _cost; }
25 };
26 vector<Edge> G[MAXN];
27 bool vis[MAXN];
28 int dist[MAXN];
29 //点的编号从1开始
30 void Dijkstra(int n, int start)
31 {
32     memset(vis, false, sizeof(vis));
33     for (int i = 1; i <= n; i++)
34         dist[i] = INF;
35     priority_queue<node> que;
36     while (!que.empty())
37         que.pop();
38     dist[start] = 0;
39     que.push(node(start, 0));
40     node temp;
41     while (!que.empty())
42     {
43         temp = que.top();
44         que.pop();
45         int u = temp.v;
46         if (vis[u])
47             continue;
48         vis[u] = true;
49         for (int i = 0; i < G[u].size(); i++)
50         {
51             int v = G[temp.v][i].v;
52             int cost = G[u][i].cost;
53             if (!vis[v] && dist[v] > dist[u] + cost)
54             {
55                 dist[v] = dist[u] + cost;
56                 que.push(node(v, dist[v]));
57             }
58         }
59     }
60 }
61 void addedge(int u, int v, int w)
62 {
63     G[u].push_back(Edge(v, w));
64 }
65 void init(int n)
66 {
67     for (int i = 1; i <= n; i++)
68         G[i].clear();
69     return;
70 }
71 int main()
72 {
73     ios::sync_with_stdio(false);
74     cin.tie(0);
75     cout.tie(0);
76     int n, m;
77     while (cin >> n >> m && n && m)
78     {
79         init(n);
80         for (int i = 0; i < m; i++)
81         {
82             int x, y, len;
83             cin >> x >> y >> len;
84             addedge(x, y, len);
85             addedge(y, x, len);
86         }
87         Dijkstra(n, 1);
88         cout << dist[n] << endl;
89     }
90     return 0;
91 }
Dijkstra邻接表写法

Floyd版:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 typedef unsigned long long ull;
 5 #define INF 0X3f3f3f3f
 6 const ll MAXN = 1e3 + 7;
 7 const ll mod = 1e9 + 7;
 8 //可处理权值为负的情况
 9 int n,m;
10 int e[MAXN][MAXN];
11 void floyd()
12 {
13     for(int i=1;i<=n;i++)
14     for(int j=1;j<=n;j++)
15     for(int k=1;k<=n;k++)
16     e[j][k]=min(e[j][i]+e[i][k],e[j][k]);
17 }
18 int main()
19 {
20     while(cin>>n>>m&&n&&m)
21     {
22         memset(e,INF,sizeof(e));
23         for(int i=0;i<m;i++)
24         {
25             int a,b,c;
26             cin>>a>>b>>c;
27             e[a][b]=c;
28             e[b][a]=c;
29         }
30         floyd();
31         cout<<e[1][n]<<endl;
32     }
33     return 0;
34 }
Floyd版

 Spfa:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 typedef unsigned long long ull;
 5 #define INF 0x3f3f3f3f
 6 const ll MAXN = 1e5 + 7;
 7 const ll MOD = 1e9 + 7;
 8 const double pi = acos(-1);
 9 int dist[MAXN];
10 bool vis[MAXN];
11 int pre[MAXN]; //pre[x]记录的是起点为x的链表之首在数组p的位置(相当于头插法)
12 int n, m;
13 struct Edge
14 {
15     int to;  //边的终点
16     int w;   //边的权值
17     int pre; //同一个起点的上一个边在数组E里的位置
18     Edge(int _to = 0, int _w = 0, int _pre = 0) { to = _to, w = _w, pre = _pre; }
19 } E[MAXN]; //边的数目
20 void Spfa()
21 {
22     queue<int> q;
23     int g, j; //起点为1, 终点为end
24     int start = 1;
25     int end = n;
26     for (int i = 1; i <= n; i++)
27         dist[i] = INF;
28     dist[start] = 0;
29     q.push(start);
30     vis[start] = 1;
31     while (!q.empty())
32     {
33         g = q.front();
34         q.pop();
35         vis[g] = 0;
36         for (j = pre[g]; j != -1; j = E[j].pre)
37         {
38             if (dist[E[j].to] > E[j].w + dist[g]) //能够进行松弛
39             {
40                 dist[E[j].to] = E[j].w + dist[g];
41                 if (!vis[E[j].to]) //该节点v不在序列中
42                 {
43                     vis[E[j].to] = 1;
44                     q.push(E[j].to);
45                 }
46             }
47         }
48     }
49     return;
50 }
51 int main()
52 {
53     ios::sync_with_stdio(false);
54     cin.tie(0);
55     cout.tie(0);
56     while (cin >> n >> m && n + m)
57     {
58         memset(pre, -1, sizeof(pre));
59         int id = 0;
60         for (int i = 0; i < m; i++)
61         {
62             int x, y, len;
63             cin >> x >> y >> len;
64             E[id].to = y;
65             E[id].w = len;
66             E[id].pre = pre[x];
67             pre[x] = id;
68             id++;
69             //双向
70             E[id].to = x;
71             E[id].w = len;
72             E[id].pre = pre[y];
73             pre[y] = id;
74             id++;
75         }
76         Spfa();
77         cout << dist[n] << endl;
78     }
79     return 0;
80 }//未判负环(如果某个点进入队列的次数超过V次则存在负环
Spfa邻接表
  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 typedef long long ll;
  4 typedef unsigned long long ull;
  5 #define INF 0X3f3f3f3f
  6 const ll MAXN = 1e3 + 7;
  7 const ll mod = 1e9 + 7;
  8 int e[MAXN][MAXN];     //邻接矩阵
  9 bool vis[MAXN];        //标记数组
 10 int dist[MAXN];        //源点到顶点i的最短距离
 11 int path[MAXN];        //记录最短路的路径
 12 int enqueue_num[MAXN]; //记录入队次数
 13 int n;                 //顶点数
 14 int m;                 //边数
 15 int s;                 //源点
 16 bool SPFA()
 17 {
 18     memset(vis, 0, sizeof(vis));
 19     memset(enqueue_num, 0, sizeof(enqueue_num));
 20     for (int i = 1; i <= n; i++)
 21     {
 22         dist[i] = INF;
 23         path[i] = s;
 24     }
 25     queue<int> Q;
 26     Q.push(s);
 27     dist[s] = 0;
 28     vis[s] = 1;
 29     enqueue_num[s]++;
 30     while (!Q.empty())
 31     {
 32         int u = Q.front();
 33         Q.pop();
 34         vis[u] = 0;
 35         for (int v = 1; v <= n; v++)
 36         {
 37             if (e[u][v] != INF) //u与v直接邻接
 38             {
 39                 if (dist[u] + e[u][v] < dist[v]) //松弛操作
 40                 {
 41                     dist[v] = dist[u] + e[u][v];
 42                     path[v] = u;
 43                     if (!vis[v])
 44                     {
 45                         Q.push(v);
 46                         enqueue_num[v]++;
 47                         if (enqueue_num[v] >= n)//说明存在负环
 48                             return false;
 49                         vis[v] = 1;
 50                     }
 51                 }
 52             }
 53         }
 54     }
 55     return true;
 56 }
 57 /* 到某点的最短path及最短path的len */
 58 void Print()
 59 {
 60     for (int i = 1; i <= n; i++)
 61     {
 62         if (i != s)
 63         {
 64             int p = i;
 65             stack<int> st;
 66             cout << "顶点 " << s << " 到顶点 " << p << " 的最短路径是: ";
 67             while (s != p) //路径顺序是逆向的,所以先保存到栈
 68             {
 69                 st.push(p);
 70                 p = path[p];
 71             }
 72             cout << s;
 73             while (!st.empty()) //依次从栈中取出的才是正序路径
 74             {
 75                 cout << "--" << st.top();
 76                 st.pop();
 77             }
 78             cout << "    最短路径长度是:" << dist[i] << endl;
 79         }
 80     }
 81 }
 82 int main()
 83 {
 84     while (cin >> n >> m && n && m)
 85     {
 86         s=1;
 87         for (int i = 1; i <= n; i++)
 88             for (int j = 1; j <= n; j++)
 89                 e[i][j] = INF; //初始化e数组
 90         int u, v, w;
 91         for (int i = 0; i < m; i++)
 92         {
 93             cin >> u >> v >> w;
 94             e[u][v] = w;
 95             e[v][u] = w;
 96         }
 97         if (SPFA())
 98             cout<<dist[n]<<endl;
 99         else
100             cout << "Sorry,it have negative circle!\n"; //存在负环
101     }
102 
103     return 0;
104 }
Spfa邻接矩阵
posted @ 2019-03-24 10:00  GrayKido  阅读(320)  评论(0编辑  收藏  举报