算法训练1-day50-图论

4 阅读3分钟
  1. 47. 参加科学大会(第六期模拟笔试) Dijkstra算法,适用于有向图、边的权值没有负数,解决一个点到其他所有点的最短路径;采用贪心策略。维护一个集合S,包含已找到最短路径的节点。每次从集合S之外的节点中,选择一个距离源点最近的节点加入S,并松弛(更新)其所有邻居的距离。
    1. 准备distance数组,保存源点到i点的最短距离;准备visited数组,记录节点i是否从小根堆中弹出过
    2. 准备小根堆,保存节点i以及源点到节点i的距离(邻接表保存i和源点到i的距离,邻接矩阵保存x,y与源点到i的距离),小根堆根据距离组织
      1. 默认情况std::less → 大根堆 → 较大的元素在堆顶
      2. 比较函数返回true表示第一个参数应该排在第二个参数后面
    3. 设置distance[源点]为0,将(源点k, 0)加入小根堆
    4. 从小根堆中弹出节点(u, dis),然后:
      1. 如果visited[u]为true,continue。
      2. 如果visited[u]为false,设置visited[u]为true,然后检查u的所有边(k, weight):
        1. 检查是否越界(需要的话)
        2. 检查visited[k]== false且distance[u] + weight < distance[k],则更新distance[k]=distance[u] + weight,并将(k, distance[k])加入小根堆
        3. 处理u的所有边后,重复步骤4
    5. 小根堆为空,流程结束,distance表记录了源点到其他点的最短距离
#include <climits>
#include <iostream>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

class Comparer
{
public:
  bool operator()(vector<int> a, vector<int> b){
    return a[1] > b[1];
  }
};

int main() {
  int n = 0, m = 0;
  cin >> n >> m;
  vector<vector<int>> edges(n + 1, vector<int>(n + 1, INT_MAX));
  int from, to, weight;
  for (int j = 0; j < m; j++) {
    cin >> from >> to >> weight;
    edges[from][to] = weight;
  }

  int start = 1;
  int end = n;
  vector<int> distance(n + 1, INT_MAX);
  vector<bool> visited(n + 1);
  priority_queue<vector<int>,vector<vector<int>>,Comparer> pq;
  pq.push({start, 0});
  distance[start] = 0;
  while(!pq.empty()){
    vector<int> info = pq.top();
    pq.pop();
    int curr = info[0];
    if(visited[curr]) continue;
    //标记已访问
    visited[curr] = true;

    //更新从开始节点到其他节点的距离
    for (int j = 1; j <= n; ++j) {
      //节点还没有加入路径,且从开始节点到edge[0]的长度要大于从开始节点到curr再到edge[0]的长度,那么开始节点到edge[0]的长度进行更新
      if (!visited[j] && edges[curr][j] != INT_MAX &&
          distance[j] > distance[curr] + edges[curr][j]) {
        distance[j] = distance[curr] + edges[curr][j];
        pq.push({j, distance[j]});
      }
    }
  }

  if (distance[end] == INT_MAX) {
    cout << -1 << endl;
  } else {
    cout << distance[end] << endl;
  }

  return 0;
}
  1. 94. 城市间货物运输 I

对所有边松弛一次 能得到 与起点 一条边相连的节点最短距离。

那对所有边松弛两次 可以得到与起点 两条边相连的节点的最短距离。

#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;
  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) {
        minDist[to] = minDist[from] + price;
      }
    }
  }
  if (minDist[n] == INT_MAX) {
    cout << "unconnected" << endl;
  } else {
    cout << minDist[n] << endl;
  }
  return 0;
}

```# 算法训练1-day49-图论