Dijkstra算法 模板题

题意

众所周知,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

思路

商业线和经济线,但是商业线最多坐一次,所以需要枚举坐与不坐商业线以及坐哪一条商业线。不坐,则需求出从起点到终点的最短路。假设坐k-l段,则需求出起点到k的最短路,以及终点到l的最短路。
利用Dijkstra算法我们可以求单源最短路,所以可以分别求出起点和终点到其他各点的最短路,然后枚举商业线,求最短路。
详细见代码;

代码

#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include 
#include 
const int inf=5*1e8;   //未联通则距为最大值
using namespace std;
int N,S,E,M,K,X,Y,Z,k,l;   
struct Edge
{
     
	int to,next,w;   //链式前向星
} e[510*510];
int head[1100],tot=0;
void add(int &u,int &v,int &w)
{
     
	e[++tot].to=v;
	e[tot].w=w;
	e[tot].next=head[u];
	head[u]=tot;
}
//以上是链式前向星需要的东西 以下是Dijkstra算法需要的东西
bool vis[1100];  //判断是否访问过
int dis1[1100],dis2[1100],p1[1100],p2[1100];  //分别为距离和路径
priority_queue<pair<int ,int >> q;  //Dijkstra堆优化
void dijkstra1(int &s)
{
     
	while(q.size()) q.pop();  //堆清空
	memset(vis,0,sizeof(vis));  //初始都没访问过
	for(int i=1; i<=N; i++) dis1[i]=inf;   //初始距离全为 inf 最大
 	dis1[s]=0;    //到达自己的距离为0
  	q.push(make_pair(0,s));    //前一项为距离 后一项为 点
	while(q.size())
	{
     
		int x=q.top().second;   //取堆顶
		q.pop();
		vis[x]=1;   //标记访问
		for(int i=head[x]; i!=-1; i=e[i].next)
		{
     //遍历此点所有可能到达的地方
			int y=e[i].to,w=e[i].w;   
			if(dis1[y]>dis1[x]+w)
			{
     //松弛操作  更新最短距离
				p1[y]=x;   //记录前一个结点 即路径
				dis1[y]=dis1[x]+w;  
				q.push(make_pair(-dis1[y],y));  //入队列 加入负值 意思是将最小的放入堆顶
			}
		}
	}
}
void dijkstra2(int &s)
{
     //与上面一样
	while(q.size()) q.pop();
	memset(vis,0,sizeof(vis));
	for(int i=1; i<=N; i++) dis2[i]=inf;
	dis2[s]=0;
	q.push(make_pair(0,s));
	while(q.size())
	{
     
		int x=q.top().second;
		q.pop();
		vis[x]=1;
		for(int i=head[x]; i!=-1; i=e[i].next)
		{
     
			int y=e[i].to,w=e[i].w;
			if(dis2[y]>dis2[x]+w)
			{
     
				dis2[y]=dis2[x]+w;
				q.push(make_pair(-dis2[y],y));
				p2[y]=x;
			}
		}
	}
}

//一下 dfs输出 要保证输出格式 自己看一下
void dfs1(int &u)
{
     //递归输出路径  从起点开始向 u  
	if(u==S)
	{
     
		printf("%d",u);
		return;
	}
	dfs1(p1[u]);
	printf(" %d",u);
}
void dfs2(int &u)
{
     //同上  从u开始向终点
	printf(" %d",u);
	if(u==E)
	{
     
		return;
	}
	dfs2(p2[u]);
}
void clear()
{
     //初始化操作 
	memset(head,-1,sizeof(head));
	memset(p1,-1,sizeof(p1));
	memset(p2,-1,sizeof(p2));
	tot=1;
	k=0;
	l=0;
}
int main()
{
     

	int index=0;
	while(scanf("%d %d %d",&N,&S,&E)!=EOF)
	{
     
		if(index) printf("\n");
		clear();
		scanf("%d",&M);
		for(int i=0; i<M; i++)
		{
     
			scanf("%d %d %d",&X,&Y,&Z);
			add(X,Y,Z);    //无向图加入两边
			add(Y,X,Z);
		}
		dijkstra1(S);
		dijkstra2(E);
		int minTime=dis1[E];//只坐经济线的最短时间
		scanf("%d",&K);
		for(int i=1; i<=K; i++)
		{
     
			scanf("%d %d %d",&X,&Y,&Z);
			int ans=dis1[X]+dis2[Y]+Z;  //枚举商业线
			if(minTime>ans)
			{
     //如果做了商业线时间更短 则更新
				minTime=ans;
				k=X;
				l=Y;
			}
			ans=dis1[Y]+dis2[X]+Z;   //枚举商业线 无向图  可以反向坐
			if(minTime>ans)
			{
     //同上
				minTime=ans;
				k=Y;
				l=X;
			}
		}
		if(k==0)
		{
     //k==0 没坐商业线
			dfs1(E);   //从起点到终点
			printf("\nTicket Not Used\n%d\n",minTime);
		}
		else
		{
     
			dfs1(k);   //从起点到k
			dfs2(l);    //从l到终点
			printf("\n%d\n%d\n",k,minTime);
		}
		index=1;   //格式需要
	}
	return 0;
}

总结:

注意,题中没有给出S起点一点过小于E终点,所以枚举的时候要正反枚举商业线,还有也没有说必须要坐商业线,所以更要考虑这种方案。

你可能感兴趣的:(c++,算法,Dijkstra,dijkstra,算法,c++,dfs)