AcWing 1137. 选择最佳线路

\(AcWing\) \(1137\). 选择最佳线路

一、题目描述

有一天,琪琪想乘坐公交车去拜访她的一位朋友。

由于琪琪非常容易晕车,所以她想 尽快 到达朋友家。

现在给定你一张城市交通路线图,上面包含城市的公交站台以及公交线路的具体分布。

已知城市中共包含 \(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<w<n,0<t≤1000\)

输入样例

5 8 5
1 2 2
1 5 3
1 3 4
2 4 7
2 5 6
2 3 5
3 5 1
4 5 1
2
2 3
4 3 4
1 2 3
1 3 4
2 3 2
1
1

输出样例

1
-1

二、超级源点法

最短路多个起点,不需要做多遍最短路,只需要构造一个超级源点,使其到其他起点的花费都是\(0\),以这点为起点做一遍最短路即可,图论中一种很常见的小技巧。

注意:加边了,注意\(N\)\(M\)的范围要多开一些。

\(Code\)

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> 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<PII, vector<PII>, greater<PII>> 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;
}

三、反向建图法

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> 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<PII, vector<PII>, greater<PII>> 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;
}
posted @ 2022-03-18 13:31  糖豆爸爸  阅读(130)  评论(0编辑  收藏  举报
Live2D