[leetcode] 135. Candy

There areN 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?

这道题和糖果相关,给一排带优先级的孩子发糖果,题目难度为Hard。

如何才能发最少的糖果呢?我们知道,优先级比旁边两个孩子低的可以发一颗糖;而优先级介于旁边两个孩子之间的发多少颗糖要取决于比他优先级低的那个孩子,只要比他多发一颗就可以了;优先级比旁边两个孩子高的要满足至少比旁边两个中糖果多的那个孩子多一颗。换言之,波谷的孩子可以优先发一颗糖,在上升沿和下降沿上的孩子根据旁边比他优先级低的孩子确定,在波峰上的孩子则根据两边的孩子来确定,这样通过贪婪策略能够达到总糖果数最小。

通过以上分析,我们首先要找出波谷的孩子,然后通过波谷的孩子向两边延伸直到找到两边的波峰,这样两个波峰和这个波谷之间的孩子发多少颗糖就可以确定了。在遍历所有孩子之后我们再根据上面的策略确定所有波峰上孩子所发的糖果数。

还有一个问题是题目中没有说相邻两个孩子优先级相同时如何处理,最初想当然的把它们发了相同的糖果,不过测试没有通过,查看测试用例发现原来这两个相同优先级的孩子根本没有关系,这也算符合题目的说明。这样如果存在相邻的孩子优先级相同,我们就可以从他们之间把孩子分开然后单独计算,通过这种分治策略来得到最终结果。具体代码:

class Solution {
    bool minCheck(const vector<int>& ratings, int idx, int bgn, int end) {
    	if(bgn == end) 
    	    return true;
        else if(idx == bgn) 
            return ratings[idx] < ratings[idx+1];
        else if(idx == end) 
            return ratings[idx-1] > ratings[idx];
        else 
            return ratings[idx]<ratings[idx-1] && ratings[idx]<ratings[idx+1];
    }
    
    bool maxCheck(const vector<int>& ratings, int idx, int bgn, int end) {
        if(idx == bgn) 
            return ratings[idx] > ratings[idx+1];
        else if(idx == end) 
            return ratings[idx-1] < ratings[idx];
        else 
            return ratings[idx]>ratings[idx-1] && ratings[idx]>ratings[idx+1];
    }
    
    int candyCnt(const vector<int>& ratings, int bgn, int end) {
        int cnt = 0;
        vector<int> num(end-bgn+1, 0);
        for(int i=bgn; i<=end; i++) {
            if(!minCheck(ratings, i, bgn, end)) 
                continue;
            num[i-bgn] = 1;
            int cur = i - 1;
            while(cur>=bgn && !maxCheck(ratings, cur, bgn, end)) {
                num[cur-bgn] = num[cur-bgn+1] + 1;
                cur--;
            }
            cur = i + 1;
            while(cur<=end && !maxCheck(ratings, cur, bgn, end)) {
                num[cur-bgn] = num[cur-bgn-1] + 1;
                cur++;
            }
            i = cur;
        }
            
        for(int i=bgn; i<=end; i++) {
            if(num[i-bgn]) 
                cnt += num[i-bgn];
            else {
                if(i == bgn) 
                    cnt += num[1] + 1;
                else if(i == end) 
                    cnt += num[i-bgn-1] + 1;
                else 
                    cnt += max(num[i-bgn-1], num[i-bgn+1]) + 1;
            }
        }
            
        return cnt;
    }
public:
    int candy(vector<int>& ratings) {
        int cnt = 0;
        int pos = 0;
        int sz = ratings.size();
        for(int i=0; i<sz; i++) {
            if(i == sz-1) {
                cnt += candyCnt(ratings, pos, i);
            }
            else if(ratings[i] == ratings[i+1]) {
                cnt += candyCnt(ratings, pos, i);
                pos = i + 1;
            }
        }
        return cnt;
    }
};
问题解决了,不过代码看起来有些庞大。下面推荐一下别人的方法,写的非常简洁,Orz,具体代码:
class Solution {
public:
    int candy(vector<int>& ratings) {
    	int cnt = 0;
        int sz=ratings.size();
        vector<int> num(sz,1);
        for(int i=1; i<sz; i++) {
            if(ratings[i]>ratings[i-1])
                num[i] = num[i-1] + 1;
        }
        for(int i=sz-1; i>0 ; i--) {
            if(ratings[i-1]>ratings[i])
                num[i-1] = max(num[i]+1, num[i-1]);
        }
        for (int i=0; i<sz; i++) {
            cnt += num[i];
        }
        return cnt;
    }
};

上面的方法中给所有孩子初始发一颗糖,然后通过正向和反向两次遍历把上升沿和下降沿上的孩子都处理掉,同时波峰上的孩子也照顾到了,更神奇的是对于相邻孩子优先级相同的情况也处理到了,不得不说佩服,代码写得很漂亮!

奇怪的是第一种方法的运行时间比第二种短,我想可能是第一种方法上升沿上的孩子只遍历了一次(不含最终计算结果那轮遍历)的原因吧。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值