您的位置:首页 > 其它

[leetcode] 135. Candy

2015-12-02 16:12 323 查看
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;
}
};


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

奇怪的是第一种方法的运行时间比第二种短,我想可能是第一种方法上升沿上的孩子只遍历了一次(不含最终计算结果那轮遍历)的原因吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  leetcode greedy