1 条题解

  • 0
    @ 2026-10-27 13:20:36

    思路与算法

    把一条乘完的线路看成状态。每站两条入线与两条出线只有两种一一接续配对;入线a接出线b的等待为(l_b-(l_a+d_a) mod 24+24) mod 24。枚举1号站的两种配对与被切开的两条接续之一:切开处的出线作为第一班,入线作为最终一班,费用用首班发车l替换该接续的环形等待。其余站各选等待总和更小的配对,所有边状态形成若干互不相交的循环。翻转某站的配对会交换两条入线的后继,若它们原在两个循环中,就把两个循环合为一个;代价是该站另一配对减当前最小配对的非负差值。将基础循环缩成点,各可翻转站连接它的两个入线所在循环,求最小生成树。连接成功的最小费用加所有线路行驶时间与基础等待,四种首尾情况取小。

    正确性说明

    任何Euler行程在每站都给两入线与两出线指定一个双射,在1号站把末线虚接首线后也成立,因此被枚举的中央配对与切口必含最优行程。给定该选择后,每条接续等待只依赖线路时刻,行驶与等待费用精确可加。其余站独立选最小配对使任何翻转的增量非负。基础接续排列分成循环;一次跨循环翻转恰合并两个循环,同循环翻转会拆环。任何最终单循环所用的跨基础环翻转,必须在基础环之间形成连通图,否则不可能跨越被分离的环集合,因此其非负增量之和至少为该图MST权。反过来,按MST逐条翻转时,每条树边总连接两个尚分离的环组,归纳每组对应一个已合并循环,最终得到一个循环且增量恰为MST权。沿所定切口展开即可得到合法1号首尾行程,所以下界可实现,四种情况最小值为全局最优。

    复杂度

    每种首尾情况初始化和找基础环O(n),构造至多n-1条候选翻转边并排序、Kruskal O(n log n),一共4种,总O(n log n)时间、O(n)空间。输入与所有数组初始化O(n)。

    易错点

    到站恰逢发车不必多等一天,等待取0..23。d可超过24小时,不能只把d mod 24计入总行驶时间。1号站首班从绝对0时等待,其费用必须替换而不是额外叠加环形等待;中央配对在当前枚举中固定,不能再作为MST翻转边。相同起终点的两条线路仍是不同状态。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    struct DSU {
        vector<int> p;
        DSU(int n) : p(n) {
            iota(p.begin(), p.end(), 0);
        }
        int find(int a) {
            return p[a] == a ? a : p[a] = find(p[a]);
        }
        bool join(int a, int b) {
            a = find(a);
            b = find(b);
            if (a == b)
                return false;
            p[a] = b;
            return true;
        }
    };
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        if (!(cin >> n))
            return 0;
        int m = 2 * n;
        vector<int> dest(m), leave(m), duration(m), incomingCount(n);
        vector<array<int, 2>> in(n);
        long long total = 0;
        for (int i = 0; i < m; i++) {
            cin >> dest[i] >> leave[i] >> duration[i];
            --dest[i];
            in[dest[i]][incomingCount[dest[i]]++] = i;
            total += duration[i];
        }
        auto wait = [&](int a, int b) {
            return (leave[b] - (leave[a] + duration[a]) % 24 + 24) % 24;
        };
        vector<array<int, 2>> cost(n);
        for (int v = 0; v < n; v++)
            for (int t = 0; t < 2; t++)
                cost[v][t] = wait(in[v][0], 2 * v + t) + wait(in[v][1], 2 * v + (t ^ 1));
        long long answer = LLONG_MAX;
        for (int rootMatch = 0; rootMatch < 2; rootMatch++)
            for (int cut = 0; cut < 2; cut++) {
                vector<int> choice(n), next(m);
                long long base = total;
                for (int v = 0; v < n; v++) {
                    int t = v == 0 ? rootMatch : (cost[v][0] <= cost[v][1] ? 0 : 1);
                    choice[v] = t;
                    base += cost[v][t];
                    next[in[v][0]] = 2 * v + t;
                    next[in[v][1]] = 2 * v + (t ^ 1);
                }
                int last = in[0][cut], first = next[last];
                base += leave[first] - wait(last, first);
                vector<int> cycle(m, -1);
                int count = 0;
                for (int i = 0; i < m; i++)
                    if (cycle[i] < 0) {
                        for (int x = i; cycle[x] < 0; x = next[x])
                            cycle[x] = count;
                        count++;
                    }
                vector<array<int, 3>> edges;
                for (int v = 1; v < n; v++) {
                    int a = cycle[in[v][0]], b = cycle[in[v][1]];
                    if (a != b)
                        edges.push_back({{cost[v][choice[v] ^ 1] - cost[v][choice[v]], a, b}});
                }
                sort(edges.begin(), edges.end());
                DSU d(count);
                int components = count;
                for (auto e : edges)
                    if (d.join(e[1], e[2]))
                        base += e[0], components--;
                if (components == 1)
                    answer = min(answer, base);
            }
        cout << answer << '\n';
    }
    
    • 1

    信息

    ID
    2112
    时间
    1000ms
    内存
    128MiB
    难度
    提高+
    标签
    递交数
    0
    已通过
    0
    上传者