luoguP4568 [JLOI2011]飞行路线(分层图最短路模版题)

问题描述

Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在nn个城市设有业务,设这些城市分别标记为 0 到 n−1,一共有 m 种航线,每种航线连接两个城市,并且航线有一定的价格。
Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 k 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少?

Input

数据的第一行有三个整数,n,m,k,分别表示城市数,航线数和免费乘坐次数。

第二行有两个整数,s,t,分别表示他们出行的起点城市编号和终点城市编号。

接下来有 m 行,每行三个整数,a,b,c,表示存在一种航线,能从城市 a 到达城市 b,或从城市 b 到达城市 a,价格为 c。

Output

输出一行一个整数,为最少花费。

Sample input

5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100

Sample output

8

数据规模与约定

2n104,1m5×104,0k10,0s,t,a,bn,a!=b,0c1032\le n\le 10^4,1\le m \le 5\times 10^4,0\le k \le 10,0\le s,t,a,b\le n,a!=b,0\le c\le 10^3

解题思路

有k次免费飞行的机会,也就意味着有k条路径权值为0,这是一个分层图最短路模版题。

那分层图怎么用呢?

先看看分两层的图,我们可以做图如下:
在这里插入图片描述
图中,上面一层是原本的图,下面一层和上层数据相同,红线表示两者之间有路线,重点是蓝线和绿线。蓝线是上一层到下一层到路线,比如,我们可以通过红线从本层的1号到本层的2号,那么我们就可以通过蓝线从本层的1号到下一层的2号,蓝线代表的权值是另外一条路线。在本题中,所有蓝线权值都是0,就是免费线路。而绿线是上一层终点到下一层终点的路线,赋值为0,作用等下再说。

我们不难发现,这个图做出来后,直接用dijkstra来计算就可以了,在这个图中,我们只能从本层中运动(通过红线),或者从上层到下层(通过蓝线或者绿线),每下降一层,就意味着用了一次特殊的通道,因此我们要分k+1层图。最终我们要计算的结果是从起始点s,到最后一层的终点t。

可能有人有疑问,如果不用k条特殊路线到达终点的路线更短呢?我们的绿线就起作用了,如果你只使用了m条特殊路线,你最终就会到达第m+1层地图,而所有层的终点都是通过一条权值为0的路线连接的,所以如果你到第m+1层停下了,那么这个结果和从s到第k+1层停下一样。

分层图是一种概念上的分层,在存储时,假设第一层各个结点是1n1\sim n,那么第2层就是n+12nn+1\sim 2n,依次类推。更多详细内容见下方代码。

完整代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;

const int maxn=5000000+10;//数据范围... ...无语,以后我开到百万级别的
struct node
{
    int to,w,next;
};
node edge[maxn];
int head[maxn],dis[maxn];
int len,n,m,k,s,t;
bool visit[maxn];

void add(int x,int to,int w);
void dijkstra(int s);

int main()
{
    cin>>n>>m>>k;
    cin>>s>>t;//s->t
    for (int i=1; i<=m; i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
        add(b,a,c);
        for(int j=1;j<=k;++j)
        {
            add(a+(j-1)*n,b+j*n,0);
            add(b+(j-1)*n,a+j*n,0);
            add(a+j*n,b+j*n,c);
            add(b+j*n,a+j*n,c);
        }
    }
    for(int i=1;i<=k;++i)
    {
        add(t+(i-1)*n,t+i*n,0);
    }//预防没有用k次机会就到了

    dijkstra(s);

    cout<<dis[t+n*k]<<endl;

    return 0;
}
void add(int x,int to,int w)
{
    len++;
    edge[len].to=to; edge[len].w=w;
    edge[len].next=head[x];
    head[x]=len;
}
void dijkstra(int s)
{
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;

    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > points;
    //代码返回pair的比较结果,先按照pair的first元素升序,first元素相等时,再按照second元素升序:
    points.push(make_pair(0,s));//第一个参数指从s到第二个参数到距离

    while(!points.empty())
    {
        int x=points.top().second;
        points.pop();
        if(!visit[x])
        {
            visit[x]=true;
            for(int i=head[x];i;i=edge[i].next)
            {
                int to=edge[i].to;
                if(dis[to]>dis[x]+edge[i].w)
                {
                    dis[to]=dis[x]+edge[i].w;
                    points.push(make_pair(dis[to],to));
                }
            }
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章