这道题好难,首先要知道Dijkstra算法喵~
其实就是最短路径算法喵~
Dijkstra算法的核心思想:贪心+广度优先搜索
想象一下,猫猫(起点)在一个有很多房间(顶点)和走廊(边)的大房子里,每个走廊的长度(权重)都不一样。猫猫想知道自己去每个房间的最短路径,特别是去那个藏着小鱼干的房间(终点)的最短路!Dijkstra算法就是这个聪明的找路方法。
它的核心思想是:从一个起点出发,逐步向外探索,每次都选择当前已知距离最短且尚未确认的房间作为“中转站”,并用这个中转站到起点的距离,去更新它所有邻居房间到起点的可能最短距离。 这个过程会一直持续,直到所有房间的最短距离都被确定下来。
Dijkstra算法的工作步骤喵!
- 初始化准备喵 :准备一个 “最短距离记录本” (通常是一个数组),记录从起点猫窝到每个房间的当前最短距离。一开始,只有起点到自己的距离是0,到其他所有房间的距离都是 “无穷大” (表示暂时还不知道路,或者路不通)。准备一个 “待探索房间清单” (通常是一个优先队列/最小堆),里面放着所有还没最终确定最短路径的房间。一开始,这个清单里包含所有房间。(可选)还可以准备一个 “前驱房间记录本” (比如prev数组),用来记录最快到达某个房间之前,是从哪个房间过来的,方便最后回溯整条最短路径。
- 开始探索循环喵:从 “待探索房间清单” 里,找出那个离起点(猫窝)当前距离最短的房间,我们叫它 “当前房间”。第一次循环时,找到的肯定是起点本身,因为它的距离是0,最小喵!把这个 “当前房间” 标记为 “已确认” (从待探索清单中移出),意味着它的最短距离已经确定,不会再被改变了。非常重要的一步喵!扫描“当前房间”的所有邻居:看看从起点经过“当前房间”再到它的每个邻居,会不会比之前记录的去邻居的路径更短。如果发现了更短的路径,就更新邻居的 “最短距离记录本” ,并把这条新路径的距离和邻居房间放进 “待探索房间清单”。
- 一直重复上面的循环步骤,直到 “待探索房间清单” 变空(意味着所有房间的最短路径都已确定)或者找到了特定目标房间的最短路径。这时,“最短距离记录本” 里存储的就是从起点猫窝到每个房间的真正最短距离了喵!
最终,它就能可靠地找出从起点到所有其他点的最短路径啦(前提是路径权重不能是负数)!
如果还是很难理解的话可以看一看b站上的视频喵!
接下来说说怎么实现吧!
首先猫猫构建了两个图:
- 正向图 (z):存储从路口s到路口e的路,用于计算从1号房子(起点)到其他所有路口的最短时间 (to数组)。
- 反向图 (f):存储的是与原方向相反的路,即从路口e到路口s的路。(注意喵!)在反向图上以1号房子为起点跑最短路径算法,得到的结果back[i],实际意义上就是从i号房子到达1号房子的最短时间。
这样,只需要跑两次Dijkstra算法,就可以分别得到 to[i](从1到i的时间)和 back[i](从i回到1的时间),最后把每个路口的这两个时间加起来就得到总和了喵!
接下来可以仔细研究研究猫猫的代码啦!(猫猫有点发烧了,可能发题解有点晚喵~,对不起喵~ᯠ _ ̫ _ ̥ ᯄ ੭)
#include <bits/stdc++.h>
using namespace std;
struct lu//路结点
{
int e;//这条小路通向哪个路口呀
int v;//走完这条小路要花多少时间喵
};
int main()
{
cin.tie(0)->sync_with_stdio(0);
int n,m;cin >> n >> m;
vector<int> to(n+1,1e9);//到达i所需要的时间
vector<int> back(n+1,1e9);//从i返回所需要的时间
vector<vector<lu>> z(n+1);//正向图
vector<vector<lu>> f(n+1);//反向图
for(int i=0;i<m;i++)
{
int s,e,v;cin >> s >> e >> v;//起点,终点,所花时间
z[s].push_back({e,v});
f[e].push_back({s,v});
}
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> pq;
//<所花时间,起点>
//计算从邮局出发去各路口的最短时间喵!
pq.push({0,1});//从邮局开始探索喵,所花时间当然是0啦
while(!pq.empty())
{
auto [time,start]=pq.top();//到达start所花时间time
pq.pop();
//如果发现更近的路已经找到了,就跳过这个需要花更长时间才能到的路口喵
if(time>to[start]) continue;
// 检查从这个路口能去的所有相邻路口喵
for(auto& i:z[start])
{
int new_time =i.v+time;
if(new_time<to[i.e])//发现更近的路啦,更新地图~
{
to[i.e]=new_time;
pq.push({new_time,i.e});
}
}
}
//计算从各路口出发去各路口的最短时间喵!
//以下和上方除了变量名一摸一样
pq.push({0,1});
while(!pq.empty())
{
auto [time,start]=pq.top();
pq.pop();
if(time>back[start]) continue;
for(auto& i:f[start])
{
int new_time =i.v+time;
if(new_time<back[i.e])
{
back[i.e]=new_time;
pq.push({new_time,i.e});
}
}
}
//输出结果
int ans=0;
for(int i=2;i<=n;i++) ans+=to[i]+back[i];
cout << ans;
}
/*
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⡀⣀⣠⣤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⡀⢀⣴⣾⣷⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣷⣾⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⠿⠛⠛⠉⠉⠉⠉⠉⠉⠛⠻⠿⣿⣿⣿⣿⣿⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⡿⠿⠛⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠿⣿⣿⣿⣷⣄⡀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣀⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⣰⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⣠⣾⣿⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣶⣄⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣻⣿⣿⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢹⣿⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⠁⠈⢢⡀⠀⠀⠀⢸⡇⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⡟⠒⢦⡀⠀⠀⠀
⠀⠀⣠⣤⣤⣼⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡇⠀⠀⠀⠉⢢⣄⠀⠀⢿⠊⠳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣷⡄⠀⢷⠀⠀⠀
⠀⢰⠇⠀⣰⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⡌⣹⠗⠦⣬⣇⠀⠉⢢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⡀⢸⡄⠀⠀
⠀⡟⠀⣼⣯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣆⢹⡀⠀⠀⠀⠉⠁⠀⠀⢀⣀⡁⠀⠀⠉⠳⢴⡆⠀⠀⠀⠀⠀⠀⢹⣧⠈⡇⠀⠀
⠀⡇⠀⠀⢻⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣾⠻⠉⠛⠂⠀⠀⠀⠀⠀⠀⠻⠿⣿⣿⣿⣶⣦⡀⠛⣇⠀⠀⠀⠀⠀⣈⣿⠀⡇⠀⠀
⢸⡇⠀⠀⢠⣿⣷⣦⣀⡸⣷⣦⣶⡂⠉⠉⠉⢁⣤⣶⡶⠀⠀⠀⣀⣀⡴⠀⠀⠀⠀⠀⠀⠈⠉⠉⠁⠀⡟⢀⣴⣟⣰⣾⣿⣏⣠⠇⠀⠀
⠈⡇⠀⠀⢸⣿⠁⠉⣿⠛⠛⠃⡇⠀⠀⢠⣶⣿⡿⠛⠁⠀⠀⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠼⢿⠟⠿⢿⡏⠀⠘⣿⡀⠀⠀⠀
⠀⢷⣀⣀⣿⠇⠀⠀⢿⡇⠀⢀⢱⡀⠀⠛⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⠀⠀⢸⠇⠀⠀⢹⣿⣄⠀⠀
⠀⠀⣉⣿⡏⠀⠀⠀⠀⠀⠀⢸⣇⣳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⣿⠃⠀⠀⠀⠀⠀⠀⣿⠈⢧⠀
⠀⠘⣿⣿⠁⠀⠀⠀⠀⠀⠀⠘⣿⡛⣶⠀⠀⣠⠔⠒⠛⠒⠦⡀⠀⠀⠀⠀⣠⡤⠶⠤⢤⣀⠀⠀⠀⢀⣏⡄⠀⠀⠀⠀⠀⡀⣿⡆⠈⣧
⣠⡾⠛⣿⣿⣧⠀⠀⠀⠀⢸⣿⠾⢿⡿⠀⣰⠃⠀⠀⠀⠀⠀⢹⡄⠀⠀⡼⠁⠀⠀⠀⠀⠈⠙⣦⠀⢸⣿⡇⣾⣣⡀⠀⢰⣿⣿⣿⣤⠾
⡟⠀⠀⠻⣿⡟⢷⡄⣤⡀⠈⣿⡀⣸⠇⠀⠏⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⡇⢀⡀⠀⠀⠀⠀⢀⡟⠀⠀⠋⣿⣿⣿⡇⣠⣿⠿⠛⢷⡀⠀
⠀⠀⠀⠀⣿⣇⣨⣿⣿⣿⣦⣽⣷⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠃⠀⠙⠢⠤⠤⠴⢾⠀⠀⠀⠀⢸⣷⣿⣿⠟⠁⠀⠀⠈⣧⠀
⠀⠀⠀⠀⠈⠉⠉⠁⠈⠉⠈⢉⣿⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⣿⠀
*/

京公网安备 11010502036488号