Week7 作业 B - TT 的旅行日记 UVA - 11374 Dijkstra

题目
众所周知,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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值