题目
众所周知,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 算法主要用于解决图中没有负边的单源最短路问题,设置 s 为源点, 𝑑𝑖𝑠[𝑎] 表示源点 s 到点 a 的最短距离, 初始化 𝑑𝑖𝑠[𝑠]=0, 𝑑𝑖𝑠[𝑖]=𝑖𝑛𝑓 (无穷大), 将 s 加入最小堆,每次从堆中取出一个点 x,遍历 x 的所有邻接边 (𝑥 𝑦 𝑤) ,比较 𝑑𝑖𝑠[𝑦] 与 𝑑𝑖𝑠[𝑥]+𝑤 的大小,这一比较过程称为松弛操作。
题目给定了起点与终点,而且要求商业线最多乘坐一次,我们可以枚举每一条商业线,计算起点到 u 的最短路以及 v 到终点的最短路再加上该商业线所花费的时间。具体地,以起点为源点求单源最短路,得到 dis1 数组,再以终点为源点求单源最短路,得到 dis2 数组,枚举商业线(u, v, w),取 min{dis1[u]+dis2[v]+w, dis1[v]+dis2[u]+w},最终再与不走商业线的答案取最小值进行判断,输出最终的结果。最后分别从起点和终点出发记录下经过的车站,按序输出。
程序源码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include<queue>
#include<list>
#include<string.h>
using namespace std;
int themax = 200000;
int dis1[502];
int dis2[502];
struct point { //经济线结点
int to;
int time;
point* next;
point(int to, int time, point* next) :to(to), time(time), next(next) {}
point() {
next = NULL;
}
};
struct compoint { //商业线结点
int from;
int to;
int time;
compoint(int from, int to, int time) :from(from), to(to), time(time) {}
};
struct queuenode { //队列结点
int weight;
int node;
queuenode(int weight, int node) :weight(weight), node(node) {}
bool operator<(const queuenode& q2)const {
return weight > q2.weight;
}
};
int main() {
int N, S, E, M, X, Y, Z, K;
bool firstrun = true;
while (scanf("%d%d%d", &N, &S, &E) != EOF) {
if (firstrun) {
firstrun = false;
}
else { //组间输出空行
printf("\n");
}
point* ecoline[502] = { NULL }; //经济线链表头结点
scanf("%d", &M);
//shuru m tiao jinjixian
for (int m = 0; m < M; m++) { //读取经济线数据
scanf("%d%d%d", &X, &Y, &Z);
ecoline[X] = new point(Y, Z, ecoline[X]); //加入 X->Y 的经济线
ecoline[Y] = new point(X, Z, ecoline[Y]); //加入 Y->X 的经济线
}
vector<compoint>comline; //vector存储商业线
scanf("%d", &K);
for (int k = 0; k < K; k++) {
scanf("%d%d%d", &X, &Y, &Z);
comline.push_back(compoint(X, Y, Z)); //加入 X->Y 的商业线
comline.push_back(compoint(Y, X, Z)); //加入 Y->X 的商业线
}
for (int i = 0; i < 502; i++) {
dis1[i] = themax;
dis2[i] = themax;
}
int touch1[502] = { 0 };
int touch2[502] = { 0 };
int former1[502] = { 0 };
int former2[502] = { 0 };
dis1[S] = 0; dis2[E] = 0; //起点dis1 终点dis2 距离置零
priority_queue<queuenode> q1;
q1.push(queuenode(0, S)); //计算dis1,加入起点
while (q1.size()) { //Dijkstra
int current = q1.top().node;
q1.pop();
if (touch1[current]) continue;
touch1[current] = 1;
point* thisnode = ecoline[current];
int currentdis = dis1[current];
while (thisnode != NULL) {
if (dis1[thisnode->to] > currentdis + thisnode->time) { //可松弛
dis1[thisnode->to] = currentdis + thisnode->time;
former1[thisnode->to] = current;
q1.push(queuenode(dis1[thisnode->to], thisnode->to));
}
thisnode = thisnode->next;
}
}
priority_queue<queuenode> q2;
q2.push(queuenode(0, E)); //计算dis2,加入起点
while (q2.size()) { //Dijkstra
int current = q2.top().node;
q2.pop();
if (touch2[current]) continue;
touch2[current] = 1;
point* thisnode = ecoline[current];
int currentdis = dis2[current];
while (thisnode != NULL) {
if (dis2[thisnode->to] > currentdis + thisnode->time) { //可松弛
dis2[thisnode->to] = currentdis + thisnode->time;
former2[thisnode->to] = current;
q2.push(queuenode(dis2[thisnode->to], thisnode->to));
}
thisnode = thisnode->next;
}
}
int mindis = themax; //枚举商业线(u, v, w),取 min{dis1[u]+dis2[v]+w, dis1[v]+dis2[u]+w}
compoint* maxp = NULL;
for (vector<compoint>::iterator it = comline.begin(); it != comline.end(); it++) {
if (dis1[it->from] + dis2[it->to] + it->time < mindis) {
mindis = dis1[it->from] + dis2[it->to] + it->time;
maxp = &(*it);
}
}
if (mindis < dis1[E]) { //走商业线更快
list<int>route; //list存储路径
int crt = maxp->from;
while (1) { //起点开始的路径
route.push_front(crt);
if (crt == S) {
break;
}
crt = former1[crt];
}
crt = maxp->to;
while (1) { //终点开始的路径
route.push_back(crt);
if (crt == E) {
break;
}
crt = former2[crt];
}
int tcount = route.size();
list<int>::iterator it = route.begin();
while (tcount) { //输出路径
if (tcount == 1) {
printf("%d\n", *it);
}
else {
printf("%d ", *it);
}
it++;
tcount--;
}
printf("%d\n", maxp->from); //输出换乘站
printf("%d\n", mindis); //总时间
}
else { //不走商业线
int crt = S;
while (1) {
if (crt == E || crt == 0) { //输出路径
printf("%d\n", crt);
break;
}
else {
printf("%d ", crt);
}
crt = former2[crt];
}
printf("Ticket Not Used\n");
printf("%d\n", dis1[E]); //总耗时
}
}
return 0;
}