题目描述:
众所周知,TT 有一只魔法猫。
今天他在 B 站上开启了一次旅行直播,记录他与魔法猫在喵星旅游时的奇遇。 TT 从家里出发,准备乘坐猫猫快线前往喵星机场。猫猫快线分为经济线和商业线两种,它们的速度与价钱都不同。当然啦,商业线要比经济线贵,TT 平常只能坐经济线,但是今天 TT 的魔法猫变出了一张商业线车票,可以坐一站商业线。假设 TT 换乘的时间忽略不计,请你帮 TT 找到一条去喵星机场最快的线路,不然就要误机了!
Input:
- 输入包含多组数据。每组数据第一行为 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 行是商业线路段的描述,格式同经济线。
- 所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。
Ouput:
对于每组数据,输出3行。第一行按访问顺序给出 TT 经过的各个车站(包括起点和终点),第二行是 TT 换乘商业线的车站编号(如果没有使用商业线车票,输出"Ticket Not Used",不含引号),第三行是 TT 前往喵星机场花费的总时间。
本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行
sample input:
4 1 4
4
1 2 2
1 3 3
2 4 4
3 4 5
1
2 4 3
sample output:
1 2 4
2
5
个人思路:
- 根据题目描述,必定有解,也就是 S→E一定是联通的。
- 特殊情况:必须使用商业线才能到达。说明在不考虑商业线的时候,S→E不可达。
- 考虑以上情况,应用Dijkstra算法,从S和E各跑一遍最短路,得到dis[0/1][maxn];如果S→E普通可达,那么dis[0/1][maxn]在某种程度上可以说是一样的;如果S→E特殊可达,那么dis[0/1][maxn]就不同了。
- Dijkstra算法堆优化:利用最小堆初始化为未确定的顶点,因为每次松弛要保证的是距离源点最近的顶点,所以每次将堆顶元素取出来就可以了。如果松弛成功,那么再将其加入堆中;如果松弛失败,说明其已经最小了,不需要再加入堆中。
- 基于以上描述,枚举商业线;通过比较用商业线和不用商业线的最小值,输出结果。
- 路径输出:这里我是用了一个数组index_pre[],在松弛的时候记录了被松弛元素的前驱节点。结束的时候再将路径整理到一个数组op[]中。再输出。
代码细节:
- 用number1记录距离S近的商业线的顶点,number2是较远的点。
- 输出时的路径:S→number1→number2→E;所以对于数组index_pre[0][maxn],我们需要从index_pre[0][number1]向S方向找到所有的前驱节点,输出到op[0][cnt1]中;对于number2也是这样。
- 输出,因为S一直是源点,所以op[0][cnt1]是倒序输出,op[1][cnt2]是正序输出。
- 如果不用商业线,只需要倒序输出op[0][cnt1]就可以,对应到上面我说的普通情况。
代码块:
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
const int maxn = 2005;
int N, S, E, dis[2][maxn];
int indx_pre[2][maxn];
struct Edge {
int u, v, w, nxt;
}Edges[maxn];
int head[maxn], tot, vis[maxn];//tot 是edge的下标
void init() {
tot = 1; for (int i = 0; i <= N; ++i)head[i] = 0;
}
void addEdge(int x, int y, int z) {
Edges[tot].u = x; Edges[tot].v = y; Edges[tot].w = z;
Edges[tot].nxt = head[x];
head[x] = tot; tot++;
}
priority_queue<pair<int,int> >min_heap;
void dijkstra(int s, int id) {
while (min_heap.size())min_heap.pop();
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= N; ++i)dis[id][i] = 999999999;
dis[id][s] = 0;
min_heap.push(make_pair(0, s));
while (!min_heap.empty()) {
int x = min_heap.top().second;
min_heap.pop();
if (vis[x])continue;
vis[x] = 1;
for (int i = head[x]; i != 0 ; i = Edges[i].nxt) {
int y = Edges[i].v, w = Edges[i].w;
//cout << dis[id][y] << " " << dis[id][x] + w << endl;
if (dis[id][y] > dis[id][x] + w) {
dis[id][y] = dis[id][x] + w;
min_heap.push(make_pair(-dis[id][y], y));
indx_pre[id][y] = x;
}
}
}
}
int main() {
int sign = 0;
while (~scanf("%d%d%d", &N, &S, &E)) {
int u, v, w, m, k, op[2][maxn];
cin >> m;
init();
for (int i = 1; i <= m; ++i) {
cin >> u >> v >> w;
addEdge(u, v, w);
addEdge(v, u, w);
}
indx_pre[0][S] = 0; indx_pre[1][E] = 0;
dijkstra(S, 0); dijkstra(E, 1);
cin >> k;
int ans = dis[0][E], number1 = -1, number2 = -1;
for (int i = 1; i <= k; ++i) {
cin >> u >> v >> w;
if (min(dis[0][u] + dis[1][v] + w, dis[0][v] + dis[1][u] + w) < ans) {
if (dis[0][u] + dis[1][v] + w < dis[0][v] + dis[1][u] + w)number1 = u, number2 = v;
else number1 = v, number2 = u;
ans = min(dis[0][u] + dis[1][v] + w, dis[0][v] + dis[1][u] + w);
}
}
op[0][0] = S; op[1][0] = E;
if (sign != 0)cout << endl;
int cnt1 = 0, cnt2 = 0;
if (number1 != -1) {
for (int p = indx_pre[0][number1]; p; p = indx_pre[0][p])op[0][++cnt1] = p;
for (int p = indx_pre[1][number2]; p; p = indx_pre[1][p])op[1][++cnt2] = p;
for (int i = cnt1; i > 0; --i) {
cout << op[0][i] << " ";
}cout << number1 << " " << number2;
for (int i = 1; i <= cnt2; ++i) {
cout << " " << op[1][i];
}cout << endl;
cout << number1 << endl;
}
else {
for (int p = indx_pre[0][E]; p; p = indx_pre[0][p])op[0][++cnt1] = p;
for (int i = cnt1; i > 0; --i)cout << op[0][i] << " ";
cout << E << endl;
cout << "Ticket Not Used" << endl;
}
cout << ans << endl;
sign++;
}
return 0;
}