文章目录
- 前言
- Dijkstra核心思想
- 代码详解、
- 附完整AC代码
- 总结
前言
题目链接:
洛谷
大一小白初学Dikstra算法,在勉强弄清楚最短路径模版题([模版] (https://www.luogu.com.cn/problem/P4779))后,尝试了一下稍微进阶一点的题,脑子彻底紊乱了,好在在逐步摸索下,逐渐搞清楚了这道题。
(我真是猪脑 )
Dijkstra核心思想
Dijkstra算法:用于解决 单源最短路径问题。
其核心的两点为
(1).找到最短距离已经确定的顶点,从它出发更新相邻顶点的最短距离(注意:是相邻顶点距离,可能包含多个点本彩笔就是忽略了这点而陷入了迷茫 )
(2).此后就不再需要关心1中的“最短距离已经确定的顶点”。
(1)(2)中提到的“最短距离已经确定的顶点”如何得到是本题的关键。在最开始时,只有起点的最短距离是确定的,而在尚未使用的顶点中,距离distant[i]最小的顶点就是最短距离已经确定的顶点。
PS:这是因为在本算法可行的条件中,不存在负边,所以distant[i]不会在之后的更新中变小。
思路来源《挑战程序设计竞赛》 基本都是我抄来的
代码详解、
进入代码:
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn=5e5+5 ;
const int maxm=1e5+5;
struct edge{ //构建结构体 to-顶点,这里是道路终点农场 cost-边权值,这里即距离
int to,cost;
};
typedef pair<int ,int > P; //first 是最短距离,second是顶点编号
int n,m; //没什么好说的,n个顶点,m条边。
int distant1[maxn];//记录最短距离
int distant2[maxn];//记录次短距离
vector<edge> g[maxm];//图的邻接表表示
其中 pair是c++的模板类,用于存储一对值。它包含两个公共成员变量 first 和 second,分别表示第一个值和第二个值。
pair 在很多情况下非常有用,例如在算法中返回多个值、构建映射等
``
接下来是主函数
int main() //求次短路径长度
{
scanf("%d%d",&n,&m);
for(int i=0;i<m;++i){
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
g[u].push_back({v,c});
g[v].push_back({u,c}); //根据题目背景可知本题是无向图,所以构建双向道路
}
这里即简单的输入数据
以下为本题代码实现过程以及主要的批注
priority_queue<P,vector<P>,greater<P> >que;
//构建优先队列用于寻找最短路径
//元素类型为之前typedef后的pair ——— P
//用vector作为底层容器,采用greater作为比较器。
//greater是STL中的函数对象(function object),用于提供元素的比较规则,
//这里优先队列会将元素按照从小到大的顺序进行排序。
fill(distant1+1,distant1+n+1,INF); // 初始化最短距离为无穷大
fill(distant2+1,distant2+n+1,INF); // 初始化次短距离为无穷大
distant1[1]=0; // 起始点到自身的距离为0
que.push(P(0,1)); // 将起始点加入优先队列
while(!que.empty()){
P p=que.top();
que.pop(); //取出队列的队首
int v=p.second; // v 获取顶点编号
int d=p.first; // d 获取当前最短距离
if(distant2[v]<d){ //若当前顶点的次短距离比最短距离还低,进入下一轮。
continue;
}
for(unsigned long long int i=0;i<g[v].size();++i){ 遍历
edge &e=g[v][i];
//这里使用了引用赋值
//g[v][i]表示图中从顶点 v 出发的第 i 条边,
//而 edge &e 则是将该边赋值给引用变量 e。
int d2=d+e.cost;//暂时存储下一次的距离,遍历得到最短距离
//首先,判断 distant1[e.to] 是否大于 d2。
//如果是,则说明通过顶点 v 到达顶点 e.to 的路径更短,
//需要更新最短距离和将该顶点加入优先队列。
if(distant1[e.to]>d2){ //更新最短距离
swap(distant1[e.to],d2);
//这里用swap是怕丢失原先存储在 distant1[e.to]的数据
que.push(P(distant1[e.to],e.to));
}
//接下来,判断distant2[e.to]是否大于d2且distant1[e.to]小于d2。
//如果是,则说明通过顶点v到达顶点e.to的路径
//虽然不是最短路径,但是可以更新为次短路径
if(distant2[e.to]>d2 && distant1[e.to]<d2){
distant2[e.to]=d2;
que.push(P(distant2[e.to],e.to));
}
}
}
printf("%d",distant2[n]);
//最后的结果储存在distant2[n]里,输出。
return 0;
}
---
附完整AC代码
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn=5e5+5 ;
const int maxm=1e5+5;
struct edge{
int to,cost;
};
typedef pair<int ,int > P; //first 是最短距离,second是顶点编号
int n,m;
int distant1[maxn];
int distant2[maxn];
vector<edge> g[maxm];
int main() //求次短路径长度
{
scanf("%d%d",&n,&m);
for(int i=0;i<m;++i){
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
g[u].push_back({v,c});
g[v].push_back({u,c});
}
priority_queue<P,vector<P>,greater<P> >que;
fill(distant1+1,distant1+n+1,INF);
fill(distant2+1,distant2+n+1,INF);
distant1[1]=0;
que.push(P(0,1));
while(!que.empty()){
P p=que.top();
que.pop();
int v=p.second;//顶点编号
int d=p.first;//最短距离
if(distant2[v]<d){ //若次短距离比最短距离还低,继续
continue;
}
for(unsigned int i=0;i<g[v].size();++i){
edge &e=g[v][i];
int d2=d+e.cost;
if(distant1[e.to]>d2){
swap(distant1[e.to],d2);
que.push(P(distant1[e.to],e.to));
}
if(distant2[e.to]>d2 && distant1[e.to]<d2){
distant2[e.to]=d2;
que.push(P(distant2[e.to],e.to));
}
}
}
printf("%d",distant2[n]);
return 0;
}
总结
本题巧妙地利用Dijkstra算法的思路,在其基本算法思路上稍加修改,相当于进行了两次Dijkstra即可得到最后的答案。