Problem Statement in English
You’re given an array cookies where cookies[i] contains the number of cookies in the bag, and , which is the number of children that the cookies must be distributed amongst.
Your task is to return the minimum amongst the maximum number of cookies that a child gets in each unique distribution.
Approach
There’s no real “smart” way to do this, you simply have to bruteforce it. However, you can dramatically reduce the amount of computation by realizing the fact that we don’t have to recompute distributions that have already been considered.
Let’s call child 1 Ant, and child 2 Bob.
For example if index and represents cookies given to Ant and index , the cookies given to Bob, then, [0,1] and [1,0] are the same thing! Giving Ant cookies, and Bob cookie is the exact same as giving Ant cookie and Bob cookies.
So once we’ve calculated the outcome of giving Ant cookies, and Bob cookie, we shouldn’t calculate Ant getting cookie and Bob getting cookies.
Take a look at this tree to get a strong visual grip of it.
Consider the input, cookies = [1,2,3], k = 2
If you look closely, you’ll realise that half the tree is redundant. We can save a lot on compute by memoization/caching.
Solution in Python
class Solution:
def distributeCookies(self, cookies: List[int], k: int) -> int:
ans = -1
# this decorator internally handles memoization
# by caching function calls
#
# we're assigning `currDistribution` a type of tuple
# as it can be hashed, while lists can't,
# and the cache decorator only accepts hashable types
@cache
def recursiveCall(bagIndex, currDistribution: tuple[int]):
# bind to `ans`
nonlocal ans
# we've processed all bags
if bagIndex >= len(cookies):
if ans == -1:
ans = max(currDistribution)
else:
ans = min(ans, max(currDistribution))
# terminate this path as we've processed all cookie bags
return
# convert the tuple to a list
# since we need to mutate it
# (list) currDistribution
lcd = list(currDistribution)
# add the current cookie bag to each distinct child as separate possibilities
for i in range(len(lcd)):
# copy the list
copied_currentDistribution = lcd.copy()
# add the current cookie bag to each index separately
copied_currentDistribution[i] += cookies[bagIndex]
# backtrack
recursiveCall(bagIndex + 1, tuple(sorted(copied_currentDistribution)))
# init call
# init tuple with `k` 0s
recursiveCall(0, (0 for _ in range(k)))
# return answer
return ans
Complexity
Time:
represents the number of cookie bags, (len(cookies)). Instead of the regular , it’s because we generate different possibilities.Since we do this for each cookie in the cookie bag, it’s .
Space:
Since we generate arrays, each with a max length of .
Note: For what it’s worth, we don’t actually get anywhere near these numbers as we achieve massive savings, thanks to memoization.
Mistakes I Made
I didn’t realize just how much duplication was occuring, and so I wasn’t caching function calls. Needless to say, I got a Time Limit Exceeded error :(
And we are done.