4.2
解决难题
7-10 相会地灵殿
分数 25
作者 Sunri
单位 石家庄铁道大学
题目描述
a2dcce1b07225d3864669c6452401f94.jpg
在旧地狱的地灵殿中,Satori 与 Koishi 姐妹因故需要会面。地灵殿由 n 个区域(编号 1 到 n)和 m 条连接通道组成。每条通道需要 w(w为偶数) 秒的通行时间。Satori 从区域 1(地灵殿主厅)出发,Koishi 从区域 n(灼热地狱遗迹)出发。
地灵殿中有 h 个区域栖息着可骑乘的宠物(如火焰猫燐、灵乌路空等),骑乘后通道的通行时间减半。两小只均可瞬间骑上宠物,且一旦骑上可持续使用。会面必须在区域进行(不能在通道内),两小只均可在任意区域停下等待。
请计算两小只能够会面的最早时间。若区域 1 与 n 不连通,输出 −1。
输入格式
第一行由三个整数 n, m, h (2≤n≤2⋅10
5
,1≤m≤2⋅10
5
,1≤h≤n) 组成,分别代表区域的数量,通道的数量和存在宠物的区域的数量。
第二行包含 h 个不同的整数 a
1
,a
2
,…,a
h
(1≤a
i
≤n) ,表示存在宠物的区域。
接下来的 m 行,每行有三个整数 u
i
, v
i
, w
i
(1≤u
i
,v
i
≤n,2≤w
i
≤10
6
) ,代表在区域 u
i
和 v
i
之间有一条通道,在没有骑上宠物的情况下穿行需要花费 w
i
秒。
题目保证没有重边或自环,且所有 w
i
都是偶数。
输出格式
一个整数,表示最早相会时间。若无法相会,输出 −1。
输入样例#1:
3 2 1
2
1 2 4
1 3 16
输出样例#1:
14
输入样例#2:
7 7 1
3
1 5 2
2 6 12
1 2 12
6 4 8
7 3 4
6 3 4
7 6 4
输出样例#2:
12
样例解释:
在样例 1 中:Koishi 在区域 3 等待,Satori 在没有骑乘的情况下前往区域 2 ,在区域 2 骑上宠物,然后经过区域 1 骑到区域 3 去见 Koishi,此时相会时间最短。
代码长度限制
16 KB
时间限制
1000 ms
内存限制
64 MB
栈限制
8192 KB
include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
const ll INF=1e18;
vector<pair<int,int>>g[N];
bool is_pet[N];
ll dist1[N][2],distn[N][2];
int n,m,h;
void djstra(int start,ll dist[][2])
{
for(int i=1;i<=n;i++)
{
dist[i][0]=dist[i][1]=INF;
}
priority_queue<tuple<ll,int,int>,vector<tuple<ll,int,int>>,greater<>>q;
dist[start][0]=0;
q.emplace(0,start,0);
if(is_pet[start]){
dist[start][1]=0;
q.emplace(0,start,1);
}
while(!q.empty())
{
auto [d,u,s]=q.top();
q.pop();
if(d>dist[u][s])continue;
if(s0&&is_pet[u]&&dist[u][1]>d)
{
dist[u][1]=d;
q.emplace(d,u,1);
}
for(auto [v,w]: g[u])
{
ll cost;
if(s0)cost=w;
else cost=w/2;
if(dist[v][s]>d+cost)
{
dist[v][s]=d+cost;
q.emplace(dist[v][s],v,s);
}
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>h;
for(int i=0;i<h;i++)
{
int x;
cin>>x;
is_pet[x]=1;
}
for(int i=0;i<m;i++)
{
int u,v,w;
cin>>u>>v>>w;
g[u].emplace_back(v,w);
g[v].emplace_back(u,w);
}
djstra(1,dist1);
djstra(n,distn);
ll ans=INF;
for(int u=1;u<=n;u++)
{
ll t1=max(dist1[u][0],distn[u][0]);
ll t2=max(dist1[u][0],distn[u][1]);
ll t3=max(dist1[u][1],distn[u][0]);
ll t4=max(dist1[u][1],distn[u][1]);
ans=min({ans,t1,t2,t3,t4});
}
if(ans==INF)cout<<-1<<endl;
else cout<<ans<<endl;
return 0;
}
问题1.为什么二维数组传参不能加 &?加 & 不是更快吗?
答:数组传参本来就是传地址,自带引用效果,根本不需要加 &,加了直接语法报错.
加 & 更快、无拷贝」—— 完全没错,但对象错了!
你记的规则,适用于这些类型:
vector / string 容器
结构体 struct
普通变量 int / long long
自定义对象
问题2.什么时候用 emplace?什么时候用 emplace_back?
队列 / 优先队列 / 栈 emplace() priority_queue
向量 / 数组 (vector) emplace_back() vector
思路:两人在任意节点会面,会面时间 = 两人到达该节点的时间的最大值
dist数组分两个状态,0:没坐骑,1:有坐骑
@#####
不用主动选骑不骑,走到宠物点自动获得坐骑
有坐骑后,所有路都走快的,没有任何代价
起点如果是宠物点,开局就有坐骑
// start:起点(1或n)
// dist:存储结果的二维数组(dist[u][0/1])
void dijkstra(int start, ll dist[][2])
{
// 1. 初始化:所有距离设为无穷大(代表初始不可达)
for(int i=1;i<=n;i++)
dist[i][0] = dist[i][1] = INF;
// 2. 优先队列(小根堆):存 {当前时间, 当前节点, 当前状态}
// 作用:每次取「时间最短」的状态,保证最短路
priority_queue<tuple<ll,int,int>,vector<tuple<ll,int,int>>,greater<>>q;
// 3. 起点初始化:默认状态0(没坐骑),时间=0
dist[start][0] = 0;
q.emplace(0, start, 0);
// 4. 如果起点是宠物点 → 开局直接有坐骑!状态1时间=0
if(is_pet[start]){
dist[start][1] = 0;
q.emplace(0, start, 1);
}
// 5. 堆优化Dijkstra核心循环
while(!q.empty())
{
// 取出当前时间最短的:d=时间,u=节点,s=状态(0/1)
auto [d,u,s] = q.top();
q.pop();
// 剪枝:如果这个时间比已记录的最短时间大,直接跳过
if(d > dist[u][s]) continue;
// ==============================================
// 🔥 坐骑状态切换(最关键!)
// 如果当前是【没坐骑】,且在【宠物节点】
// 免费切换成【有坐骑】,时间不变!
// ==============================================
if(s == 0 && is_pet[u] && dist[u][1] > d)
{
dist[u][1] = d;
q.emplace(d, u, 1);
}
// 6. 遍历当前节点的所有邻边
for(auto [v,w]: g[u])
{
ll cost;
// 根据状态算走路时间:
if(s == 0) cost = w; // 没坐骑:原时间
else cost = w / 2; // 有坐骑:时间减半
// 松弛操作:如果能更短,就更新
if(dist[v][s] > d + cost)
{
dist[v][s] = d + cost;
q.emplace(dist[v][s], v, s);
}
}
}
}

浙公网安备 33010602011771号