[LeeCode ]分糖果2

一、题目

LeeCode 题目链接:1103. 分糖果 II - 力扣(LeetCode)

题目描述:

排排坐,分糖果。

我们买了一些糖果 candies,打算把它们分给排好队的 n = num_people 个小朋友。

给第一个小朋友 1 颗糖果,第二个小朋友 2 颗,依此类推,直到给最后一个小朋友 n 颗糖果。

然后,我们再回到队伍的起点,给第一个小朋友 n + 1 颗糖果,第二个小朋友 n + 2 颗,依此类推,直到给最后一个小朋友 2 * n 颗糖果。

重复上述过程(每次都比上一次多给出一颗糖果,当到达队伍终点后再次从队伍起点开始),直到我们分完所有的糖果。注意,就算我们手中的剩下糖果数不够(不比前一次发出的糖果多),这些糖果也会全部发给当前的小朋友。

返回一个长度为 num_people、元素之和为 candies 的数组,以表示糖果的最终分发情况(即 ans[i] 表示第 i 个小朋友分到的糖果数)。

示例 1:

输入:candies = 7, num_people = 4

输出:[1,2,3,1]

解释: 第一次,ans[0] += 1,数组变为 [1,0,0,0]。 第二次,ans[1] += 2,数组变为 [1,2,0,0]。 第三次,ans[2] += 3,数组变为 [1,2,3,0]。 第四次,ans[3] += 1(因为此时只剩下 1 颗糖果),最终数组变为 [1,2,3,1]。

示例 2:

输入:candies = 10, num_people = 3

输出:[5,2,3]

解释: 第一次,ans[0] += 1,数组变为 [1,0,0]。 第二次,ans[1] += 2,数组变为 [1,2,0]。 第三次,ans[2] += 3,数组变为 [1,2,3]。 第四次,ans[0] += 4,最终数组变为 [5,2,3]。

提示:

  • 1 <= candies <= 10^9
  • 1 <= num_people <= 1000

二、解题思路&代码实现

方案一:暴力循环求解

解题思路:

最直观的方法是不断地遍历数组,如果还有糖就一直分,直到没有糖为止。

复杂度分析:

  • 时间复杂度:O(n+candies​)。循环 O(candies​) 次,理由见方法二。
  • 空间复杂度:O(1)。返回值不计入。

代码实现:

golang:

func distributeCandies(candies int, num_people int) []int {
    ans := make([]int, num_people);
    i := 0;
    for candies != 0 {
        num := min(candies,i+1)
        ans[i%num_people] += num
        candies -= num
        i++
    }
    return ans
}

PHP:

class Solution {

    /**
     * @param Integer $candies
     * @param Integer $num_people
     * @return Integer[]
     */
    function distributeCandies($candies, $num_people) {
        $i = 0;
        $ans = array_fill(0, $num_people, 0);
        while($candies > 0){
            $ans[$i%$num_people] += min($candies, $i+1);
            $candies -= min($candies, $i+1);
            $i++;
        }
        return $ans;
    }
}

方案二:等差数列

解题思路:

用数学公式等差数列来实现。

按照顺序分配的糖果,除最后一次不够的情况外,构成公差为1的等差数列,且每个小朋友每次分到的糖果数量也构成公差为numPeople的等差数列。

复杂度分析:

  • 时间复杂度:O(N),计算 N 个人的糖果数量。

  • 空间复杂度:O(1),除了答案数组只需要常数空间来存储若干变量。

代码实现:

golang:

func distributeCandies(candies int, numPeople int) []int {
	last := int((math.Sqrt(float64(candies*8+1)) - 1) / 2) // 可以按照等差数列分配的最后数量
	turns := last / (numPeople)                            // 完整轮数
	ans := make([]int, numPeople)
	for i := range ans {
		ans[i] = (i + 1 + i + 1 + (turns-1)*numPeople) * turns / 2
		// 部分靠前的小朋友可能多一轮
		if i < last%numPeople {
			ans[i] += i + 1 + (turns)*numPeople
		}
	}
	// 若最终无法按等差数列分配,则剩余给最后轮到的小朋友
	ans[last%numPeople] += candies - (1+last)*last/2
	return ans
}
func distributeCandies(candies int, num_people int) []int {
    n := num_people
    // how many people received complete gifts
	p := int(math.Sqrt(float64(2 * candies) + 0.25) - 0.5)
	remaining := candies - int(float64((p + 1) * p) * 0.5)
	rows, cols := p/n, p%n

	d := make([]int, n)
	for i := 0; i < n; i++ {
         // complete rows
		d[i] = (i + 1) * rows + int(float64(rows * (rows - 1) * n) * 0.5)
         // cols in the last row
		if i < cols {
			d[i] += i + 1 + rows*n
		}
	}
    // remaining candies 
	d[cols] += remaining
	return d

}

方案三:二分查找

解题思路:

整个过程可以分为2部分:第一步所有人都能分到糖果,第二步只有部分人可以分到糖果(也可以是没有人)。假设第一步有round轮,第一步第i(0<=i<round)轮第j(1<=j<=num_people>>)个人可以获得的糖果是确定的:i * num_people + j,因此可以得知第一步需要糖果数为:f(round)=(num_peopleround)(num_people*round+1)/2。只要求出满足f(x)<=candies<f(x+1)的x值,即可求出第一步后,每个人获得的糖果数,第二步只需要按照第round+1轮,正常分配剩余的糖果即可。

复杂度分析:

 

代码实现:

golang:

func distributeCandies(candies int, num_people int) []int {
     //(n*r)*(n*r+1)/2 <= candies < (n*r+n)*(n*r+n+1)/2
    sum := candies << 1
    ans := make([]int, num_people)
    rnd := 0
    for num_people * (rnd + 1) * (num_people * (rnd + 1) + 1) < sum {
        plus := 1
        for num_people * (rnd + plus << 1) * (num_people * (rnd + plus << 1) + 1) < sum {
            plus <<= 1
        }
        rnd += plus
    }
    candies -= num_people * rnd * (num_people * rnd + 1) / 2
    // rnd--
    for k := range ans {
        ans[k] = (k + 1) * rnd + rnd * (rnd - 1) * num_people >> 1
        // ans[k] = (k + 1) * (rnd + 1) + rnd * (rnd + 1) * num_people >> 1
    }
    // rnd++
    for k := range ans {
        add := rnd * num_people + k + 1
        if candies > add {
            candies -= add
            ans[k] += add
        } else {
            ans[k] += candies
            break
        }
    }
    return ans
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值