1. 题目描述
题目中转:135.分发糖果
2.详细题解
- 方法一谈:贪心算法:遍历两轮
分糖果:每个孩子有一个分数,相邻的孩子高分的分得更多糖果,要求最少准备多少糖果数能满足要求。即然要求最少,那就只分满足条件的最低数量即可,即贪心的少,相邻的如果分数更高,则在满足条件下仅比低分的孩子多1个即可*,但需要注意的是,每个孩子都有一个左邻和右邻孩子(两端除外),且注意分数相同的情况下,则只分配1颗的情况也是满足题目要求的,现在总结规则,如果比相邻分数高,则至少大1,如果比左右邻的孩子都大,则应该至少是左右孩子分配数最大值+1,如果不是最大值,则不满足条件2,例如
[
3
,
4
,
5
,
0
]
[3,4,5,0]
[3,4,5,0],此时对于第二个孩子分配2颗,第四个孩子分配1颗,对于第三个孩子应该分配3颗而不是2颗才能满足条件。
比如分数
[
1
,
2
,
3
]
[1,2,3]
[1,2,3],从左右至右*第一个孩子分配1颗,第二个孩子分更高则分配2颗,第三个孩子分比第二个孩子分高,则分配3颗;
但对于分数
[
3
,
2
,
1
]
[3,2,1]
[3,2,1],从左至右第一个孩子分配1颗,第二个孩子分低,则应该比第1个孩子的数量小,但至少为1,此时就需要重新给第一个孩子分配数量,但如果从右至左分配呢,第三个孩子分配1颗,第二个孩子分高分配2颗,第一个孩子分配3颗。
通过上面的例子可以发现,针对递增或者递减的情况,分别从不同的方向遍历计算即可,但既有递增又有递减混合的分数该如何分配呢?那就两个方向各进行一次遍历,现在闭上眼睛思考下,从左至右可以解决递增的分数,从右至左可以解决递减的分数,二合一岂不是都解决了呢?好像挺有道理!
例如对于分数
[
1
,
2
,
3
,
4
,
5
,
3
,
2
,
1
]
[1,2,3,4,5,3,2,1]
[1,2,3,4,5,3,2,1],首先从左至右则分配糖数量,如果当前分数大于前一个分数,则分配糖数加1,否则为1,那么分配结果为
[
1
,
2
,
3
,
4
,
5
,
1
,
1
,
1
]
[1,2,3,4,5,1,1,1]
[1,2,3,4,5,1,1,1],此时解决了递增分数的分配情况;其次,现在从右至左则分配糖数量,同理,分配结果为
[
1
,
2
,
3
,
4
,
5
,
3
,
2
,
1
]
[1,2,3,4,5,3,2,1]
[1,2,3,4,5,3,2,1],其中需要注意的时,从右至左分配数,对于分数为5的孩子的糖数应该大于其右边分数为3的孩子,此时即分配4颗糖即可,但在从左至右时,其糖数应该大于左边分数为4的孩子,即分配5颗,因为需要同时满足二者条件,所以取最大值5颗。
2. 方法二:遍历一轮
建议掌握方法一即可,相对比较直观一点,符合我们的思考认知
方法二是在方法一的基础上,不进行第二轮即从右至左的遍历,但此时只能解决递增分数的分配,那如何解决递减的分配呢?现在观察下递减分数
[
5
,
4
,
3
,
2
,
1
]
[5,4,3,2,1]
[5,4,3,2,1],首先先分配第一个孩子,数量分配1;现在分配第二个孩子至少为1,发现其分数小于第一个孩子,因此第一个孩子的分配糖数应该加1;同理,第三个孩子分配的时候,前两个孩子的糖数都应该各自加1,;进一步的第四个孩子分配时前三个孩子的糖数都应该各自加1、第五个分配时前四个孩子的糖数都应该各自加1,观察发现没有,总的增加的糖数是连续递减序列的个数。
因此,针对递增的情况,采用从左至右变量的方法,一旦遇到递减则记录其长度,每增加一个长度,则分配的糖的数目直接加上递减序列的长度即可。但需要注意的是,递减是严格递减。,如果不是严格递减相等的时候,此时分配的糖数又可从1开始,但有个细节需要注意的时,例如
[
1
,
6
,
10
,
8
,
7
,
3
,
2
]
[1,6,10,8,7,3,2]
[1,6,10,8,7,3,2],其中10既属于递增,又属于递减,这时候为了保证10比两边都大,当递减序列等于10在递增分配的颗数时,下一步应该把10也纳入递减序列考虑了,故需判断上一个递增分配的最大糖数是否和递减序列长度相等,相等则递减序列长度需增加1。
3.代码实现
3.1 Python
# 方法一:
class Solution:
def candy(self, ratings: List[int]) -> int:
ans = [1 for _ in ratings]
length = len(ratings)
for i in range(1, length):
if ratings[i] > ratings[i-1]:
ans[i] = ans[i-1] + 1
for i in range(length-2,-1, -1):
if ratings[i] > ratings[i+1]:
ans[i] = max(ans[i], ans[i+1]+1)
return sum(ans)
# 方法二
class Solution:
def candy(self, ratings: List[int]) -> int:
# 题目已经提示孩子数至少为1,因此不用考虑长度的异常情况了
ans = 1 # 总数目
n = 0 # 递减序列长度
pre = 1 # 前孩子糖数
inc = 1 # 递增序列最大分数糖数
for i in range(1, len(ratings)):
if ratings[i] >= ratings[i-1]:
n = 0
pre = 1 if ratings[i]==ratings[i-1] else pre+1 # 当前孩子的分配糖数
ans += pre
inc = pre
else:
n += 1
if n == inc: # 如果递减序列的长度已经等于递增的糖数了,此时也应该把递增的最大值加入递减序列
n += 1
ans += n # 把增加的糖数加上
pre = 1 # 前糖数又变为1
return ans
3.2 Java
//方法一:
class Solution {
public int candy(int[] ratings) {
int n = ratings.length;
int ans[] = new int[n];
ans[0] = 1;
for (int i=1;i<n;i++){
if (ratings[i] > ratings[i-1]){
ans[i] = ans[i-1]+1;
}else{
ans[i]=1;
}
}
for (int i=n-2;i>-1;i--){
if (ratings[i]>ratings[i+1]){
ans[i] = ans[i]>ans[i+1]+1 ? ans[i] : ans[i+1]+1;
}
}
return Arrays.stream(ans).sum();
}
}
//方法二:
class Solution {
public int candy(int[] ratings) {
int n = ratings.length;
int res = 1; //初始化结果
int inc = 1; //递增序列最后一个分配数
int len = 0; //递减序列长度
int pre = 1; //前分配数
for (int i=1;i<n;i++){
if (ratings[i] >= ratings[i-1]){
len = 0;
pre = ratings[i] == ratings[i-1] ? 1 : pre+1;
res += pre;
inc = pre;
}else{
len++;
len = len==inc ? ++len : len;
res += len;
pre = 1;
}
}
return res;
}
}
执行用时不必过于纠结,对比可以发现,对于python和java完全相同的编写,java的时间一般是优于python的;至于编写的代码的执行用时击败多少对手,执行用时和网络环境、当前提交代码人数等均有关系,可以尝试完全相同的代码多次执行用时也不是完全相同,只要确保自己代码的算法时间复杂度满足相应要求即可,也可以通过点击分布图查看其它coder的code