【Week7作业 B】TT的旅行日记【dijkstra】

题意:

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


思路:

题目给定了起点与终点,而且要求商业线最多乘坐一次。可以枚举每一条商业线(u,v),计算起点s到u的最短路以及v到终点e的最短路再加上该商业线所花费的时间,然后取最短时间与不走商业线的时间进行比较,取较小值。
用dijkstra来实现:以起点s为源点求最短路,得到dis1数组,再以终点e为源点求最短路,得到dis2数组。枚举商业线(u,v,w),取min{dis1[u]+dis2[v]+w,dis1[v]+dis2[u]+w},最终再与不走商业线的答案取min。
存经济线使用了链式前向星,防止用邻接矩阵导致TE。而商业线只需要存储u,v,w即可,所以用了边来存储。
dijkstra:很正常的dijkstra,唯一不同就是要用pre数组来存储每个点的上一个顶点,用于输出时回溯路径。
经过两次dijkstra求最短路后,要枚举商业线求最小时间,此时用syU和syV来表示最小时间对应的商业线顶点。枚举完商业线后要进行输出。输出分为从后往前输出(从syU到起点s)和从前往后输出(从syV到终点e),前者使用了递归回溯路径实现输出,后者遍历pre数组完成输出。


总结:

一道两遍dijkstra的题目,很容易T,还卡输出格式,还多组数据。交了19发才A了。
代码修改的地方:读入由cin(没关流同步)到scanf(但改完还是T了);链式前向星存经济线的数组由1100开到了2100(一开始开小了,1000的边存两次是2000,1100是不够的,但开小了是TE而不是RE就想不通为什么,这个地方调了好久);枚举商业线的地方又换了一种写法(但还是不知道前一种写法哪里错了);最后PE又改了输出格式,最后一组数据不用再输出空行。
其实还有思路二:跑一次dijkstra变形,记录答案dis[u][0/1],其中dis[u][0]表示从起点到终点u没有经过商业线时的最短路,在松弛的时候可以选择商业线或者经济线;dis[u][1]表示从起点到终点u经过商业线后的最短路,在松弛的时候只能选择经济线。
一开始觉得思路二只需要改一改dijkstra就能写完,感觉很简单就先写的思路二。结果调了好久还是T,就转为思路一了,而思路一也是写了好长时间才搞定。看看其他大佬交了一两发就A了,看来自己写代码的路还很长啊。


代码:

#include <iostream>
#include <stdio.h>
#include <queue>
#include <stack>
using namespace std;

int MAX_G=1e8;
int n,s,e,m,k;
//链式前向星
struct edge
{
	int to,next,w;
};
edge ed[2100];
int head[600],tot;
void add(int x,int y,int w)
{
	ed[++tot].to=y,ed[tot].next=head[x];
	ed[tot].w=w,head[x]=tot;
}
//边,用于存储商业线
struct shangye
{
	int u,v,w;
} syEdge[1100];
int dis1[600],dis2[600];	//距离起点和终点的距离
int pre1[600],pre2[600];
bool vis1[600],vis2[600];
struct point	//记录顶点v到源点的距离
{
	int v,juli;
	point(int v1,int juli1)
	{
		v=v1,juli=juli1;
	}
};
struct cmp
{
	bool operator () (point a,point b)
	{
		return a.juli>b.juli;
	}
};
priority_queue<point,vector<point>,cmp> q;
void dijkstra(int source,int dis[],int pre[],bool vis[])
{
	while(!q.empty())
		q.pop();
	dis[source]=0;
	point thePoint(source,0);
	q.push(thePoint);
	while(!q.empty())
	{
		int x=q.top().v;
		q.pop();
		if(vis[x])
			continue;
		vis[x]=1;
		for(int i=head[x]; i!=0; i=ed[i].next)	//经济线
		{
			int y=ed[i].to,w=ed[i].w;
			if(dis[y]>dis[x]+w)
			{
				dis[y]=dis[x]+w;
				pre[y]=x;
				point pp(y,dis[y]);
				q.push(pp);
			}
		}
	}
}
void output1(int v)	//从后往前输出
{
	if(v!=s)
		output1(pre1[v]);
	if(v==s)
		printf("%d",v);
	else
		printf(" %d",v);
}
void output2(int v)	//从前往后输出
{
	while(v!=e)
	{
		printf(" %d",v);
		v=pre2[v];
	}
	printf(" %d",v);
}
int main()
{
	int sjzs=0;	//数据组数
	while(~scanf("%d%d%d",&n,&s,&e))
	{
		//清空表
		for(int i=0; i<600; i++)
			head[i]=0,dis1[i]=MAX_G,dis2[i]=MAX_G,pre1[i]=0,
			                                pre2[i]=0,vis1[i]=0,vis2[i]=0;
		tot=0;
		scanf("%d",&m);
		//读入
		for(int i=0; i<m; i++)
		{
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			add(x,y,z);
			add(y,x,z);
		}
		scanf("%d",&k);
		for(int i=1; i<=k; i++)
		{
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			syEdge[i].u=x,syEdge[i].v=y,syEdge[i].w=z;
		}
		dijkstra(s,dis1,pre1,vis1);
		dijkstra(e,dis2,pre2,vis2);
		sjzs++;
		if(sjzs>1)	cout<<endl;
		int minAns=MAX_G;
		int syU,syV;	//syU连接起点s,syV连接终点e 
		for(int i=1; i<=k; i++)
		{
			int u=syEdge[i].u,v=syEdge[i].v,w=syEdge[i].w;
			if(minAns>min(dis1[u]+dis2[v]+w,dis1[v]+dis2[u]+w))
			{
				if(dis1[u]+dis2[v]+w<dis2[u]+dis1[v]+w)
				{
					minAns=dis1[u]+dis2[v]+w;
					syU=u,syV=v;
				}
				else
				{
					minAns=dis1[v]+dis2[u]+w;
					syU=v,syV=u;
				}
			}
		}
		if(minAns>dis1[e])	//经济线
		{
			minAns=dis1[e];
			output1(e);
			printf("\n");
			printf("Ticket Not Used\n");
			printf("%d\n",minAns);
		}
		else	//含商业线syU,syV
		{
			output1(syU);
			output2(syV);
			printf("\n");
			printf("%d\n",syU);
			printf("%d\n",minAns);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值