B - TT 的旅行日记
题目描述
众所周知,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算法,当然不是纯粹的Dijkstra,需要做一些变形。在这个题目中我们首先初始化dis[s] = 0,dis[i] = inf(无穷大),将s加入最小堆中,然后每次从堆中取处一个点,然后对其进行松弛操作。在这个图中所有的边都是正边,因此每个点只会被最小堆弹出一次。即一旦某个点被最小堆弹出,则不会松弛,dis的值为最短路。在这个题目中,我们可以有两种思路:
思路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经过商业线后的最短路,在松弛的时候只能选择经济线。
这里我们选用的是第一种思路,这里我们首先从起点和终点跑两边Dijkstra,并且使用两个path数组记录路径信息,其中path[i]是结点i的前一个邻接点。然后枚举商业线,求出商业线中的最小值,然后和不走商业线的答案取min。最后分走商业线和不走商业线两种情况输出路径。这里我们使用的输出路径的方法是使用一个双向队列deque类型来存储,即将path数组中的路径信息经过转换copy到双向队列中。最后再输出。
实现代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <utility>
#define _for(i,a,b) for(int i = (a); i < (b); i++)
#define _rep(i,a,b) for(int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 1010;
const int MAXM = 510;
const int inf = 5 * 1e7;
int n,s,e,m,k;
int dis1[MAXN],dis2[MAXN],head[MAXN],vis[MAXN],path1[MAXN],path2[MAXN];
int flag,ans = inf;
int l,r;
int tot,cnt;
int count = 1;
struct Edge{
int v,w,next;
}ed[4*MAXN];
void add_edge(int u,int v,int w)
{
ed[tot].v = v;
ed[tot].w = w;
ed[tot].next = head[u];
head[u] = tot++;
}
priority_queue<pair<int,int> > q;
deque<int> v;
void dijkstra(int s,int dis[],int path[])
{
while(q.size()) q.pop();
memset(vis,0,sizeof(vis));
_rep(i,1,n) dis[i] = inf;
dis[s] = 0;
q.push(make_pair(0,s));
while(q.size()){
int x = q.top().second;
q.pop();
if(vis[x]) continue;
vis[x] = 1;
for(int i = head[x]; i != -1; i = ed[i].next){
int y = ed[i].v, w = ed[i].w;
if(dis[y] > dis[x] + w){
dis[y] = dis[x] + w;
q.push(make_pair(-dis[y],y));
path[y] = x;
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
while(cin >> n >> s >> e)
{
v.clear();
memset(head,-1,sizeof(head));
memset(path1,0,sizeof(path1));
memset(path2,0,sizeof(path2));
flag = 0;
tot = 1;
ans = inf;
cin >> m;
_for(i,0,m)
{
int x,y,z;
cin >> x >> y >> z;
add_edge(x,y,z);
add_edge(y,x,z);
}
dijkstra(s,dis1,path1);
dijkstra(e,dis2,path2);
cin >> k;
_for(i,0,k)
{
int x,y,z;
cin >> x >> y >> z;
if(dis1[x] + dis2[y] + z < ans){
l = x;
r = y;
ans = dis1[x] + dis2[y] + z;
flag = x;
}
if(dis2[x] + dis1[y] + z < ans){
l = y;
r = x;
ans = dis2[x] + dis1[y] + z;
flag = y;
}
}
if(count == 1)count++;
else cout << endl;
if(dis1[e] < ans)
{
for(int i = e; i != s; i = path1[i])
v.push_back(i);
v.push_back(s);
cnt = v.size();
for(int i = cnt - 1; i >= 1; i--)
cout << v[i] << " ";
cout << v[0] << endl;
cout << "Ticket Not Used" << endl;
cout << dis1[e] << endl;
}
else{
for(int i = r; i != e; i = path2[i])
v.push_back(i);
v.push_back(e);
for(int i = l; i != s; i = path1[i])
v.push_front(i);
v.push_front(s);
cnt = v.size();
_for(i,0,cnt-1)
cout << v[i] << " ";
cout << v[cnt-1] << endl;
cout << flag << endl << ans << endl;
}
}
}