題目:給定嚴格遞增正整數序列 a₁..aₙ(已丟失),僅知其數位和序列 b₁..bₙ (1≤bᵢ≤300)。要求重構 a 序列,使 aₙ 盡可能小(字典序最小等價)。n≤300。
從 a₁ 到 aₙ 依次構造。對於 aᵢ,已知 aᵢ₋₁ 且需 aᵢ > aᵢ₋₁ 且 digitSum(aᵢ) = bᵢ。要使最終 aₙ 最小,每一步都應使 aᵢ 盡可能小(貪心最優子結構)。
將 L 與目標數對齊(高位補 0)。從低位向高位枚舉「修改位」:
pos 將數字增大(d > L[pos]),低位填最小數字使總和達標。pos 從低到高(優先修改低位,影響較小),d 從 L[pos]+1 到 9。need = S - (高位和) - d,需滿足 0 ≤ need ≤ 9*pos。a₁:直接構造數位和為 b₁ 的最小正整數(低位堆 9)。i=2..n:prev = aᵢ₋₁ 的數位陣列(低位在前)。prefix_sum = 高位已固定部分的數位和。pos = 0..max_len:prefix_sum(減去原 pos-1 位)。d = prev[pos]+1 .. 9:add = d - prev[pos],new_sum = prefix_sum + add。new_sum ≤ bᵢ ≤ new_sum + 9*pos → 可行。rem = bᵢ - new_sum,低位貪心放 9。pos+1.. 保持與 prev 相同。aᵢ,跳出循環。pos 都不可行(理論上不會),增加位數(高位補 1,低位構造)。// 核心函數:給定前一個數 prev_digits[], 目標和 target, 返回最小的下一個數 vectorconstruct_next(const vector & prev, int target) { int len = prev.size(); vector cur(len, 0); int prefix_sum = accumulate(prev.begin(), prev.end(), 0); for (int pos = 0; pos <= len; ++pos) { // pos 為修改位,允許擴展新位 if (pos > 0) prefix_sum -= prev[pos-1]; int start_d = (pos < len) ? prev[pos] + 1 : 1; // 新位從 1 開始 for (int d = start_d; d <= 9; ++d) { int new_sum = prefix_sum + d; int low_pos = pos; // 低位個數 if (new_sum <= target && target <= new_sum + 9 * low_pos) { // 可行,構造 int rem = target - new_sum; // 低位貪心放 9 for (int i = 0; i < low_pos; ++i) { cur[i] = min(9, rem); rem -= cur[i]; } cur[pos] = d; // 高位複製 prev for (int i = pos+1; i < len; ++i) cur[i] = prev[i]; // 去除前導零(高位) while (cur.size() > 1 && cur.back() == 0) cur.pop_back(); return cur; } } } // 理論上不可達,題目保證有解 return {}; }
pos 時減去 pos-1 位,O(1) 更新。new_sum ≤ target ≤ new_sum + 9*pos,低位能否湊足餘量。pos == len 時,表示需增加一位(如 999 → 1999)。| 指標 | 複雜度 | 說明 |
|---|---|---|
| 時間 | O(n · L · 10) | L 為數位長度,最大 ~35(300/9),n≤300,極快 |
| 空間 | O(L) | 數位陣列 |
CF 509C 是「貪心構造 + 數位 DP 思想」的精妙結合。它展示了如何在「嚴格遞增 + 數位和約束」下,通過從低位向高位嘗試修改,構造字典序最小的序列。核心技巧:保持高位不變、在最低可能位增大、低位貪心填 9。此模式適用於「給定下界求最小滿足數位屬性的數」等問題(如 LeetCode 1085、數位 DP 類)。