题目:
我们买了一些糖果 candies,打算把它们分给排好队的 n = num_people 个小朋友。
给第一个小朋友 1 颗糖果,第二个小朋友 2 颗,依此类推,直到给最后一个小朋友 n 颗糖果。
然后,我们再回到队伍的起点,给第一个小朋友 n + 1 颗糖果,第二个小朋友 n + 2 颗,依此类推,直到给最后一个小朋友 2 * n 颗糖果。
重复上述过程(每次都比上一次多给出一颗糖果,当到达队伍终点后再次从队伍起点开始),直到我们分完所有的糖果。注意,就算我们手中的剩下糖果数不够(不比前一次发出的糖果多),这些糖果也会全部发给当前的小朋友。
返回一个长度为 num_people、元素之和为 candies 的数组,以表示糖果的最终分发情况(即 ans[i] 表示第 i 个小朋友分到的糖果数)。
方法一:
for循环分糖果
public int[] distributeCandies(int candies, int num_people) {
//返回的数组
int[] result = new int[num_people];
//控制循环
int i = 0;
//控制下一次应该分多少颗糖果
int count = 1;
//循环体,条件是是否还有剩余糖果
while (candies != 0) {
//如果派到最后一位小朋友,下一位就轮到第一位小朋友
if (i == num_people) {
i = 0;
}
//判断糖果的数量是否充足
if (candies >= count) {
//给小朋友发糖果
result[i] += count;
//从糖果里减去发出去的
candies -= count;
} else {
//如果糖果数量不足,就把剩下的糖果全部分给这位小朋友,并退出循环返回结果
result[i] += candies;
return result;
}
i++;
count++;
}
return result;
}
结果:
方法二:
结合数学的等差求和公式,可以先求出糖果在第几次发的时候不够。
变成了一个一元二次方程,它的求根公式:
求出的 x 的值,向下取整,计算Sn与 candies 比较,如果恰好相等,则刚好分完,不然就把剩下的糖果派给第n+1位小朋友。
先封装好等差数列的求和公式:
//等差数列求和
public int colSn(int a1,int n,int d) {
int Sn = (int) (n*a1 + 0.5*(n*(n-1)*d));
return Sn;
}
方法:
public int[] distributeCandies2(int candies, int num_people) {
//返回的数组
int[] result = new int[num_people];
//求二次方程求解
int x = (int) Math.floor(-0.5 +Math.sqrt((0.25 + 2 * candies)));
//派出糖果的数量
int Sn = colSn(1,x,1);
//派了多少轮
int n = x/num_people;
//最后一轮派到了哪一位
int m = x%num_people;
//计算每一位小朋友一共拿到多少糖果
for (int i = 0;i<num_people;i++) {
//第m位小朋友 和 m之前的小朋友 比 m之后的小朋友多拿一次
if(i<m) {
//多拿一次糖果的小朋友
result[i] = colSn(i+1,n+1,num_people);
}else if(i==m){
//最后一位分到糖果的小朋友拿走剩下的(不管是不是符合数据的数量,都会全部拿走)
result[i] = colSn(i+1,n,num_people)+candies-Sn;
}else {
//后面的小朋友就少拿一次
result[i] = colSn(i+1,n,num_people);
}
}
return result;
}
结果:
PS:计算机,计算机,本来就是为了计数而生的,我们在使用的时候应该多点联系上数学,用数学优化我们的算法。