给你两个正整数 n 和 limit 。
请你将 n 颗糖果分给 3 位小朋友,确保没有任何小朋友得到超过 limit 颗糖果,请你返回满足此条件下的 总方案数 。
示例 1:
输入:n = 5, limit = 2
输出:3
解释:总共有 3 种方法分配 5 颗糖果,且每位小朋友的糖果数不超过 2 :(1, 2, 2) ,(2, 1, 2) 和 (2, 2, 1) 。
示例 2:
输入:n = 3, limit = 3
输出:10
解释:总共有 10 种方法分配 3 颗糖果,且每位小朋友的糖果数不超过 3 :(0, 0, 3) ,(0, 1, 2) ,(0, 2, 1) ,(0, 3, 0) ,(1, 0, 2) ,(1, 1, 1) ,(1, 2, 0) ,(2, 0, 1) ,(2, 1, 0) 和 (3, 0, 0) 。
提示:
1 <= n <= 10⁶
1 <= limit <= 10⁶
问题分析
我们需要计算满足以下条件的非负整数解 (x,y,z) 的个数:
- x+y+z=n;
- 0≤x,y,z≤limit。
如果不考虑上界限制,则“将 n 个相同的糖果分给 3 位小朋友”的非负整数解数量为星与条公式:
(3−1n+3−1)=(2n+2)=2(n+2)(n+1).
但由于每位小朋友最多只能拿到 limit 颗,需要排除掉至少有一个孩子拿到超过 limit 颗的情况。可通过包容–排斥原理计算满足上界约束的解的个数。
设事件:
- Ax:满足 x>limit 的解;
- Ay:满足 y>limit 的解;
- Az:满足 z>limit 的解。
目标是计算:
{(x,y,z)∣x+y+z=n,0≤x,y,z≤limit}=(2n+2)−Ax∪Ay∪Az.
其中
Ax∪Ay∪Az=∣Ax∣+∣Ay∣+∣Az∣−(∣Ax∩Ay∣+∣Ax∩Az∣+∣Ay∩Az∣)+∣Ax∩Ay∩Az∣.
由于对称性,
∣Ax∣=∣Ay∣=∣Az∣,∣Ax∩Ay∣=∣Ax∩Az∣=∣Ay∩Az∣.
-
当 x>limit 时,令 x′=x−(limit+1),则 x′≥0,原方程变为
x′+y+z=n−(limit+1).
此时 x′,y,z≥0,无上界,解的个数为
(2(n−(limit+1))+2),
仅当 n−(limit+1)≥0 时才非零,否则视作 0。
-
当同时 x>limit 且 y>limit 时,令
x′=x−(limit+1),y′=y−(limit+1),
则
x′+y′+z=n−2(limit+1),
解的个数为
(2(n−2(limit+1))+2),
条件是 n−2(limit+1)≥0。
-
当 x,y,z 都超过 limit 时,令
x′=x−(limit+1),y′=y−(limit+1),z′=z−(limit+1),
则
x′+y′+z′=n−3(limit+1),
解的个数为
(2(n−3(limit+1))+2),
条件是 n−3(limit+1)≥0。
由包容–排斥得到:
Ax∪Ay∪Az=3(2n−(limit+1)+2)−3(2n−2(limit+1)+2)+(2n−3(limit+1)+2),
(不足条件时对应组合数取 0)。因此,合法分配数:
Ans=(2n+2)−3(2n−(limit+1)+2)+3(2n−2(limit+1)+2)−(2n−3(limit+1)+2).
算法思路
- 定义辅助函数 comb2(m),用于计算
(2m+2)=2(m+2)(m+1),
若 m<0,则返回 0,以便处理 n−k(limit+1)<0 时组合数为 0 的情形。
- 计算无约束总数:
total=comb2(n).
- 令 step=limit+1,计算包容–排斥各项:
- term1=3comb2(n−step);
- term2=3comb2(n−2step);
- term3=comb2(n−3step)。
- 返回 total−term1+term2−term3 即为最终结果。
时间复杂度
- 该方法只进行常数次的组合数计算和加减乘除运算,整个算法的时间复杂度为 O(1),满足 n,limit 最大到 106 的情况。
- 空间复杂度为 O(1),只使用常数个整型变量。
代码分解
-
辅助函数 comb2(m)
- 功能:若 m≥0,返回 (2m+2)=2(m+2)(m+1);否则返回 0。
- 作用:统一处理“n−k(limit+1)<0 时的组合数为 0”的情形。
-
主函数 distributeCandies(n, limit)
- 计算无上界总方案数
total = comb2(n)。
- 设
step = limit + 1。
- 计算:
term1 = 3 * comb2(n - step) 对应排除掉任一孩子超过上限的情况;
term2 = 3 * comb2(n - 2*step) 对应排除掉任意两位孩子都超过上限的情况;
term3 = comb2(n - 3*step) 对应排除掉三位孩子都超过上限的情况。
- 最终返回
total - term1 + term2 - term3。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Solution: def distributeCandies(self, n: int, limit: int) -> int: def comb2(m: int) -> int: if m < 0: return 0 return (m + 2) * (m + 1) // 2
total = comb2(n)
step = limit + 1
term1 = 3 * comb2(n - step) term2 = 3 * comb2(n - 2 * step) term3 = comb2(n - 3 * step)
return total - term1 + term2 - term3
|