题目描述

✅ 1103. 分糖果 II

image-20260929074800468

image-20260929074800579

题意分析

小朋友按固定顺序排成一圈,从第一个人开始,计划依次发一颗、两颗、三颗,之后每次比上一次多一颗。走过最后一个人后回到第一个人,但计划发放量继续增长,不会重新从一开始。

糖果不足本次计划数量时,把剩下的全部发给当前人,然后结束。返回每个人累计收到的糖果数,而不是最后一次收到的数量;有的人可能收多次,也可能还没有轮到就已经发完。

解法:模拟发放

核心思路

[!blue]

直接以“发放一次”为循环单位。变量 give 同时表示本次是第几次发放,以及正常情况下应发多少颗;它从一开始不断增加。人数设为 p,当前接收者的零基下标是 (give - 1) % p,取模只让接收位置循环,不影响发放量继续增加。

每次实际发出的数量是 cur = min(candies, give)。糖果足够就按计划发放,不够就一次发光;将 cur 累加到接收者答案,并从剩余糖果中扣除同一个数量。这样已发数量与剩余数量之和始终等于初始总数,不会凭空多发或留下未分配的尾数。

循环只要求剩余糖果大于零。因为每轮实际发放至少一颗,剩余量严格减少;最后不足计划量时也会进入循环,由取最小值的规则自然结束。同一个人再次被轮到时使用累加,保留之前所有轮次的收获。

虽然初始糖果数可能很大,但循环不是逐颗发。前 t 次完整发放消耗 t(t + 1) / 2 颗糖,增长为平方量级,因此最多执行平方根量级的完整发放,再加至多一次收尾。直接模拟已经足以处理题目的范围。

解题步骤

  1. 创建长度为人数的零数组,令 give = 1。
  2. 剩余糖果非零时,用 (give - 1) % p 找到接收者。
  3. 将实际数量 min(candies, give) 累加给他,并从剩余糖果中扣除。
  4. 将 give 加一,直到糖果耗尽,返回累计数组。

代码实现

class Solution {
    public int[] distributeCandies(int candies, int num_people) {
        int[] res = new int[num_people];

        for (int give = 1; candies > 0; give++) {
            // 发放量一直递增,只有接收人的下标循环回到开头。
            int idx = (give - 1) % num_people;
            // 取较小值同时覆盖正常发放与最后一次发光两种情况。
            int cur = Math.min(candies, give);

            res[idx] += cur;
            candies -= cur;
        }

        return res;
    }
}
func distributeCandies(candies int, numPeople int) []int {
    res := make([]int, numPeople)
    for give := 1; candies > 0; give++ {
        // 发放量一直递增,只有接收人的下标循环回到开头。
        idx := (give - 1) % numPeople
        // 取较小值同时覆盖正常发放与最后一次发光两种情况。
        cur := give
        if candies < cur {
            cur = candies
        }
        res[idx] += cur
        candies -= cur
    }

    return res
}

复杂度分析

  • 时间复杂度:$O(p + \sqrt C)$,p 为人数,C 为初始糖果数。结果初始化需要 $O(p)$,实际发放次数为 $O(\sqrt C)$。
  • 空间复杂度:返回数组占 $O(p)$,除此之外只保存发放量、剩余量和下标,辅助空间为 $O(1)$。

关键点总结

[!green]

  • 接收位置按人数循环,计划发放量始终递增,二者不能一起重置。
  • 使用实际发放量同时更新结果和剩余量,统一处理最后不足的一次。
  • 模拟次数由三角数增长决定,是平方根量级,不是糖果总数那么多轮。

易错点总结

[!yellow]

  • 回到队首时把发放量也重置为一,改变了题目要求的递增序列。
  • 给结果累加计划数量、却只扣除剩余糖果,会在最后一轮多记糖果。
  • 循环只在糖果足够计划量时执行,会遗漏最后不够一次的剩余糖果。
  • 用赋值覆盖某人的结果,会丢掉他在前几轮收到的糖果。
  • 下标写成 give % p,会从第二个人开始,偏移了接收顺序。

相似题目

题目 难度 关联与区别
441. 排列硬币 简单 完整发放1、2、3等数量的次数可由三角数确定,再按接收者位置汇总等差序列。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2023/60049703
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!