问题描述
众所周知,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;
}