## [$AcWing$ $1137$. 选择最佳线路](https://www.acwing.com/problem/content/1139/) ### 一、题目描述 有一天,琪琪想乘坐公交车去拜访她的一位朋友。 由于琪琪非常容易晕车,所以她想 **尽快** 到达朋友家。 现在给定你一张城市交通路线图,上面包含城市的公交站台以及公交线路的具体分布。 已知城市中共包含 $n$ 个车站(编号$1$~$n$)以及 $m$ 条公交线路。 每条公交线路都是 **单向** 的,从一个车站出发直接到达另一个车站,**两个车站之间可能存在多条公交线路**。 琪琪的朋友住在 $S$ 号车站附近。 琪琪可以在任何车站选择换乘其它公共汽车。 请找出琪琪到达她的朋友家(附近的公交车站)需要 **花费的最少时间** 。 **输入格式** 输入包含多组测试数据。 每组测试数据第一行包含三个整数 $n,m,s$,分别表示 **车站数量**,**公交线路数量** 以及 **朋友家附近车站的编号**。 接下来 $m$ 行,每行包含三个整数 $p,q,t$,表示存在一条线路从车站 $p$ 到达车站 $q$,用时为 $t$。 接下来一行,包含一个整数 $w$,表示琪琪家附近共有 $w$ 个车站,她可以在这 $w$ 个车站中选择一个车站作为始发站。 再一行,包含 $w$ 个整数,表示琪琪家附近的 $w$ 个车站的编号。 **输出格式** 每个测试数据输出一个整数作为结果,表示所需花费的最少时间。 如果无法达到朋友家的车站,则输出 $-1$。 每个结果占一行。 **数据范围** $n≤1000,m≤20000,1≤s≤n,0 using namespace std; typedef pair PII; // 建立虚拟源点0 const int N = 1010, M = 40010; const int INF = 0x3f3f3f3f; int n, m, S; // 邻接表 int h[N], e[M], w[M], ne[M], idx; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } int d[N]; // 最短距离数组 bool st[N]; // 是否进过队列 // 迪杰斯特拉 void dijkstra() { memset(d, 0x3f, sizeof d); // 初始化大 memset(st, 0, sizeof st); // 初始化为未出队列过 priority_queue, greater> pq; // 小顶堆 pq.push({0, 0}); // 出发点入队列 d[0] = 0; // 出发点距离0 while (pq.size()) { auto t = pq.top(); pq.pop(); int u = t.second; if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (d[j] > d[u] + w[i]) { d[j] = d[u] + w[i]; pq.push({d[j], j}); } } } // 注意:此处的S是终点,不是起点,不是起点,不是起点! printf("%d\n", d[S] == INF ? -1 : d[S]); } int main() { while (cin >> n >> m >> S) { // 注意清空链表头 memset(h, -1, sizeof h); idx = 0; // m条边 while (m--) { int a, b, c; cin >> a >> b >> c; add(a, b, c); } int T; scanf("%d", &T); while (T--) { int x; cin >> x; add(0, x, 0); } dijkstra(); } return 0; } ``` ### 三、反向建图法 ```cpp {.line-numbers} #include using namespace std; typedef pair PII; const int INF = 0x3f3f3f3f; const int M = 2e5 + 5, N = 1005; // 存图 int idx, h[N], e[M], w[M], ne[M]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } int n, m; // n个点,m条边 int S; // 出发点 int d[N]; // 距离数组 bool st[N]; // Dijkstra是不是入过队列 void dijkstra() { priority_queue, greater> q; q.push({0, S}); d[S] = 0; while (q.size()) { auto t = q.top(); int u = t.second, dist = t.first; q.pop(); if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (d[j] > dist + w[i]) { d[j] = dist + w[i]; q.push({d[j], j}); } } } } int main() { while (cin >> n >> m >> S) { // 初始化 memset(st, 0, sizeof st); memset(h, -1, sizeof h); memset(d, 0x3f, sizeof d); idx = 0; int ans = INF; while (m--) { int a, b, c; cin >> a >> b >> c; add(b, a, c); // 反向建边 } // 最短路 dijkstra(); int T; // T个终点 int x; // 终点ID cin >> T; while (T--) { cin >> x; ans = min(ans, d[x]); } printf("%d\n", ans == INF ? -1 : ans); } return 0; } ```