Week7—B—TT 的旅行日记

题目描述:

众所周知,TT 有一只魔法猫。

今天他在 B 站上开启了一次旅行直播,记录他与魔法猫在喵星旅游时的奇遇。 TT 从家里出发,准备乘坐猫猫快线前往喵星机场。猫猫快线分为经济线和商业线两种,它们的速度与价钱都不同。当然啦,商业线要比经济线贵,TT 平常只能坐经济线,但是今天 TT 的魔法猫变出了一张商业线车票,可以坐一站商业线。假设 TT 换乘的时间忽略不计,请你帮 TT 找到一条去喵星机场最快的线路,不然就要误机了!


Input:

  1. 输入包含多组数据。每组数据第一行为 3 个整数 N, S 和 E (2 ≤ N ≤ 500, 1 ≤ S, E ≤100),即猫猫快线中的车站总数,起点和终点(即喵星机场所在站)编号。
  2. 下一行包含一个整数 M (1 ≤ M ≤ 1000),即经济线的路段条数。
  3. 接下来有 M 行,每行 3 个整数 X, Y, Z (1 ≤ X, Y ≤ N, 1 ≤ Z ≤ 100),表示 TT
    可以乘坐经济线在车站 X 和车站 Y 之间往返,其中单程需要 Z 分钟。
  4. 下一行为商业线的路段条数 K (1 ≤ K ≤ 1000)。
  5. 接下来 K 行是商业线路段的描述,格式同经济线。
  6. 所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值