• 【最短路/Dp】飞行路线(洛谷P4568)


    题目描述

    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 }
  • 相关阅读:
    SQL Server 2005存储过程示例
    SQL Server 存储过程
    SQL Server 2005存储过程示例
    转正申请书
    SQL注入天书
    转:毕业半年,我是如何从一名程序员成长为一名项目经理
    DIMFOM
    MONSA
    GLOBSYMM
    MASSHA
  • 原文地址:https://www.cnblogs.com/Takarada-Rikka/p/13452701.html
Copyright © 2020-2023  润新知