Luogu P4568 [JLOI2011]飞行路线【题解】(分层图最短路模板)

题目描述

A l i c e Alice Alice B o b Bob Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 n n n个城市设有业务,设这些城市分别标记为 0 0 0 n − 1 n-1 n1,一共有 m m m种航线,每种航线连接两个城市,并且航线有一定的价格。

A l i c e Alice Alice B o b Bob Bob现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 k k k种航线上搭乘飞机。那么 A l i c e Alice Alice B o b Bob Bob这次出行最少花费多少?

输入输出格式

输入格式:

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

输出格式:

只有一行,包含一个整数,为最少花费。

输入输出样例
输入样例#1:

5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100

输出样例#1:

8

说明

对于30%的数据, 2 ≤ n ≤ 50 2 \le n \le 50 2n50, 1 ≤ m ≤ 300 1 \le m \le 300 1m300, k = 0 k=0 k=0,
对于50%的数据, 2 ≤ n ≤ 600 2 \le n \le 600 2n600, 1 ≤ m ≤ 6000 1 \le m \le 6000 1m6000, 0 ≤ k ≤ 1 0\le k \le 1 0k1;
对于100%的数据, 2 ≤ n ≤ 10000 2 \le n \le 10000 2n10000, 1 ≤ m ≤ 50000 1 \le m \le 50000 1m50000, 0 ≤ k ≤ 10 0≤k≤10 0k10,
0 ≤ c ≤ 10000 ≤ s , t &lt; n 0 \le c \le 10000≤s,t&lt;n 0c10000s,t<n, 0 ≤ a 0≤a 0a, b &lt; n b&lt;n b<n, a ≠ b a≠b a̸=b, 0 ≤ c ≤ 1000 0≤c≤1000 0c1000


今上午考试考了这道题,数组开小了 Q A Q QAQ QAQ

这道题是分层图最短路问题
就是说在最短路的基础上有几条边的代价可以修改为零。

基本模型:

在给定的图上,有k次机会可以直接通过一条边,问起点与终点之间的最短路径

这样的题好像都挺裸的

做法是分层,
这里详细说一下吧

我们建立第 0 0 0层至第 k k k层一共 k + 1 k+1 k+1层图,每一层的边都是一样的,第 i i i层表示用掉 i i i次机会所能到达的最小价值。

int u=read()+1,v=read()+1,w=read();//因为此题点从0开始,个人习惯从1开始,就+1
for(int j=0;j<=k;++j){
	add_edge(u+j*n,v+j*n,w);
	add_edge(v+j*n,u+j*n,w);//每n个一层
}

像这样。

然后在每层图之间建边,因为有 k k k次机会,所以,把第 i i i层的节点建一条通往 i + 1 i+1 i+1层的边,边权为 0 0 0

for(int j=0;j<k;++j)
	add_edge(u+j*n,v+j*n+n,0),add_edge(v+j*n,u+j*n+n,0);

至此,边就建完啦!

然后就是一个一丁都不用改的最短路(个人推荐堆优 d i j k s t r a dijkstra dijkstra)

(关于SPFA,它死了)

对了!点数组的大小要开 k + 1 k+1 k+1倍!上午考试只开了 k k k倍挂了一个点 Q A Q QAQ QAQ

奉上完整代码:
#include<iostream>
#include<cstdio>
#include<ctype.h>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
typedef pair<int,int> pairs;
priority_queue<pairs,vector<pairs>,greater<pairs> > q;
inline int read(){
	int x=0,f=0;char ch=getchar();
	while(!isdigit(ch))f|=ch=='-',ch=getchar();
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return f?-x:x;
}
int head[110007],cnt;
bool vis[110007];
int dis[110007];
int n,m,k,s,t,ans=99999999;
struct Edge{
	int next,to,w;
}edge[20000007];
inline void add_edge(int from,int to,int w){
	edge[++cnt].next=head[from];edge[cnt].w=w;
	edge[cnt].to=to;head[from]=cnt;
}
inline void dijkstra(){
	memset(dis,0x3f,sizeof dis);
	dis[s]=0;q.push(make_pair(dis[s],s));
	while(!q.empty()){
		int x=q.top().second;q.pop();
		if(vis[x])continue;vis[x]=1;
		for(int i=head[x];i;i=edge[i].next){
			int to=edge[i].to;
			if(dis[to]>dis[x]+edge[i].w){
				dis[to]=dis[x]+edge[i].w;
				q.push(make_pair(dis[to],to));
			}
		}
	}
}
int main(){
//	freopen("pass.in","r",stdin);
//	freopen("pass.out","w",stdout);
	n=read(),m=read(),k=read();
	s=read()+1,t=read()+1;
	for(int i=1;i<=m;++i){
		int u=read()+1,v=read()+1,w=read();
		for(int j=0;j<k;++j)add_edge(u+j*n,v+j*n+n,0),add_edge(v+j*n,u+j*n+n,0);
		for(int j=0;j<=k;++j){
			add_edge(u+j*n,v+j*n,w);
			add_edge(v+j*n,u+j*n,w);
		}
	}
	dijkstra();
	for(int i=0;i<=k;++i)ans=min(ans,dis[t+i*n]);
	printf("%d",ans);
//	fclose(stdin);fclose(stdout);
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值