1 条题解

  • 0
    @ 2026-10-27 6:55:30

    思路与算法

    按颜色范围平衡分治。每层把柱子分成低半颜色和高半颜色,再递归处理各自实际柱号。始终保留一根空柱。先说明二色整理:设满柱A中白球w个,借一根满柱H和空柱E,将H顶部t=min(w,m-w)个球暂存E;把A的少数色送H、多数色送E。两个桶满后按白色、黑色顺序倒回A,再把H原来暂存的t球归还。H恢复原样,E仍空,总共2m+2t步。 先整理首柱作为携带柱。依次整理下一柱B,设携带柱白球a个、B黑球u个。先把携带柱的m-a个黑球倒到空柱。若u≤a,再把B的u黑球倒到空柱,把B的m-a白球补满携带柱,最后把空柱黑球倒回B,完成一个纯白柱,B成为新的携带柱。若u>a,把B的a黑球倒空柱使其成为纯黑柱,把携带柱的a白球倒B,此时原携带柱成为新空柱;再用刚完成的纯黑柱作可恢复辅助柱,二色整理B。最后携带柱也必为纯色,按颜色归组递归。

    正确性说明

    二色整理时H只接收A的少数色,E只接收另一色,两桶中A的球都位于原辅助球上方;倒回后辅助球逐个归位,故不损坏其他柱。两种合并分支逐步满足容量约束,并形成一个同色满柱及一个有白下黑上结构的携带柱。总白球数是m的倍数,处理完所有柱后最后携带柱也只能全白或全黑,因此每层确实把两组颜色分开,子问题互不干扰。 整理首柱至多3m步。每个后续柱的整理加合并至多7m:第二分支令s=a+b<m,代价为5m+a+2min(b,m-b)+2min(s,m-s),按s≤m/2或s≥m/2分别可界为7m。k柱一层至多(7k-4)m。平衡递归n=50时叶深和286、内节点49,总界(7×286-4×49)×400=722400;对2≤n≤50递归界单调且不超过此值,小于820000。

    复杂度

    初始化所有球与操作表O(nm),每层扫描和搬运O(nm),总时间O(nm log n+操作数),空间O(nm+操作数)。n=1直接输出0。

    易错点

    输入每列从下到上。每次只移动顶部球;借用满辅助柱必须恢复。空柱的实际编号会改变,递归传实际柱号。特判逐步模拟容量、最终颜色及820000上限,自编数据并非官方原始数据。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    struct Solver {
        int n, m, empty;
        vector<vector<int>> col;
        vector<pair<int, int>> ops;
        void move(int u, int v) {
            assert(!col[u].empty() && (int)col[v].size() < m && u != v);
            col[v].push_back(col[u].back());
            col[u].pop_back();
            ops.push_back({u, v});
        }
        int white(int u, int cut) {
            int t = 0;
            for (int x : col[u])
                t += x <= cut;
            return t;
        }
        void sorted(int u, int helper, int cut) {
            int w = white(u, cut), small = min(w, m - w);
            bool chosenWhite = w <= m - w;
            int e = empty;
            for (int i = 0; i < small; i++)
                move(helper, e);
            while (!col[u].empty()) {
                bool isWhite = col[u].back() <= cut;
                move(u, isWhite == chosenWhite ? helper : e);
            }
            if (chosenWhite) {
                for (int i = 0; i < w; i++)
                    move(helper, u);
                for (int i = 0; i < m - w; i++)
                    move(e, u);
            } else {
                for (int i = 0; i < w; i++)
                    move(e, u);
                for (int i = 0; i < m - w; i++)
                    move(helper, u);
            }
            for (int i = 0; i < small; i++)
                move(e, helper);
        }
        void solve(vector<int> ids, int low, int high) {
            if (low == high)
                return;
            int cut = (low + high) / 2;
            vector<int> left, right;
            int carry = ids[0];
            sorted(carry, ids[1], cut);
            for (int j = 1; j < (int)ids.size(); j++) {
                int b = ids[j];
                sorted(b, carry, cut);
                int aWhite = white(carry, cut), bBlack = m - white(b, cut), e = empty;
                for (int t = 0; t < m - aWhite; t++)
                    move(carry, e);
                if (bBlack <= aWhite) {
                    for (int t = 0; t < bBlack; t++)
                        move(b, e);
                    for (int t = 0; t < m - aWhite; t++)
                        move(b, carry);
                    left.push_back(carry);
                    while (!col[e].empty())
                        move(e, b);
                } else {
                    for (int t = 0; t < aWhite; t++)
                        move(b, e);
                    for (int t = 0; t < aWhite; t++)
                        move(carry, b);
                    right.push_back(e);
                    empty = carry;
                    sorted(b, e, cut);
                }
                carry = b;
            }
            if (white(carry, cut) == m)
                left.push_back(carry);
            else {
                assert(white(carry, cut) == 0);
                right.push_back(carry);
            }
            assert((int)left.size() == cut - low + 1);
            assert((int)right.size() == high - cut);
            solve(left, low, cut);
            solve(right, cut + 1, high);
        }
        void run() {
            cin >> n >> m;
            col.resize(n + 2);
            empty = n + 1;
            for (int i = 1; i <= n; i++) {
                col[i].resize(m);
                for (int &x : col[i])
                    cin >> x;
            }
            vector<int> ids(n);
            iota(ids.begin(), ids.end(), 1);
            solve(ids, 1, n);
            assert(ops.size() <= 820000);
            cout << ops.size() << '\n';
            for (auto p : ops)
                cout << p.first << ' ' << p.second << '\n';
        }
    };
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        Solver().run();
    }
    
    • 1

    信息

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