题目描述
给定一个 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≤𝑛≤1051≤n≤105;
1≤𝑚≤2×1051≤m≤2×105;
𝑠=1s=1;
1≤𝑢𝑖,𝑣𝑖≤𝑛1≤ui,vi≤n;
0≤𝑤𝑖≤1090≤wi≤109,
0≤∑𝑤𝑖≤1090≤∑wi≤109。
-
题目链接:
-
题目思路
-
最短路径----Dijkstra算法
-
什么是Dijkstra:
本质上是一个贪心算法,有点类似于prim算法,都是从一个给定顶点开始,将每个点连接的边记录,同时更新下一节点到达原点的距离,直到所有的边都遍历过。
-
Dijkstra的堆优化
在遍历每个点时,将与这个点相连的边都放入一个小根堆维护,保证每次弹出的都是最边(注意不考虑遍历过的点)
-
Dijkstra的流程
我们将所有点分为两种一个称为白点,一个为黑点,白点表示未被记录过的,黑点表示记录过。
将初始点的距离dis[s] = 0,其他点都为无穷。
找到dis最小的值并将其改为黑点。
遍历节点x所连接的点y,权值为w,若dis[y] > dis[x] + w,同时点y是白点是,更新dis[y] 的值
重复上述操作直到所有的点都为黑点。
-
#include<iostream> #include<queue> #define MAX 1e9 using namespace std; long long int n, m, s; typedef struct { long long int next, to, val; }node; long long int cnt = 1, dis[100005], vis[100005]; node edge[200005]; long long int head[100005]; void add_edge(long long int u, long long int v, long long int w) //链式前向星建图 { edge[cnt].val = w; edge[cnt].to = v; edge[cnt].next = head[u]; head[u] = cnt++; } typedef pair<long long int, long long int>pll; //第一个为边,第二为原点到此点的距离 priority_queue<pll, vector<pll>, greater<pll>>q; //建立优先队列 int main() { cin >> n >> m >> s; for (int i = 0; i < m; i++) { long long int u, v, w; cin >> u >> v >> w; add_edge(u, v, w); } fill(dis + 1, dis + n + 1, MAX); //将离第一个点的每个距离设为无穷 dis[s] = 0; //将第一个设为0 q.push(make_pair(0, s)); while (!q.empty()) { int x = q.top().first; int y = q.top().second; q.pop(); if (vis[y]) continue; //如果此点先前以被弹出,则不必理会重复先前操作 vis[y] = 1; for (int i = head[y]; i != 0; i = edge[i].next) { if (dis[edge[i].to] > dis[y] + edge[i].val && vis[edge[i].to] == 0) {//如果离原点的距离更小,同时满足没有被弹出的节点,则更新长度 dis[edge[i].to] = dis[y] + edge[i].val; q.push(make_pair(dis[edge[i].to], edge[i].to)); //将没有弹出过的节点加入队列 } } } for (int i = 1; i <= n; i++) { cout << dis[i] << ' '; } return 0; }
最后代码提交: