SDU 程序设计思维与实践 week7 B TT 的旅行日记【Dijkstra】

B 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

Dijkstra算法分析

  • Dijkstra算法用于求解途中没有负边的单源最短路问题
  • 实现思想:设s为起点,dis[a]代表从起点s到a的最短距离,初始化dis[s]=0,dis[i]=inf(无穷大),将s加入到最小堆中。每次从堆中取一个点x,遍历x的所有邻接边(x,y,z),比较dis[y]dis[x]+w的大小.如果dis[y]>dis[x]+w,那么更新dis[y]的 大小,加入到最小堆中
  • 算法复杂度: O ( ( n + m ) l o g n ) O((n+m)logn) O((n+m)logn).

思路分析

通过枚举每一条商业线,计算起点到每一条商业线起点的最短路,终点到每一条商业线终点的最短路,再加上该商业线所花费的时间。

  • 以起点为源点求单源最短路,得到dis1[]
  • 以终点为源点求单源最短路,得到dis2[]
  • 枚举商业线(u,v,w),取min(dis1[u]+dis2[v]+w,dis1[v]+dis2[u]+w),最终再与不走商业线的答案取min。

实现细节

  • priority_queue<T>默认是最小堆,使其编程最大堆取负数即可。
  • 利用pre[]数组来记录当前节点的父节点是谁,对于正向和反向要使用不同的数组。在输出路径时也要根据正向输出还是反向输出编写不同输出函数。

AC代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
using namespace std;
const int inf=1e8;
const int M=1005*2;
const int N=505;
//数组模拟链表 
struct Edge{
	int to,next,w;
}e[M]; 
int head[N],tot,n,m,S,E,k,x,y,z,station,spost;
int vis[N],dis1[N],dis2[N],ans,pre1[N],pre2[N];

void add(int x,int y,int z){
	e[++tot].to=y,e[tot].next=head[x],head[x]=tot;
	e[tot].w=z;
}
//最大堆 丢负数,
priority_queue<pair<int,int>> q;
void dijkstra1(int s){	
	while(q.size()) q.pop();
	for(int i=1;i<=n;i++){
		vis[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(vis[x]) continue;
		vis[x]=1;
		for(int i=head[x];i;i=e[i].next){
			int y=e[i].to,w=e[i].w;//(x,y,w))
			if(dis1[y]>dis1[x]+w){
				dis1[y]=dis1[x]+w;
				pre1[y]=x;								
				q.push(make_pair(-dis1[y],y));
			}
		} 
	}	 
}
void dijkstra2(int s){	
	while(q.size()) q.pop();
	for(int i=1;i<=n;i++){
		vis[i]=0,dis2[i]=inf;
	} 
	dis2[s]=0;
	q.push(make_pair(0,s));	
	while(q.size()){
		int x=q.top().second;q.pop();
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=head[x];i;i=e[i].next){
			int y=e[i].to,w=e[i].w;//(x,y,w))
			if(dis2[y]>dis2[x]+w){
				dis2[y]=dis2[x]+w;
				pre2[y]=x;								
				q.push(make_pair(-dis2[y],y));
			}
		} 
	}	 
}


void path0(int p){
	//从后向前输出 
	if(p!=S){
		path0(pre1[p]);
	}
	if(p==S) printf("%d",p);
	else printf(" %d",p);
}
void path1(int p){
	//从前向后输出
	while(p!=E){
		printf(" %d",p);
		p=pre2[p];
	} 
	printf(" %d",p);
}

int main(){
	int tt=0;
	while(scanf("%d%d%d",&n,&S,&E)!=EOF){
		if(tt!=0){
			printf("\n");
		}
		cin>>m;			
		memset(pre1,0,sizeof(pre1));
		memset(pre2,0,sizeof(pre2));
		memset(head,0,sizeof(head));				 
		ans=inf;		
		tot=0;
		while(m--){			
			scanf("%d%d%d",&x,&y,&z);
			add(x,y,z);
			add(y,x,z);			 
		}
		dijkstra1(S);		
		dijkstra2(E);
		scanf("%d",&k);
		while(k--){
			scanf("%d%d%d",&x,&y,&z);
			if(ans>min(dis1[x]+dis2[y]+z,dis1[y]+dis2[x]+z)){
				if(dis1[x]+dis2[y]+z<dis1[y]+dis2[x]+z){
					ans=dis1[x]+dis2[y]+z;station=x;spost=y;
				}
				else{
					ans=dis1[y]+dis2[x]+z;station=y;spost=x;
				}
			}						
		}
		if(ans<dis1[E]){				
			path0(station);path1(spost); printf("\n");
			printf("%d\n",station);			 						
		}
		else{
			ans=dis1[E];			
			path0(E);printf("\n");
			printf("Ticket Not Used\n");			
		}
		printf("%d\n",ans);
		tt++;				
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值