题目
今天他在 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 行是商业线路段的描述,格式同经济线。
所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。
Ouput
对于每组数据,输出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 Ouput
1 2 4
2
5
思路
本题给定了起点和终点,且商业线最多乘坐一次(故可以枚举每一条商业线)。枚举每一条商业线(u v w),计算起点到u的最短路以及v到终点的最短路再加上该商业线花费的时间w。
使用dijkstra求没有负边的单源最短路。
以起点为源点求单源最短路,得dis1[];以终点为源点求单源最短路,得dis2[]。枚举商业线(u,v,w),取min{dis1[u]+dis2[v]+w, dis1[v]+dis2[u]+w},得到走商业线的最短路。再与不走商业线的最短路比较,取较小的。
Dijkstra
用于解决图中没有负边的单源最短路问题。
复杂度O((n+m)logn)。
实现流程:
- 设s为源点,dis[a]表示源点s到点a的最短路径,初始化dis[s]=0,dis[i]=inf,将s加入最小堆。
- 每次从堆中取出堆顶x,遍历x的所有临界边(x y w),比较dis[y]与dis[x]+w的大小。——松弛操作
- 若dis[y]>dis[x]+w,则松弛成功。更新dis[y]的大小,将y加入最小堆。
- 不断执行上述操作,直到最小堆为空,最后得到的dis数组记为所求单源最短路值。
由于图中只有正边,因此每个点只会被最小堆弹出一次,即一旦某个点被最小堆弹出,则不会再被松弛,dis的值即为最短路。
代码
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
struct Edge{
int to,w,next=-1;
}edge[2005];
const int inf=1e8;
int n,s,e,m,k,head[505],tot,dis1[505],dis2[505],path1[505],path2[505];
bool vis[505];
priority_queue<pair<int,int>> q;
void add(int x,int y,int w){
edge[++tot].to=y;
edge[tot].next=head[x];
edge[tot].w=w;
head[x]=tot;
}
void dijkstra(int s,int *dis,int *path){
while(q.size())
q.pop();
memset(vis, false, sizeof(vis));
path[s]=-1;dis[s]=0;
q.push(make_pair(0, s));
while(q.size()){
int x=q.top().second;
q.pop();
if(vis[x])//访问过,取下一个点
continue;
vis[x]=true;
for(int i=head[x];i!=-1;i=edge[i].next){
int y=edge[i].to,w=edge[i].w;
if(dis[y]>dis[x]+w){
dis[y]=dis[x]+w;
q.push(make_pair(-dis[y], y));
path[y]=x;
}
}
}
}
int main() {
bool firstInput=true;
while(scanf("%d%d%d",&n,&s,&e)!=EOF){
for(int i=1;i<=n;i++){
dis1[i]=inf;
dis2[i]=inf;
path1[i]=-1;
path2[i]=-1;
head[i]=-1;
}
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);
}
dijkstra(s, dis1, path1);
dijkstra(e, dis2, path2);
int u,v,ans=inf;
scanf("%d",&k);
for(int i=0;i<k;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(dis1[x]+dis2[y]+z<ans){
u=x;v=y;
ans=dis1[x]+dis2[y]+z;
}
if(dis1[y]+dis2[x]+z<ans){
u=y;v=x;
ans=dis1[y]+dis2[x]+z;
}
}
if(firstInput)
firstInput=false;
else
printf("\n");
vector<int>vec;
if(dis1[e]<ans){//不使用商业线
for(int i=e;i!=s;i=path1[i])
vec.push_back(i);
vec.push_back(s);
for(int i=(int)vec.size()-1;i>0;i--)
printf("%d ",vec[i]);
printf("%d\nTicket Not Used\n%d\n",vec[0],dis1[e]);
}
else{//使用商业线
for(int i=u;i!=s;i=path1[i])
vec.push_back(i);
vec.push_back(s);
for(int i=(int)vec.size()-1;i>=0;i--)
printf("%d ",vec[i]);
for(int i=v;i!=e;i=path2[i])
printf("%d ",i);
printf("%d\n",e);
printf("%d\n%d\n",u,ans);
}
}
}
总结
这道题一开始在main函数中memset(vis, false, sizeof(vis)),还忘记在第二次dijkstra之前初始化了,wa了好久。
题目链接