刷题记录:牛客NC26257小雨坐地铁 [分层图跑最短路]

传送门:牛客

题目描述:

题目暂略
输入:
5 2 1 4
2 2 3 1 3 5
2 1 4 2 3 4 5
输出:
7

一道分层图的经典题型,可以细细体会,这道题模拟出了经典的分层图题型

主要思路:

  1. 首先我们那道这道题应该不难想到最短路(这是显然的吧).大多数人应该都是卡在了建边的部分.每一条线路各自的边是好建的,不过是双向边而已.不好想到的是如何完成换线的操作.这个似乎很难完成了
  2. 此时我们仔细想一下我们的换线过程,不就是在地铁站上下车吗.也就是在地铁站下车是不花钱的,上车是花钱的,那么我们是不是能将我们的地铁站看成一个个图中的节点呢,也就是我们的不同地铁线路中有各自的节点,然后我们的地铁站也是一个个节点,然后我们是通过不同线路中的各个节点在不同地铁站中穿梭
  3. 也就是我们的不同地铁线建图并且对应的地铁编号都再与各自的地铁站继续连线.这样我们的地铁就可以下车(不花钱),到地铁站再由地铁站上车到其他线路了(花钱).并且为了方便起见,我们可以假定我们的每一条线路中的编号不再是 1 − > n 1->n 1>n,而是 n ∗ i + u − > n ∗ i + v n*i+u->n*i+v ni+u>ni+v,这样的话建图更加清楚明白.

如果还是不太明白的话,我就样例举个栗子吧:

在这里插入图片描述
最终建图情况如上,中间的12345是地铁站,上下分别是地铁线路

这种分层建图的算法就是我们的分层图了,接下来直接跑 d i j k s t r a dijkstra dijkstra即可

下面是具体的代码部分:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
#include <deque>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
struct Node{
	int v,w;
};
struct heapnode{
	int u,d;
	bool operator<(const heapnode &rhs) const {
		return d>rhs.d;
	}
};
int dis[maxn],vis[maxn];
vector<Node>edge[maxn];
int n,m;
void dij(int S) {
	for(int i=1;i<=n*(m+2);i++) dis[i]=inf;
	dis[S]=0;
	priority_queue<heapnode>q;
	q.push({S,0});
	while(!q.empty()) {
		heapnode f=q.top();q.pop();
		int u=f.u;
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=0;i<edge[u].size();i++) {
			int v=edge[u][i].v;
			if(dis[v]>dis[u]+edge[u][i].w) {
				dis[v]=dis[u]+edge[u][i].w;
				q.push({v,dis[v]});
			}
		}
	}
}
int s,t;
int main() {
	n=read();m=read();s=read();t=read();
	for(int i=1;i<=m;i++) {
		int ai=read(),bi=read(),ci=read();
		int U=read();int V;
		edge[n*i+U].push_back({U,0});
		edge[U].push_back({n*i+U,ai});
		for(int j=2;j<=ci;j++) {
			V=read();
			edge[U+n*i].push_back({V+n*i,bi});
			edge[V+n*i].push_back({U+n*i,bi});
			edge[n*i+V].push_back({V,0});
			edge[V].push_back({n*i+V,ai});
			U=V;
		}
	}
	dij(s);
	if(dis[t]==inf) printf("-1\n");
	else cout<<dis[t]<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值