解法一:一次遍历
操作稍显复杂,细节多,暂不讨论。
解法二:两次遍历
证明:两次遍历可行
对任意一个元素,一共三种情况,
- 两边都比自己大
- 两边都比自己小
- 一边大一边小
对于第一种情况,此处元素值一定为1,求max后依然满足。
对于第二种情况,两边都小,求max后仍然满足。
对于第三种情况,求max后一定还满足比自己小的那边,但是否还满足比自己大的那边呢?
答案是肯定的,因为在其中一次遍历中,比自己大的那边一定等于max后的值+1,所以满足。
综上,两次遍历是保证正确的。
以下文字可忽略
数据结构:一个左满意数组,右满意数组
左满意数组第i个位置:对自己左边的安排满意的糖数
此题最重要的点是,一个点多高,取决于左右相邻的元素。当我们从左向右遍历时,每个点都是一定满足左条件的,如果小于就为1.如果前面的元素已经为1,这个点也为1,但这样其实不满足左条件,因为至少有一颗糖。此时的关键在于 ,对于这颗糖,肯定是1了,不可能为2。他是完全ok的,毕竟自己小,他不会不满意。所以问题的关键在他左边那颗糖,他会不满意。但左边那颗糖是完全满足左条件的,所以也ok。这样下来,所有点对自己都是满意的。
遍历两遍,每个点取最大,必然对两边都是满意的。有一个问题就是,本来有一个点,对自己右边是满意的,但右边突然变大了,这样会不会不满意?这种是不可能的,如果不满意,说明左边比右边的大,但如果左边比右边大,那在右向左过程中,肯定会比他多1,故没有问题。
class Solution {
public:
int candy(vector<int>& ratings) {
int n = ratings.size();
vector<int> left(n, 1), right(n, 1);
for(int i=1;i<n;i++)
{
if(ratings[i] > ratings[i-1])left[i] = left[i-1]+1;
}
for(int i=n-2;i>=0;i--)
{
if(ratings[i] > ratings[i+1])right[i] = right[i+1]+1;
}
int ret = 0;
for(int i=0;i<n;i++)ret += max(left[i], right[i]);
return ret;
}
};
空间优化:右数组可以直接在左满意数组上操作,就不需要额外的 O ( N ) O(N) O(N)空间了。
class Solution {
public:
int candy(vector<int>& ratings) {
int n = ratings.size();
vector<int> left(n, 1);
for(int i=1;i<n;i++)
{
if(ratings[i] > ratings[i-1])left[i] = left[i-1]+1;
}
int ret = left[n-1];
for(int i=n-2;i>=0;i--)
{
if(ratings[i] > ratings[i+1])left[i] = max(left[i], left[i+1]+1);
ret += left[i];
}
return ret;
}
};