開啟章節選單
10665 Diatribe against Pigeonholes
題目連結
題目翻譯
有一排 N 個信箱(1 < N < 26),依序編號 1 到 N,分配給 N 位員工,員工依序以大寫字母 A, B, C, ... 表示。這排信箱架子在正中央(第 (N+1)/2 個位置附近)比較脆弱,因此包裹數量越多(越重)的員工,信箱要安排在離中間越遠的地方,藉此保護架子。所有包裹重量相同。每位員工收到的包裹數量,以一串大寫字母表示(用 # 結尾),字母出現的次數就是該員工收到的包裹數,不合法的字母需忽略。
請找出一種信箱安排方式,使得信箱裡的包裹數量符合「離中間越遠,包裹數不可以比較靠近中間的信箱少」的規則;如果有多種排法都滿足條件,輸出員工字母排列中,字典序最小的那一組。
輸入
第一行是測資數 M。每筆測資第一行是整數 N (1 < N < 26);第二行是一串大寫字母,代表每位員工收到的包裹數量,以 # 結尾。
輸出
對每筆測資輸出兩行:第一行是信箱 1 到 N 由左到右對應的員工字母;第二行是對應信箱裡的包裹數量,皆以空白分隔。
解題思路
- 先統計每位員工(A 到第 N 個字母,沒出現視為 0)收到的包裹數,並把所有數值由大到小排序。
- 把信箱依照「離中間的距離」分組:距離相同的兩個信箱(例如左邊第 i 個、右邊第 i 個)為一組,由外到內共有 N/2 組;若 N 是奇數,正中央會多一個落單的信箱。離中間越遠的組別,必須分配到排序後越前面(數值越大)的兩個包裹數,因此把排序好的數列由外到內,依序每 2 個切成一組分給各組(正中央的落單信箱則分到排序後剩下的那一個,也就是最小值,符合「中間最輕」的要求)。
- 同一組的兩個信箱因為離中間一樣遠,彼此之間沒有大小限制,所以該組的兩個包裹數可以自由分給左邊或右邊的信箱;當好幾個員工的包裹數相同時,這個「相同的值要分給哪個信箱」的自由度甚至會跨越不同組別。由於 N < 26,最多只有 12 組有這種左右可自由調換的情形,直接對所有「數值不同」的組別窮舉「左邊拿大的/右邊拿大的」兩種情況(最多 2^12 ≈ 4096 種組合),對每種組合推算出每個信箱「需要的包裹數」。
- 包裹數決定後,同一個數值若對應到多個信箱,就把這些員工的字母由小到大排序,依信箱編號由小到大依序配對,這樣能保證字典序最小。
- 最後在所有窮舉出來的結果中,取整體字串字典序最小的那一組作為答案。
程式碼
#include <bits/stdc++.h> using namespace std; #define pb push_back #define all(x) (x).begin(), (x).end() #define WA() cin.tie(0)->sync_with_stdio(0) signed main() { WA(); int T; cin >> T; while (T--) { int n; cin >> n; string s; cin >> s; vector<int> w(n, 0); for (char c : s) if (c >= 'A' && c < 'A' + n) w[c - 'A']++; vector<int> sv = w; sort(all(sv), greater<int>()); int k = n / 2; bool hasCenter = n % 2; vector<int> hi(k), lo(k); for (int i = 0; i < k; i++) { hi[i] = sv[2*i]; lo[i] = sv[2*i+1]; } int centerVal = hasCenter ? sv[2*k] : -1; vector<int> freeTier; for (int i = 0; i < k; i++) if (hi[i] != lo[i]) freeTier.pb(i); int fk = freeTier.size(); map<int, vector<char> > valLet; for (int i = 0; i < n; i++) valLet[w[i]].pb('A' + i); string best; vector<int> bestVal; for (int mask = 0; mask < (1 << fk); mask++) { vector<int> b(k, 0); for (int j = 0; j < fk; j++) b[freeTier[j]] = (mask >> j) & 1; vector<int> posVal(n + 1, 0); // 1-indexed for (int i = 0; i < k; i++) { int L = i + 1, R = n - i; if (!b[i]) { posVal[L] = hi[i]; posVal[R] = lo[i]; } else { posVal[L] = lo[i]; posVal[R] = hi[i]; } } if (hasCenter) posVal[k + 1] = centerVal; map<int, vector<int> > valPos; for (int p = 1; p <= n; p++) valPos[posVal[p]].pb(p); vector<char> letter(n + 1); for (map<int, vector<int> >::iterator it = valPos.begin(); it != valPos.end(); ++it) { vector<int> &ps = it->second; vector<char> &ls = valLet[it->first]; for (size_t i = 0; i < ps.size(); i++) letter[ps[i]] = ls[i]; } string seq(n, ' '); for (int p = 1; p <= n; p++) seq[p-1] = letter[p]; if (best.empty() || seq < best) { best = seq; bestVal.assign(posVal.begin() + 1, posVal.end()); } } cout << best << '\n'; for (int i = 0; i < n; i++) cout << bestVal[i] << " \n"[i == n-1]; } }