众所周知,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终点,所以枚举的时候要正反枚举商业线,还有也没有说必须要坐商业线,所以更要考虑这种方案。