# 135. Candy

**<font color=red>难度: Hard</font>**

## 刷题内容

> 原题连接

* https://leetcode.com/problems/candy/

> 内容描述

```

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.
```

## 解题方案

> 思路 1
******- 时间复杂度: O(N)******- 空间复杂度: O(N)******



- 首先我们从左往右边看，如果当前child的rating比它左边child的rating大，那么当前child分的candy肯定要比它左边多1
- 然后从右往左边看，如果当前child的rating比它右边child的rating大，那么当前child分的candy肯定要比它右边多1

我们知道这两种情况我们都必须满足，所以最后我们取两者中的大者（这样才能同时满足两种情况）,beats 24.83%

```python
class Solution:
    def candy(self, ratings):
        """
        :type ratings: List[int]
        :rtype: int
        """
        left2right = [1] * len(ratings)
        right2left = [1] * len(ratings)
        for i in range(1, len(ratings)):
            if ratings[i] > ratings[i-1]:
                left2right[i] = left2right[i-1] + 1
        for i in range(len(ratings)-2, -1, -1):
            if ratings[i] > ratings[i+1]:
                right2left[i] = right2left[i+1] + 1
        res = 0
        for i in range(len(ratings)):
            res += max(left2right[i], right2left[i])
        return res
```

> 思路 2
******- 时间复杂度: O(N)******- 空间复杂度: O(N)******

刚才用了2个array，其实我们可以只用一个array， beats 28.77%

```python
class Solution:
    def candy(self, ratings):
        """
        :type ratings: List[int]
        :rtype: int
        """
        res = [1] * len(ratings) # also compatable with [] input
        # left scan
        for i in range(1, len(ratings)):
            if ratings[i] > ratings[i-1]:
                res[i] = res[i-1] + 1
        # right scan
        for i in range(len(ratings)-2, -1, -1):
            if ratings[i] > ratings[i+1]:
                res[i] = max(res[i+1]+1, res[i])
        return sum(res)
```


> 思路 3
******- 时间复杂度: O(N)******- 空间复杂度: O(1)******

其实我们还有另外一种思路，可以将空间降到O(1)

假设：
```
      input: [2, 4, 6, 8, 7, 6, 5, 4, 3, 2, 7, 8, 9, 8, 7, 6, 5, 4, 3]
child index: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19]

```

我们先直接给第一个孩子1颗candy，然后继续往下遍历，我们发现第2个孩子的rating比第一个孩子的多，于是我们给第二个孩子2颗candy，
以此类推，第3个孩子3颗candy，第4个孩子4颗candy，这时候我们发现第5个孩子的rating比第4个孩子的rating要小，
所以第5个孩子的candy肯定要比第4个孩子的少才行，但是不能直接给第5个孩子1颗candy这么简单，因为说不定后面孩子的rating比第5个孩子更小呢？
如果第5个孩子只有1颗candy，后面的孩子岂不是没有candy了？这不符合题目要求，所以我们暂时继续走下去，我们发现一共降序走了6步，此时来到了第11个孩子，
rating终于又变成升序了，这时候我们知道了，第5个孩子最少需要给6颗candy了，然后第6，7，8，9，10个孩子依次给5，4，3，2，1颗candy，总数就是6*(1+6)/2
但是问题来了，第5个孩子给了6颗candy，第4个孩子应该要比第5个孩子的candy更多呀， 于是我们补给第4个孩子3颗candy，此时第4个孩子有7颗candy
（比第5个孩子的6颗多，符合题目要求）。继续走下去，第11个孩子给2颗candy，第12个孩子给3颗candy，第13个孩子给4颗candy，接下来又是降序了，
又是降序走了6步，一样的原理，第14,15,16,17,18,19个孩子分别给6,5,4,3,2,1颗candy，发现第13个孩子需要补3颗candy（现在7颗candy）

于是我们的candy的总和为

```
      input: [2, 4, 6, 8, 7, 6, 5, 4, 3, 2, 7, 8, 9, 8, 7, 6, 5, 4, 3]
child candy: [1, 2, 3, 7, 6, 5, 4, 3, 2, 1, 2, 3, 7, 6, 5, 4, 3, 2, 1]
child index: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19]

一共67颗candy
```

编码实现, beats 59.40%

```python
class Solution:
    def candy(self, ratings):
        """
        :type ratings: List[int]
        :rtype: int
        """
        if not ratings:
            return 0
        res, prev, desc_cnt = 1, 1, 0
        for i in range(1, len(ratings)):
            if ratings[i] >= ratings[i-1]:
                if desc_cnt > 0:
                    res += desc_cnt * (desc_cnt + 1) / 2 # arithmetic progression
                    if desc_cnt >= prev:
                        res += desc_cnt - prev + 1
                    desc_cnt = 0
                    prev = 1
                # 当前child的rating和它左边child的rating一样，直接给1颗candy就满足了，否则需要给prev+1颗candy
                prev = 1 if ratings[i] == ratings[i-1] else prev + 1 
                res += prev
            else:
                desc_cnt += 1
        if desc_cnt > 0: # if we were descending at the end
            res += desc_cnt * (desc_cnt + 1) / 2 # arithmetic progression
            if desc_cnt >= prev:
                res += desc_cnt - prev + 1
        return int(res)
```




































