Candy
There are n
children standing in a line. Each child is assigned a rating value given in the integer array ratings
.
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.
Return the minimum number of candies you need to have to distribute the candies to the children
Examples
Input: ratings = [1, 0, 2]
–> 5
Explanation: The first, second, and third child can be given 2, 1, and 2 candies respectively.
Input: ratings = [1, 2, 2]
–> 4
Explanation: The first, second and third child can be given 1, 2, and 1 candies respectively as it satisfies the above conditions.
Solution 1: Two Pass Array Construction
There are two conditions on each of the childrens' candy amounts: One from the left, and one from the right. To solve the problem, we use two passes: one to ensure that the candy distribution satisfies the minimum demand from the left, and one to ensure that the candy distribution satisfies the minimum demand from the right (while also satisfying the left side's requirements). The algorithm has three steps:
- Create a new array
candies
with the same length asratings
and setcandies[0]=1
- Iterate through
ratings
andcandies
from the left: ifratings[i] > ratings[i-1]
thencandies[i] = candies[i-1] + 1
- Iterate through
ratings
andcandies
from the right: ifratings[i] > ratings[i+1]
thencandies[i] = std::max(candies[i], candies[i+1]+1)
Coding this up, we get the following solution:class Solution{ public: int candy(std::vector<int>& ratings) { int n = ratings.size(); int candies[n]; candies[0] = 1; for(int i = 1; i < n; i++){ if(ratings[i-1] < ratings[i]) candies[i] = candies[i-1]+1; else candies[i] = 1; } int sum = candies[n-1]; for(int i = n-2; i >=0; i--){ if(ratings[i] > ratings[i+1]) candies[i] = std::max(candies[i], candies[i+1] + 1); sum += candies[i]; } return sum; } };
Runtime Analysis:
- In this function, there are two loops, both of which perform
n
linear operations. The time complexity isO(n)
. - There is one array created with size
n
. The space complexity isO(n)
.
Solution 2: One Pass Greedy Algorithm
We have a solution that uses linear space, but can we do better? How could we calculate the sum of all childrens' candies at the same time? We can accomplish constant space complexity with streak tracking.
To calculate the sum of all childrens' candy, we initialize candy
to n, as each child must have at least one candy. We iterate through ratings
, comparing ratings[i]
to ratings[i-1]
. At each point, there are 3 cases:
ratings[i] > ratings[i-1]
: In this case, the candies allocated to child[i]
will be one higher than the candies allocated to child[i-1]
. Begin a count of increasing streaks, add the current streak tocandy
and increase the streak.ratings[i] < ratings[i-1]
: In this case, the candies allocated to child[i-1]
must have been one higher than the candies allocated to child[i]
. Because the last child in a downward streak always gets one candy, the second-last two candies, etc., we keep track of the streak of downward ratings, with knowledge that the minimum sum is the triangular number corresponding to the streak.ratings[i] == ratings[i-1]
: In this case, the candies allocated to child[i]
can be 1, so we break out of our loop iteration and reset both downward and upward streaks. In each case, we check if the current streak can be continued before checking for other conditions.
Because an upward streak of upstreak
and a downward streak of downstreak
would result in double counting at the child where the upward streak ends and the downward streak begins, we save upstreak
and downstreak
and subtract std::min(upstreak, downstreak)
from candy
after each downward streak.
Translating this algorithm into code, we get the following solution:
class Solution {
public:
int candy(vector<int>& ratings) {
int n = ratings.size();
int candy = n, i=1;
while(i<n){
if(ratings[i] == ratings[i-1]){
i++;
continue;
}
int upstreak = 0;
while(i<n && ratings[i] > ratings [i-1]){
peak++;
candy += peak;
i++;
}
int downstreak = 0;
while(i<n && ratings[i] < ratings[i-1]){
valley++;
candy += valley;
i++;
}
candy -= min(upstreak, downstreak); //Keep only the higher peak
}
return candy;
}
};
Runtime Analysis
- In this solution, we iterate through
ratings
once, performing a constant time operation on each member. Therefore, the time complexity isO(n)
. - The only memory used is two temporary integers
upstreak
anddownstreak
. Therefore, the space complexity isO(1)
.