题目
题意
众所周知,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
题目大意
本题的题意很好理解。输入多组样例,每组样例首先给出 N ,S ,E ,分别表示车站数量,起点站编号,终点站编号。随后给出一个整数 M 表示图中可以往来的经济线路的数目,之后给出 M 个线路的起止站点号和对应的票价。之后以同样的方式给出 K 个商务线的起止站点和票价。题目要求我们求出由起点到终点的最小票价走法,但要注意最多只能选一条商务线路。最终要得出线路中经过的所有站点、总票价和换乘商务线的站点,不坐商务线输出"Ticket Not Used".
解题思路
本题是一道求最短路的问题,但是对于商务线的选定比较难处理,于是可以使用两种解决方法:
- 起点终点两次Dijkstar,然后枚举商业线(u, v, w),取 min{dis1[u]+dis2[v]+w, dis1[v]+dis2[u]+w},最终再与不走商业线的答案取min;
- 采用分层图最短路,得到的最短路既可以判断是否选取商业线,还能够得到换乘站点。分层图最短路可以借鉴大佬们的博客,例如这篇:多层图最短路讲解
以上两种方法均可以求解本题,本题中我学习采用了第二种方法,学习了一些有关多层图最短路的运用方法。Dijkstar算法关键在于不断向与已得到的生成树中加入边,更新起点到其他所有点的最短距离,同时每次更新时还可以记录对应最短路中每个节点的前驱节点,便于输出路径。通过优先队列把放入的点按照距离起点的距离进行排序也可以提高程序运行速度。有了Dijkstar求解的基本思路,便可以开始考虑如何使用多层图解决商务线的问题了。
对于多层图,我们可以建k+1层图,对于有边的两个点,多建一条到下一层边权为0的单向边,如果走了这条边就表示用了一次机会。这非常适合解决本题,只需要建两层图即可,非常方便地解决了最多选一条商务线的问题。建图时1~n表示第一层,(1+n)~(n+n)代表第二层,所有商务线建一条连接两层路径,随后使用Dijkstar即可。算法中记得加入记录路径的数组 pre[] ,pre[x] = y 表示 x 的前一个站点为 y,路径只要将pre数组回溯一下就能得到。下面就到了最关键的判断商务线的部分了,观察最终走到的终点是第一层的终点还是第二层的终点,如果起点和终点不在一层,说明选用了商务线,发生跳跃的站点位置就是换乘站点位置。这种方法虽然一开始不好理解,但想明白之后还是很清晰简洁的。
另外注意本题中的一些细节,例如输入输出优化,输出格式等。同时本题数据较多,操作较多,需要重置的数组等也比较繁琐,处理时一定要十分细心,否则便会出错。
具体代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <vector>
#define MAXN 100005
#define inf 1e9
using namespace std;
int getint()
{
int x=0,ss=1;
char ch=' ';
while(ch<'0' || ch>'9')
{
ch=getchar();
if(ch=='-') ss=-1;
}
while(ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*ss;
}
struct Edge{
int from,to,nxt,w;
}e[100005];
int head[100005],tot,vis[100005],dis[100005],n;
int pre[100005];
vector<int> v;
void add(int x, int y, int w)
{
e[++tot].to = y;
e[tot].nxt = head[x];
e[tot].w = w;
head[x] = tot;
}
void dijkstar(int s)
{
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
memset(dis,0x3f,sizeof(dis));
dis[s] = 0;
q.push(make_pair(0,s));
while(!q.empty())
{
int x = q.top().second;
q.pop();
if(!vis[x])
{
vis[x] = 1;
for(int i = head[x]; i; i = e[i].nxt)
{
int y = e[i].to;
int w = e[i].w;
if(dis[y] > dis[x] + w)
{
dis[y] = dis[x] + w;
pre[y] = x;
q.push(make_pair(dis[y],y));
}
}
}
}
}
int main(int argc, char** argv)
{
int s,e,cnt1,cnt2,a,b,c,cnt = 0;
while(scanf("%d %d %d",&n,&s,&e) != EOF)
{
int num = 0;
cnt1 = getint();
for(int i = 1; i <= cnt1; i++)
{
a = getint();
b = getint();
c = getint();
add(a,b,c);
add(b,a,c);
add(a+n,b+n,c);
add(b+n,a+n,c);
}
cnt2 = getint();
for(int i = 1; i <= cnt2; i++)
{
a = getint();
b = getint();
c = getint();
add(a,b+n,c);
add(b,a+n,c);
}
add(e,e+n,0);
dijkstar(s);
if(cnt != 0)
{
printf("\n");
}
for (int i = e + n; i != s; i = pre[i])
{
v.push_back(i);
}
v.push_back(s);
if(v[0] - n == v[1])
{
for (int i = v.size()-1; i >= 2; i--)
{
printf("%d ",v[i]);
}
printf("%d\n",v[1]);
printf("Ticket Not Used\n");
printf("%d\n",dis[e+n]);
}
else
{
for (int i = v.size()-1; i >= 1; i--)
{
if(v[i] > n)
{
printf("%d ",v[i] - n);
}
else
{
printf("%d ",v[i]);
num = i;
}
}
printf("%d\n",v[0]-n);
printf("%d\n",v[num]);
printf("%d\n",dis[e+n]);
}
memset(vis,0,sizeof(vis));
memset(head,0,sizeof(head));
v.clear();
tot = 0;
cnt++;
}
return 0;
}