LeetCode-算法-分糖果

力扣题目地址:https://leetcode-cn.com/problems/distribute-candies-to-people/

首先看题目
排排坐,分糖果。

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

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

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

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

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

示例

示例一:
输入: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]。

示例二:
输入: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

解决思路
这道题让人第一想到的解法估计就是暴力破解法了。不断计算下一个数组元素需要的糖果数,如果当前剩余糖果树大于需要糖果数,则分配需要的糖果,如果不够则全部分给当前元素;然后返回数组。

我个人不太想这样暴力破解,所以没有采用这种方法,想通过一些规律找出能够节省时间的解法。

这也是这篇博客今天才被发布的原因;接下来给大家讲一讲我的骚操作。

开始我们的解题之旅:
我的大概思路是这样的。首先我知道糖果总数 candies 和数组的元素个数 num_people 。
第一步:因为每个元素分配的糖果都是递增的;所以我可以通过 num_people 知道每一次循环一共需要多少糖果 sum。我们通过 sum 和 candies 比较大小,来计算当前是第几次循环。
第二步:然后根据循环到当前循环的总糖果数减去我们有的糖果总数,计算出差值,然后从当前循环的最后一位反向往前面推导,计算出糖果分完的元素位置end_index。
第三步:计算 end_index 之后,我们可以知道在小于 end_index 的其他元素都是count+1次循环的总和,第 end_index 元素是第count次循环的总和加上剩余糖果数量,大于 end_index 元素的其他元素都是第 count 次循环的总和。

第一步代码实现:

第一次循环分配糖果总和:从 1 到 num_people  
递增正整数列求和公式:sum = (1+n)n/2;

循环示例:
第一次循环:1 到 num_people  
int sum1 = (1+num_people)*num_people/2;
第二次循环:num_people+12*num_people 
int sum2 = (1+3*num_people)*num_people/2;
第三次循环:2*num_people+13*num_people 
int sum3 = (1+5*num_people)*num_people/2;
第四次循环:3*num_people+14*num_people 
int sum4 = (1+7*num_people)*num_people/2;
总结规律:
完成第 count 次循环所需糖果数量总和:
(1+(2*count-1)*num_people)*num_people/2

代码实现:
	//知道了规律我们接下来就可以计算 循环次数
	//完成循环次数;从0开始,因为不知道第一次循环的糖果是否能分配完
	int count = 0;
	//计算完成第一次循环需要的糖果数量
	int sum = (1+num_people)*num_people/2;
	//算出循环次数  判断总糖果数量是否大于循环增加的糖果数量总和
	while (candies > sum){
	    //循环次数加一
	    count++;
		//计算出下次循环所需要的糖果数 并加入sum
	    sum += (1+(2*(count+1)-1)*num_people)*num_people/2;
	}

第二步代码实现:

1.注意count是完成循环的次数,并不是循环到的次数,所以我们能够循环到第count+1次循环。
2.计算出 candies 数量的糖果能够循环到第 count + 1次之后。
3.我们接下来要考虑的是我们如何定位到 count+1 次循环时,糖果分配完的那个元素位置。
4.首先利用上面计算循环次数的sum和糖果总数candies,计算出完成第count+1次循环所差的糖果值。

反向推导示例:
第 count 次循环最后 1 位所需糖果数:
int sub1 = count*num_people-0;
第 count 次循环倒数第 2 位所需糖果数:
int sub2 = count*num_people-1;
第 count 次循环倒数第 3 位所需糖果数:
int sub3 = count*num_people-2;
第 count 次循环倒数第 4 位所需糖果数:
int sub4 = count*num_people-3;
总结规律:
倒数第 sub_count 位所需要的糖果数:
count*num_people-sub_count 

代码实现:
	//计算差值
	int sub = sum - candies;
	//反向推导次数
    int sub_count = 0;
    //反向推导:倒数第一个位置所需要的糖果数量
    int sub_now = (count+1)*num_people-sub_count;
    //当剩余糖果数量小于下一个位置所需的糖果数量,说明剩余糖果不够分配给下一个位置了。
    while(sub >= sub_now){
    	//下一个位置所需的糖果数量
        sub_now = (count+1)*num_people-sub_count;
        //剩余糖果数量减去当前位置所需糖果数量;
        sub -= sub_now;
        //完成反向推导次数+1
        sub_count++;
    }
    //计算出下一个位置需要的糖果数
    sub_now = (count+1)*num_people-sub_count;
    //计算出下一个位置剩余的糖果数
    sub = sub_now - sub;
    //循环到糖果不足的位置
    int end_index = num_people - sub_count;

第三步代码实现:

1.计算 end_index 之后;小于 end_index 的其他元素都是count+1次循环的总和。
2.第 end_index 元素是第count次循环的总和加上剩余糖果数量。
3.大于 end_index 元素的其他元素都是第 count 次循环的总和。

元素总和计算示例:
//计算每一位元素需要的值,并放入
第一次循环第一个元素所需糖果数总和:
int number1_1 = 1;
int number1_2 = 2+num_people;(1+1+num_people)
int number1_3 = 3+3*num_people;(1 + 1+num_people + 1+2*num_people)
int number1_4 = 4+6*num_people;
int number1_5 = 5+10*num_people;
int number1_6 = 6+15*num_people;
int number1_7 = 7+21*num_people;
int number1_8 = 8+28*num_people;
第二次循环第一个元素所需糖果数总和:
int number2_1 = 2;
int number2_2 = 4+num_people;
第三次循环第一个元素所需糖果数总和:
int number3_1 = 3;
int number3_2 = 6+num_people;
总结规律:
x 等于从 1 到 count 的和,递增正整数列求和公式:
x = (1+count)*count/2
第 count 次循环 第 i 个位置所需糖果总数:
count*(i+1) + x * num_people

代码实现:
		//计算x的值
        int x = count*(count+1)/2;
        //循环设置值
        for(int i = 0; i < num_people; i++) {
            if(i < end_index-1){
            	//1.小于 end_index 的其他元素都是count+1次循环的总和。
                result[i] = (count+1)*(i+1) + x * num_people;
            }else if(i == end_index-1){
            	//2.第 end_index 元素是第count次循环的总和加上剩余糖果数量。
                result[i] = count*(i+1) +(x-count) * num_people + sub;
            }else{
            	//3.大于 end_index 元素的其他元素都是第 count 次循环的总和。
                result[i] = count*(i+1) +(x-count) * num_people;
            }
        }

完整代码:

/**
     * 分糖果   https://leetcode-cn.com/problems/distribute-candies-to-people/
     * @param candies
     * @param num_people
     * @author GeYuxuan 2020-03-06 22:18:03
     * @return int[]
     */
    public int[] distributeCandies(int candies, int num_people) {
        int [] result = new int[num_people];
        //循环次数
        int count = 0;
        int sum = (1+num_people)*num_people/2;
        //算出循环次数
        while (candies > sum){
            //循环次数加一
            count++;
            //计算出下次循环所需要的糖果数 并加入sum
            sum += (1+(2*(count+1)-1)*num_people)*num_people/2;
        }
        //计算差值
        int sub = sum - candies;
        int sub_count = 0;
        int sub_now = (count+1)*num_people-sub_count;
        while(sub >= sub_now){
            sub_now = (count+1)*num_people-sub_count;
            sub -= sub_now;
            sub_count++;
        }
        sub_now = (count+1)*num_people-sub_count;
        sub = sub_now - sub;
        //循环到余额不足的位置
        int end_index = num_people - sub_count;
        //计算count总和
        int x = count*(count+1)/2;
        //计算每一位需要的值,并放入
        for(int i = 0; i < num_people; i++) {
            if(i < end_index-1){
                result[i] = (count+1)*(i+1) + x * num_people;
            }else if(i == end_index-1){
                result[i] = count*(i+1) +(x-count) * num_people + sub;
            }else{
                result[i] = count*(i+1) +(x-count) * num_people;
            }
        }
        return result;
    }

这篇博客是3月5号应该发出的文章,但是当天晚上3个小时还是没有解出这道题目,其实在中间我有想过放弃这种解法,但是我一直没有想明白我的解法哪里有问题。因为第二天还要上班,为了不影响上班,这题我打算第二天有空再解。直到第二天晚上有时间去解出这道题的时候,我是十分兴奋;我觉得暴力解法永远都是最后的方法,只有动脑子才会让脑子变得灵活。

不忘初心,砥砺前行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LeetCode-Editor是一种在线编码工具,它提供了一个用户友好的界面编写和运行代码。在使用LeetCode-Editor时,有时候会出现乱码的问题。 乱码的原因可能是由于编码格式不兼容或者编码错误导致的。在这种情况下,我们可以尝试以下几种解决方法: 1. 检查文件编码格式:首先,我们可以检查所编辑的文件的编码格式。通常来说,常用的编码格式有UTF-8和ASCII等。我们可以将编码格式更改为正确的格式。在LeetCode-Editor中,可以通过界面设置或编辑器设置来更改编码格式。 2. 使用正确的字符集:如果乱码是由于使用了不同的字符集导致的,我们可以尝试更改使用正确的字符集。常见的字符集如Unicode或者UTF-8等。在LeetCode-Editor中,可以在编辑器中选择正确的字符集。 3. 使用合适的编辑器:有时候,乱码问题可能与LeetCode-Editor自身相关。我们可以尝试使用其他编码工具,如Text Editor、Sublime Text或者IDE,看是否能够解决乱码问题。 4. 查找特殊字符:如果乱码问题只出现在某些特殊字符上,我们可以尝试找到并替换这些字符。通过仔细检查代码,我们可以找到导致乱码的特定字符,并进行修正或替换。 总之,解决LeetCode-Editor乱码问题的方法有很多。根据具体情况,我们可以尝试更改文件编码格式、使用正确的字符集、更换编辑器或者查找并替换特殊字符等方法来解决这个问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值