1. 程式人生 > >p4568 [JLOI2011]飛行路線

p4568 [JLOI2011]飛行路線

ans use code 表示 直接 城市 string cst 現在

Description

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

只有一行,包含一個整數,為最少花費

分析

? 明顯,此題為最短路問題,但是考慮到可以免費搭乘(即直接通過一條邊無需費用.)

這種問題有一個較官方的名字 分層圖最短路問題

分層圖最短路是指在可以進行分層圖的圖上解決最短路問題.

是不是聽起來就很nb?

具體分層圖是啥,我也不知道

一般模型:

? 在圖上,有\(k\)次機會可以直接通過一條邊,問起點與終點之間的最短路徑.

很明顯,這道題是一個裸的分層圖最短路問題 (貌似這類問題都挺裸的 emm

解法

我們設

\(dis[i][j]\)代表到達\(i\)用了\(j\)次免費機會的最小花費.

\(vis[i][j]\)代表到達\(i\)用了\(j\)次免費機會的情況是否出現過.

對於某條路徑我們可以選擇使用機會,也可以選擇不使用機會.

討論這兩種情況即可

#include<cstdio>
#include<queue>
#include<cstring>
#define R register
#define N 20008
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s>'9' or s<'0'){if(s=='-')f=-1;s=getchar();}
    while(s>='0' and s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int head[N],tot,n,m,s,t,k;
int dis[N][15],ans=2147483647;
bool vis[N][15];
struct cod{int u,v,w;}edge[N*6+8];
inline void add(int x,int y,int z)
{
    edge[++tot].u=head[x];
    edge[tot].v=y;
    edge[tot].w=z;
    head[x]=tot;
}
struct coc{
    int u,d,used;
    bool operator <(const coc&a) const 
    {
        return d>a.d;
    }
};
inline void dijkstra()
{
    memset(dis,127,sizeof dis);
    dis[s][0]=0;
    priority_queue<coc>q;
    q.push((coc){s,0,0});
    while(!q.empty())
    {
        int u=q.top().u,now=q.top().used;
        q.pop();
        if(vis[u][now])continue;
        vis[u][now]=true;
        for(R int i=head[u];i;i=edge[i].u)
        {
            if(now<k and !vis[edge[i].v][now+1] and dis[edge[i].v][now+1]>dis[u][now])//當前路徑,使用一次免費機會.註意判斷 now<k
            {
                dis[edge[i].v][now+1]=dis[u][now];
                q.push((coc){edge[i].v,dis[edge[i].v][now+1],now+1});
            }
            if(!vis[edge[i].v][now] and dis[edge[i].v][now]>dis[u][now]+edge[i].w)//當前路徑,不使用免費機會
            {
                dis[edge[i].v][now]=dis[u][now]+edge[i].w;
                q.push((coc){edge[i].v,dis[edge[i].v][now],now});
            }
        }
    }
}
int main()
{
    in(n),in(m),in(k);
    in(s),in(t);
    s++;t++;//這裏個人習慣不同.我選擇記錄編號為1~n
    for(R int i=1,x,y,z;i<=m;i++)
    {
        in(x),in(y),in(z);
        x++;y++;
        add(x,y,z);
        add(y,x,z);
    }
    dijkstra();//直接跑dijkstra
    for(R int i=0;i<=k;i++)
        ans=min(ans,dis[t][i]);//到達t我們需要對使用免費機會的情況枚舉.取min
    printf("%d",ans);
}

p4568 [JLOI2011]飛行路線