P4568 [JLOI2011]飞行路线(分层图)

分层图板子题

题目描述

Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在(n)城市设有业务,设这些城市分别标记为 (0)(n-1),一共有 (m) 种航线,每种航线连接两个城市,并且航线有一定的价格。
Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 (k) 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少?

输入格式

数据的第一行有三个整数,(n,m,k),分别表示城市数,航线数和免费乘坐次数。
第二行有两个整数,(s,t),分别表示他们出行的起点城市编号和终点城市编号。
接下来有 (m) 行,每行三个整数,(a,b,c),表示存在一种航线,能从城市 (a) 到达城市 (b),或从城市 (b) 到达城市 (a),价格为 (c)

输出格式

输出一行一个整数,为最少花费。


建立 ((k+1)n) 个点,其中第 (i*n+j) 个表示的是已经使用了(i)次免费机会,目前在第 (j) 个实际节点的最短距离
这是一个分层的感觉,以只能用一次免费机会为例,没用过的是一张图,用过的是一张图,这每张图内部的边就按照题目信息连接就行
然后还有两张图之间的边,像这样,(u',v')指的是用过免费机会以后的那个图:

(u ightarrow v',v ightarrow u')的权值为(0),意思是花费(0)的代价可以用一次免费机会来走这些边
而在每张图内部的边权值是(len),意思时不花费免费机会需要用 (len) 的代价来走

然后跑一边 dij 就行,但是当他的终点是(end),实际路程要为:(min_{i=0}^k dis(i*n+end))
因为不一定每一次机会都要用

在我们构建的这张分层的图中,点数是 ((k+1)n),边数是 (m(2k+1))

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
int n,m,k;
int start,end;
int nex[2100006],to[2100006],w[2100006];
int fir[110006],tot;
int heap[110006],size;
int dis[110006],in[110006];
inline void add(int u,int v,int len){
	to[++tot]=v;w[tot]=len;
	nex[tot]=fir[u];fir[u]=tot;
}
inline void link(int u,int v,int len){
	add(u,v,len);add(v,u,len);
}
inline void push(int x){
	heap[++size]=x;
	reg int i=size,fa;
	while(i>1){
		fa=i>>1;
		if(dis[heap[fa]]<=dis[heap[i]]) return;
		std::swap(heap[fa],heap[i]);i=fa;
	}
}
inline int pop(){
	int ret=heap[1];heap[1]=heap[size--];
	int i=1,ls,rs;
	while((i<<1)<=size){
		ls=i<<1;rs=ls|1;
		if(rs<=size&&dis[heap[rs]]<dis[heap[ls]]) ls=rs;
		if(dis[heap[ls]]>=dis[heap[i]]) break;
		std::swap(heap[i],heap[ls]);i=ls;
	}
	return ret;
}
inline int dij(){
	std::memset(dis,0x3f,sizeof dis);dis[start]=0;
	push(start);in[start]=1;
	while(size){
		reg int u=pop(),v;in[u]=0;
		for(reg int i=fir[u];i;i=nex[i]){
			v=to[i];
			if(dis[v]>dis[u]+w[i]){
				dis[v]=dis[u]+w[i];
				if(!in[v]) push(v),in[v]=1;
			}
		}
	}
	int ans=2e9;
	for(reg int i=0;i<=k;i++) ans=std::min(ans,dis[i*n+end]);
	return ans;
}
int main(){
	n=read(),m=read(),k=read();
	start=read()+1;end=read()+1;
	for(reg int u,v,len,i=1;i<=m;i++){
		u=read()+1;v=read()+1;len=read();
		link(u,v,len);
		for(reg int j=1;j<=k;j++) link(j*n+u,j*n+v,len),add((j-1)*n+u,j*n+v,0),add((j-1)*n+v,j*n+u,0);
	}
	std::printf("%d",dij());
	return 0;
}
原文地址:https://www.cnblogs.com/suxxsfe/p/12689916.html