[ACM]【分层图最短路/dijkstra】牛客算法周周练5 小雨坐地铁

小雨坐地铁

传送门
题意:n个站,m条双向地铁线路,每条地铁上地铁要掏ai元,每坐一站多掏bi元,有ci站。求从s点到t点最少花费。
在这里插入图片描述

思路:

显然dijkstra,但是怎么建图呢,是个好问题()。不会分层图的我大概纠结了一万年吧(如果直接朴素dijkstra的话,要记录每个点现在所处的线路,有转线和继续该线路走两种。而继续该线路走的话要记录此点在该线路的位置,有两个方向…总之我写了贼久最后放弃了)。
看了题解才知道,原来还有分层图这么个东西,了解了逻辑之后,这道题其实就是分层图最短路的裸题。思路不难,理解最重要。
逻辑大概就是,有m条地铁线路,就建m个图,再另外建一个第m+1图来存储前m个图中相互切换(转线)的花费
为了方便理解,我就把第m+1图视作阴间,前m个图视作阳间(。
阴间的点之间并不互联,而阳间的n个点在阴间都有一个对应的点。这个对应的点与阳间的它自己互联。但是来去的花费不同。从阴间到阳间相当于转入阳间的这条地铁线,花费ai元,从阳间到阴间相当于下线,为转向同样交织在这个站点的其他线路做准备。
由于多条地铁线如果要转线,必须有相同站点,那么从该站点的阴间点到多条线路在该站点的阳间点(分布在不同的图层中)的状态转移就能模拟转线的过程。
i × n + x i\times n+x i×n+x的点就代表,第 i i i层(从零开始算)图的第 x x x个点。
更多的分层图最短路可以参考这个博客

代码:

#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");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值