题目
众所周知,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
算法。
对于商业线我们可以采取两头计算最短距离的办法巧妙降低计算量,也即若走商业线(从x,到y),则这种乘车方案距离为
从头到[x]+从尾到[y]+w
。
路径输出
该题目除了上述逻辑外,路径保存和输出也是比较复杂的地方,他的解决方案也很经典。
从起点开始递归输出,从终点开始循环输出。
总的步骤
- 以起点为源点求单源最短路,得到从起点开始的dis数组
- 再以终点为源点求单源最短路,得到从终点开始的dis数组
- 枚举两数组求得最佳商业线,注意商业线可正反走。
- 从起点开始递归输出,从终点开始循环输出。
心得
- 卡输出格式,使用标志位进行计算
- 这道题的破题思路,用到的算法,输出 三个方面都有挑战性
代码
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
//#define DEBUG
#ifdef DEBUG
#define dprintf printf
#else
#define dprintf /\
/printf
#endif
const int M = 5 * 1e6 + 100;
const int N = 5 * 1e6 + 100;
const int inf = 5 * 1e8;
struct Edge {
int to, next, w;
} e[M];
int head[N], tot, n, m;
int vis[N], dis_from_start[N], dis_from_end[N], pre1[N], pre2[N];
int S, E, k, x, y, z, station, spost;
int ans;
void add(int x, int y, int z) {
e[++tot].to = y, e[tot].next = head[x], head[x] = tot;
e[tot].w = z;
}
void dijkstra(int s, int* dis1, int* pre1) {
priority_queue<pair<int, int>> q;
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; i++) dis1[i] = inf;
dis1[s] = 0;
q.push({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; i = e[i].next) {
int to = e[i].to;
int w = e[i].w;
dprintf("111110qqq%d %d\n", to, w);
if (dis1[to] > dis1[x] + w) {
dis1[to] = dis1[x] + w;
pre1[to] = x;
q.push({-dis1[to], to});
}
}
}
}
void pathend(int p) {
if (p != S) {
pathend(pre1[p]);
}
if (p == S)
printf("%d", p);
else
printf(" %d", p);
}
void pathstart(int p) {
while (p != E) {
printf(" %d", p);
p = pre2[p];
}
printf(" %d", p);
}
int main() {
bool _c = false;
while (~scanf("%d%d%d", &n, &S, &E)) {
if (_c == true) {
printf("\n");
}
scanf("%d", &m);
memset(pre1, 0, sizeof(pre1));
memset(pre2, 0, sizeof(pre2));
memset(head, 0, sizeof(head));
ans = inf;
tot = 0;
while (m--) {
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
add(y, x, z);
}
dijkstra(S, dis_from_start, pre1);
dijkstra(E, dis_from_end, pre2);
scanf("%d", &k);
while (k--) {
scanf("%d%d%d", &x, &y, &z);
//一条线有来回两种情况,做两个判断
int xtoy = dis_from_start[x] + dis_from_end[y] + z;
int ytox = dis_from_start[y] + dis_from_end[x] + z;
if (ans > min(xtoy, ytox)) {
if (xtoy < ytox) {
ans = xtoy;
station = x;
spost = y;
} else {
ans = ytox;
station = y;
spost = x;
}
}
}
//判断走商业线优不优
if (ans < dis_from_start[E]) {
pathend(station);
pathstart(spost);
printf("\n%d\n", station);
} else {
ans = dis_from_start[E];
pathend(E);
printf("\nTicket Not Used\n");
}
printf("%d\n", ans);
_c = true;
}
}