week-7-B(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 前往喵星机场花费的总时间。
本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行

	样例:
	input:
	4 1 4
    4
    1 2 2
    1 3 3
    2 4 4
    3 4 5
    1
    2 4 3
    output:
    1 2 4
    2
    5

思路

  • 首先这是一个求最短路径的问题,从家到机场;

  • 其次路径分为经济线和商业线,需要做的就是通过选择适当的路线使得路径最短;

  • 我们需要求最短路径,可以注意到我们可能需要在中间的某一条线换乘商业线,所以我们需要求出到从起点到每一个车站的最短路(为了加入商业线)我们就采用dijkstra算法;

  • 利用dijkstra算法我们能求出到任何一个车站的经济线最短路径

  • 为了考虑加入商业线使得整体最短路径最小,那么我们有3种选择

      	1 家到商业线的起点的最短路径(经济线)+商业线长度+商业线终点到机场的最短路径 (经济线)
      	2 到商业线的终点的最短路径(经济线)+商业线长度+商业线起点到机场的最短路径(经济线)(路是双向的)
      	3 不加商业线,家到机场的经济线最短路
    
  • 第一种和第二种需要进行两次dijkstra算法,一次以起点为源点,一次以终点为源点;

  • 这三种路线取最小值及就是最短路径长度

  • 为了输出最短路径的车站,我们采用递归的方法,使用一个数组记住到车站的前导结点;

  • 追溯路径的时候,有两个函数,一个是从X站到起点,一个Y站到终点(X,Y分别为商业线的起始站)

总结

  • 我们在加入商业线以后应该注意的就是线路是双向的,1和2应该进行比较一下;
  • 其次就是dijsktra不能解决负边问题;
  • 在以终点为源点进行dijsktra算法的时候应该也在存储线路的数组中加入逆向线路长度;
  • 在很多条商业线的时候,我们应该标记下我们选择的上述三种操作中的哪一种,还得记住我们采用的是哪一条商业线,应当记住起始站(为了递归追溯路径);

代码

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

const int N = 510;
const int inf = 50000;
int e[N][N];
int  dis[N],dis1[N],path[N],path1[N],tol=0;
bool vis[N];
priority_queue<pair<int, int> >q;
void dijkstra(int s,int n)
{
	path[s] = -1;//表明x为原点;
	while (!q.empty())
		q.pop();//清空队列
	for (int i = 0; i <= n; i++)
		vis[i] = false;
	for (int i = 0; i <= n; i++)
		dis[i] = inf;//初始化距离数组
	dis[s] = 0;
	q.push(make_pair(0, s));
	while (!q.empty())
	{
		int x = q.top().second;
		q.pop();
		if (vis[x])
			continue;
		vis[x] = true;
		for (int i = 1; i<=n; i++)
		{
			
			if (dis[i] > e[x][i] + dis[x])
			{
				dis[i] = e[x][i] + dis[x];
				path[i] = x;
				q.push(make_pair(-dis[i], i));
			}
		}
	}
}
void dijkstra1(int s, int n)
{
	path1[s] = -1;//初始点没有上一个结点;故路径数组记-1
	while (!q.empty())
		q.pop();//清空队列
	for (int i = 0; i <= n; i++)
		vis[i] = false;
	for (int i = 0; i <= n; i++)
		dis1[i] = inf;//初始化距离数组
	dis1[s] = 0;
	q.push(make_pair(0, s));
	while (!q.empty())
	{
		int x = q.top().second;
		q.pop();
		if (vis[x])
			continue;
		vis[x] = true;
		for (int i = 1; i <= n; i++)
		{

			if (dis1[i] > e[x][i] + dis1[x])
			{
				dis1[i] = e[x][i] + dis1[x];
				path1[i] = x;
				q.push(make_pair(-dis1[i], i));
			}
		}
	}
}
void path_(int s,int temp)//从起点经过的车站
{
	
	if (path[s] == -1)
	{
		cout << s << " ";
		return;
	}
	path_(path[s],temp);
	if (temp == s)
		cout << s;
	else
		cout << s << " ";
}
void path_1(int s)//从终点经过的车站
{
	if (path1[s] == -1)
	{
		cout << s ;
		return;
	}
	cout <<s<<" ";
	path_1(path1[s]);
}
void out_put(pair<int, int> min, int X1, int Y1, int S, int E,int n)
{
	switch(min.first)
	{
		case 1:
		{
			path_(X1,E);
			path_1(Y1);
			cout << endl;
			cout << X1 << endl;
			break;
		}
		case 2:
		{
			tol = 0;
			path_(Y1,E);
			path_1(X1);
			cout << endl;
			cout << Y1 << endl;
			break;
		}
		case 3:
		{
			tol = 0;
			path_(E,E);
			cout << endl;
			cout << "Ticket Not Used" << endl;
			break;
		}
	}
}
int main()
{
	int n, S, E,K;
	bool ans = false;
	while (scanf("%d%d%d", &n, &S, &E) != EOF)
	{
		if(ans)
			cout << endl;
		ans = true;
		int m, X, Y, Z, count = 1, count1 = 1;
		cin >> m;
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {
				e[i][j] = inf;
			}
		}
		for (int i = 1; i <= m; i++) {//进行路径的初始化
			scanf("%d%d%d", &X, &Y, &Z);
			e[X][Y] = Z;
			e[Y][X] = Z;
		}
		pair<int, int> sum;
		sum.second = 0, sum.first = 0;  //sum和sum1,min,min_i的first是为了记录哪一种路径,second记录总时间
		pair<int, int> sum1;
		sum1.second = 0, sum1.first = 0;
		pair<int, int> min;
		min.second =inf, min.first = 0;
		
		int X1=0, Y1=0;//记住商务车线(因为商务可能有很多条,所以要一直记得最优是哪条)


		dijkstra(S, n);     //从起点开始进行一次单源最短路径的求取
		dijkstra1(E, n);    //从终点开始进行一次单源最短路径的求取
  
		sum1.second = dis[E], sum1.first = 3;               //3代表dis[E];没有使用商务线

		cin >> K;
		for (int i = 0; i < K; i++)            //商务线的初始化
		{

			scanf("%d%d%d", &X, &Y, &Z);
			//进行各种选项的比较,选出最优
			if ((dis[X] + dis1[Y] + Z) >= (dis[Y] + dis1[X] + Z))
				sum.second = dis[Y] + dis1[X] + Z, sum.first = 2;   //2代表dis[Y] + dis1[X] + Z
			else
				sum.second = dis[X] + dis1[Y] + Z, sum.first = 1;   //1代表dis[X] + dis1[Y] + Z
			pair<int, int> min_i;
			min_i.second = 0, min_i.first = 0;
			if (sum.second >= sum1.second)
			{
				min_i = sum1;
			}
			else
				min_i = sum;
			//得到最优
			if (min_i.second <= min.second)
			{
				min = min_i;
				X1 = X;   //记录最优时商务线
				Y1 = Y;
			}
		}
		out_put(min, X1, Y1, S, E,n);   //进行输出
		cout << min.second << endl;
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值