#include using namespace std; // 使用 long long 防止路径总长度超过 int 范围(约 20 亿) using ll = long long; // 定义一个极大的数表示“无穷大”。 // 0x3f3f3f3f3f3f3f3f 在 long long 下是一个非常大的数, // 且两个这样的数相加一般不会溢出,非常适合做最短路的初始值。 constexpr ll inf = 0x3f3f3f3f3f3f3f3f; // Edge 结构体有两个用途: // 1. 在邻接表 adj 中:u 表示邻接点编号,w 表示边的权重。 // 2. 在优先队列 q 中:u 表示节点编号,w 表示从起点 s 到 u 的当前最短距离。 struct Edge { int u; ll w; // 重载小于号运算符,用于优先队列的排序。 // C++ 的 priority_queue 默认是“大根堆”(最大值在堆顶)。 // 我们需要“小根堆”(距离最小的在堆顶),所以这里逻辑反过来: // 如果当前 w 大于对方的 w,则认为优先级更低。 bool operator< (const Edge& o) const { return w > o.w; } }; int main() { // 关闭输入输出同步流,提高 cin/cout 的读写速度,防止 TLE(超时) ios::sync_with_stdio(false); cin.tie(nullptr); int n, m, s; cin >> n >> m >> s; // 输入点数 n,边数 m,起点 s // 邻接表存储图,adj[u] 存储的是从 u 出发的所有边 vector > adj(n + 1); // 读入 m 条边 for (int i = 1; i <= m; ++i) { int u, v; ll w; cin >> u >> v >> w; // 无向图,所以要双向建边 adj[u].push_back({v, w}); // u -> v 权重 w adj[v].push_back({u, w}); // v -> u 权重 w } // 优先队列(小根堆),存储 pair {节点 u, 距离 w} // 堆顶永远是当前未确定的点中,距离起点最近的那个点 priority_queue q; // dist 数组:存储起点 s 到各个点的最短距离,初始化为无穷大 vector dist(n + 1, inf); // vis 数组:标记某个点是否已经确定了最短路(是否被“出队”过) vector vis(n + 1, false); // 初始状态:起点 s 到自己的距离为 0 q.push({s, 0}); dist[s] = 0; // Dijkstra 主循环 while (!q.empty()) { // 取出堆顶元素(当前距离起点最近的点) Edge now = q.top(); q.pop(); int u = now.u; // 当前节点编号 // 如果该点已经被访问过(即已经确定了最短路),则跳过。 // 这是因为同一个点可能多次入队(每次发现更短路径都会入队), // 只有第一次出队时的距离才是最短的。 if (vis[u]) continue; // 标记该点已确定最短路 vis[u] = true; // 遍历 u 的所有邻接点(扫描出边) for (int i = 0; i < adj[u].size(); ++i) { int v = adj[u][i].u; // 邻接点 v ll w = adj[u][i].w; // 边 u-v 的权重 // 松弛操作 (Relaxation): // 如果经由 u 到达 v 的距离(dist[u] + w)比当前记录的 dist[v] 更短 if (dist[v] > dist[u] + w) { dist[v] = dist[u] + w; // 更新 v 的最短距离 q.push({v, dist[v]}); // 将更新后的 v 放入队列,以便后续扩展 } } } // 输出从起点 s 到所有点 1~n 的最短距离 for (int i = 1; i <= n; ++i) { cout << dist[i] << " "; } cout << "\n"; return 0; }