Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
Program to find maximum number of groups getting fresh donuts in Python
Suppose we have a value batchSize and an array groups where groups[i] denotes that there is a group of groups[i] customers that will visit the shop. So there is a donuts shop that bakes donuts in batches of given batchSize. But they have one rule: they must serve all of the donuts of a batch before serving any donuts of the next batch. Each customer will get exactly one donut.
When a group enters the shop, all customers of that group must be served before addressing any next groups. One group may be happy if they all get fresh donuts (the first customer of the group does not accept a donut that was left over from the last group).
We can rearrange the groups, and we have to find the maximum possible number of happy groups after rearranging.
Example
If the input is batchSize = 4 and groups = [2,1,8,4,3], then the output will be 4.
We can rearrange them like [8,4,2,3,1]:
- First group (8): needs 2 batches, gets fresh donuts
- Second group (4): exactly 1 batch, gets fresh donuts
- Third group (2): uses remaining donuts from previous batch
- Fourth group (3): gets fresh donuts from new batch
- Fifth group (1): uses remaining donut
So groups 1, 2, and 4 are happy (3 total), but we can do better and achieve 4 happy groups.
Algorithm
To solve this, we will follow these steps:
- Calculate remainders:
l = [g % batchSize for g in groups] - Count frequency of each remainder:
count = Counter(l) - Create frequency array:
g = [count[i] for i in range(batchSize)] - Use dynamic programming with memoization to try all arrangements
Implementation
from collections import Counter
def solve(batchSize, groups):
# Calculate remainders when each group size is divided by batchSize
remainders = [g % batchSize for g in groups]
count = Counter(remainders)
freq = [count[i] for i in range(batchSize)]
def dp(current_sum, frequency_tuple):
# Base case: no more groups to process
if max(frequency_tuple) == 0:
return 0
best_result = 0
freq_list = list(frequency_tuple)
# Try using each available remainder
for remainder in range(batchSize):
if freq_list[remainder] == 0:
continue
# Use one group with this remainder
freq_list[remainder] -= 1
new_sum = (current_sum + remainder) % batchSize
result = dp(new_sum, tuple(freq_list))
best_result = max(best_result, result)
freq_list[remainder] += 1
# Add 1 if current sum is 0 (group gets fresh batch)
return best_result + (1 if current_sum == 0 else 0)
return dp(0, tuple(freq))
# Test the function
batchSize = 4
groups = [2, 1, 8, 4, 3]
result = solve(batchSize, groups)
print(f"Maximum happy groups: {result}")
Maximum happy groups: 4
How It Works
The algorithm uses dynamic programming with the key insight that only the remainder matters, not the actual group size. A group is happy if the current sum of remainders is 0 (meaning they start a fresh batch).
The recursive function dp(current_sum, frequency_tuple) tries all possible arrangements and returns the maximum number of happy groups achievable.
Optimized Version with Memoization
from collections import Counter
from functools import lru_cache
def solve_optimized(batchSize, groups):
remainders = [g % batchSize for g in groups]
count = Counter(remainders)
freq = tuple(count[i] for i in range(batchSize))
@lru_cache(maxsize=None)
def dp(current_sum, frequency_tuple):
if max(frequency_tuple) == 0:
return 0
best_result = 0
freq_list = list(frequency_tuple)
for remainder in range(batchSize):
if freq_list[remainder] == 0:
continue
freq_list[remainder] -= 1
new_sum = (current_sum + remainder) % batchSize
result = dp(new_sum, tuple(freq_list))
best_result = max(best_result, result)
freq_list[remainder] += 1
return best_result + (1 if current_sum == 0 else 0)
return dp(0, freq)
# Test with the same example
batchSize = 4
groups = [2, 1, 8, 4, 3]
result = solve_optimized(batchSize, groups)
print(f"Maximum happy groups (optimized): {result}")
Maximum happy groups (optimized): 4
Conclusion
This problem uses dynamic programming to find the optimal arrangement of groups. The key insight is that only remainders matter when determining if a group gets fresh donuts. The memoized version significantly improves performance for larger inputs.
