P4779 【模板】单源最短路径(标准版)
目录
题目背景
题目描述
输入格式
输出格式
输入输出样例
说明/提示
代码
无注释版
有注释版
题目背景
2018 年 7 月 19 日,某位同学在 NOI Day 1 T1 归程 一题里非常熟练地使用了一个广为人知的算法求最短路。
然后呢?
100→60;
Ag→Cu;
最终,他因此没能与理想的大学达成契约。
小 F 衷心祝愿大家不再重蹈覆辙。
题目描述
给定一个 n 个点,m 条有向边的带非负权图,请你计算从 s 出发,到每个点的距离。
数据保证你能从 s 出发到任意点。
输入格式
第一行为三个正整数 n,m,s。 第二行起 m 行,每行三个非负整数 ui,vi,wi,表示从 ui 到 vi 有一条权值为 wi 的有向边。
输出格式
输出一行 n 个空格分隔的非负整数,表示 s 到每个点的距离。
输入输出样例
输入
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
输出
0 2 4 3
说明/提示
样例解释请参考 数据随机的模板题。
1≤n≤105;
1≤m≤2×105;
s=1;
1≤ui,vi≤n;
0≤wi≤109,
0≤∑wi≤109。
代码
无注释版
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10;
struct edge{
int to,dis,next;
}e[N];
int head[N],dis[N],cnt;
bool vis[N];
int m,n,s;
void addedge(int u,int v,int d){
cnt++;
e[cnt].dis=d;
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
struct node{
int dis;
int pos;
bool operator <(const node &x)const{
return x.dis<dis;
}
};
priority_queue<node> q;
void dijkstra(){
dis[s]=0;
q.push({0,s});
while(!q.empty()){
node t=q.top();
q.pop();
int x=t.pos,d=t.dis;
if(vis[x]) continue;
vis[x]=1;
for(int i=head[x];i;i=e[i].next){
int y=e[i].to;
if(dis[y]>dis[x]+e[i].dis){
dis[y]=dis[x]+e[i].dis;
if(!vis[y]){
q.push({dis[y],y});
}
}
}
}
}
signed main(){
cin>>n>>m>>s;
for(int i=1;i<=n;i++){
dis[i]=INT_MAX;
}
for(int i=0;i<m;i++){
int a,b,c;
cin>>a>>b>>c;
addedge(a,b,c);
}
dijkstra();
for(int i=1;i<=n;i++){
cout<<dis[i]<<" ";
}
}
有注释版
#include<bits/stdc++.h> // 引入 C++ 标准库,包含常用的输入输出、容器、算法等
using namespace std; // 使用标准命名空间,避免每次都加 std::
#define int long long // 将 int 类型替换为 long long,确保能够处理更大的数值范围
const int N = 1e6 + 10; // 定义最大节点数的常量,N 用来存储最大节点数和边数
struct edge { // 定义一个结构体表示边
int to, dis, next; // `to` 表示边的目标节点,`dis` 表示边的权值,`next` 用来链接下一条边
} e[N]; // 用数组 `e` 存储所有边
int head[N], dis[N], cnt; // `head` 数组用于存储每个节点的边的起始位置,`dis` 数组存储最短距离,`cnt` 是边的计数器
bool vis[N]; // `vis` 数组表示节点是否被访问过
int m, n, s; // `n` 是节点数,`m` 是边数,`s` 是起始节点
// 添加一条边的方法
void addedge(int u, int v, int d) {
cnt++; // 边计数器加 1
e[cnt].dis = d; // 设置边的权值
e[cnt].to = v; // 设置边的目标节点
e[cnt].next = head[u]; // 将当前边插入到 `u` 的边链表头部
head[u] = cnt; // 更新 `u` 节点的边链表头
}
// 定义节点结构体,用于优先队列中存储节点和其对应的最短距离
struct node {
int dis; // 节点的当前最短距离
int pos; // 节点的位置(编号)
bool operator <(const node &x) const { // 重载小于运算符,用于优先队列中的排序(按最短距离排序)
return x.dis < dis; // 小的优先
}
};
priority_queue<node> q; // 优先队列,用来存储节点及其最短距离
// Dijkstra 算法实现
void dijkstra() {
dis[s] = 0; // 起点的最短距离为 0
q.push({0, s}); // 将起点加入优先队列
while (!q.empty()) { // 当队列不为空时,继续计算
node t = q.top(); // 取出最小距离的节点
q.pop(); // 从队列中移除该节点
int x = t.pos, d = t.dis; // `x` 是节点编号,`d` 是该节点的当前最短距离
if (vis[x]) continue; // 如果该节点已经访问过,跳过
vis[x] = 1; // 标记节点 `x` 已经访问
// 遍历当前节点 `x` 的所有邻接节点
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].to; // 获取目标节点 `y`
if (dis[y] > dis[x] + e[i].dis) { // 如果通过当前节点 `x` 到节点 `y` 的距离更短
dis[y] = dis[x] + e[i].dis; // 更新 `y` 的最短距离
if (!vis[y]) { // 如果 `y` 还没有被访问过
q.push({dis[y], y}); // 将 `y` 加入队列,更新它的最短距离
}
}
}
}
}
signed main() {
cin >> n >> m >> s; // 输入节点数 `n`,边数 `m`,起始节点 `s`
for (int i = 1; i <= n; i++) { // 初始化所有节点的最短距离为无穷大
dis[i] = LLONG_MAX;
}
for (int i = 0; i < m; i++) { // 输入每一条边,并构建图
int a, b, c;
cin >> a >> b >> c; // 输入边的起始节点 `a`,目标节点 `b`,权值 `c`
addedge(a, b, c); // 添加边
}
dijkstra(); // 执行 Dijkstra 算法,计算最短距离
for (int i = 1; i <= n; i++) { // 输出从起点 `s` 到所有节点的最短距离
cout << dis[i] << " ";
}
}