P4568 [JLOI2011]飞行路线(分层图最短路)
题目描述:
Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在n个城市设有业务,设这些城市分别标记为 0到 n−1,
一共有 m 种航线,每种航线连接两个城市,并且航线有一定的价格。Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。
航空公司对他们这次旅行也推出优惠,他们可以免费在最多 k 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少?
思路:分层图,把图分为k+1层,每上一层的边的起点对下一层的相同边的终点连一条权值为0的边,表示可以免费乘坐,
从上一层到下一层只会选择一条权值为0的边,所以再跑最短路就能选择出k条边来免费通过。
注意k>m的情况,我们需要把每一层的终点对下一层终点连权值为0的边,这样就能在之前就到达终点然后直接走到
最后一层的终点,或者就直接把k取min(k,m);
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 10000005; struct edge { int f, t,w,nxt; }e[maxn*2]; int hd[maxn], tot; void add(int f, int t,int w) { e[++tot] = { f,t,w,hd[f] }; hd[f] = tot; } int dis[maxn]; bool vis[maxn]; struct node { int dis,id; bool operator<(node b)const { return dis > b.dis; } }; int dj(int s,int t) { memset(dis, 0x3f, sizeof(dis)); memset(vis, 0, sizeof(vis)); priority_queue<node>q; dis[s] = 0; q.push({ 0,s }); while (!q.empty()) { node t = q.top(); q.pop(); if (vis[t.id])continue; vis[t.id] = 1; int u = t.id; for (int i = hd[u]; i; i = e[i].nxt) { int v = e[i].t; if (vis[v])continue; if (dis[u] + e[i].w < dis[v]) { dis[v] = dis[u] + e[i].w; q.push({ dis[v], v }); } } } return dis[t]; } int n, m, k; int s, t; int main() { //freopen("test.txt", "r", stdin); scanf("%d%d%d", &n, &m, &k); k = min(m, k); scanf("%d%d", &s, &t); for (int i = 1; i <= m; i++) { int a, b,w; scanf("%d%d%d", &a, &b, &w); for (int j = 0; j <= k; j++) { add(j * n + a, j * n + b, w); add(j * n + b, j * n + a, w); if (j != 0) { //上一层与下一层连边 add((j - 1) * n + a, j * n + b, 0); add((j - 1) * n + b, j * n + a, 0); } } } printf("%d",dj(s, k * n + t)); return 0; }