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?
tag: greedy
虽然说的是贪婪算法,但我第一个想到的是回溯和动态规划。其实我也不清楚三者的明确区别,不管怎样。能做出来就好。
基本思想,先给第一个孩子一块糖,然后尝试给第二个孩子一块糖,如果不满足条件,则向上层回溯。
暂时来不及写代码,先写到这。占个坑,稍后更新。
版本一,采用回溯算法,用一个stack记录当前状态,如果当前层没有满足条件的解,则返回上一层继续深搜。被Leetcode判为内存超出限额MEMORY LIMIT EXCEEDED。晕,考虑用时间换空间中。
//版本一,内存超出限制
class Solution {
public:
stack<int> data;
int candy(vector<int> &ratings) {
/*mark backtracking*/
_candy(ratings, 0);
int sum = 0;
while(data.empty() == false){
sum += data.top();
data.pop();
}
return sum;
}
bool _candy(vector<int> ratings, int start){
if(start == 0){
int i = 1;
bool state;
while(true){
data.push(i);
state = _candy(ratings, 1);
if(state == true){
return true;
}
else{
data.pop();
i++;
}
}
}
else if(start == ratings.size()){
return true;
}
else{
int lastCandyNum = data.top();
int min = -1;
int max = -1;
if (ratings[start - 1] > ratings[start]){
max = lastCandyNum - 1;
for(int i = 1; i <= max; i++){
data.push(i);
bool state = _candy(ratings, start + 1);
if(state == true){
return true;
}
else{
data.pop();
}
}
return false;
}
else{
min = lastCandyNum + 1;
while(true){
data.push(min);
bool state = _candy(ratings, start + 1);
if(state == true){
return true;
}
else{
data.pop();
min++;
}
}
}
}
}
};
更新:第二版删掉了stack。因为递归中已经隐性地用到stack了,只要在递归函数中多加一个参数便可实现stack的功能。但是,删掉stack后,仍然是内存超限。看来不能用递归了,尝试优化中。
class Solution {
public:
int candy(vector<int> &ratings) {
/*mark backtracking*/
return _candy(ratings, 0, 0);
}
int _candy(vector<int> ratings, int start, int lastCandy){
if(start == 0){
int i = 1;
while(true){
int sum = _candy(ratings, 1, i);
if(sum >= 0){
return sum + i;
}
else{
i++;
}
}
}
else if(start == ratings.size()){
return 0;
}
else{
int min;
int max;
if (ratings[start - 1] > ratings[start]){
max = lastCandy - 1;
for(int i = 1; i <= max; i++){
int sum = _candy(ratings, start + 1, i);
if(sum >= 0){
return sum + i;
}
}
return -1;
}
else{
min = lastCandy + 1;
while(true){
int sum = _candy(ratings, start + 1, min);
if(sum >= 0){
return sum + min;
}
else{
min++;
}
}
}
}
}
};
更新:
版本三:AC
贪心算法,每次尝试放入最小的糖果数。空间复杂度O(1),时间复杂度O(N)。
该题需要分多种情况讨论:
记第i人的rating值为rating[i],给第i人的糖果数为candy[i]。
则:
如果rating[i]处于上升阶段(rating[i – 1] < rating[i] < rating[i + 1]),则candy[i] = candy[i – 1] + 1.
如果rating[i]处于下降阶段(rating[i – 1] >rating[i] > rating[i + 1]),则此时candy[i]不能确定,需要记录下该下降阶段的总长度,最后用calculateDescendSum()一起求出。
如果rating[i]处于极大值,处于极小值,或rating[i]与相邻的某一元素相等,都要分情况讨论。
calcuateDescendSum()需要借助minPeakVal成员变量。因为peak的值必须比它前一个值大,这个最小值记录在minPeakVal中。
感觉,根据一个序列的大小变化来求某一相关的值(比如此题,或何时卖股票收益最大问题等等),都可以用贪心或动态规划来实现。并不一定非要用到stack。以减少空间。
class Solution {
public:
int minPeakVal = 0;
int candy(vector<int> &ratings) {
/*greedy algorithm... I think it is.*/
if(ratings.size() == 1){
return 1;
}
else{
int descendLength = 0;
int sum = 0;
int lastCandyNum = 0;
for(int i = 0; i < ratings.size(); i++){
if(i == 0){//start
if(ratings[i + 1] >= ratings[i]){//ascending or stable
lastCandyNum = 1;
sum += lastCandyNum;
}
else{//descending
descendLength = 1;
minPeakVal = 0;
}
}
else if(i == ratings.size() - 1){//end
if(ratings[i - 1] > ratings[i]){//descending
descendLength++;
sum += calculateDescendSum(descendLength);
descendLength = 0;
}
else if(ratings[i - 1] < ratings[i]){//ascending
lastCandyNum++;
sum += lastCandyNum;
}
else{//stable
lastCandyNum = 1;
sum += 1;
}
}
else{//non-boundary case
int a = ratings[i - 1];
int b = ratings[i];
int c = ratings[i + 1];
if(b > a && b > c){//peak
minPeakVal = lastCandyNum + 1;
descendLength = 1;
}
else if(b < a && b < c){//bottom
descendLength++;
sum += calculateDescendSum(descendLength);
descendLength = 0;
lastCandyNum = 1;
}
else if(a < b && b < c){//ascending
lastCandyNum++;
sum += lastCandyNum;
}
else if(a > b && b > c){//descending
descendLength++;
}
else{//stable related scenarios
if(a > b){ //a > b && b == c
descendLength++;
sum += calculateDescendSum(descendLength);
descendLength = 0;
lastCandyNum = 1;
}
else if(a < b){//a < b && b == c
lastCandyNum++;
sum += lastCandyNum;
}
else{
if(b < c){//a == b && b < c
lastCandyNum = 1;
sum += lastCandyNum;
}
else if(b > c){//a == b && b > c
minPeakVal = 0;
descendLength = 1;
}
else{//a == b && b == c
lastCandyNum = 1;
sum += lastCandyNum;
}
}
}
}
}
return sum;
}
}
int calculateDescendSum(int num){
int sum = 0;
for(int i = 1; i < num; i++){
sum += i;
}
sum += max(num, minPeakVal);
return sum;
}
};
