zoukankan      html  css  js  c++  java
  • 最短路径-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!
    "; //存在负环
    101     }
    102 
    103     return 0;
    104 }
    Spfa邻接矩阵
  • 相关阅读:
    git apply、git am打补丁.diff 和 .patch【转】
    RK3288 GPIO 输出问题【转】
    [RK3288][Android6.0] 调试笔记 --- 通用GPIO驱动控制LED【转】
    [RK3288][Android6.0] 调试笔记 --- 系统识别不同硬件版本方法【转】
    Android驱动开发之earlysuspend睡眠模式--实现代码【转】
    触摸屏唤醒实现【转】
    强加密RNGCryptoServiceProvider
    java中有类似C#里ref或out的功能吗?
    mysql中char与varchar的区别分析
    JVM再了解了解
  • 原文地址:https://www.cnblogs.com/graytido/p/10587084.html
Copyright © 2011-2022 走看看