题目链接
LeetCode 135. 分发糖果[1]
题目描述
那么这样下来,老师至少需要准备多少颗糖果呢?
示例1
输入:
[1,0,2]
输出:
5
解释:
你可以分别给这三个孩子分发 2、1、2 颗糖果。
示例2
输入:
[1,2,2]
输出:
4
解释:
你可以分别给这三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这已满足上述两个条件。
题解
这题虽然难度定义成困难,但其实代码不长,思路也比较简单清晰。
首先明确一下题目中的两个条件,我们可以把所有人的分数在坐标轴中连起来,这样就形成了一个波形图(图片来自官方题解):
一次遍历
从上面方法中可以看出,本题求解的难点就在于从左向右遍历的时候,下坡到底有多长没法知道,必须全部遍历完了才能知道。还有就是山峰的值必须看左右两边的上坡下坡有多长。
继续看下面这张图:
贴一段官方的样例解释:
代码
两次遍历(c++)
class Solution {
public:
int candy(vector<int>& ratings) {
int n = ratings.size();
vector<int> res(n, 1);
for (int i = 1; i < n; ++i) {
if (ratings[i] > ratings[i-1]) {
res[i] = res[i-1] + 1;
}
}
for (int i = n-2; i >= 0; --i) {
if (ratings[i] > ratings[i+1]) {
res[i] = max(res[i], res[i+1]+1);
}
}
return accumulate(res.begin(), res.end(), 0);
}
};
一次遍历(c++)
class Solution {
public:
int count(int n) {
return n*(n+1)/2;
}
int candy(vector<int>& ratings) {
int n = ratings.size();
int sum = 0;
int up = 0, down = 0;
int os = 0, ns = 0;
for (int i = 1; i < n; ++i) {
ns = ratings[i]>ratings[i-1] ? 1 : (ratings[i]<ratings[i-1] ? -1 : 0);
// 这座山峰遍历结束,计算糖果数。
if ((os < 0 && ns >= 0) || os > 0 && ns == 0) {
// 这里看似好像峰顶没有加 1,其实是 count(down) 减去了 1。
// 因为谷底是共享的,所以将谷底给了下一座山峰的上坡。
sum += count(up) + count(down) + max(up, down);
up = down = 0;
}
if (ns > 0) up++;
else if (ns < 0) down++;
// 如果是平原,说明谷底不会共享,之前少加的 1 再补上。
else if (!ns) sum++;
os = ns;
}
// 最后一座山峰循环里不会计算到,再加上。
sum += count(up) + count(down) + max(up, down) + 1;
return sum;
}
};
单调栈(c++)
class Solution {
public:
int candy(vector<int>& ratings) {
ratings.push_back(INT_MAX);
int n = ratings.size();
vector<int> res(n, 1);
stack<int> st;
int sum = 0;
for (int i = 0; i < n; ++i) {
if (!st.empty() && ratings[i] >= ratings[st.top()]) {
while (!st.empty()) {
int j = st.top();
st.pop();
if (j < n-1 && ratings[j] > ratings[j+1]) {
res[j] = max(res[j], res[j+1]+1);
}
if (j > 0 && ratings[j] > ratings[j-1]) {
res[j] = max(res[j], res[j-1]+1);
}
sum += res[j];
}
}
st.push(i);
}
return sum;
}
};
参考资料
[1]
LeetCode 135. 分发糖果: https://leetcode-cn.com/problems/candy/