算法-程序设计课week7-作业-B - 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 算法。

对于商业线我们可以采取两头计算最短距离的办法巧妙降低计算量,也即若走商业线(从x,到y),则这种乘车方案距离为从头到[x]+从尾到[y]+w

路径输出

该题目除了上述逻辑外,路径保存和输出也是比较复杂的地方,他的解决方案也很经典。
从起点开始递归输出,从终点开始循环输出。

总的步骤

  1. 以起点为源点求单源最短路,得到从起点开始的dis数组
  2. 再以终点为源点求单源最短路,得到从终点开始的dis数组
  3. 枚举两数组求得最佳商业线,注意商业线可正反走。
  4. 从起点开始递归输出,从终点开始循环输出。

心得

  1. 卡输出格式,使用标志位进行计算
  2. 这道题的破题思路,用到的算法,输出 三个方面都有挑战性

代码

#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;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值