day 8
题目描述
思路
初次思路:此题初看,从前向后遍历,发现无非考虑三种情况
- 如果这个孩子分数大于前一个孩子,则他的苹果数是前一个孩子苹果数加1。
- 如果这个孩子分数与前一个孩子分数相同,则他的苹果数为1。
- 如果这个孩子分数小于前一个孩子,就会存在两种情况:1)前一个孩子的苹果数大于1,则这个孩子的苹果数为1。2)前一个孩子的苹果数为1,就会出现问题。对于前一个孩子的苹果数为1,就需要往前回溯,将前一个孩子的苹果数加1,但是加一后不能确保前一个孩子满足要求。于是需要判断前一个孩子是否满足要求(由于前一个孩子是进行加1操作,所以唯一可能出现的问题是他的分数比前一个孩子低,但苹果数与前一个孩子相等或大,就得接着向前回溯),直到回溯到第一个满足题目要求的,就可以停止回溯了。
class Solution {
public int candy(int[] ratings) {
int sum=0;
int n=ratings.length;
int[] apple=new int[n];
apple[0]=1;
int i;
for(i=1;i<ratings.length;i++){
if(apple[i]!=0){//说明是回溯的
//能回溯说明 对于这个孩子的下一个是满足条件的,只用判断与前一个孩子是否有矛盾
if(i-1>=0){
if(ratings[i-1]>ratings[i]&&apple[i-1]<=apple[i]){
apple[i-1]=apple[i]+1;
i=i-2;
}
}
}
else{
if(ratings[i-1]>ratings[i]){//这个孩子比上一个孩子分数低
if(apple[i-1]==1){//上一个孩子苹果为1,回溯
apple[i-1]++;//增加上一个孩子苹果树
i=i-2;
}
else{
apple[i]=1;
}
}
else{
if(ratings[i-1]==ratings[i]){//这个小孩与上一个分数相同
apple[i]=1;
}
else{//这个小孩得分比上一个高
apple[i]=apple[i-1]+1;
}
}
}
}
for(i=0;i<ratings.length;i++){
sum+=apple[i];
}
return sum;
}
}
问题
时间复杂度很高,需要优化算法。
优化思路:官方题解提供了一个两次遍历的做法,思路如下
- 将条件中「相邻的孩子中,评分高的孩子必须获得更多的糖果」拆分为两个规则
- 左规则:当 ratings[i−1]<ratings[i] 时,i 号学生的糖果数量将比 i−1 号孩子的糖果数量多。
- 右规则:当 ratings[i]>ratings[i+1] 时,i 号学生的糖果数量将比 i+1 号孩子的糖果数量多。
- 做法:先从前向后遍历,让每个元素满足左规则,得到一个数组left,再从后向前遍历,让每个元素满足右规则,得到数组right(题目没用right数组,直接采取了right变量存储),最后每个孩子分的的实际苹果数是这个两个数组取最大值。
个人理解: - 可能会对其拆分为左右规则感到困惑:但根据我的初次想法可以发现,从前向后遍历,除了前一个元素比该元素分数小的情况,是不是都可以将该元素赋值为1.同理,从后向前遍历,除了下一个元素分数比该元素分数小,是不是可以将该元素赋值为1。(建议画张图理解下)
- 为什么取的是左右规则实现后的最大值:这也很好理解,对于分别左右规则实现后每个元素得到的值而言,是分别相对于左右规则所得到的最小值,根据拆分的方式可知必须要使左右。如果取最小值,就可能导致左右规则中的一个不成立。那为什么要取最大值呢?我们再来细看以下左右规则,如果对于左规则而言,如果满足 ratings[i−1]<ratings[i] 的情况下,我们取最小值肯定是取上一个元素苹果数加1,那我们取上一个元素苹果数加2成立吗,当然成立。这就说明了取最大值不会影响左右规则。
class Solution {
public int candy(int[] ratings) {
int n = ratings.length;
int[] left = new int[n];
for (int i = 0; i < n; i++) {
if (i > 0 && ratings[i] > ratings[i - 1]) {
left[i] = left[i - 1] + 1;
} else {
left[i] = 1;
}
}
int right = 0, ret = 0;
for (int i = n - 1; i >= 0; i--) {
if (i < n - 1 && ratings[i] > ratings[i + 1]) {
right++;
} else {
right = 1;
}
ret += Math.max(left[i], right);
}
return ret;
}
}