問題描述
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
數據規模與約定
解題思路
有k次免費飛行的機會,也就意味着有k條路徑權值爲0,這是一個分層圖最短路模版題。
那分層圖怎麼用呢?
先看看分兩層的圖,我們可以做圖如下:
圖中,上面一層是原本的圖,下面一層和上層數據相同,紅線表示兩者之間有路線,重點是藍線和綠線。藍線是上一層到下一層到路線,比如,我們可以通過紅線從本層的1號到本層的2號,那麼我們就可以通過藍線從本層的1號到下一層的2號,藍線代表的權值是另外一條路線。在本題中,所有藍線權值都是0,就是免費線路。而綠線是上一層終點到下一層終點的路線,賦值爲0,作用等下再說。
我們不難發現,這個圖做出來後,直接用dijkstra來計算就可以了,在這個圖中,我們只能從本層中運動(通過紅線),或者從上層到下層(通過藍線或者綠線),每下降一層,就意味着用了一次特殊的通道,因此我們要分k+1層圖。最終我們要計算的結果是從起始點s,到最後一層的終點t。
可能有人有疑問,如果不用k條特殊路線到達終點的路線更短呢?我們的綠線就起作用了,如果你只使用了m條特殊路線,你最終就會到達第m+1層地圖,而所有層的終點都是通過一條權值爲0的路線連接的,所以如果你到第m+1層停下了,那麼這個結果和從s到第k+1層停下一樣。
分層圖是一種概念上的分層,在存儲時,假設第一層各個結點是,那麼第2層就是,依次類推。更多詳細內容見下方代碼。
完整代碼
#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));
}
}
}
}
}