NO.94十六届蓝桥杯备战|图论根本-单源最短路|常规dijkstra|堆优化dijkstra| ...

打印 上一主题 下一主题

主题 1807|帖子 1807|积分 5421

在图G中,假设                                             v                            i                                       v_{i}                  vi​和                                             v                            j                                       v_{j}                  vj​为图中的两个极点,那么                                             v                            i                                       v_{i}                  vi​到                                             v                            j                                       v_{j}                  vj​路径上所颠末边的权值之和就称为带权路径⻓度。
由于                                             v                            i                                       v_{i}                  vi​到                                             v                            j                                       v_{j}                  vj​的路径可能有多条,将带权路径⻓度最短的那条路径称为最短路径。
最短路⼀般分为两类:


  • 单源最短路,即图中⼀个极点到其它各极点的最短路径。
  • 多源最短路,即图中每对极点间的最短路径

常规版dijkstra算法

Dijkstra算法是基于贪⼼思想的单源最短路算法,求解的是"⾮负权图"上单源最短路径

常规版dijkstra算法流程:


  • 准备⼯作:

    • 创建⼀个⻓度为 n 的 dist 数组,此中 dist 表⽰从出发点到 i 结点的最短路;
    • 创建⼀个⻓度为 n 的 bool 数组 st ,此中 st 表⽰ i 点是否已经确定了最短路。

  • 初始化: dist[1] = 0 ,其余结点的 dist 值为⽆穷⼤,表⽰还没有找到最短路。
  • 重复:在所有没有确定最短路的点中,找出最短路⻓度最⼩的点 u 。打上确定最短路的标记,然后对 u 的出边进⾏松弛操纵;
  • 重复上述操纵,直到所有点的最短路都确定
P3371 【模板】单源最短路径(弱化版) - 洛谷

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef pair<int, int> PII;
  4. typedef long long LL;
  5. const int N = 1e4 + 10, INF = 2147483647;
  6. int n, m, s;
  7. vector<PII> edges[N];
  8. LL dist[N];
  9. bool st[N];
  10. void dijkstra()
  11. {
  12.     //初始化
  13.     for (int i = 0; i <= n; i++) dist[i] = INF;
  14.     dist[s] = 0;
  15.     for (int i = 1; i < n; i++)
  16.     {
  17.         //找出没有确定最短路的点中,当前最短路最小的点
  18.         int a = 0;
  19.         for (int j = 1; j <= n; j++)
  20.             if (!st[j] && dist[j] < dist[a])
  21.                 a = j;
  22.         //打上标记,松弛
  23.         st[a] = true;
  24.         for (auto& t : edges[a])
  25.         {
  26.             int b = t.first, c = t.second;
  27.             if (dist[a] + c < dist[b])
  28.             {
  29.                 dist[b] = dist[a] + c;
  30.             }
  31.         }
  32.     }
  33.     for (int i = 1; i <= n; i++) cout << dist[i] << " ";
  34. }
  35. int main()
  36. {
  37.     ios::sync_with_stdio(false);
  38.     cin.tie(0);
  39.     cin >> n >> m >> s;
  40.     for (int i = 1; i <= m; i++)
  41.     {
  42.         int u, v, w; cin >> u >> v >> w;
  43.         edges[u].push_back({v, w});
  44.     }
  45.     dijkstra();
  46.     return 0;
  47. }
复制代码
堆优化版dijkstra算法

在常规版的根本上,⽤优先级队列去维护待确定最短路的结点。
堆优化版的dijkstra算法流程:


  • 准备⼯作:

    • 创建⼀个⻓度为 n 的 dist 数组,此中 dist 表⽰从出发点到 i 结点的最短路;
    • 创建⼀个⻓度为 n 的 bool 数组 st ,此中 st 表⽰ i 点是否已经确定了最短路;
    • 创建⼀个⼩根堆,维护更新后的结点。(也就是需要确定最短路的结点)

  • 初始化: dist[1] = 0 ,然后将 {0, s} 加到堆⾥;其余结点的 dist 值为⽆穷⼤,表⽰还没有找到最短路。
  • 重复:弹出堆顶元素,假如该元素已经标记过,就跳过;假如没有标记过,打上标记,进⾏松弛操纵。
  • 重复上述操纵,直到堆⾥⾯没有元素为⽌
P4779 【模板】单源最短路径(标准版) - 洛谷

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef pair<int, int> PII;
  4. const int N = 1e5 + 10;
  5. int n, m, s;
  6. vector<PII> edges[N];
  7. int dist[N];
  8. bool st[N];
  9. priority_queue<PII, vector<PII>, greater<PII>> heap;
  10. void dijkstra()
  11. {
  12.     //初始化
  13.     memset(dist, 0x3f, sizeof dist);
  14.     dist[s] = 0;
  15.     heap.push({0, s});
  16.     while (heap.size())
  17.     {
  18.         auto t = heap.top(); heap.pop();
  19.         int a = t.second;
  20.         if (st[a]) continue;
  21.         st[a] = true;
  22.         
  23.         for (auto& x : edges[a])
  24.         {
  25.             int b = x.first, c = x.second;
  26.             if (dist[a] + c < dist[b])
  27.             {
  28.                 dist[b] = dist[a] + c;
  29.                 heap.push({dist[b], b});
  30.             }
  31.         }
  32.     }
  33.     for (int i = 1; i <= n; i++) cout << dist[i] << " ";
  34. }
  35. int main()
  36. {
  37.     ios::sync_with_stdio(false);
  38.     cin.tie(0);
  39.     cin >> n >> m >> s;
  40.     for (int i = 1; i <= m; i++)
  41.     {
  42.         int a, b, c; cin >> a >> b >> c;
  43.         edges[a].push_back({b, c});
  44.     }
  45.     dijkstra();
  46.    
  47.     return 0;
  48. }
复制代码
bellman-ford算法

Bellman‒Ford算法(之后简称BF算法)是⼀种基于松弛操纵的最短路算法,可以求出有负权的图的最短路,并可以对最短路不存在的情况进⾏判定。
算法核⼼思想:不断尝试对图上每⼀条边进⾏松弛,直到所有的点都⽆法松弛为⽌

Bellman‒Ford算法流程:


  • 准备⼯作:

    • 创建⼀个⻓度为 n 的 dist 数组,此中 dist 表⽰从出发点到 i 结点的最短路。

  • 初始化: dist[1] = 0 ,其余结点的 dist 值为⽆穷⼤,表⽰还没有找到最短路。
  • 重复:每次都对所有的边进⾏⼀次松弛操纵。
  • 重复上述操纵,直到所有边都不需要松弛操纵为⽌
    最多重复多少轮松弛操纵?
    在最短路存在的情况下,由于⼀次松弛操纵会使最短路的边数⾄少增长1,⽽最短路的边数最多为n-1。因此整个算法最多执⾏轮松弛操纵n-1轮。故总时间复杂度为O(nm)
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef pair<int, int> PII;
  4. typedef long long LL;
  5. const int N = 1e4 + 10, INF = 2147483647;
  6. int n, m, s;
  7. vector<PII> edges[N];
  8. LL dist[N];
  9. void bf()
  10. {
  11.     //初始化
  12.     for (int i = 0; i <= n; i++) dist[i] = INF;
  13.     dist[s] = 0;
  14.     bool flg = false;
  15.     for (int i = 1; i < n; i++)
  16.     {
  17.         flg = false;
  18.         
  19.         for (int u = 1; u <= n; u++)
  20.         {
  21.                         if (dist[u] == INF) continue;
  22.             for (auto& t : edges[u])
  23.             {
  24.                 int v = t.first, w = t.second;
  25.                 if (dist[u] + w < dist[v])
  26.                 {
  27.                     dist[v] = dist[u] + w;
  28.                     flg = true;
  29.                 }
  30.             }
  31.         }
  32.         if (flg == false) break;
  33.     }
  34.     for (int i = 1; i <= n; i++) cout << dist[i] << " ";
  35. }
  36. int main()
  37. {
  38.     ios::sync_with_stdio(false);
  39.     cin.tie(0);
  40.     cin >> n >> m >> s;
  41.     for (int i = 1; i <= m; i++)
  42.     {
  43.         int a, b, c; cin >> a >> b >> c;
  44.         edges[a].push_back({b, c});
  45.     }
  46.     bf();
  47.     return 0;
  48. }
复制代码
spfa算法

spfa即Shortest Path Faster Algorithm,本质是⽤队列对BF算法做优化。
在BF算法中,很多时候我们并不需要那么多⽆⽤的松弛操纵:


  • 只有上⼀次被松弛的结点,它的出边,才有可能引起下⼀次的松弛操纵;
  • 因此,假如⽤队列来维护"哪些结点可能会引起松弛操纵",就能只访问必要的边了,时间复杂度就能低落。
    spfa算法流程:
  • 准备⼯作:

    • 创建⼀个⻓度为 n 的 dist 数组,此中 dist 表⽰从出发点到 i 结点的最短路;
    • 创建⼀个⻓度为 n 的 bool 数组 st ,此中 st 表⽰ i 点是否已经在队列中。

  • 初始化:标记 dist[1] = 0 ,同时 1 ⼊队;其余结点的 dist 值为⽆穷⼤,表⽰还没有找到最短路。
  • 重复:每次拿出队头元素 u ,去掉在队列中的标记,同时对 u 所有相连的点 v 进⾏松弛操纵。
    假如结点 v 被松弛,那就放进队列中。
  • 重复上述操纵,直到队列中没有结点为⽌。
    注意注意注意:
    固然在⼤多数情况下spfa跑得很快,但其最坏情况下的时间复杂度为O(nm) 。将其卡到这个复杂度也是不难的,所以在没有负权边时最好使⽤Dijkstra算法。
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef pair<int, int> PII;
  4. typedef long long LL;
  5. const int N = 1e4 + 10, INF = 2147483647;
  6. int n, m, s;
  7. vector<PII> edges[N];
  8. LL dist[N];
  9. bool st[N]; //标记哪些节点在队列中
  10. void spfa()
  11. {
  12.     //初始化
  13.     for (int i = 0; i <= n; i++) dist[i] = INF;
  14.     queue<int> q;
  15.     q.push(s);
  16.     dist[s] = 0;
  17.     st[s] = true;
  18.     while (q.size())
  19.     {
  20.         auto a = q.front(); q.pop();
  21.         st[a] = false;
  22.         for (auto& t : edges[a])
  23.         {
  24.             int b = t.first, c = t.second;
  25.             if (dist[a] + c < dist[b])
  26.             {
  27.                 dist[b] = dist[a] + c;
  28.                 if (!st[b])
  29.                 {
  30.                     q.push(b);
  31.                     st[b] = true;
  32.                 }
  33.             }
  34.         }
  35.     }
  36.     for (int i = 1; i <= n; i++) cout << dist[i] << " ";
  37. }
  38. int main()
  39. {
  40.     ios::sync_with_stdio(false);
  41.     cin.tie(0);
  42.     cin >> n >> m >> s;
  43.     for (int i = 1; i <= m; i++)
  44.     {
  45.         int a, b, c; cin >> a >> b >> c;
  46.         edges[a].push_back({b, c});
  47.     }
  48.     spfa();
  49.     return 0;
  50. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

道家人

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表