1. 程式人生 > >浙大PAT CCCC L3-014 周遊世界 ( 最短路變形 )

浙大PAT CCCC L3-014 周遊世界 ( 最短路變形 )

進行 jks none 多少 錯誤 include 情況 ide continue

題目鏈接

題意 : 中文題請點鏈接,挺復雜的...

分析 : 乍一看是個最短路,實際就真的是個最短路。如果沒有 “ 在有多條最短路徑的時候輸出換乘次數最少的” 這一條件的約束,那麽這題就是直接建圖然後跑個 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; }
View Code

浙大PAT CCCC L3-014 周遊世界 ( 最短路變形 )