dijkstra 算法笔记

dijkstra 的本质是贪心。
首先设 \(dis_i\) 为从 \(s\to i\) 的最短路(\(s\) 为起点),贪心过程其实就是,找到所有确定了最短路的点中最短路最短的点记为 \(i\),它的最短路一定是确定了的,然后将其松弛:

\[dis_j=\min\{dis_j,dis_i+w\} \]

\(j\)\(i\) 的相邻点,\(w\)\(i\to j\) 这条边的权值。
\(dis_i\) 初始值为正无穷。


于是就得到了代码:

#include <bits/stdc++.h>

using namespace std;
using Pii = pair<int, int>;

const int kMaxN = 2e5 + 5, kInf = 1e9 + 7;

int T, n, m, s, t, dis[kMaxN];
bool vis[kMaxN];
vector<Pii> e[kMaxN];

void dijkstra(int s, int n) {
  fill(vis + 1, vis + 1 + n, 0);
  fill(dis + 1, dis + 1 + n, kInf);  // 初始化。
  dis[s] = 0;
  for (int t = 1; t <= n; t++) {
    int u = -1;
    for (int i = 1; i <= n; i++) {
      if (!vis[i] && (u == -1 || dis[i] < dis[u])) {
        u = i;
      }  // 找到所有确定了最短路的点中最短路最短的点。
    }
    vis[u] = 1;            // 最短路确定。
    for (auto i : e[u]) {  // 松弛相邻的点。
      int v = i.first, w = i.second;
      if (dis[v] > dis[u] + w) {
        dis[v] = dis[u] + w;
      }
    }
  }
  return;
}

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  for (cin >> T; T-- && cin >> n >> m >> s >> t;) {
    for (int i = 1; i <= n; i++) {
      e[i].clear();
    }
    for (int i = 1, u, v, w; i <= m; i++) {
      cin >> u >> v >> w;
      e[u].emplace_back(v, w);
      e[v].emplace_back(u, w);
    }
    dijkstra(s, n);
    if (dis[t] == kInf) {
      cout << "NONE\n";
    } else {
      cout << dis[t] << "\n";
    }
  }  // 多组询问。
  return 0;
}

我们可以发现,这一段代码是没有必要的:

for (int i = 1; i <= n; i++) {
  if (!vis[i] && (u == -1 || dis[i] < dis[u])) {
    u = i;
  }
}

其实可以用堆优化,也就是 priority_queue 来做这个代码。
时间复杂度从 \(O(n^2)\) 变成了\(O((n+m)\log_{2}{n})\) 的时间复杂度。

#include <bits/stdc++.h>

using namespace std;
using Pii = pair<int, int>;

const int kMaxN = 2e5 + 5, kInf = 1e9 + 7;

int T, n, m, s, t, dis[kMaxN];
bool vis[kMaxN];
vector<Pii> e[kMaxN];

void dijkstra(int s, int n) {
  fill(vis + 1, vis + 1 + n, 0);
  fill(dis + 1, dis + 1 + n, kInf);            // 初始化数组。
  priority_queue<Pii, vector<Pii>, greater<Pii> > q;  // 堆优化。
  for (dis[s] = 0, q.emplace(0, s); !q.empty();) {
    Pii now = q.top();
    q.pop();
    int u = now.second;
    if (vis[u]) {
      continue;  // 找到所有确定了最短路的点中最短路最短的点。
    }
    vis[u] = 1;
    for (auto i : e[u]) {
      int v = i.first, w = i.second;
      if (dis[v] > dis[u] + w) {
        dis[v] = dis[u] + w;
        q.emplace(dis[v], v);
      }
    }
  }
  return;
}

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  for (cin >> T; T-- && cin >> n >> m >> s >> t;) {
    for (int i = 1; i <= n; i++) {
      e[i].clear();
    }
    for (int i = 1, u, v, w; i <= m; i++) {
      cin >> u >> v >> w;
      e[u].emplace_back(v, w);
      e[v].emplace_back(u, w);
    }
    dijkstra(s, n);
    if (dis[t] == kInf) {
      cout << "NONE\n";
    } else {
      cout << dis[t] << "\n";
    }
  }  // 多组询问。
  return 0;
}

dijkstra 的本质是通过寻找最短路最小的点,以此可以确定这个点的最短路,但是如果图中有负权边的话,最短路就不一定了,这种情况可以使用势能 dijkstra 或者 spfa。


例题:

posted @ 2023-07-06 18:33  liruixiong0101  阅读(27)  评论(0编辑  收藏  举报