TT 的旅行日记(Dijkstra)

问题描述:

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

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),表示 TT 可以乘坐经济线在车站 X 和车站 Y 之间往返,其中单程需要 Z 分钟。
下一行为商业线的路段条数 K (1 ≤ K ≤ 1000)。
接下来 K 行是商业线路段的描述,格式同经济线。
所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。
output:
对于每组数据,输出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来求解。其中最关键的一步为松弛操作,将已经松弛的点放入最小堆中,每次中最小堆中取出距离最小的一个点,一旦一个点从最小堆中弹出,则该点的距离到达了最小值。然后通过取出来的点,编译它的所有邻接点,进行松弛。

由于题目中牵扯到商业线,且只能乘坐一次。所以我们可以通过枚举每一条商业线,计算起点到u的最短路以及v到 终点的最短路再加上该商业线所花费的时间。故我们可以在经济线上求解起点到终点的最短路,然后求解从终点到起点的最短路,然后通过枚举每一条商业线,得到走该条商业线的最小值,然后和不走商业线进行比较。由于该图是无向图,所以走这条商业线有两种走法,一种是从起点到商业线的起点,另一种是终点到商业线的起点。

对于实现,由于要用两次Dijkstra,所以使用了完全相同的两种数组。由于最后要输出所走的线路,所以在Dijkstra算法的基础上又增加的一个数组pre,用来找该节点的前一个节点,通过pre数组,就能找到一条完整的路径。

并且注意,该题对输出格式有很大的要求,一定要仔细读题,考虑好输出的格式。

代码:

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
struct point{//结构体,有一条边的终点和权值两域
	int end;
	int time;
	bool operator < (const point &p)const{//重载<,供堆使用
		return time>p.time;
	}
};
int N,S,E,M,K,min_path;
int vis1[510];//两次Dijkstra用来标记一个点是否在堆中
int vis2[510];
int pre1[510];//两次Dijkstra分别记录一个点的前驱
int pre2[510];
int dis1[510];//两次Dijkstra一个点到原点的距离
int dis2[510];
int main()
{	
	bool huanh=false;
	while(cin>>N>>S>>E)
	{
		if(huanh)
			cout<<endl;
		huanh=true;
		for(int i=0;i<510;i++)//初始化
			vis1[i]=vis2[i]=pre1[i]=pre2[i]=0;
		vector<point> line[510];
		cin>>M;
		for(int i=0;i<M;i++)
		{
			int x,y,z;
			cin>>x>>y>>z;
			point temp;
			temp.end=y;
			temp.time=z;
			line[x].push_back(temp);//构造无向图
			temp.end=x;
			line[y].push_back(temp);	
		}	
		priority_queue<point> q1,q2;//最小堆
		for(int i=1;i<=N;i++)
			dis1[i]=10000;
		dis1[S]=0;
		pre1[S]=-1;
		point tt;
		tt.end=S;
		tt.time=0;
		q1.push(tt);
		while(q1.size())
		{
			int x=q1.top().end;//从堆中取出距离最小的点
			q1.pop();
			if(vis1[x]==1) 
				continue;
			vis1[x]=1;
			for(int i=0;i<line[x].size();i++)
			{
				int y=line[x][i].end;
				int w=line[x][i].time;
				if(dis1[y]>(dis1[x]+w))//松弛操作
				{
					dis1[y]=dis1[x]+w;
					point pp;
					pp.end=y;
					pp.time=dis1[y];
					q1.push(pp);
					pre1[y]=x;//记录其前一个点
				}
			}
		}
		min_path=dis1[E];//不使用商业线的最短距离
		for(int i=1;i<=N;i++)	
			dis2[i]=10000;
		dis2[E]=0;
		pre2[E]=-1;
		tt.end=E;
		tt.time=0;
		q2.push(tt);
		while(q2.size())//第二次Dijkstra,同理
		{
			int x=q2.top().end;
			q2.pop();
			if(vis2[x]==1)	
				continue;
			vis2[x]=1;
			for(int i=0;i<line[x].size();i++)
			{
				int y=line[x][i].end;
				int w=line[x][i].time;
				if(dis2[y]>(dis2[x]+w))
				{
					dis2[y]=dis2[x]+w;
					point pp;
					pp.end=y;
					pp.time=dis2[y];
					q2.push(pp);
					pre2[y]=x;
				}
			}
		}
		cin>>K;
		bool shangye=false;//是否使用了商业线
		int st,ed;//商业线的起点和终点
		for(int i=0;i<K;i++)
		{//对每一条商业线进行枚举
			int xx,yy,zz,temp_min;
			cin>>xx>>yy>>zz;
			if(dis1[xx]+zz+dis2[yy]>dis1[yy]+zz+dis2[xx])
			//取两者的最小值
			{
				temp_min=dis1[yy]+zz+dis2[xx];
				if(temp_min<min_path)//若使用商业线的距离小于经济线
				{
					min_path=temp_min;//更新最短距离
					shangye=true;//记录
					st=yy,ed=xx;
				}	
			}
			else
			{
				temp_min=dis1[xx]+zz+dis2[yy];
				if(temp_min<min_path)
				{
					min_path=temp_min;
					shangye=true;
					st=xx,ed=yy;
				}
			}
			
		}
		if(shangye==true)
		{//如果使用了商业线
			vector<int> te1,te2;
			int sst=st;
			int eed=ed;
			te1.push_back(sst);
			while(pre1[sst]!=-1)//将路线分成了两段输出
			{
				te1.push_back(pre1[sst]);
				sst=pre1[sst];
			}
			te2.push_back(eed);
			while(pre2[eed]!=-1)
			{
				te2.push_back(pre2[eed]);
				eed=pre2[eed];
			}
			for(int i=te1.size()-1;i>=0;i--)
				cout<<te1[i]<<" ";
			for(int i=0;i<te2.size();i++)
			{
				cout<<te2[i];
				if(i!=te2.size()-1)
					cout<<" ";
			}
			cout<<endl;
			cout<<st<<endl;
			cout<<min_path<<endl;
		}
		else
		{//未使用商业线
			vector<int> te;
			int thend=E;
			te.push_back(thend);
			while(pre1[thend]!=-1)
			{
				te.push_back(pre1[thend]);
				thend=pre1[thend];
			}
			for(int i=te.size()-1;i>=0;i--)
			{
				cout<<te[i];
				if(i!=0)
					cout<<" ";
			}
			cout<<endl;
			cout<<"Ticket Not Used"<<endl;
			cout<<min_path<<endl;
		}
	} 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值