题意:
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);
}
}
}