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 前往喵星机场花费的总时间。

本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行

Sample Input

4 1 4
4
1 2 2
1 3 3
2 4 4
3 4 5
1
2 4 3

Sample Output

1 2 4
2
5

解题思路以及关键代码

首先分析题目
给定了起点与终点,而且要求商业线最多乘坐一次
• 可以枚举每一条商业线,计算起点到u的最短路以及v到
终点的最短路再加上该商业线所花费的时间

具体做法

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

关于最后输出路径

注意,输出路径的时候,要注意从起点输出的时候递归输出(因为记录的是上一个
位置,对应反向查找的过程),从终点输出的时候直接循环正序输出即可。

全部代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 5005;
const int maxm = 10005;
const int inf = 100005; 
int n,m,k,s,e,L,R,mindis=INT_MAX;
//链式前向星
struct Edge
{
	int to,next,w;
 } edge[maxm];

int head[maxn],cnt;
void init()
{
	cnt=0;
	//memset(head,-1,sizeof(head)); //memset只能初始化为0或-1   (-)127/3可以表示一个很大(小)的数
	for(int i=1;i<=n;i++)
		head[i]=-1; 
}
void add(int u,int v,int w)
{
	edge[cnt].to=v;
	edge[cnt].w=w;
	edge[cnt].next=head[u];
	head[u]=cnt;
	cnt++;
}
//迪杰特斯拉
int dis1[maxn],dis2[maxn],path1[maxn],path2[maxn];
bool vis[maxn],one = 0;
priority_queue< pair<int,int> > q;
void Dijkstra(int s,int* dis,int* path) //有权图单源最短路 
{
	//初始化 
	while(q.size()) q.pop();
	for(int i=1;i<=n;i++)
	 	vis[i]=0,dis[i]=inf,path[i]=-1;
	//memset(dis,127/3,sizeof(dis));
	//memset(vis,0,sizeof(vis));
	//memset(path,-1,sizeof(path));
	dis[s]=0;
	q.push(make_pair(0,s));  //pair排序是按照pair.first进行排序的 所以第一个存距离,第二个存点 
	
	while(q.size())
	{
		int now = q.top().second; //堆中dis最小值的点
		q.pop();
		if(vis[now]) continue;
		vis[now] = true;
		//松弛
		for(int i=head[now] ; ~i ; i=edge[i].next) 
		{
			int u = now,v = edge[i].to,w = edge[i].w; // u(now) v w
			if(dis[v] > dis[u] + w)
			{
				dis[v] = dis[u] + w; 
				q.push(make_pair(-dis[v],v));  //最大堆丢负数 变最小堆
				path[v] = u; 
			}	
		}
	 } 
 } 
void output(int ss)
{
	if(ss==s)
	{
		printf("%d", ss);
        return;
	}
	output(path1[ss]); 
    printf(" %d", ss); 
}

int main()
{
	while(scanf("%d%d%d",&n,&s,&e)!=EOF)
	{
		scanf("%d",&m);
		init();
		while(m--)
		{
			int u,v,w;
			scanf("%d%d%d",&u,&v,&w);
			add(u,v,w);	
			add(v,u,w);
		}
		Dijkstra(s,dis1,path1);
		Dijkstra(e,dis2,path2);
		scanf("%d",&k);
		int count=-1;
		for(int i=1;i<=k;i++)
		{
			int u,v,w;
			scanf("%d%d%d",&u,&v,&w);
			if(mindis > dis1[u]+dis2[v]+w)
			{
                L=u; R=v; 
				mindis=dis1[u]+dis2[v]+w; count=u;
            }
           	if(mindis > dis1[v]+dis2[u]+w)
			{
                L=v; R=u; 
				mindis=dis1[v]+dis2[u]+w; count=v;
            }		
		}
		if(!one) one=true;//格式控制
        else printf("\n");
		if(dis1[e]<mindis)//不使用商业票
        {
            output(e);
            //cout<<endl<<"";
            printf("\nTicket Not Used\n%d\n",dis1[e]);
            //cout<<endl<<dis1[e]<<endl;
        }
		else
		{
			output(L);
			//cout<<" ";
			printf(" ");
			//output2(e);
			int tmp = R;
			while(tmp!=-1)
			{
				if(path2[tmp]!=-1)
				{
					printf("%d ",tmp);
					tmp = path2[tmp];
				}
				else
				{
					printf("%d",tmp);
					tmp = path2[tmp];
				}
					
			}
			printf("\n%d\n%d\n",count,mindis);
		}
		mindis = INT_MAX;
	}
	
	return 0;
 }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值