洛谷题单指南-最短路-P4568 [JLOI2011] 飞行路线
原题链接:https://www.luogu.com.cn/problem/P4568
题意解读:n个节点带权图中,从起点走到终点,最多有k条边长度可以清0,求最短路径长度是多少。
解题思路:
有k条边长度可以视为0,也就是从每一个点走到邻点总有一条长度为0的边,要确保走长度为0的边不超过k次,可以用到分层图的技巧。
所谓分层图,就是建立0~k一共k+1个图,每个图内部节点关系完全一样。
如此以来,图中节点编号x需要调整为第k层是x+n*k,可以确保不会重复。
对于输入给定的任意一条边u->v,在每一层图中建立双向连接,边长为给定权值;
同时还要将每一层的u与下一层的v相连,也要将每一层的v与下一层的u相连,边长为0。
这样从起点s到第k层的终点t+n*k的最短路径,就是正好走了k次0边后所能得到的最短路径长度。
需要注意,题目要求是不超过k次走长度为0的路径,因此允许在中间任意一层提前到达终点,只需要将相邻层的终点用一条长度为0的边相连即可。
完成建图之后,跑一遍Dijikstra算法就大功告成。
100分代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 110005, M = 2200005;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
bool vis[N];
int n, m, k;
int s, t;
void add(int a, int b, int c)
{
e[++idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a]= idx;
}
void dijikstra()
{
memset(dist, 0x3f, sizeof(dist));
dist[s] = 0;
priority_queue<PII, vector<PII>, greater<PII>> pq;
pq.push({0, s});
while(pq.size())
{
PII p = pq.top(); pq.pop();
int u = p.second;
if(vis[u]) continue;
vis[u] = true;
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(dist[v] > dist[u] + w[i])
{
dist[v] = dist[u] + w[i];
pq.push({dist[v], v});
}
}
}
}
int main()
{
cin >> n >> m >> k;
cin >> s >> t;
memset(h, -1, sizeof(h));
for(int i = 1; i <= m; i++)
{
int u, v, w;
cin >> u >> v >> w;
for(int j = 0; j <= k; j++)
{
add(u + n * j, v + n * j, w); //将当前层的u->v连起来
add(v + n * j, u + n * j, w); //将当前层的v->u连起来
}
for(int j = 0; j < k; j++)
{
add(u + n * j, v + n * (j + 1), 0); //将当前层的u与下一层的v连起来
add(v + n * j, u + n * (j + 1), 0); //将当前层的v与下一层的u连起来
}
}
for(int j = 0; j < k; j++) //将相邻层的终点连起来,应对跑到终点花费为0的情况,可以提前走到终点,不需要跳跃所有层
{
add(t + n * j, t + n * (j + 1), 0);
}
dijikstra();
cout << dist[t + n * k];
return 0;
}
浙公网安备 33010602011771号