算法训练1-day51-图论

10 阅读3分钟
  1. 94. 城市间货物运输 I Bellman_ford 算法每次都是对所有边进行松弛,其实是多做了一些无用功。

只需要对上一次松弛的时候更新过的节点作为出发节点所连接的边进行松弛就够了

用队列来记录更新过的节点

#include <climits>
#include <iostream>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

int main() {
  int n, m;
  cin >> n >> m;

  vector<vector<pair<int, int>>> grid(n + 1);

  int from, to, price;
  for (int i = 0; i < m; ++i) {
    cin >> from >> to >> price;
    grid[from].push_back({to, price});
  }

  vector<int> minDist(n + 1, INT_MAX);
  minDist[1] = 0;
  vector<int> visited(n + 1);
  queue<int> q;
  q.push(1);
  while (!q.empty()) {
    int from = q.front();
    q.pop();
    visited[from] = false;

    for (auto &edge : grid[from]) {
      int to = edge.first;
      int price = edge.second;

      if (minDist[from] != INT_MAX && minDist[to] > minDist[from] + price) {
        minDist[to] = minDist[from] + price;
        if (!visited[to]) {
          q.push(to);
        }
      }
    }
  }
  if (minDist[n] == INT_MAX) {
    cout << "unconnected" << endl;
  } else {
    cout << minDist[n] << endl;
  }
  return 0;
}

  1. 95. 城市间货物运输 II

在 bellman_ford 算法中,松弛 n-1 次所有的边 就可以求得 起点到任何节点的最短路径,松弛 n 次以上,minDist数组(记录起到到其他节点的最短距离)中的结果也不会有改变。

有负权回路的情况下,一直都会有更短的最短路,所以 松弛 第n次,minDist数组 也会发生改变。

那么解决本题的 核心思路,就是在 kama94.城市间货物运输I 的基础上,再多松弛一次,看minDist数组 是否发生变化。

#include <climits>
#include <iostream>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

int main() {
  int n, m;
  cin >> n >> m;

  vector<vector<int>> grid;

  int from, to, price;
  for (int i = 0; i < m; ++i) {
    cin >> from >> to >> price;
    grid.push_back({from, to, price});
  }

  vector<int> minDist(n + 1, INT_MAX);
  minDist[1] = 0;
  bool flag = false;
  for (int i = 1; i <= n; ++i) {
    for (vector<int> &info : grid) {
      int from = info[0];
      int to = info[1];
      int price = info[2];

      if (minDist[from] != INT_MAX && minDist[to] > minDist[from] + price) {
        if (i < n) {
          minDist[to] = minDist[from] + price;
        } else {
          flag = true;
          break;
        }
      }
    }
  }
  if (flag) {
    cout << "circle" << endl;
  } else if (minDist[n] == INT_MAX) {
    cout << "unconnected" << endl;
  } else {
    cout << minDist[n] << endl;
  }
  return 0;
}

  1. 96. 城市间货物运输 III 本题是最多经过 k 个城市, 那么是 k + 1条边相连的节点。对所有边松弛一次,相当于计算起点到达与起点一条边相连的节点的最短距离,那么对所有边松弛 k + 1次,就是求起点到达与起点k + 1条边相连的节点的最短距离。

单纯的将松弛次数从n-1变为k+1会出错,因为我们计算时基于的是本次可能已经更新的minDist数组,那么,计算minDist数组的时候,基于了本次松弛的 minDist数值,而不是上一次 松弛时候minDist的数值,导致不仅仅与起点一条边相连的节点更新了,所有节点都更新了。所以在每次计算 minDist 时候,要基于对所有边上一次松弛的 minDist 数值才行,所以我们要记录上一次松弛的minDist。

#include <climits>
#include <iostream>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

int main() {
  int n, m;
  cin >> n >> m;

  vector<vector<int>> grid;

  int from, to, price;
  for (int i = 0; i < m; ++i) {
    cin >> from >> to >> price;
    grid.push_back({from, to, price});
  }

  int src, dst, k;
  cin >> src >> dst >> k;

  vector<int> minDist(n + 1, INT_MAX);
  vector<int> minDist_copy(n + 1, INT_MAX);
  minDist[src] = 0;
  for (int i = 0; i <= k; ++i) {
    minDist_copy = minDist;
    for (vector<int> &info : grid) {
      int from = info[0];
      int to = info[1];
      int price = info[2];

      if (minDist_copy[from] != INT_MAX && minDist[to] > minDist_copy[from] + price) {
        minDist[to] = minDist_copy[from] + price;
      }
    }
  }
  if (minDist[dst] == INT_MAX) {
    cout << "unreachable" << endl;
  } else {
    cout << minDist[dst] << endl;
  }
  return 0;
}