题意 : 中文题请点链接,挺复杂的...
分析 : 乍一看是个最短路,实际就真的是个最短路。如果没有 “ 在有多条最短路径的时候输出换乘次数最少的” 这一条件的约束,那么这题就是直接建图然后跑个 Dij 就行了,那有了这个约束条件还是要大胆的向最短路思路靠,题目既然需要换乘次数少的,那么我们在进行最短路松弛操作的时候,面对松弛过后最短路径相等的情况就要分开讨论,这时候为了方法取最优值,需要多记录一个信息 ==> 跑到当前点时候换乘次数是多少次,开个数组来记录就行了,其他的还是按最短路来跑。这题就是编码烦了点,不对!是非常烦_(:3 」∠)_
#include<bits/stdc++.h> using namespace std; const int maxn = 1e4 + 10; struct EDGE{ int v, nxt; }Edge[maxn<<2]; ///向前星存图 struct NODE{ ///跑DIJ时塞在优先队列的结构体 int v, Pre_v, TransferCnt, Dist; ///当前是哪个点、其前一个点是什么、换乘次数、源点到此点的最短距离 NODE(int V, int D, int Pv, int TCnt):v(V),Dist(D),Pre_v(Pv),TransferCnt(TCnt){}; bool operator < (const NODE &rhs) const{ if(this->Dist == rhs.Dist){ ///最短距离相等应当选择换乘次数小的 return this->TransferCnt > rhs.TransferCnt; ///由于是优先队列、重载小于号需要注意方向.... }else{ return this->Dist > rhs.Dist; } }; }; int cnt; ///边数量 int Head[maxn]; ///邻接表头 int Pre[maxn]; ///答案路径中每个点的前驱、便于恢复路径 int Dis[maxn]; ///记录Dij中源点到其他点的最短路距离 int TransNum[maxn]; ///到达这个点的时候换乘了多少次 int Line[maxn][maxn]; ///记录路线信息 int path[maxn<<2]; ///存储答案路径 bool vis[maxn]; ///DIJ中的标记数组 inline void init() ///初始化表头和计数变量 { memset(Head, -1, sizeof(Head)); cnt = 0; } inline void AddEdge(int from, int to) ///加边函数 { Edge[cnt].v = to; Edge[cnt].nxt = Head[from]; Head[from] = cnt++; } inline void Run_Dijkstra(int st, int en) { memset(vis, false, sizeof(vis)); memset(Dis, 0x3f3f3f3f, sizeof(Dis)); memset(TransNum, 0, sizeof(TransNum)); priority_queue<NODE> que; while(!que.empty()) que.pop(); Dis[st] = 0; que.push(NODE(st,0,0,0)); while(!que.empty()){ NODE T = que.top(); que.pop(); if(vis[T.v]) continue; else vis[T.v] = true; for(int i=Head[T.v]; i!=-1; i=Edge[i].nxt){ int Eiv = Edge[i].v; if(Dis[Eiv] > Dis[T.v] + 1){ ///满足松弛条件 Dis[Eiv] = Dis[T.v] + 1; int NewTrans = (T.v==st ? 0 : T.TransferCnt + (Line[T.v][Eiv] != Line[T.Pre_v][T.v])); ///计算新的换乘次数 que.push(NODE(Eiv, Dis[Eiv], T.v, NewTrans)); Pre[Eiv] = T.v; ///记录前驱、便于恢复路径 TransNum[Eiv] = NewTrans; ///记录当前点的换乘次数 } else if(Dis[Eiv] == Dis[T.v] + 1 && ///最短距离与松弛后相等则接下来比较换乘次数 TransNum[Eiv] > T.TransferCnt + (Line[T.v][Eiv] != Line[T.Pre_v][T.v])){ que.push(NODE(Eiv, Dis[Eiv], T.v, T.TransferCnt + (Line[T.v][Eiv] != Line[T.Pre_v][T.v]))); Pre[Eiv] = T.v; ///改变前驱 TransNum[Eiv] = T.TransferCnt + (Line[T.v][Eiv] != Line[T.Pre_v][T.v]); ///更新换乘次数 } } } if(Dis[en] == 0x3f3f3f3f){ ///不可达、输出 No Solution puts("Sorry, no line is available."); return ; }else{ int top = 0; ///记录路径中节点个数 int now = en; ///由于记录的是前驱、所以从终点开始恢复路径 int StNext; path[top++] = en; while(now != st){ int temp = Pre[now]; if(temp==st) StNext = now; ///记录起点的后继 ==> 我接下来的输出满足题目所需的答案有需要 path[top++] = temp; now = temp; } printf("%d\n", Dis[en]); ///先输出最短距离 int CurLine = Line[st][StNext]; ///从起点开始记录当前所在的铁路编号 int CurPoint = st; ///当前的点 for(int i=top-1; i>=1; i--){ if(Line[path[i]][path[i-1]] == CurLine) continue; ///如果下一个点和仍然在和之前一样的铁路编号则说明不是换乘点 else{ printf("Go by the line of company #%d from %04d to %04d.\n",CurLine, CurPoint, path[i]); ///输出格式需要注意.... ///printf("Go by the line of company #%d from %d to %d.\n", CurLine, CurPoint, path[i]); ///!!!错误的输出格式!!! CurPoint = path[i]; ///更新 CurPoint、CurLine CurLine = Line[path[i]][path[i-1]]; } } printf("Go by the line of company #%d from %04d to %04d.\n",CurLine, CurPoint, path[0]); ///printf("Go by the line of company #%d from %d to %d.\n", CurLine, CurPoint, path[0]); ///!!!错误的输出格式!!! } } int main(void) { init(); int n; scanf("%d", &n); for(int i=1; i<=n; i++){ int num, A, B; scanf("%d %d", &num, &A); for(int j=1; j<num; j++){ scanf("%d", &B); AddEdge(A, B); AddEdge(B, A); Line[A][B] = Line[B][A] = i; A = B; } } int Query; scanf("%d", &Query); while(Query--){ int A, B; scanf("%d %d", &A, &B); Run_Dijkstra(A, B); } return 0; }