leetcode【135】Candy【c++版,双数组,单数组,坡峰坡谷】

问题描述:

There are N children standing in a line. Each child is assigned a rating value.

You are giving candies to these children subjected to the following requirements:

  • Each child must have at least one candy.
  • Children with a higher rating get more candies than their neighbors.

What is the minimum candies you must give?

Example 1:

Input: [1,0,2]
Output: 5
Explanation: You can allocate to the first, second and third child with 2, 1, 2 candies respectively.

Example 2:

Input: [1,2,2]
Output: 4
Explanation: You can allocate to the first, second and third child with 1, 2, 1 candies respectively.
             The third child gets 1 candy because it satisfies the above two conditions.

源码:

最近都变找规律和脑筋急转弯的题了。

方法一:双数组法

定义两个一维的数组left,right。left用于从左向右扫描,只要ratings[i}>ratings[i-1],则将其值+1,这样保证从左到右满足题目条件。right则用于从右向左扫描,只要ratings[i+1]

效率不高,甚至说很低,时间24%,空间7%。

class Solution {
public:
    int candy(vector& ratings) {
        int n=ratings.size();
        if(n==0)    return 0;
        vector left(n, 1), right(n, 1);
        for(int i=1; iratings[i-1])     left[i] = left[i-1]+1;
        }
        for(int i=n-2; i>=0; i--){
            if(ratings[i+1]

方法二:单数组法

之前的做法用了两个数组,仔细分析就可以发现其实一个数组就可以解决,数组就存储之前left和right的最大值。这样也不用最后在用一个循环求和。

class Solution {
public:
    int candy(vector& ratings) {
        int n=ratings.size();
        if(n==0)    return 0;
        vector candy(n, 1);
        int result = 0;
        for(int i=1; iratings[i-1])     candy[i] = candy[i-1]+1;
        }
        for(int i=n-2; i>=0; i--){
            if(ratings[i+1]

方法三:坡峰坡谷法

这个方法通过观察(如下面的图所展示)发现,为了获得最少总数的糖果,糖果的分配每次都是增加 1 的。进一步的,在分配糖果时,给一个学生的最少数目是 1 。所以,局部的分配形式一定是 1, 2, 3, ..., n 或者 n, ..., 2, 1,

现在我们可以把评分数组 ratings当做一些上升和下降的坡。每当坡是上升的,糖果的分配一定是1, 2, 3, ..., m 这样的。同样的,如果是一个下降的坡,一定是 k, ..., 2, 1 的形式。一个随之而来的情况是,每个峰都只会在这些坡中的一个出现。那么我们应该把这个峰放在上升的坡中还是下降的坡中呢?

为了解决这个问题,我们观察到为了同时满足左右邻居的约束,峰值一定是上升的坡和下降的坡中所有点的最大值,所以为了决定需要的糖果数,峰点需要算在上升坡和下降坡较多点的那一边。局部谷点也只能被包括在一个坡中,但是这种情况很容易解决,因为局部谷点总是只会被分配 1 个糖果(可以在下一个坡开始计数时减去)。

接下来考虑实现,我们维护两个变量 old_slope和 new_slope 来决定现在是在峰还是在谷,同时我们用 up 和 down两个变量分别记录上升或者下降坡中的学生个数(不包括峰点)。我们总是在一个下降的坡接着上升坡(或者上升坡接一个下降坡)的时候更新 candies的总数。

在一个山的结束处,我们决定将峰点算在上升坡还是下降坡中,决定的依据是比较 up 和 down两个变量。因此,峰值的数目应该为 max(up,down)+1。此时,我们将 up 和 down变量重新初始化,表示一个新的山的开始。

下面的图展示了如下样例的结果。

rankings: [1 2 3 4 5 3 2 1 2 6 5 4 3 3 2 1 1 3 3 3 4 2]

leetcode【135】Candy【c++版,双数组,单数组,坡峰坡谷】_第1张图片

从这个图中,我们可以看到糖果在局部的分配中一定是1, 2, ..., n 或者n, ..., 2, 1 这样的形式。对于由 a 和 b 组成的第一座山,在分配峰点(pt.5)糖果的时候,它应该被分配到 a 中满足左邻居约束,b 中的局部谷点(pt.8)标志着第一座山c的结束。在计算的时候,我们可以把这个点归属为当前的山,也可以归属到接下来的山中。点 pt.13 标记的是第二座山的结束,因为 pt.13 和 pt.14 两个学生的评分是相同的。因此,区域 e比区域 d 有更多的点,局部峰点 (pt.10)应该被划分到 e 区域满足右邻居的约束。现在第三座山 f 应该被考虑为一座只有下降坡没有上升坡的山 (up=0)。

class Solution {
public:
    int count(int n){
        return n*(n+1)/2;
    }
    int candy(vector& ratings) {
        int n=ratings.size();
        if(n<=1)    return n;
        int result=0, up=0, down=0, old_slope=0;
        for(int i=1; iratings[i-1])? 1: (ratings[i]0 && new_slope==0 || old_slope<0 && new_slope>=0){
                result += count(up) + count(down) + max(up, down);
                up = 0;
                down = 0;
            }
            if(new_slope>0)     up++;
            if(new_slope<0)     down++;
            if(new_slope==0)    result++;
            old_slope = new_slope;
        }
        result += count(up) + count(down) + max(up, down) + 1;
        return result;
    }
};

 

你可能感兴趣的:(leetcode【135】Candy【c++版,双数组,单数组,坡峰坡谷】)