您的位置:首页 > 编程语言 > C语言/C++

[LeetCode] Candy

2015-03-28 16:57 246 查看


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?

解题思路:

贪心算法,能少给就少给。最初的想法是用一个数组记录每个小孩应该给的糖果数,从左往右依次扫描,若当前小孩比左边的小孩得分高,则比左边的小孩多拿一个糖,若得分低(或相等),则只分配一个糖。但是要考虑到若左边的小孩若比自己得分高,但只有一个糖,当前小孩若比其分配的少,那么只能分配一个糖了。故可以调整左边的小孩分的糖数。维持左边已经分配的小孩的糖果数目。于是版本一的代码如下:

class Solution {
public:
int candy(vector<int> &ratings) {
int number = ratings.size();
if(number == 0){
return 0;
}
int* d = new int[number];   //存储前i个已经分配的糖的数目

d[0] = 1;   //贪心,给第一个小孩先给一个糖

for (int i=1; i<number; i++){
if(ratings[i]<ratings[i-1]){    //当前小孩得分比左边的少,只给他一个糖,但是得修正,防止左边的已经是一个糖了
d[i] = 1;
for(int j=i-1; j>=0; j--){
if(d[j] == d[j+1] && ratings[j]>ratings[j + 1]){    //左边的小孩得分高,却只得到相同的糖,他不干了,得给他加糖
d[j] = d[j]+1;
}else{
break;
}
}
}else if(ratings[i] == ratings[i-1]){   //当前小孩得分与左边的相同,给他左边小孩同样多的糖
d[i] = 1;
}else{          //当前小孩得分比左边多,给他比左边多一个糖
d[i] = d[i-1] + 1;
}
}

int result = 0;
for(int i=0; i<number; i++){
result += d[i];
}
delete[] d;
return result;
}
};
但是出现了执行时间超时的情况。因为该方法的时间复杂度为O(n2),特别是当小孩得分是逆序的情况,执行的最慢。

于是想其他办法。上网查了一下,终于得知,其实可以左右两边分别扫描,从左往右扫描,只需记录每个小孩相对于左边小孩所获得的糖果。从右往左扫描,只需记录每个小孩相对于右边小孩获得的糖果数,然后两个数组对应元素取较大值即为结果。下面是代码:

class Solution {
public:
int candy(vector<int> &ratings) {
int number = ratings.size();
if(number == 0){
return 0;
}
int* d = new int[number];   //存储前i个已经分配的糖的数目

//两边扫,取较大的

//从左往右扫
d[0] = 1;   //贪心,第一个小孩先给一个糖
for (int i=1; i<number; i++){
if(ratings[i]<=ratings[i-1]){    //当前小孩得分比左边的少,只给他一个糖
d[i] = 1;
}else{          //当前小孩得分比左边多,给他比左边多一个糖
d[i] = d[i-1] + 1;
}
}

//从右往左扫
int* r = new int[number];
r[number - 1] = 1;
for(int i = number-2; i>=0; i--){
if(ratings[i]<=ratings[i+1]){    //当前小孩得分比右边的少,只给他一个糖
r[i] = 1;
}else{          //当前小孩得分比右边多,给他比右边多一个糖
r[i] = r[i+1] + 1;
}
}

int result = 0;
for(int i=0; i<number; i++){
result += (d[i]>r[i]?d[i]:r[i]);
}
delete[] r;
delete[] d;
return result;
}
};
上述代码中,只扫描了两遍,所以时间复杂度为O(n),用的空间为2*n。可以优化一下代码,使得用的空间为n,如下所示:

class Solution {
public:
int candy(vector<int> &ratings) {
int number = ratings.size();
if(number == 0){
return 0;
}
int* d = new int[number];   //存储前i个已经分配的糖的数目

//两边扫,取较大的

//从左往右扫
d[0] = 1;   //贪心,第一个小孩先给一个糖
for (int i=1; i<number; i++){
if(ratings[i]<=ratings[i-1]){    //当前小孩得分比左边的少,只给他一个糖
d[i] = 1;
}else{          //当前小孩得分比左边多,给他比左边多一个糖
d[i] = d[i-1] + 1;
}
}

int result = 0;
int lastCandyNumber = 1;    //从右往左扫面上一个孩子的糖果数目
result += (d[number-1]>lastCandyNumber?d[number-1]:lastCandyNumber);
//从右往左扫
for(int i = number-2; i>=0; i--){
if(ratings[i]<=ratings[i+1]){    //当前小孩得分比右边的少,只给他一个糖
lastCandyNumber = 1;
}else{          //当前小孩得分比右边多,给他比右边多一个糖
lastCandyNumber++;
}
result += (d[i]>lastCandyNumber?d[i]:lastCandyNumber);
}

delete[] d;
return result;
}
};
相关的若与左右两边有关的问题,都可以考虑两边扫的方法。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ leetcode