欢迎访问LeetCode题解合集
题目描述
老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:
- 每个孩子至少分配到 1 个糖果。
- 评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?
示例 1:
输入:[1,0,2]
输出:5
解释:你可以分别给这三个孩子分发 2、1、2 颗糖果。
示例 2:
示例 2:
输入:[1,2,2]
输出:4
解释:你可以分别给这三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这已满足上述两个条件。
题解:
法一:
动态规划。
分两次动态规划:
- 第一次,从左往右遍历
ratings
数组,若ratrings[i] > ratings[i - 1]
,则f[i] = f[i - 1] + 1
,否则f[i] = 1
- 第二次,从右往左遍历
ratings
数组,若ratings[i] > ratings[i + 1] && f[i] <= f[i + 1]
,则f[i] = f[i + 1] + 1
最后对 f
累加求和即可。
第一次动规很好理解,保证每个分数高的比他左边同学糖果多一个即可。第二次主要是为了处理这种递减序列的情况,比如:[3, 2, 1]
,第一次遍历只会将 f
数组都置为 1
,所以需要第二次来处理。
时间复杂度:$O(n)$
额外空间复杂度:$O(n)$
class Solution {
public:
int candy(vector<int>& ratings) {
int n = ratings.size();
vector<int> f(n, 1);
for ( int i = 1; i < n; ++i ) {
if ( ratings[i] > ratings[i - 1] )
f[i] = f[i - 1] + 1;
}
int ret = f[n - 1];
for ( int i = n - 2; i >= 0; --i ) {
if ( ratings[i] > ratings[i + 1] && f[i] <= f[i + 1] )
f[i] = f[i + 1] + 1;
ret += f[i];
}
return ret;
}
};
/*
时间:16ms,击败:99.41%
内存:17.4MB,击败:15.26%
*/
法二:
方法一 中第二次动规主要是为了处理 递减 情况,如果我们在遍历过程中可以对递减元素个数计数,那么遇到递减序列就不需要第二次处理了,可以只使用常数空间搞定。
时间复杂度:$O(n)$
额外空间复杂度:$O(1)$
class Solution {
public:
int candy(vector<int>& ratings) {
int pre = 1, add = 1, sub = 0;
int ret = 1;
for ( int i = 1; i < ratings.size(); ++i ) {
if ( ratings[i] >= ratings[i - 1] ) {
sub = 0;
pre = ratings[i] == ratings[i - 1] ? 1 : pre + 1;
ret += pre;
add = pre;
} else {
++sub;
if ( sub == add ) ++sub;
ret += sub;
pre = 1;
}
}
return ret;
}
};
/*
时间:16ms,击败:99.41%
内存:16.7MB,击败:85.42%
*/