zoukankan      html  css  js  c++  java
  • Leetcode | 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?

    首先关于题意,higher rating get more candies, 但是如果是equal呢,可以比它的邻居多,也可以比它的邻居少。 也就是说,假设 rating=[2,4,4,4,2],那么结果可以是[1,2,1,2,1].

    分析一下知道,需要对于连续上升和下降趋势计数。对于平行的趋势,中间的都可以设为1。初始状态可以看作平行趋势。对于平行趋势到上升(或下降)趋势时的变化,其实就和初始一样。

    平行状态:每个children可以只给一颗糖。比如rating=[4,4,2],那么结果就是[1,1,2];

    下降状态:下降状态需要设置上升趋势的初始计数为2(这是因为下降实际是以1,2,...,n再翻过来去计数的,翻过来之后最后一个是1,所以上升的初始应该是2),这样才能正确地从下降到上升的转换。比如rating=[3,1,4],计数是[1,2,2],最终结果是[2,1,2];

    上升状态:上升状态需要设置下降趋势的初始计数为1。为了确保下降的计数翻转过来之后,下降的第一个点会比上升的最后一个点的糖数少。我们必须在计算下降趋势的时候,如果大于或等于之前上升趋势的最大趋势,我们就要在最终的计数加1。比如rating=[1,3,6,5,4,3,2,1],计数是[1,2,3,1,2,3,4,5],下降趋势部分翻转过来就是[1,2,3,5,4,3,2,1],当下降计数>=3时就可累加1,这一部分1可以看作是累加到上升趋势的最后一个数上,也就是[1,2,6,5,4,3,2,1]。

    初始状态:对于N>1,第一个可以直接先计着,从2开始,将第i元素与第i-1元素比较判断处于哪个状态。初始状态和平行状态一样。根据上面的分析,上升趋势初始值为2,下升趋势初始值为1. 前一个上升趋势的最大值为1. 

     1 class Solution {
     2 public:
     3     int candy(vector<int> &ratings) {
     4         int n = ratings.size();
     5         if (n <= 1) {
     6             return n;
     7         }
     8         
     9         int sum = 1, increasing = 2, decreasing = 1, maxIncreasing = 1;
    10         
    11         for (int i = 1; i < n; ++i) {
    12             if (ratings[i] > ratings[i - 1]) {
    13                 maxIncreasing = increasing;
    14                 sum += increasing++;
    15                 decreasing = 1;
    16             } else if (ratings[i] == ratings[i - 1]) {
    17                 increasing = 2;
    18                 decreasing = 1;
    19                 maxIncreasing = 1;
    20                 sum++;
    21             } else {
    22                 sum += decreasing++;
    23                 if (decreasing > maxIncreasing) {
    24                     sum++;
    25                 }
    26                 increasing = 2;
    27             }
    28         }
    29         
    30         return sum;
    31     }
    32 };

    时间复杂度O(n),空间复杂度O(1)。

    第二次刷,照着思路写,卡在第16行了。应该只要是上升趋势就要更新pre. pre就是上升趋势的最大值。pre的初始值就应该很大。 120ms

     1 class Solution {
     2 public:
     3     int candy(vector<int> &ratings) {
     4         int n = ratings.size();
     5         if (n <= 1) return n;
     6         
     7         int sum = 1, pre = ratings.size() + 1, candy = 1, state = -1;
     8         
     9         for (int i = 1; i < ratings.size(); ++i) {
    10             if (ratings[i - 1] < ratings[i]) {
    11                 if (state == -1 || state == 1) {
    12                     candy++;
    13                 } else {
    14                     candy = 2;
    15                 }
    16                 pre = candy;
    17                 state = 1;
    18             } else if (ratings[i - 1] > ratings[i]) {
    19                 if (state == -1 || state == 0) {
    20                     candy++;
    21                     if (candy >= pre) sum++;
    22                 } else {
    23                     candy = 1;
    24                 }
    25                 state = 0;
    26             } else {
    27                 candy = 1;
    28                 state = -1;
    29                 pre = ratings.size() + 1;
    30             }
    31             sum += candy;
    32         }
    33 
    34         return sum;
    35     }
    36 };

     Method II

    网上有另一种解决思路,需要扫两遍,一遍是从左到右扫,一遍是从右到左扫,这样得到两个最小糖数组,最后对这两个数据每个位置求一下最大值就可以了。这样的时间复杂度仍是O(n),空间复杂度也是O(n)。思想和Trapping Rain Water类似。

    和z神讨论了一下,z神给出了一个不需要O(n)空间的做法,想想也是。

    有两个指针,左指针指向的是当前位置左边连续递减的边界位置;右指针指向的是当前位置右边连续递减的边界位置;

    每个位置的糖数由max(cur - left, right - cur) + 1决定;

    但是要额外注意一下相等的情况。如果ratings[i] == ratings[i-1]的话,那么left=cur; 如果ratings[i]==ratings[i+1]的话,那么right=cur;

    左指针的更新在O(n)可以做到,右指针的更新最怀情况是O(2n)。

    对于右边的递减序列,右指针的更新只在right<=cur的时候才有必要更新。如果right>cur,证明当前还在递减序列当中,所以right不变。(Line14)

     1 class Solution {
     2 public:
     3     int candy(vector<int> &ratings) {
     4         int n = ratings.size();
     5         if (n <= 1) return n;
     6         ratings.push_back(ratings.back() + 1);
     7         int sum = 0, l = 0, r = 0, candy1 = 0;
     8         for (int i = 0; i < n; ++i) {
     9             if (i == 0 || ratings[i] <= ratings[i - 1]) {
    10                 l = i;
    11             } 
    12             if (ratings[i] <= ratings[i + 1]) {
    13                 r = i;
    14             } else if (r <= i) {
    15                 r = i;
    16                 for (int j = i + 1; ratings[j] < ratings[j - 1]; ++j) r++;
    17             }
    18             sum += max(r - i, i - l) + 1;
    19         }
    20         ratings.pop_back();
    21         return sum;
    22     }
    23 };

     136ms

    如果没有Line 14,就是1780ms。

    第三次刷,最容易想出来的还是正反向扫一遍的做法。算是一次过吧。--i老是容易写成++i。

     1 class Solution {
     2 public:
     3     int candy(vector<int> &ratings) {
     4         if (ratings.empty()) return 0;
     5         int n = ratings.size();
     6         vector<int> dp(n, 0);
     7         for (int i = 1; i < n; ++i) {
     8             if (ratings[i] > ratings[i - 1]) {
     9                 dp[i] = dp[i - 1] + 1;
    10             }
    11         }
    12         
    13         int min = 0, right = 0;
    14         for (int i = n - 1; i >= 0; --i) {
    15             if (i != n - 1 && ratings[i] > ratings[i + 1]) {
    16                 right++;
    17             } else {
    18                 right = 0;
    19             }
    20             min += max(dp[i], right) + 1;
    21         }
    22         return min;
    23     }
    24 };
  • 相关阅读:
    iOS开发>学无止境
    iOS开发>学无止境
    lua学习笔记——逻辑运算符和三目运算符
    lua学习笔记——变量的声明
    lua学习笔记——冒泡排序(if else 和for循环的使用)
    lua学习笔记——表的继承()
    lua学习笔记——面向对象
    lua学习笔记——携程
    lua学习笔记-全局变量与局部变量(require与dofile的区别)
    lua与unity 的交互
  • 原文地址:https://www.cnblogs.com/linyx/p/3691821.html
Copyright © 2011-2022 走看看