题目描述
Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在n个城市设有业务,设这些城市分别标记为 0到 n-1,一共有 m 种航线,每种航线连接两个城市,并且航线有一定的价格。
Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 kk 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少?
输入
第一行三个整数 n,m,k,分别表示城市数,航线数和免费乘坐次数。
接下来一行两个整数 s,t,分别表示他们出行的起点城市编号和终点城市编号。
接下来 m 行,每行三个整数a,b,c表示存在一种航线,能从城市 a 到达城市 b,或从城市 b 到达城市 a,价格为 c。
输出
输出一行一个整数,为最少花费。
Solution:
乍一看不就是多了一个免费功能的最短路,最初的想法应该就是跑一遍最短路贪心把最贵的k条边免费掉,但是这显然可以被证误,很容易就能找出反例。
那么这题运用的是分层图的思路,什么是分层图呢?其实分层图也是一种dp的思想我们可以把原图分为k+1张完全相同的图,也相当于将每个点拆成k+1个点,我们把这k+1张图就看成1楼、2楼、3楼等等这个样子,然后原本有连边的点除了同层的对应点连上对应权值的边,再向下一层对应的点连上一条权值为0的单向边,我们也就可以理解成,每上升一楼,我们就会消耗一次免费机会,然后再在把这k+1层图连接成的大图上跑最短路即可,起点是第一层的起点,终点就是最后一层的终点。值得注意的是,向下一层的连边一定得是单向边,毕竟没有消耗完免费次数还能回来的说法。
另外,luogu在原基础上还增加了一组hack数据为k>m的情况,此时只是按照上面步骤最后会始终跑不到终点,此时只要在每层的终点之间加个权值为0的单向边即可
Code:
1 #include<bits/stdc++.h>
2 using namespace std;
3 struct Xhs{
4 int id,Lenth;
5 friend bool operator <(Xhs a,Xhs b){
6 return a.Lenth>b.Lenth;
7 }
8 };
9 priority_queue<Xhs>Q;
10 int Head[5000007];
11 int N,M,K,S,T;
12 const int inf=INT_MAX;
13 int tot,Dis[5000007],vis[5000007];
14 struct Node{
15 int X;
16 int Y;
17 int Val;
18 int Next;
19 }Edge[5000007];
20 void AddEdge(int x,int y,int val)
21 {
22 Edge[++tot].Next=Head[x];
23 Head[x]=tot;
24 Edge[tot].X=x;
25 Edge[tot].Y=y;
26 Edge[tot].Val=val;
27 }
28 int main()
29 {
30 cin>>N>>M>>K;
31 scanf("%d%d",&S,&T);
32 for(int i=1;i<=M;i++){
33 int a,b,c;
34 scanf("%d%d%d",&a,&b,&c);
35 AddEdge(a,b,c);
36 AddEdge(b,a,c);
37 for(int j=1;j<=K;j++){
38 AddEdge(a+N*j,b+N*j,c);
39 AddEdge(b+N*j,a+N*j,c);
40 AddEdge(a+N*(j-1),b+N*j,0);
41 AddEdge(b+N*(j-1),a+N*j,0);
42 }
43 }
44 for(int i=0;i<=N*(K+1);i++)<% Dis[i]=inf;%>
45 for(int i=1;i<=K;i++)
46 AddEdge(T+(i-1)*N,T+i*N,0);
47 Dis[S]=0;
48 Q.push({S,0});
49 while(!Q.empty()){
50 int QAQ=Q.top().Lenth;
51 int TAT=Q.top().id;
52 Q.pop();
53 if(vis[TAT]) continue;
54 vis[TAT]=1;
55 for(int i=Head[TAT];i;i=Edge[i].Next){
56 if(Dis[Edge[i].Y]>Dis[Edge[i].X]+Edge[i].Val){
57 Dis[Edge[i].Y]=Dis[Edge[i].X]+Edge[i].Val;
58 if(!vis[Edge[i].Y]) Q.push({Edge[i].Y,Dis[Edge[i].Y]});
59 }
60 }
61 }
62 cout<<Dis[T+N*K];
63 return 0;
64 }