Loading

P4779 题解

P4779 【模板】单源最短路径(标准版)

题目传送门

题意

给定一个 \(n\) 个点,\(m\) 条有向边的带非负权图,请你计算从 \(s\) 出发,到每个点的距离(最短距离)。

数据保证你能从 \(s\) 出发到任意点。

\(1 \le n \le 10 ^ 5\)

\(1 \le m \le 2 \times 10 ^ 5\)

\(0 \le w_i \le 10 ^ 9\)

思路

首先,是最短路,并且数据范围也不小,所以考虑广搜。

但是,这里的图是带权图,边权并不是都为 \(1\),所以,如果直接用队列实现,就会破坏队列的单调性。

那应该怎么办呢?

既然每一次都是要从队列里面取出最小的元素,那么我们可以考虑用堆来维护这个队列。

但是,我们并不知道一种状态可以由多少种状态转移而来,所以需要标记一下。

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int n, m, s, u, v, w, d[N];
bool vis[N];

struct Node {
  int v;
  long long w;
};

struct cmp {
  bool operator()(Node i, Node j) {
    return i.w > j.w;
  }
};

vector<Node> f[N];
priority_queue<Node, vector<Node>, cmp> que;

void Record(int x, int step) {
  if (step >= d[x]) {
    return ;
  }
  d[x] = step;
  que.push({x, step});  // 入队列
}

void bfs() {
  for (Record(s, 0); !que.empty(); ) {
    Node u = que.top();
    que.pop();
    if (vis[u.v]) {  // 标记
      continue;
    }
    vis[u.v] = 1;
    for (int i = 0; i < f[u.v].size(); i++) {  
      Record(f[u.v][i].v, u.w + f[u.v][i].w);  // 转移
    }
  }
}

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  cin >> n >> m >> s;
  fill(d + 1, d + n + 1, INT_MAX);  // 初始化
  while (m--) {
    cin >> u >> v >> w;
    f[u].push_back({v, w});
  }
  bfs();
  for (int i = 1; i <= n; i++) {
    cout << d[i] << ' ';
  }
  return 0;
}
posted @ 2023-03-02 22:48  chengning0909  阅读(25)  评论(0编辑  收藏  举报