题目描述:
众所周知,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 前往喵星机场花费的总时间。
本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行
思路:
既然只能用一条商业线,可以每次取一条商业线,跑最短路,跑完所有商业线取最小,虽然可行,但复杂度是O(K*单源最短路);
更好的想法是,从原点S求一次最短路,再从目的地E求一次最短路;假设现在有一条商业线(u,v,w),只需看disS[u]+w+disE[v]与disS[E]的大小(或disS[v]+w+disE[u]与disS[E]),枚举商业线,取最小的即可。
值得注意的是,在从E点跑最短路时,实际上路径数组path是按顺序记录的;在从S点跑最短路时,path按逆序记录;所以递归输出时,有一点点细微的区别。
总结:
起初WA,找不到缘由,后来穷究了原因,发现优先队列定义记错了
升序优先队列:
1 #include <queue> 2 priority_queue<int,vector<int>,greater<int> > q; 3 //pair 4 priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
可以这样记忆:greater是较大的,认为是越来越大,就是升序,less则相反。
代码:
#include <cstdio> #include <iostream> #include <queue> #include <cstring> #include <climits> using namespace std; const int MAXN=1e5+5; struct e { int v,w; int next; }Edge[MAXN]; int last[MAXN]; int tot; int visited[MAXN]; int N,S,E,M,K; int disS[MAXN],disE[MAXN]; int pre[MAXN],nxt[MAXN]; void addEdge(int u,int v,int w) { tot++; Edge[tot].v=v; Edge[tot].w=w; Edge[tot].next=last[u]; last[u]=tot; } void dijkstra(int s,int *dis,int *path) { memset(visited,0,sizeof(visited)); for(int i=1;i<=N;i++) dis[i]=INT_MAX; priority_queue< pair<int,int>,vector< pair<int,int> >,greater< pair<int,int> > > q; // priority_queue< pair<int,int> > q; q.push(make_pair(0,s)); dis[s]=0; while(!q.empty()) { int t=q.top().second,t2=q.top().first; q.pop(); if(visited[t]==1) continue; visited[t]=1; for(int i=last[t];i!=0;i=Edge[i].next) { int v=Edge[i].v,w=Edge[i].w; if(dis[v]>t2+w) { dis[v]=t2+w; path[v]=t; q.push(make_pair(dis[v],v)); } } } } void outputPath(int *path,int x) //输出达到x的路径 { if(path[x]!=0) outputPath(path,path[x]); printf(path[x]==0 ? "%d" : " %d",x); } void outputPath2(int *path,int x) //输出x到终点的路径 { printf(" %d",x); if(path[x]!=0) outputPath2(path,path[x]); } int main() { int first=1; while( scanf("%d %d %d",&N,&S,&E)==3 ) { if(!first) cout<<endl; tot=0; memset(Edge,0,sizeof(Edge)); memset(last,0,sizeof(last)); memset(pre,0,sizeof(pre)); memset(nxt,0,sizeof(nxt)); scanf("%d",&M); for(int i=1;i<=M;i++) { int t1,t2,t3; scanf("%d %d %d",&t1,&t2,&t3); addEdge(t1,t2,t3); addEdge(t2,t1,t3); } dijkstra(S,disS,pre); dijkstra(E,disE,nxt); scanf("%d",&K); int ans=0,numU=0,numV=0; ans=disS[E]; for(int i=1;i<=K;i++) { int u,v,w; scanf("%d %d %d",&u,&v,&w); //u在靠近S处 if(disS[u]!=INT_MAX&&disE[v]!=INT_MAX) { if(disS[u]+w+disE[v]<ans) ans=disS[u]+w+disE[v],numU=u,numV=v; } //u在靠近E处 if(disE[u]!=INT_MAX&&disS[v]!=INT_MAX) { if(disS[v]+w+disE[u]<ans) ans=disS[v]+w+disE[u],numU=v,numV=u; } } if(numU==0) //没经过商业线 { outputPath(pre,E); printf(" "); printf("Ticket Not Used %d ",disS[E]); } else { outputPath(pre,numU); outputPath2(nxt,numV); printf(" %d %d ",numU,ans); } first=0; } return 0; }