程序设计思维与实践 Week7 B - TT 的旅行日记——迪杰斯特拉

题意:

有个人现在要从起点到达终点,有n条经济线和m条商务线连接着N个点。现在他有一条商务线的票,即可以乘坐一条商务线路。每条线路给定所需要花费的时间,求出从起点到达终点花费时间最少的线路。

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),表示在xy之间有一条线路,需要 花费Z 分钟。
下一行为商业线的路段条数 K (1 ≤ K ≤ 1000),接下来 K 行是商业线路段的描述,格式同经济线。注意:所有的线路都是双向的,并且商务线可以乘坐可以不乘坐。

output:

对于每组数据,输出3行。第一行依次给出所经过的点的序号,第二行是 换乘商业线的车站编号(如果没有使用商业线车票,输出"Ticket Not Used",不含引号),第三行花费的总时间。

思路:

因为商务只可以乘坐一次,故枚举每条商务线,得到使用这条商务线的最短时间和路线,枚举结束后,得到使用商务线的最短路径和花费的时间。最后和不适用商务线进行对比,得到花费时间最少的那条线路。 做法:在输入经济线的时候,构建图,并从起点S和终点E,跑一遍dij算法,得到从S和E开始,到达每一个点的所花费的最短时间的路线。在每得到一条商务线的时候x,y,z。即可得到dis[x]+dis[y]+z的值和dis[y]+dis[x]+z的值,如果所花费时间比现在最小的时间少,则更新记录下来的那条记录。最后得到使用商务座的最少时间的线路,再与不使用的进行比较,得到花费时间最少的那条线路,(提醒自己:前向星中最终的边数会是M的两倍)。
dij算法:首先有vis[]数组,记录该点是否已经访问到,以及dis[]数组记录当前的路径长度,lu[]数组,记录前序点。首先将起点放进堆中,(最小堆可以通过向最大堆中的数变为负数即可。)依次取出当前堆中,路径最短的,然后进行标记,在以这点开始,更新它所能到达的所有点,(如果dis1[t1] > dis1[x] + w1,即更新),包括该点的路径长度和前序点。当所有点更新完后,即完成。
输出:该题输出格式要求巨多,要特别注意。另外,从起点开始的记录的前序点数组lu1[]和从终点开始记录的前序点数组lu2[]最终输出的顺序是不同的。

题目代码:

#include <cstdio>
#include <algorithm> 
#include <iostream>
#include <string.h> 
#include <queue>
using namespace std;


const int inf = 1e8;
int N, S,E;
bool vis1[505];  //标记
bool vis2[505];
int dis1[505];   //距离
int dis2[505];
int lu1[505];   //前序点
int lu2[505];

int minq, minz; //走的商业的线路的起点终点
int minw;          //所走的最短时间
int minlu[505];   //记录路线
int con;


struct Edge {
	int to, next, w;
}e[2050];
int head[505];
int tot;

priority_queue< pair<int, int> > q;  //最大堆

void add_edge(int t, int f, int W) {
	e[tot].to = t;
	e[tot].next = head[f];
	head[f] = tot;
	e[tot].w = W;
	tot++;
}

void dij1() {
	while (q.size()) q.pop();

	//初始化
	for (int i = 1; i <= N; i++) {
		vis1[i] = 0, dis1[i] = inf;
	}
	dis1[S] = 0;
	q.push(make_pair(0, S));
	while (q.size()) {
		int x = q.top().second;
		q.pop();
		if (vis1[x]) continue; //已经加入过堆
		vis1[x] = 1;
		
		int i = head[x];
		while (i != -1) {
			int t1 = e[i].to;
			int w1 = e[i].w;
			if (dis1[t1] > dis1[x] + w1) {
				dis1[t1] = dis1[x] + w1;
				lu1[t1] = x;
				q.push(make_pair(-dis1[t1], t1));
			}
			i = e[i].next;
		}
	}
}

void dij2() {
	while (q.size()) q.pop();

	//初始化
	for (int i = 1; i <= N; i++) {
		vis2[i] = 0, dis2[i] = inf;
	}
	dis2[E] = 0;
	q.push(make_pair(0, E));
	while (q.size()) {
		int x = q.top().second;
		q.pop();
		if (vis2[x]) continue; //已经加入过堆
		vis2[x] = 1;  //标记

		int i = head[x];
		while (i != -1) {
			int t1 = e[i].to;
			int w1 = e[i].w;
			if (dis2[t1] > dis2[x] + w1) {
				dis2[t1] = dis2[x] + w1;
				lu2[t1] = x;
				q.push(make_pair(-dis2[t1], t1));
			}
			i = e[i].next;
		}
	}
}

//起点终点
void LU1(int x, int y) {  //找出路线
	con = 0;
	
	while (y != x) {
		minlu[con] = y;
		con++;
		y = lu1[y];
	}
	cout << x;
	for (int i = con - 1; i >= 0; i--) {
		cout << " " << minlu[i];
	}
}

void LU2(int x, int y) {  //找出路线
	con = 0;
	while (y != x) {
		minlu[con] = y;
		con++;
		y = lu2[y];
	}
	
	for (int i = 0; i < con; i++) {
		cout<<" "<<minlu[i];
	}
	cout<<" "<< x;
}


int main() {
	ios::sync_with_stdio(0);
	int M,K;
	//车站数,起点,终点数
	int ret = 0;
	while (cin>>N && cin>>S && cin>>E) {
		tot = 0;
		if (ret == 0) {
			ret = 1;
		}
		else {
			cout<<endl;
		}
		minw = inf;    //记录最终路线
		minq = -1;
		minz = -1;

		cin >> M;
		int x, y, z;
		for (int i = 1; i <= N; i++) {
			head[i] = -1;
		}

		for (int i = 0; i < M; i++) {
			cin >> x >> y >> z;
			add_edge(y, x, z);
			add_edge(x, y, z);
		}
		dij1();
		dij2();
		cin >> K;
		for (int i = 0; i < K; i++) {
			cin >> x >> y >> z;
			if (minw > (dis1[y] + dis2[x] + z)) {
				minq = y, minz = x, minw = dis1[y] + dis2[x] + z;
			}
			if(minw > dis1[x] + dis2[y] + z){
				minq = x, minz = y, minw = dis1[x] + dis2[y] + z;
			}
		}
		if (minw > dis1[E]) {
			LU1(S, E);
			cout << endl;
			cout << "Ticket Not Used"<< endl;
			cout << dis1[E]  << endl;;
		}
		else {
			LU1(S, minq);
			LU2(E, minz);
			cout << endl;
			cout << minq << endl;
			cout << minw  << endl;
		}

	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值