庫(kù)題解深度解析:用隔板法與容斥原理 O(1) 求解「分糖果給小朋友 II」)
科學(xué)計(jì)算【免費(fèi)下載鏈接】codeforces-go算法競(jìng)賽模板庫(kù) by 靈茶山艾府 項(xiàng)目地址https://gitcode.com/GitHub_Trending/co/codeforces-go點(diǎn)擊查看免費(fèi)下載導(dǎo)讀本文以 leetcode/biweekly/117/b/README.md 為骨架完整還原力扣第 117 場(chǎng)雙周賽第二題「分糖果給小朋友 IIDistribute Candies Among Children II」的數(shù)學(xué)解法先用隔板法統(tǒng)計(jì)無(wú)上限約束的所有分配方案數(shù)再用容斥原理剔除至少一個(gè)小朋友分到的糖果超過 limit的非法方案最終得到一個(gè)可直接套用的組合數(shù)公式。讀完本文你將掌握無(wú)區(qū)別物體放入有區(qū)別盒子的組合計(jì)數(shù)建模、三集合容斥的逐步推導(dǎo)技巧以及如何在 codeforces-go 倉(cāng)庫(kù)中一行代碼落地該公式并借助倉(cāng)庫(kù)自帶的測(cè)試框架驗(yàn)證正確性。一、問題背景題目在倉(cāng)庫(kù)中的位置與題面重述該題解位于倉(cāng)庫(kù)的 leetcode/biweekly/117/b/README.md對(duì)應(yīng)的可運(yùn)行實(shí)現(xiàn)是 leetcode/biweekly/117/b/b.go。從測(cè)試文件 b_test.go 末尾的注釋可以確認(rèn)本題對(duì)應(yīng)力扣題目distribute-candies-among-children-ii。題面可重述為有 $n$ 顆無(wú)區(qū)別的糖果要全部分給 $3$ 個(gè)有區(qū)別的小朋友 $A,B,C$且每個(gè)小朋友分到的糖果數(shù)不超過$\textit{limit}$ 顆求合法的分配方案數(shù)。值得一提的是同一場(chǎng)雙周賽的第一題「分糖果給小朋友 I」共享完全相同的思路第一題實(shí)現(xiàn)位于 leetcode/biweekly/117/a/a.go。兩版代碼的唯一差別在于返回值類型I 版的 $n$ 范圍較小返回intII 版的 $n$ 范圍更大返回int64以避免組合數(shù)運(yùn)算中間結(jié)果溢出。這也是同一個(gè)數(shù)學(xué)模型因數(shù)據(jù)范圍不同需要調(diào)整數(shù)值類型的典型工程案例。二、問題建模轉(zhuǎn)化為小球入盒的組合計(jì)數(shù)把 $n$ 顆無(wú)區(qū)別糖果看成 $n$ 個(gè)無(wú)區(qū)別的小球把 3 個(gè)小朋友看成 3 個(gè)有區(qū)別的盒子。合法方案數(shù)即為把 $n$ 個(gè)無(wú)區(qū)別小球放入 3 個(gè)有區(qū)別盒子允許空盒且每個(gè)盒子的小球數(shù)不超過 $\textit{limit}$ 的方案數(shù)。求解思路采用正難則反$$ \text{合法方案數(shù)} \text{所有方案數(shù)} - \text{不合法方案數(shù)} $$其中不合法指至少一個(gè)小朋友分到的糖果超過 $\textit{limit}$。三、第一步用隔板法求所有方案數(shù)在沒有 $\textit{limit}$ 限制時(shí)問題退化為經(jīng)典組合計(jì)數(shù)$n$ 個(gè)無(wú)區(qū)別小球放入 $3$ 個(gè)有區(qū)別盒子、允許空盒的方案數(shù)。隔板法是標(biāo)準(zhǔn)工具把 $n$ 個(gè)球排成一列在它們之間及兩端共 $n1$ 個(gè)空隙中插入 $2$ 個(gè)隔板用隔板把球分成三段依次對(duì)應(yīng)三個(gè)盒子。等價(jià)地可理解為 $n$ 個(gè)球與 $2$ 個(gè)隔板共 $n2$ 個(gè)位置從中選出 $2$ 個(gè)位置放隔板其余位置放球第一個(gè)隔板之前的球進(jìn)第 1 個(gè)盒子第一個(gè)隔板與第二個(gè)隔板之間的球進(jìn)第 2 個(gè)盒子第二個(gè)隔板之后的球進(jìn)第 3 個(gè)盒子。因此所有方案數(shù)為$$ \binom{n2}{2} $$隔板法天然覆蓋了空盒情形隔板可以放在最左端第 1 個(gè)盒子為空、最右端第 3 個(gè)盒子為空兩個(gè)隔板也可以相鄰第 2 個(gè)盒子為空均對(duì)應(yīng)一種合法擺放方式無(wú)需單獨(dú)討論。邊界提示組合數(shù) $\binom{n2}{2}$ 恒有意義$n \geqslant 0$但后續(xù)容斥項(xiàng)中的參數(shù)可能變成負(fù)數(shù)需要借助當(dāng) $x 2$ 時(shí) $\binom{x}{2}0$的約定來(lái)統(tǒng)一處理詳見第六節(jié)。四、第二步用容斥原理逐層統(tǒng)計(jì)不合法方案設(shè) $A,B,C$ 分別表示小朋友 $A/B/C$ 分到的糖果超過 $\textit{limit}$這一事件不合法方案數(shù)即 $|A \cup B \cup C|$用容斥原理展開為$$ |A \cup B \cup C| |A||B||C| - |A\cap B|-|A\cap C|-|B\cap C| |A\cap B\cap C| $$4.1 至少一個(gè)小朋友超過 limit$|A||B||C|$先只看 $A$。若 $A$ 分到的糖果超過 $\textit{limit}$則先固定分給他 $\textit{limit}1$ 顆剩余 $n-(\textit{limit}1)$ 顆糖果仍然可以隨意分給 3 個(gè)小朋友包括繼續(xù)分給 $A$這一點(diǎn)至關(guān)重要下文單獨(dú)強(qiáng)調(diào)于是方案數(shù)為$$ \binom{n-(\textit{limit}1)2}{2} \binom{n-\textit{limit}1}{2} $$$B$、$C$ 的情形完全對(duì)稱三者相加得 $3\cdot\binom{n-\textit{limit}1}{2}$。由于這三個(gè)集合兩兩相交直接相加會(huì)重復(fù)統(tǒng)計(jì)至少兩個(gè)小朋友超過 limit的方案故需進(jìn)入下一步扣除。?易錯(cuò)點(diǎn)固定分給 $A$ 的 $\textit{limit}1$ 顆之后剩余糖果依然可以繼續(xù)分給 $A$。也就是說(shuō)先分給他 limit1 顆只是為了保證該方案至少超過 limit 一次并不代表總共只分給他 limit1 顆遺漏這一點(diǎn)會(huì)漏計(jì)大量方案。4.2 至少兩個(gè)小朋友超過 limit$|A\cap B||A\cap C||B\cap C|$只看 $A$ 和 $B$。若兩者都超過 $\textit{limit}$先固定分給他們各 $\textit{limit}1$ 顆即共 $2\cdot(\textit{limit}1)$ 顆剩余 $n-2\cdot(\textit{limit}1)$ 顆隨意分配給 3 人$C$ 是否超過 limit 不予關(guān)注方案數(shù)為$$ \binom{n-2\cdot(\textit{limit}1)2}{2} \binom{n-2\cdot\textit{limit}}{2} $$三組配對(duì) $(A,B),(A,C),(B,C)$ 對(duì)稱相加得 $3\cdot\binom{n-2\cdot\textit{limit}}{2}$。但這里又重復(fù)統(tǒng)計(jì)了三個(gè)小朋友均超過 limit的方案三個(gè)集合的交集被三組兩兩交集各統(tǒng)計(jì)一次因此還需要最后一步修正。4.3 三個(gè)小朋友均超過 limit$|A\cap B\cap C|$先固定分給三人共 $3\cdot(\textit{limit}1)$ 顆剩余 $n-3\cdot(\textit{limit}1)$ 顆隨意分配方案數(shù)為$$ \binom{n-3\cdot(\textit{limit}1)2}{2} \binom{n-3\cdot\textit{limit}-1}{2} $$4.4 容斥匯總將各層按奇加偶減合并不合法方案數(shù)為$$ 3\cdot\binom{n-\textit{limit}1}{2} - 3\cdot\binom{n-2\cdot\textit{limit}}{2} \binom{n-3\cdot\textit{limit}-1}{2} $$再用所有方案數(shù)減去它即得最終答案公式$$ \boxed{\binom{n2}{2} - 3\cdot\binom{n-\textit{limit}1}{2} 3\cdot\binom{n-2\cdot\textit{limit}}{2} - \binom{n-3\cdot\textit{limit}-1}{2}} $$這也印證了題解中至少一個(gè) ? (至少兩個(gè) ? 三個(gè)) 至少一個(gè) ? 至少兩個(gè) 三個(gè)的歸納三個(gè)集合的容斥最終表現(xiàn)為四個(gè)組合數(shù)的交錯(cuò)和其本質(zhì)就是標(biāo)準(zhǔn)的 3 集合容斥展開。五、最終公式的多語(yǔ)言實(shí)現(xiàn)題解為 Python3、Java、C、C、Go、JavaScript、Rust 提供了完全同構(gòu)的七份實(shí)現(xiàn)。其公共要點(diǎn)是定義輔助函數(shù) $c_2(x)$$$ c_2(x)\begin{cases}\frac{x(x-1)}{2} x1\ 0 x\leqslant 1\end{cases} $$即當(dāng) $x2$ 時(shí)組合數(shù) $\binom{x}{2}0$——這正是處理剩余糖果為負(fù)這類越界參數(shù)的關(guān)鍵。以倉(cāng)庫(kù)實(shí)際使用的 Go 實(shí)現(xiàn)為例與 b.go 逐行一致func c2(n int) int64 { if n 2 { return 0 } return int64(n) * int64(n-1) / 2 } func distributeCandies(n int, limit int) int64 { return c2(n2) - 3*c2(n-limit1) 3*c2(n-2*limit) - c2(n-3*limit-1) }Python 參考實(shí)現(xiàn)同樣簡(jiǎn)潔def c2(n: int) - int: return n * (n - 1) // 2 if n 1 else 0 class Solution: def distributeCandies(self, n: int, limit: int) - int: return c2(n 2) - 3 * c2(n - limit 1) 3 * c2(n - 2 * limit) - c2(n - 3 * limit - 1)其余語(yǔ)言Java/C/C/JavaScript/Rust的寫法與上述完全等價(jià)僅語(yǔ)法與整數(shù)溢出保護(hù)策略不同C/C 使用long longJava 使用longRust 顯式做as i64轉(zhuǎn)換目的都是在乘法n*(n-1)前提升到 64 位整數(shù)避免中間結(jié)果溢出。六、復(fù)雜度分析與數(shù)值邊界時(shí)間復(fù)雜度$\mathcal{O}(1)$——每個(gè)組合數(shù)由一次乘法、一次減法、一次除法直接算出不依賴 $n$ 的大小。空間復(fù)雜度$\mathcal{O}(1)$——僅使用常數(shù)個(gè)變量。數(shù)值邊界方面需要注意兩點(diǎn)負(fù)數(shù)參數(shù)的組合數(shù)約定當(dāng) $n$ 較小時(shí)容斥項(xiàng)如 $n-2\cdot\textit{limit}$ 甚至 $n-3\cdot\textit{limit}-1$ 會(huì)變成負(fù)數(shù)此時(shí)約定 $\binom{x}{2}0$由c2的n 2分支統(tǒng)一處理。例如 $n5, \textit{limit}2$ 時(shí)后兩項(xiàng)參數(shù)分別為 $1$ 和 $-2$均返回 0。整數(shù)類型選擇$n$ 可達(dá)到 $10^6$ 量級(jí)時(shí)$n^2$ 已達(dá) $10^{12}$超過 32 位int上限因此 II 版題解b.go的c2返回int64這正是它與 I 版a.go返回int的唯一實(shí)現(xiàn)差異。七、倉(cāng)庫(kù)內(nèi)的測(cè)試驗(yàn)證從公式到可運(yùn)行用例該倉(cāng)庫(kù)為每道題配套了題解 實(shí)現(xiàn) 輸入數(shù)據(jù) 測(cè)試四件套本題的驗(yàn)證閉環(huán)如下實(shí)現(xiàn)b.go 提供distributeCandies函數(shù)輸入數(shù)據(jù)b.txt 按每 3 行一組存放測(cè)試用例2 個(gè)輸入?yún)?shù) 1 個(gè)預(yù)期輸出共兩組n5, limit2期望輸出3n3, limit3期望輸出10。測(cè)試入口b_test.go 調(diào)用testutil.RunLeetCodeFuncWithFile(t, distributeCandies, b.txt, targetCaseNum)驅(qū)動(dòng)驗(yàn)證??墒止を?yàn)算兩組數(shù)據(jù)印證公式正確性$n5,\ \textit{limit}2$$\binom{7}{2}-3\binom{4}{2}3\binom{1}{2}-\binom{-2}{2}21-180-03$ ?枚舉可得 3 種分配$(3,1,1)$ 的三組排列$n3,\ \textit{limit}3$每人上限 3 顆而總共只有 3 顆所有方案天然合法$\binom{5}{2}10$ ?即 $xyz3$ 的非負(fù)整數(shù)解個(gè)數(shù)。測(cè)試驅(qū)動(dòng)層位于 leetcode/testutil/leetcode.go 的RunLeetCodeFuncWithFile它讀取b.txt按函數(shù)簽名NumIn NumOut行一組解析輸入與期望輸出再用反射逐組調(diào)用被測(cè)函數(shù)并比對(duì)結(jié)果而 RunLeetCodeFuncWithExamples 會(huì)在運(yùn)行單個(gè)用例后自動(dòng)繼續(xù)跑完全部用例并支持-1表示最后一個(gè)用例、檢測(cè)超時(shí)TLE等細(xì)節(jié)是倉(cāng)庫(kù)所有 LeetCode 題解共用的通用測(cè)試設(shè)施。若在 leetcode/biweekly/117/b 目錄執(zhí)行g(shù)o test即可一鍵跑通上述全部驗(yàn)證。八、思維延伸從分糖果到更廣的容斥應(yīng)用本題是三集合容斥 隔板法的教科書級(jí)組合隔板法負(fù)責(zé)無(wú)約束計(jì)數(shù)容斥負(fù)責(zé)處理上界約束二者各司其職。同場(chǎng)雙周賽的第三題 README.md 是同一套思想的另一形態(tài)——統(tǒng)計(jì)恰好包含 1 個(gè)l、1 個(gè)t、2 個(gè)e的長(zhǎng)度為 $n$ 的字符串個(gè)數(shù)同樣以正難則反構(gòu)造三個(gè)違規(guī)條件并用容斥展開最終化簡(jiǎn)為四個(gè)快速冪項(xiàng)的交錯(cuò)和$\mathcal{O}(\log n)$。對(duì)照閱讀這兩份題解可以清晰看到容斥原理這一數(shù)學(xué)工具的兩種典型落地形態(tài)一者是組合數(shù)求和一者是快速冪求和。若想在類似題目中復(fù)用本文方法建議遵循三步套路先建模為無(wú)區(qū)別物體入有區(qū)別盒子再寫出無(wú)約束方案數(shù)最后按違規(guī)條件個(gè)數(shù)分層套用容斥把每一層先固定越界部分、剩余任意分配的計(jì)數(shù)模式即 $\binom{\cdot}{2}$提煉出來(lái)。總結(jié)本文從 leetcode/biweekly/117/b/README.md 出發(fā)完整推導(dǎo)了分糖果給小朋友 II的 $\mathcal{O}(1)$ 公式隔板法給出基準(zhǔn) $\binom{n2}{2}$三集合容斥給出修正項(xiàng) $-3\binom{n-\textit{limit}1}{2}3\binom{n-2\textit{limit}}{2}-\binom{n-3\textit{limit}-1}{2}$并給出了 Python/Java/C/C/Go/JavaScript/Rust 七種語(yǔ)言的等價(jià)實(shí)現(xiàn)。同時(shí)結(jié)合倉(cāng)庫(kù)內(nèi) b.go、b.txt、b_test.go 與通用測(cè)試框架 leetcode/testutil/leetcode.go用兩組可運(yùn)行用例驗(yàn)證了公式的正確性。理解隔板法計(jì)數(shù) 容斥修正這套組合拳即可舉一反三地解決帶個(gè)體上限的整數(shù)拆分計(jì)數(shù)這一大類組合問題。贊分享科學(xué)計(jì)算【免費(fèi)下載鏈接】codeforces-go算法競(jìng)賽模板庫(kù) by 靈茶山艾府 項(xiàng)目地址https://gitcode.com/GitHub_Trending/co/codeforces-go點(diǎn)擊查看免費(fèi)下載相關(guān)推薦分錢給最多孩子雙周賽 100 首題O(1) 解法數(shù)學(xué)推導(dǎo)、四語(yǔ)言實(shí)現(xiàn)與 codeforces-go 倉(cāng)庫(kù)工程實(shí)踐分錢給最多孩子雙周賽 100 首題O 1 解法數(shù)學(xué)推導(dǎo)、四語(yǔ)言實(shí)現(xiàn)與 codeforces go 倉(cāng)庫(kù)工程實(shí)踐 本篇文章以靈茶山艾府算法競(jìng)賽模板庫(kù) cod科學(xué)計(jì)算cdp高級(jí)用法如何實(shí)現(xiàn)Headless Chrome的并發(fā)控制cdp高級(jí)用法如何實(shí)現(xiàn)Headless Chrome的并發(fā)控制 在現(xiàn)代Web開發(fā)和自動(dòng)化測(cè)試中Headless Chrome已成為不可或缺的工具。而使用GoLeetCode-Go 題解 135Candy 分發(fā)糖果的雙向貪心掃描算法深度解析LeetCode Go 題解 135Candy 分發(fā)糖果的雙向貪心掃描算法深度解析 導(dǎo)讀 LeetCode 第 135 題「Candy分發(fā)糖果」是一道經(jīng)示例工程上一篇Comprehensive Rust 精講可變靜態(tài)變量static mut為何需要 unsafe以及如何在 no_std 低層代碼中安全使用下一篇將 REST API 通過 Azure API Management 發(fā)布為 MCP Server從創(chuàng)建、限流策略到 Copilot Agent 調(diào)用全指南創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考