[ACM]【分層圖最短路/dijkstra】牛客算法週週練5 小雨坐地鐵

小雨坐地鐵

傳送門
題意:n個站,m條雙向地鐵線路,每條地鐵上地鐵要掏ai元,每坐一站多掏bi元,有ci站。求從s點到t點最少花費。
在這裏插入圖片描述

思路:

顯然dijkstra,但是怎麼建圖呢,是個好問題()。不會分層圖的我大概糾結了一萬年吧(如果直接樸素dijkstra的話,要記錄每個點現在所處的線路,有轉線和繼續該線路走兩種。而繼續該線路走的話要記錄此點在該線路的位置,有兩個方向…總之我寫了賊久最後放棄了)。
看了題解才知道,原來還有分層圖這麼個東西,瞭解了邏輯之後,這道題其實就是分層圖最短路的裸題。思路不難,理解最重要。
邏輯大概就是,有m條地鐵線路,就建m個圖,再另外建一個第m+1圖來存儲前m個圖中相互切換(轉線)的花費
爲了方便理解,我就把第m+1圖視作陰間,前m個圖視作陽間(。
陰間的點之間並不互聯,而陽間的n個點在陰間都有一個對應的點。這個對應的點與陽間的它自己互聯。但是來去的花費不同。從陰間到陽間相當於轉入陽間的這條地鐵線,花費ai元,從陽間到陰間相當於下線,爲轉向同樣交織在這個站點的其他線路做準備。
由於多條地鐵線如果要轉線,必須有相同站點,那麼從該站點的陰間點到多條線路在該站點的陽間點(分佈在不同的圖層中)的狀態轉移就能模擬轉線的過程。
i×n+xi\times n+x的點就代表,第ii層(從零開始算)圖的第xx個點。
更多的分層圖最短路可以參考這個博客

代碼:

#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");
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章