To make the analysis of this algorithm more interesting I will the following input:
9
2
3
4
4
4
2
1
3
4
Firstly, notice if a kid sits next to a kid who is getting x
candies and that kid has a lower rating then the first kid should get at least x+1
candies. Making the difference more than 1 will just waste candies. The difference sometimes must be greater than 1, but I'll get to when that happens later.
Now on to finding the kids who should get only one candy. I visualise the ratings as a mountain range (The greater the rating the higher the mountains at that point) and finding the kids who should get one candy as finding valleys (points where both neighbours have a higher rating or the same rating) in the mountain range. The example given would look like (the valleys are indicated by the arrows):
*** *
**** **
****** **
*********
^ ^ ^
For the purpose of this process I assume there are 2 peaks of "infinite" height before the beginning and after the end of this line. (When I say infinite I just mean larger than any possible value in the input so you can just use 10^5+1
for "infinity". In practice, I'd use a value larger than that in case the problem setters have bugged input data.)
You can easily find the valleys using the following code:
ratings = ...
N = ...
valleys = []
def get_rating(i):
if i < 0 or i > N-1:
return INFINITY
return ratings[i]
for i from 0 to N-1:
if get_rating(i) <= get_rating(i-1) and get_rating(i) <= get_rating(i+1):
valleys.append(i)
The array valleys
contains the indices of the valleys. We know each kid representing a valley should get one candy. For illustration assume the valley is at index 4. Now we know the kids at index 3 and 5 should get at least 2 candies. If the kid at index 2 has a higher rating than the kid at index 3 that kid should get at least 3 candies. And so on for 2 and down. Similarly for 6 and up.
Note I say "at least", this is because of peaks (kids whose ratings are higher than both of their neighbour's, note unlike valleys I don't include equality in this definition). Peaks can have two minimum constraints and we simply choose the greater of the two.
Now we can find the number of candies each kid should get with the following code:
candies = [0] * N # An array of N 0s
for valley_idx in valleys:
candies[valley_idx] = 1
cur_idx = valley_idx-1
cur_candies = 2
while cur_idx >= 0 and ratings[cur_idx] > ratings[cur_idx+1]:
candies[cur_idx] = max(candies[cur_idx], cur_candies)
cur_idx -= 1
cur_candies += 1
cur_idx = valley_idx+1
cur_candies = 2
while cur_idx < N and ratings[cur_idx] > ratings[cur_idx-1]:
candies[cur_idx] = max(candies[cur_idx], cur_candies)
cur_idx += 1
cur_candies += 1
Then the number of candies the teacher needs to buy is the sum of the values in the candies
array.
Doing this the answer turns out to be 18
for our sample input or in the form of a graph:
* * *
** ** **
*********
Solution to slightly altered problem statement
In the above solution I assumed that adjacent kids with the same rating don't place any restrictions on the amount of candy either should get with relation to the other. If it is instead the case that both kids need to get the same amount of candy we can quite easily alter the algorithm to take this into account.
The basic idea is that we do a sort of run length encoding, because we can notice that whether there are 1 or more kids in a row that have the same rating it doesn't alter the amount of candies their neighbours should get. We need to keep track of the number of kids in a row though since 5 kids in a row getting 5 candies means we have to dole out 25 candies and not just 5. We do this with a multipliers
array. Using the following code we find the new ratings
array and the multipliers
array:
new_ratings = [ratings[0]]
multipliers = [1]
for i from 1 to N-1:
if ratings[i] == new_ratings[len(new_ratings)-1]:
multipliers[len(new_ratings)-1] += 1
else:
new_ratings.append(ratings[i])
multipliers.append(1)
Now we just run the original algorithm on the new_ratings
array and get a candies
array. Then to get the actual amount of candies we can just run:
answer = 0
for i from 0 to len(new_ratings)-1:
answer += multipliers[i] * candies[i]
Doing this the answer turns out to be 20
for our sample input or in the form of a graph:
*** *
***** **
*********