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 算法
该算法主要用于解决图中没有负边的单源最短路问题
复杂度𝑂((𝑛+𝑚)𝑙og𝑛)
设置 s 为源点,𝑑is[𝑎] 表示源点 s 到点 a 的最短距离, 初始化 𝑑is[s]=0,𝑑is[i]=inf(无穷大), 将 s 加入最小堆
每次从堆中取出一个点 x,遍历 x 的所有邻接边 (𝑥 𝑦 𝑤),比较𝑑is[𝑦] 与𝑑is[𝑥]+𝑤的大小 (松弛操作)
若dis[y]>dis[x]+w,则松弛成功,更新dis[y] = 𝑑is[𝑥]+𝑤,将y加入最小堆。
重复以上操作直到最小堆为空。
解题思路
题目给定了起点与终点,而且要求商业线最多乘坐一次,因此可以枚举每一条商业线(u,v),计算起点到u的最短路以及v到 终点的最短路再加上该商业线所花费的时间。
为了记录路径,定义数组pre1,pre2记录经过节点的父节点。
使用两次Dijkstra算法,以起点为源点求单源最短路,得到 dis1 数组 ,记录路径pre1,再以终点为源点求单源最短路,得到 dis2 数组,记录路径pre2。
为了记录路径,在Dijkstra算法松弛操作中,若松弛成功就记录b的上一个节点为a。
枚举商业线(u, v, w),取 min{dis1[u]+dis2[v]+w, dis1[v]+dis2[u]+w},最终再与不走商业线的答案取min
输出路径
这个题出错最多的是输出路径部分,定义一个数组a存放路径。因为pre1只能从end开始访问到start,pre2只能从start开始访问到end,因此不能直接输出。
如果没有使用商业线,把终点放入a,从终点开始把父节点依次放入a中,然后倒序输出a即可。
如果使用了商业线(x, y),则路径应该为start->x->y->end。先把y放入a,在沿着pre2从y访问到end放入,为了方便统一输出,使用swap把y->end反向记录为end->y。沿pre1从x访问到start。最后倒序输出。
代码实现
#include<stdio.h>
#include<queue>
#include<utility>
#include<vector>
#define N 550
using namespace std;
struct edge
{
int v,net;
int w;
};
int n,st,ed;
int m;
int head[N],vis[N];
int *dis1,*dis2;
int *pre1,*pre2;
edge e[5000];
priority_queue< pair<int,int> > q;
int index;
void add(int u, int v, int w)
{
e[index].v = v;
e[index].w = w;
e[index].net = head[u];
head[u] = index;
index++;
}
void dij(int start, int end, int dis[], int pre[])
{
while(!q.empty()) q.pop();
for(int i=0; i<=n; i++) pre[i] = 0,vis[i]=0, dis[i] = 1000000;
q.push(make_pair(0,start));
dis[start] = 0;
while(!q.empty())
{
int a=q.top().second;
q.pop();
if(vis[a] == 1) continue;
//存储路径
vis[a] = 1;
for(int i=head[a]; i!=-1; i=e[i].net)
{
int b = e[i].v;
int w = e[i].w;
if(dis[b] > dis[a]+w)
{
dis[b] = dis[a]+w;
pre[b] = a;
q.push(make_pair((-1)*dis[b], b));
}
}
}
}
int main()
{
int cishu=0;
while(scanf("%d%d%d", &n,&st,&ed) != EOF)
{
for(int i=1; i<=n; i++) head[i] = -1;
index=0;
scanf("%d",&m);
for(int i=0; i<m; i++)
{
int u,v,w;
scanf("%d%d%d",&u, &v, &w);
add(u,v,w);
add(v,u,w);
}
dis1 = new int[n+2];
dis2 = new int[n+2];
pre1 = new int[n+2];
pre2 = new int[n+2];
dij(st,ed,dis1,pre1);
dij(ed,st,dis2,pre2);
int i = dis1[ed];
int x=0,y=0;
scanf("%d",&m);
for(int j=0; j<m; j++)
{
int u,v,w;
scanf("%d%d%d",&u, &v, &w);
if(dis1[u]+dis2[v]+w<i)
{
i=dis1[u]+dis2[v]+w;
x=u;
y=v;
}
if(dis2[u]+dis1[v]+w<i)
{
i=dis2[u]+dis1[v]+w;
x=v;
y=u;
}
}
if(cishu>0) printf("\n");
cishu++;
int a[250];
int ia=0;
int temp=0;
if(x == 0)
{
temp = ed;
while(temp!=0)
{
a[ia] = temp;
ia++;
if(temp == st) break;
temp = pre1[temp];
}
ia--;
for(; ia>0; ia--) printf("%d ",a[ia]);
printf("%d\n",a[0]);
printf("Ticket Not Used\n");
}
else
{
temp=y;
while(temp)
{
a[++ia]=temp;
if(temp==ed) break;
temp=pre2[temp];
}
for(int t=1;t<=ia/2;t++)
swap(a[t],a[ia-t+1]);
temp=x;
while(temp)
{
a[++ia]=temp;
if(temp==st) break;
temp=pre1[temp];
}
for(int t=ia;t>1;t--) printf("%d ",a[t]);
printf("%d\n",a[1]);
printf("%d\n",x);
}
printf("%d\n",i);
}
}