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

解题思路
1.首先,我们要计算走商业路的最短路径长度。
依次记录走每一条商业路的最短路径长度。

2.计算不走商业路的路径长度。

3.比较他们

4.注意:走商业路要注意左右点
设 起点为S,终点为E, 商业路的端点分别为 u ,v
则可以分为 S->u , v->E 和 S->v,u->E两种情况
则分别比较他们的大小,就得出走该条商业路的最短路径长度

5.注意输出格式

6.用pre作为前驱数组

代码实现

#include<iostream>
#include<vector>
#include<queue>
const int Max = 505;
const int INT_MAX_ = 200000;
using namespace std;

int N,S,E,M,X,Y,Z,K;
struct edge{
	int u,v,w;
};
struct syx{  //记录对商业线两头的选择 
	int s_conect,e_conect,w_;   //s连接的点,e连接的点
};
vector<edge> G[Max];
int dis[Max];  //装距离的
syx result[1005];  //用来存走了商业线的最小耗费 
pair<int,int>  remenber_s[1005];  //用来记录起始点到每条商业线左右两点的最小时间 
pair<int,int>  remenber_e[1005];  //记录终点 到每条商业线左右两点的最小时间 
edge EE[1005]; //用来装商业线的
bool vis[Max];  //是否走过该点 
priority_queue< pair<int,int> > q;
int pre1[Max] ;  //记录每个点的前驱 
int pre2[Max] ;
int EEi; 
int rsi,rei;
int resulti;
void addE(int u,int v,int w)   //添加边操作
{
	edge E; 
	E.u=u,E.v=v,E.w=w;
	G[u].push_back(E);
	E.u=v,E.v=u,E.w=w;
	G[v].push_back(E);
}

void ini(int x)  //初始化 //x是起点 
{
	for(int i=0;i<=N;i++)  dis[i]=INT_MAX_;
	dis[x]=0;
	while(!q.empty())  q.pop(); 
 	for(int i=0;i<Max;i++)  vis[i]=0;
}

void dij(int s)     //利用迪杰斯特拉算法求最短路径
{
	ini(s);
 	pair<int,int> p; p.first = 0,p.second=s;  //第一顺序比较的是距离 
 	q.push(p);
 	while(!q.empty())
 	{
 	
 		pair<int,int> x = q.top();
 		q.pop();
 		int poi = x.second;
 		if(vis[poi]) continue;
 		vis[poi] = true;
 		
 		for(int i=0 ; i< G[poi].size();i++)
 		{
 			
 			int y = G[poi][i].v, w = G[poi][i].w;
 			if( vis[y] ) continue;
 			if(dis[y] > dis[poi]+w)
 			{
 				
 				dis[y] = dis[poi]+w; //更新 
 				if(s==S)  pre1[y] = poi;      //记录S前驱 
 				if(s==E)  pre2[y] = poi;      //记录E前驱 
 				pair<int,int> PPy;
 				PPy.first = -dis[y] , PPy.second = y; //变成负距离来实现小根堆的效果 
 				q.push(PPy);
			 }
		 }
	 }
}

int _res[1005];    
int res_i;

int main()
{
	ios::sync_with_stdio(0);
	int COUNT1=0;
	while(cin>>N&&cin>>S&&cin>>E)
	{
		if(!COUNT1)  COUNT1=1;     //控制输出回车的,最后一组和第一组不用输出回车
		else cout<<endl;
		for(int i=0;i<Max;i++) {pre1[i]=-1;pre2[i]=-1;}
		EEi = rsi = rei = resulti = 0;
		cin>>M;
		for(int I=0;I<M;I++)//经济线 
		{
			cin>>X>>Y>>Z;
			addE(X,Y,Z);
		cin>>K;
		for(int I=0;I<K;I++)//商业线 
		{
			cin>>X>>Y>>Z;
			edge E_ ; E_.u = X,E_.v=Y,E_.w=Z;
			EE[EEi++] = E_;
		dij(S);
		int Rus_1 = dis[E];   //不坐商业线的路径长度 
		for(int i=0;i<EEi;i++)   //分别记录 s连接 u或v时的最短路径
		{
			remenber_s[rsi].first = dis[EE[i].u];
			remenber_s[rsi].second = dis[EE[i].v];
			rsi++;
		}
		dij(E);
		for(int i=0;i<EEi;i++)    //分别记录 e 连接u和v时的最短路径
		{
			remenber_e[rei].first = dis[EE[i].u];
			remenber_e[rei].second = dis[EE[i].v];
			rei++;
		}
		pair<int,int> remenber_res;   //记录连了商业线哪个点 
		int min_res = INT_MAX_;   //最小时间 
		for(int i=0;i<EEi;i++)  
		{//R1是S连U,E连V,R2是S连V,E连U 
			int r1 = remenber_s[i].first,r2 = remenber_e[i].second;
			int r3 = remenber_s[i].second,r4 = remenber_e[i].first;
			
			int R1 = r1+r2+ EE[i].w ,R2 = r3+r4+EE[i].w;
			syx syx_;
			if( R1 > R2 )  //R2是S连V,E连U 
			{
				syx_.w_ = R2 ,syx_.s_conect= EE[i].v , syx_.e_conect = EE[i].u;
			}
			else    //R1 是 S->u,E->v
			{
				syx_.w_ = R1 ,syx_.s_conect=EE[i].u, syx_.e_conect = EE[i].v;
			}
			//计算走了这条商业线的最短时间 
			if(min_res>syx_.w_) {
				min_res = syx_.w_;
				remenber_res.first = syx_.s_conect; //记录S链接的商业线点
				remenber_res.second=syx_.e_conect;  //记录E链接的商业线点 
			}
		}			
		if( min_res > Rus_1 ) //不坐商业线快
		{
			int ii=E;
			res_i=0;
			int RES[505];int Ri=0;
			while (ii!=-1)
			{
				RES[Ri++] = ii;
				ii = pre1[ii];
			}
			
			for(int i=Ri-1;i>=0;i--)
			{
				_res[res_i++]=RES[i];
			}
			for(int i=0;i<res_i-1;i++) 
			cout<<_res[i]<<" ";
			cout<<_res[res_i-1];
			cout<<endl;
			cout<<"Ticket Not Used"<<endl;
			cout<<Rus_1<<endl;
		 } 
		else  //坐商业线 
		{
			int RES[505];int Ri=0;
			res_i=0;
			int i_ = remenber_res.first; //与S相连的商业线的一点
			int ii = i_;
			while(ii!=-1)
			{				
				RES[Ri++]=ii;
				ii = pre1[ii];
			}

			for(int ind=Ri-1;ind>=0;ind--)
			_res[res_i++] = RES[ind];

			int i__ = remenber_res.second;
			while(i__!=-1)
			{
				_res[res_i++]=i__;
				i__ = pre2[i__];	
			}
			for(int i=0;i<res_i-1;i++) cout<<_res[i]<<" ";
			cout<<_res[res_i-1];
			cout<<endl;
			cout<<i_<<endl;
			cout<<min_res<<endl;
		}
		
		for(int i=0;i<=N;i++)  G[i].clear();
	}
	return 0;
}

小结
这题很复杂,而且要考虑的细节很多。
最恶心人的就是格式输出了,不仅每组数据间要有一行空,而且最后一组数据还不能多回车。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值