TT的旅行日记(Dijkstra|分层最短路)

问题描述

众所周知,TT 有一只魔法猫。

今天他在 B 站上开启了一次旅行直播,记录他与魔法猫在喵星旅游时的奇遇。 TT 从家里出发,准备乘坐猫猫快线前往喵星机场。猫猫快线分为经济线和商业线两种,它们的速度与价钱都不同。当然啦,商业线要比经济线贵,TT 平常只能坐经济线,但是今天 TT 的魔法猫变出了一张商业线车票,可以坐一站商业线。假设 TT 换乘的时间忽略不计,请你帮 TT 找到一条去喵星机场最快的线路,不然就要误机了!

输入

输入包含多组数据。每组数据第一行为 3 个整数 N, S 和 E (2 ≤ N ≤ 500, 1 ≤ S, E ≤ 100),即猫猫快线中的车站总数,起点和终点(即喵星机场所在站)编号。

下一行包含一个整数 M (1 ≤ M ≤ 1000),即经济线的路段条数。

接下来有 M 行,每行 3 个整数 X, Y, Z (1 ≤ X, Y ≤ N, 1 ≤ Z ≤ 100),表示 TT 可以乘坐经济线在车站 X 和车站 Y 之间往返,其中单程需要 Z 分钟。

下一行为商业线的路段条数 K (1 ≤ K ≤ 1000)。
接下来 K 行是商业线路段的描述,格式同经济线。
所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。

输出

对于每组数据,输出3行。第一行按访问顺序给出 TT 经过的各个车站(包括起点和终点),第二行是 TT 换乘商业线的车站编号(如果没有使用商业线车票,输出"Ticket Not Used",不含引号),第三行是 TT 前往喵星机场花费的总时间。

本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行

输入样例

4 1 4
4
1 2 2
1 3 3
2 4 4
3 4 5
1
2 4 3

输出样例

1 2 4
2
5

问题分析

首先需要掌迪杰斯特拉算法(Dijkstra Algorithm),学会求单源最短路。问题设计到商业线和经济线,两种线路是不等价的,需要分开处理,下面介绍两种思路。
• 思路 1
• 题目给定了起点与终点,而且要求商业线最多乘坐一次
• 可以枚举每一条商业线,计算起点到u的最短路以及v到
终点的最短路再加上该商业线所花费的时间
• 以起点为源点求单源最短路,得到 dis1 数组
• 再以终点为源点求单源最短路,得到 dis2 数组
• 枚举商业线(u, v, w),取 min{dis1[u]+dis2[v]+w, dis1[v]+dis2[u]+w},最终再与不走商业线的答案取min
• 思路 2
• 跑一次单源最短路(变形,可以理解成分层最短路,有动态规划的思想在里面),记录答案 dis[u][0/1]
• dis[u][0] 表示从起点到结点 u 没有经过商业线时的最短路,在松弛的时候可以选择商业线或者经济线
• dis[u][1] 表示从起点到结点 u 经过商业线后的最短路,在松弛的时候只能选择经济线

#include<stdio.h>
#include<queue>
#include<utility>
using namespace std;
int head1[510],head2[510],vis[510][2],dis[510][2],pre[1010],ans[510];
struct Edge{
	int to,next,w;
}e1[2010],e2[2010];
const int inf = 5 * 1e8;
void add(Edge* e, int* head, int n) {
//加边,前向星
	int X, Y, Z;
	for (int i = 1; i <= n; i++) {
		scanf("%d%d%d",&X,&Y,&Z);
		e[2 * i].to = Y, e[2 * i].next = head[X], head[X] = 2 * i;
		e[2 * i - 1].to = X, e[2 * i - 1].next = head[Y], head[Y] = 2 * i - 1;
		e[2 * i].w = e[2 * i - 1].w = Z;
	}
}
priority_queue<pair<pair<int, int>, int> >q;
void dijkstra(int n, int s) {
	dis[s][0] =dis[s][1]= 0;
	pre[s] = s;
	q.push(make_pair(make_pair(0, 0), s));
	//((x,0/1),y)第一个数表示距离,第二个数标识是否经过商业线,第三个数表示到达哪个点
	while (q.size()) {
		int x = q.top().second, b = q.top().first.second;
		q.pop();
		if (vis[x][b])continue;//如果访问过,继续执行
		vis[x][b] = 1;
		if (b) {
		//经过商业线了,只能加经济线
			for (int i = head1[x]; i; i = e1[i].next) {
				int y = e1[i].to, w = e1[i].w;
				if (dis[y][1] > dis[x][1] + w) {
					dis[y][1] = dis[x][1] + w;
					pre[y+n] = x+n;
					q.push(make_pair(make_pair(-dis[y][1], 1), y));
				}
			}
		}
		else {
		//未经过商业线
			for (int i = head1[x]; i; i = e1[i].next) {
				int y = e1[i].to, w = e1[i].w;
				if (dis[y][0] > dis[x][0] + w) {
					dis[y][0] = dis[x][0] + w;
					if (dis[y][1] > dis[y][0])dis[y][1] = dis[y][0],pre[y+n]=y;
					pre[y]=x;
					q.push(make_pair(make_pair(-dis[y][0], 0), y));
				}
			}
			for (int i = head2[x]; i; i = e2[i].next) {
				int y = e2[i].to, w = e2[i].w;
				if (dis[y][1] > dis[x][0] + w) {
					dis[y][1] = dis[x][0] + w;
					pre[y+n] = x;
					q.push(make_pair(make_pair(-dis[y][1], 1), y));
				}
			}
		}
	}
}
int main(){
	int N,S,E,M,K,X,v;
	bool flag=false;
	while(scanf("%d%d%d%d",&N,&S,&E,&M)!=EOF){
	v=0;
	for(int i=1;i<=N;i++)
		vis[i][0]=vis[i][1]=head1[i]=head2[i]=0,
		dis[i][0]=inf,dis[i][1]=inf;
	add(e1,head1,M);
	scanf("%d",&K);
	add(e2,head2,K);
	
	dijkstra(N,S);
	
	int tot=0;X=E+N;
	while(pre[X]!=X){
		if(pre[X]+N!=X){
			if(X>N){
				if(pre[X]<N)v=pre[X];//找到换乘商业线编号
				ans[++tot]=X-N;
			}
			else ans[++tot]=X;
		}	
		X=pre[X];
	}
	//按格式要求输出,每组之间有一个换行符
	if(flag)printf("\n");
	else flag=true;
	printf("%d",S);
	for(int i=tot;i;i--)
		printf(" %d",ans[i]);
	
	if(v)printf("\n%d\n%d\n",v,dis[E][1]);
	else printf("\nTicket Not Used\n%d\n",dis[E][0]);
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值