【LeetCode】2929. 给小朋友们分糖果 II(组合数学|递归|枚举)

LeetCode 2929. 给小朋友们分糖果 II(中等)

题目描述

题目链接:LeetCode 2929. 给小朋友们分糖果 II(中等)
给你两个正整数 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 6 10^{6} 106
1 <= limit <= 10 6 10^{6} 106

递归解法推导
  • 考虑更通用的场景: n n n 颗糖果分给 x x x 个人,每个人最多 l i m i t limit limit 颗糖果。

  • 整体思路框架:使用递归的方式搜索合法的分配方式进行汇总统计。

  • 递归解法推导:

    • 定义 d f s ( n , x , l i m i t ) dfs(n,x,limit) dfs(n,x,limit)为 “ n n n 颗糖果分给 x x x 个人,每个人最多 l i m i t limit limit 颗糖果” 的方案数。对于当前的人,我们尝试给他分 i i i 颗糖果,由于每个人最多 l i m i t limit limit 颗糖果,当前剩余糖果数量为 n n n ,所以 i ∈ [ 0 , min ⁡ ( limit , n ) ] i \in [0, \min(\text{limit}, n)] i[0,min(limit,n)],得到递归公式为 dfs ( n , x , limit ) = ∑ i = 0 min ⁡ ( limit , n ) dfs ( n − i , x − 1 , limit ) \text{dfs}(n, x, \text{limit}) = \sum_{i=0}^{\min(\text{limit}, n)} \text{dfs}(n-i, x-1, \text{limit}) dfs(n,x,limit)=i=0min(limit,n)dfs(ni,x1,limit)
    • 递归终止条件为 x = 1 x=1 x=1,也就是分到最后 1 1 1 个人的时候结束分配。前面的人都分配到了糖果(且前面的人分到的糖果数量都小于等于 l i m i t limit limit ,都是合法的取值),剩余的糖果就只能分配给最后一个人。如果剩余的糖果数量小于等于 l i m i t limit limit ,则形成一种合法的分配方案,返回 1 1 1 即可。如果剩余的糖果数量大于 l i m i t limit limit,则当前分配不合法,返回 0 0 0
    • 下界优化:分析当前这个人最少能分几颗糖果。尽量让其他人多分糖果,那当前这个人分到的糖果就是最少。其他人都按最多的 l i m i t limit limit 分,则当前这个人最少要分的数量为 n − ( x − 1 ) ∗ l i m i t n-(x-1)*limit n(x1)limit。最终优化后的递归公式为 dfs ( n , x , limit ) = ∑ i = max ⁡ ( 0 , n − ( x − 1 ) ⋅ limit ) min ⁡ ( limit , n ) dfs ( n − i , x − 1 , limit ) \text{dfs}(n, x, \text{limit}) = \sum_{i=\max(0, n-(x-1)\cdot \text{limit})}^{\min(\text{limit}, n)} \text{dfs}(n-i, x-1, \text{limit}) dfs(n,x,limit)=i=max(0,n(x1)limit)min(limit,n)dfs(ni,x1,limit)
    • 时间复杂度分析: n n n 个人都尝试分配 i i i 颗糖果,且 i ∈ [ max ⁡ ( 0 , n − ( x − 1 ) , min ⁡ ( limit , n ) ] i \in [\max(0, n-(x-1), \min(\text{limit}, n)] i[max(0,n(x1),min(limit,n)],所以复杂度粗略为 l i m i t n limit^{n} limitn
  • 数学逻辑优化:

    • x x x 个人,每个人最多分 l i m i t limit limit 颗糖果。假如每个人都按最多的数量分,那糖果数为 x ∗ l i m i t x*limit xlimit 。当糖果数量 n > ( x ∗ l i m i t ) n>(x*limit) n>(xlimit) 时,在 “每个人最多分 l i m i t limit limit 颗糖果” 的条件下,糖果怎么也分不完,所以不存在合法方案,直接返回 0 0 0
  • 基于上述的分析推导,得到如下Java代码:

class Solution {
    public long distributeCandies(int n, int limit) {
        return dfs(3, n, limit);
    }

    public long dfs(int x, int n, int limit) {
        if (x == 1) {
            return n <= limit ? 1L : 0L;
        }
        if (n > x * limit) {
            return 0;
        }
        long res = 0L;
        int l = Math.max(0, n - (x - 1) * limit), r = Math.min(limit, n);
        for (int i = l; i <= r; i++) {
            res += dfs(x - 1, n - i, limit);
        }
        return res;
    }
}
  • 提交结果:当前解题方法时间复杂度较高,提交后超出时间限制。超出时间限制 522 / 958 个通过的测试用例。
超时解决
  • 经过“数学逻辑优化” 优化后,部分不合法的分配会被提前终止。经过 “下界优化” 后,每个人分配 i i i 颗糖果 i ∈ [ max ⁡ ( 0 , n − ( x − 1 ) , min ⁡ ( limit , n ) ] i \in [\max(0, n-(x-1), \min(\text{limit}, n)] i[max(0,n(x1),min(limit,n)] i i i的取值都是合法取值。
  • 当给倒数第二个人分配糖果的时候,方案数其实就已经确定了。根本不需要再递归到 x = 1 x=1 x=1。当 x = 2 x=2 x=2时, min ⁡ ( limit , n ) − max ⁡ ( 0 , n − ( x − 1 ) + 1 \min(\text{limit}, n)-\max(0, n-(x-1)+1 min(limit,n)max(0,n(x1)+1 就是方案数。也就是把递归终止条件改为 x = 2 x=2 x=2即可, x = 2 x=2 x=2 时直接返回 min ⁡ ( limit , n ) − max ⁡ ( 0 , n − ( x − 1 ) + 1 \min(\text{limit}, n)-\max(0, n-(x-1)+1 min(limit,n)max(0,n(x1)+1
AC代码
class Solution {
    public long distributeCandies(int n, int limit) {
        return dfs(3, n, limit);
    }

    public long dfs(int x, int n, int limit) {
        if (n > x * limit) {
            return 0;
        }
        int l = Math.max(0, n - (x - 1) * limit), r = Math.min(limit, n);
        if (x == 2) {
            return r - l + 1L;
        }
        long res = 0L;
        for (int i = l; i <= r; i++) {
            res += dfs(x - 1, n - i, limit);
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值