小雨坐地鐵
傳送門
題意:n個站,m條雙向地鐵線路,每條地鐵上地鐵要掏ai元,每坐一站多掏bi元,有ci站。求從s點到t點最少花費。
思路:
顯然dijkstra,但是怎麼建圖呢,是個好問題()。不會分層圖的我大概糾結了一萬年吧(如果直接樸素dijkstra的話,要記錄每個點現在所處的線路,有轉線和繼續該線路走兩種。而繼續該線路走的話要記錄此點在該線路的位置,有兩個方向…總之我寫了賊久最後放棄了)。
看了題解才知道,原來還有分層圖這麼個東西,瞭解了邏輯之後,這道題其實就是分層圖最短路的裸題。思路不難,理解最重要。
邏輯大概就是,有m條地鐵線路,就建m個圖,再另外建一個第m+1圖來存儲前m個圖中相互切換(轉線)的花費。
爲了方便理解,我就把第m+1圖視作陰間,前m個圖視作陽間(。
陰間的點之間並不互聯,而陽間的n個點在陰間都有一個對應的點。這個對應的點與陽間的它自己互聯。但是來去的花費不同。從陰間到陽間相當於轉入陽間的這條地鐵線,花費ai元,從陽間到陰間相當於下線,爲轉向同樣交織在這個站點的其他線路做準備。
由於多條地鐵線如果要轉線,必須有相同站點,那麼從該站點的陰間點到多條線路在該站點的陽間點(分佈在不同的圖層中)的狀態轉移就能模擬轉線的過程。
第的點就代表,第層(從零開始算)圖的第個點。
更多的分層圖最短路可以參考這個博客
代碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(x,y,z) for(int x=(int)y;x<=(int)z;x++)
const ll inf=1e18;
const int maxn=510000;
ll ans[maxn];
inline int read(){
int k=0,j=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') j=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){k=(k<<1)+(k<<3)+ch-'0';ch=getchar();}
return k*j;
}
struct node{
int v;//序號
ll cost;
bool operator <(node b)const{
return cost>b.cost;
}
};
vector<node> G[maxn];
void dijkstra(int s){
fill(ans,ans+maxn,inf);
priority_queue<node> pq;
ans[s]=0;
pq.push(node{s,0});
while(!pq.empty()){
node cur=pq.top();pq.pop();
if(ans[cur.v]<cur.cost) continue;//如果原答案更小 過
for(auto i:G[cur.v]){
if(ans[i.v]>cur.cost+i.cost){
ans[i.v]=cur.cost+i.cost;
pq.push(node{i.v,ans[i.v]});
}
}
}
}
int main(){
int n=read(),m=read(),s=read(),t=read();
forn(i,0,m-1){
int a=read(),b=read(),c=read(),x=read();
//陰間與陽間x互連
G[i*n+x].push_back(node{m*n+x,0});
G[m*n+x].push_back(node{i*n+x,a});
forn(j,2,c){
//陽間xy之間相連
int y=read();
G[i*n+x].push_back(node{i*n+y,b});
G[i*n+y].push_back(node{i*n+x,b});
//陰間與陽間y互連
G[i*n+y].push_back(node{m*n+y,0});
G[m*n+y].push_back(node{i*n+y,a});
x=y;//更新x
}
}
dijkstra(m*n+s);
if(ans[m*n+t]!=inf) printf("%lld\n",ans[m*n+t]);
else printf("-1\n");
}