3371 【模板】单源最短路径

题目描述

如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入输出格式

输入格式:

第一行包含三个整数N、M、S,分别表示点的个数、有向边的个数、出发点的编号。

接下来M行每行包含三个整数Fi、Gi、Wi,分别表示第i条有向边的出发点、目标点和长度。

输出格式:

一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为2147483647)

代码:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<queue>
 4 using namespace std;
 5 struct edge{
 6     int k;//终点
 7     int w;//边权
 8     //node(int kk, int ww):k(kk), w(ww){}
 9 };
10 bool operator <(const edge&n1, const edge&n2){
11     return n1.w>n2.w;
12 }
13 priority_queue<edge>pq;
14 vector<vector<edge> >v;
15 const int inf = 2147483647;
16 bool used[10010];
17 int d[10010];
18
19 int main(){
20     int n, m, s;
21     cin>>n>>m>>s;
22     v.clear();
23     v.resize(m+1);
24     int i, j;
25     edge p;//(0, 0);
26     for(i = 1; i <= m; i++){
27         int a, b, c;
28         cin>>a>>b>>c;
29         p.k = b;
30         p.w = c;
31         v[a].push_back(p);
32     }
33     for(i = 1; i <= n; i++) d[i] = inf;
34     d[s] = 0;
35     p.k = s;
36     p.w = 0;
37     pq.push(p);
38     while(!pq.empty()){
39         p = pq.top();
40         pq.pop();
41         if(used[p.k])//已经求出了最短路
42             continue;
43         used[p.k] = true;
44         d[p.k] = p.w;
45         for(i = 0; i < v[p.k].size(); i++){
46             edge q;
47             q.k = v[p.k][i].k;
48             if(!used[q.k]&&d[q.k]>p.w+v[p.k][i].w){
49                 q.w = p.w + v[p.k][i].w;
50                 pq.push(q);
51             }
52         }
53     }
54     for(i=1; i<=n; i++)
55         cout<<d[i]<<" ";
56     return 0;
57 } 

备注:

加堆优化的dijkstra模板。开始是照着gw的ppt写的,然后瞪了一个小时也没看懂for循环里面那几行,已经觉得人生绝望了。zmj帮我把程序改成了现在这样便于理解的状态,现在我要继续解析一下,边写边想。

用vector<vector<edge>>存图,结构体表示一条边,元素包括边权和终点编号。d[i]表示从源点到编号为i的点的最短距离。

首先把源点push进堆。然后进入while循环。

加堆是如何实现优化的呢?就是因为堆顶元素自动就是最近的元素。

取堆顶元素p(按照定义,这时p边所连的点p.k是堆里w最小,即距离“已选点集”最近的点),将p弹出(必须现在弹出,不能松弛完了再弹)。如果发现p.k是已经用过的点了,则跳过。如果是一个新的点,则将其标记。(pop出的点集就是“已选点集,他们全都used过了)

哎呀,写出来果然清楚多了。因为现在p.k是距离已选点集最近的点,所以它的d值其实已经确定了,于是更新(现在的d就是最终结果)。事实上现在p.k已经是我们选中的点了(即加入点集),接下来做的就是松弛跟它相连的点。

依次遍历与p.k相连的点,即q.k,如果q.k没有用过,并且d值比新路径大,则更新q.w(为什么不更新d值?因为这时d值还没有确定,当然更新也是可以的,但没这个必要。因为当它弹出去时的w值就是最终的d值,所以在这里只更新q.w就可以了),更新完了就把它push进堆,这样下一轮好直接取堆顶元素。

靠,我现在终于想明白了!为什么gw要那么写。因为d值在这里实际上一直是无穷大啊。所以这句根本是废话。弹出去的时候再更新就直接是最终的结果了啊。

再补充一句,为什么可以不用比较就直接给q.w赋值。因为赋值完了都是要把新q push进堆里的,我们总是取堆里最小的,所以那些不够小的push进去根本就无所谓,到时候就又都原封不动地弹出来了。

这个道理我tm想了一下午。写博客真是有奇效。

好了,我要附上gw的原代码写法了。orz

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<queue>
 4 using namespace std;
 5 struct edge{
 6     int k;//终点
 7     int w;//边权
 8     //node(int kk, int ww):k(kk), w(ww){}
 9 };
10 bool operator <(const edge&n1, const edge&n2){
11     return n1.w>n2.w;
12 }
13 priority_queue<edge>pq;
14 vector<vector<edge> >v;
15 const int inf = 2147483647;
16 bool used[10010];
17 int d[10010];
18
19 int main(){
20     int n, m, s;
21     cin>>n>>m>>s;
22     v.clear();
23     v.resize(m+1);
24     int i, j;
25     edge p;//(0, 0);
26     for(i = 1; i <= m; i++){
27         int a, b, c;
28         cin>>a>>b>>c;
29         p.k = b;
30         p.w = c;
31         v[a].push_back(p);
32     }
33     for(i = 1; i <= n; i++) d[i] = inf;
34     d[s] = 0;
35     p.k = s;
36     p.w = 0;
37     pq.push(p);
38     while(!pq.empty()){
39         p = pq.top();
40         pq.pop();
41         if(used[p.k])//已经求出了最短路
42             continue;
43         used[p.k] = true;
44         d[p.k] = p.w;
45         for(i = 0; i < v[p.k].size(); i++){
46             edge q;
47             q.k = v[p.k][i].k;
48             if(used[q.k])continue;
49             q.w = p.w + v[p.k][i].w;
50             pq.push(q);
51         }
52     }
53     for(i=1; i<=n; i++)
54         cout<<d[i]<<" ";
55     return 0;
56 } 

相信这一下午没有白费,对dijkstra的理解应该还是很深刻的(这样安慰自己)。

时间: 2024-10-11 23:23:01

3371 【模板】单源最短路径的相关文章

【洛谷 p3371】模板-单源最短路径(图论)

题目:给出一个有向图,请输出从某一点出发到所有点的最短路径长度. 解法:spfa算法. 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<queue> 6 using namespace std; 7 typedef long long LL; 8 9 const int N=10010,M=500010; 10 c

[模板]单源最短路径(Dijkstra)

如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度. 主要还是再打一遍最短路,这种算法我用的不多... 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 inline int read() 5 { int x=0;bool f=0;char ch=getchar(); 6 while(!isdigit(ch)){ f=(ch==45);ch=getchar();} 7 while(is

[模板]单源最短路径

https://www.luogu.org/problemnew/show/P3371 1 #include <cstring> 2 #include <cstdio> 3 #include <iostream> 4 #include <queue> 5 #define lop(i,a,b) for(register int i=a;i<=b;++i) 6 const int N = 10010, M = 500010, inf = 214748364

Luogu 3371【模板】单源最短路径

Luogu 3371[模板]单源最短路径 第一次写博客用图论题来试一试 接下来是正文部分 题目描述 如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度. 输入输出格式 输入格式: 第一行包含三个整数N.M.S,分别表示点的个数.有向边的个数.出发点的编号. 接下来M行每行包含三个整数Fi.Gi.Wi,分别表示第i条有向边的出发点.目标点和长度. 输出格式: 一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度 (若S=i则最短路径长度为0,若从点S无法到达

P3371 【模板】单源最短路径 如题

P3371 [模板]单源最短路径 时空限制1s / 128MB 题目描述 如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度. 输入输出格式 输入格式: 第一行包含三个整数N.M.S,分别表示点的个数.有向边的个数.出发点的编号. 接下来M行每行包含三个整数Fi.Gi.Wi,分别表示第i条有向边的出发点.目标点和长度. 输出格式: 一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为214

P3371 【模板】单源最短路径(弱化版)

P3371 [模板]单源最短路径(弱化版) 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn = 10005; 4 struct edge { 5 int v, w; 6 }; 7 vector<edge> maps[maxn]; 8 int dis[maxn]; 9 bool vis[maxn]; 10 int n, m, s; 11 void add(int u, int v, int w) {

单源最短路径Dijkstra、BellmanFord、SPFA【模板】

Dijkstra算法: 将所有点分为两个集合.如果源点s到u的最短路径已经确定,点u就属于集合Va,否则属于集合Vb. 1.将源点s到图中各点的直接距离当做初始值记录为s到各点的最短距离,不能到达的记为INF.S到S距离为0. 2.在集合Vb中的点中找一个点u,使得源点s到该点u路径长度最短,将u从Vb中除去,加到V1中.这时候求出了当前S到u的最短路径. 3.把新确定的点u更新s到集合Vb中每一个点v的距离,如果s到u的距离加上u到v的直接距离小于当前s到v的距离,则表示新找到的最短路径长度比

Dijkstra算法求单源最短路径

1.最短路径 在一个连通图中,从一个顶点到另一个顶点间可能存在多条路径,而每条路径的边数并不一定相同.如果是一个带权图,那么路径长度为路径上各边的权值的总和.两个顶点间路径长度最短的那条路径称为两个顶点间的最短路径,其路径长度称为最短路径长度. 最短路径在实际中有重要的应用价值.如用顶点表示城市,边表示两城市之间的道路,边上的权值表示两城市之间的距离.那么城市A到城市B连通的情况下,哪条路径距离最短呢,这样的问题可以归结为最短路径问题. 求最短路径常见的算法有Dijkstra算法和Floyd算法

单源最短路径(dij+堆优化)

单源最短路径的模板题,感谢同学余能的帮助~ #include<bits/stdc++.h> #define inf 2147483647 using namespace std; bool book [200004]; int cnt=0; int head[200020]; int n,m,qq; struct Edge { int to,next,w; }e[2000005]; struct Dis { int to,w; }dis[20000]; struct cmp1 { bool o

Dijkstra单源最短路径

Dijkstra单源最短路径 给定一个带权有向图G=(V,E) ,其中每条边的权是一个非负实数.另外,还给定 V 中的一个顶点,称为源.现在我们要计算从源到所有其他各顶点的最短路径长度.这里的长度是指路上各边权之和.这个问题通常称为单源最短路径问题. 下面给出两个计算单源最短路径的模板. Dijkstra_简化版:时间复杂度O(n^2),不可处理重边图 //计算图的以s点为起点的单源最短路径 //图中节点从1到n编号 //运行dijkstrea之前,需要先把图中两点间的距离保存在dist[i][