1 条题解
-
0
思路与算法
按颜色范围平衡分治。每层把柱子分成低半颜色和高半颜色,再递归处理各自实际柱号。始终保留一根空柱。先说明二色整理:设满柱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