2064. Minimized Maximum of Products Distributed to Any Store
Problem Description
You have n retail stores and m different product types. Each product type has a certain quantity given in the array quantities, where quantities[i] represents how many items exist of product type i.
Your task is to distribute all products to the stores with these constraints:
- Each store can receive products from only one product type (but can receive any amount from that type)
- All products must be distributed
- You want to minimize the maximum number of products any single store receives
The goal is to find the smallest possible value x, where x is the maximum number of products given to any store after distribution.
Example Understanding:
If you have quantities = [11, 6] and n = 6 stores:
- You could give the 11 items of type 1 to stores as: 4, 4, 3 (using 3 stores)
- You could give the 6 items of type 2 to stores as: 3, 3 (using 2 stores)
- Total stores used: 5 out of 6 available
- Maximum products in any store: 4
- This would give
x = 4
The solution uses binary search to find the minimum possible maximum. The check function determines if it's possible to distribute all products such that no store gets more than x items. The expression (v + x - 1) // x calculates the minimum number of stores needed to distribute v items with at most x items per store (this is equivalent to ceil(v/x)). If the total stores needed is at most n, then x is feasible.
Intuition
The key insight is recognizing this as an optimization problem where we're trying to minimize the maximum value - a classic scenario for binary search on the answer.
Think about it this way: if we pick any value x as our maximum products per store, we can quickly check if it's possible to distribute all products without exceeding this limit. For each product type with quantity v, we need at least ceil(v/x) stores to distribute all v items with at most x items per store. If the total number of stores needed across all product types is at most n, then x is achievable.
Now, there's a monotonic property here: if we can distribute all products with maximum x items per store, we can definitely do it with x+1 items per store (we'd just need fewer stores). Conversely, if x doesn't work, neither will any value smaller than x.
This monotonic property makes binary search perfect. We search for the smallest x that works:
- The minimum possible
xis 1 (each store gets at least 1 item if we use all stores) - The maximum possible
xis the largest quantity in our array (worst case: we put all items of one type in a single store)
For the feasibility check, the formula (v + x - 1) // x is a clever way to compute ceil(v/x) using integer arithmetic. This tells us how many stores we need to distribute v items with at most x items per store. We sum this across all product types and check if it's within our store limit n.
The binary search finds the boundary where the answer transitions from "not possible" to "possible", giving us the minimum feasible maximum.
Learn more about Greedy and Binary Search patterns.
Solution Implementation
1class Solution:
2 def minimizedMaximum(self, n: int, quantities: List[int]) -> int:
3 def feasible(max_products_per_store):
4 """
5 Check if we can distribute all products with at most
6 max_products_per_store items per store
7
8 For each product type, calculate minimum stores needed:
9 - Use ceiling division: (quantity + max - 1) // max
10 - This gives us ceil(quantity / max)
11 """
12 total_stores_needed = 0
13 for quantity in quantities:
14 # Calculate minimum stores needed for this product type
15 stores_for_this_product = (quantity + max_products_per_store - 1) // max_products_per_store
16 total_stores_needed += stores_for_this_product
17
18 # Return True if we can fit all products in n stores
19 return total_stores_needed <= n
20
21 # Binary search for the minimum possible maximum
22 # feasible(x) creates pattern: [false, false, ..., true, true, true]
23 # We want the first true (minimum x where distribution is possible)
24 left, right = 1, max(quantities)
25 first_true_index = -1
26
27 while left <= right:
28 mid = (left + right) // 2
29 if feasible(mid):
30 first_true_index = mid
31 right = mid - 1
32 else:
33 left = mid + 1
34
35 return first_true_index
361class Solution {
2 public int minimizedMaximum(int n, int[] quantities) {
3 // Find max quantity for upper bound
4 int maxQuantity = 0;
5 for (int q : quantities) {
6 maxQuantity = Math.max(maxQuantity, q);
7 }
8
9 // Binary search for the minimum possible maximum products per store
10 // feasible(mid) = can distribute with at most mid items per store
11 // Pattern: [false, false, ..., true, true, true]
12 int left = 1;
13 int right = maxQuantity;
14 int firstTrueIndex = -1;
15
16 while (left <= right) {
17 int mid = left + (right - left) / 2;
18
19 // Count total number of stores needed if each store can hold at most 'mid' items
20 int storesNeeded = 0;
21 for (int quantity : quantities) {
22 // Using ceiling division: (quantity + mid - 1) / mid equals ceil(quantity / mid)
23 storesNeeded += (quantity + mid - 1) / mid;
24 }
25
26 // Check if we can distribute all products with at most 'mid' items per store
27 if (storesNeeded <= n) {
28 firstTrueIndex = mid;
29 right = mid - 1;
30 } else {
31 left = mid + 1;
32 }
33 }
34
35 return firstTrueIndex;
36 }
37}
381class Solution {
2public:
3 int minimizedMaximum(int n, vector<int>& quantities) {
4 // Find max quantity for upper bound
5 int maxQuantity = *max_element(quantities.begin(), quantities.end());
6
7 // Binary search for the minimum possible maximum products per store
8 // feasible(mid) = can distribute with at most mid items per store
9 // Pattern: [false, false, ..., true, true, true]
10 int left = 1;
11 int right = maxQuantity;
12 int firstTrueIndex = -1;
13
14 while (left <= right) {
15 int mid = left + (right - left) / 2;
16
17 // Count total number of stores needed if max products per store is 'mid'
18 int storesNeeded = 0;
19 for (int quantity : quantities) {
20 // Using ceiling division: (quantity + mid - 1) / mid
21 storesNeeded += (quantity + mid - 1) / mid;
22 }
23
24 // Check if we can distribute with current maximum limit
25 if (storesNeeded <= n) {
26 firstTrueIndex = mid;
27 right = mid - 1;
28 } else {
29 left = mid + 1;
30 }
31 }
32
33 return firstTrueIndex;
34 }
35};
361/**
2 * Finds the minimized maximum number of products that can be distributed to n stores
3 * Uses binary search to find the optimal distribution value
4 *
5 * @param n - Number of stores available
6 * @param quantities - Array of quantities for each product type
7 * @returns The minimized maximum number of products any store will receive
8 */
9function minimizedMaximum(n: number, quantities: number[]): number {
10 // Find max quantity for upper bound
11 const maxQuantity: number = Math.max(...quantities);
12
13 // Binary search for the minimum possible maximum products per store
14 // feasible(mid) = can distribute with at most mid items per store
15 // Pattern: [false, false, ..., true, true, true]
16 let left: number = 1;
17 let right: number = maxQuantity;
18 let firstTrueIndex: number = -1;
19
20 while (left <= right) {
21 const mid: number = Math.floor((left + right) / 2);
22
23 // Calculate total number of stores needed with current distribution limit
24 let requiredStores: number = 0;
25 for (const quantity of quantities) {
26 // Each product type needs ceil(quantity/mid) stores
27 requiredStores += Math.ceil(quantity / mid);
28 }
29
30 // Check if we can distribute with current limit
31 if (requiredStores <= n) {
32 firstTrueIndex = mid;
33 right = mid - 1;
34 } else {
35 left = mid + 1;
36 }
37 }
38
39 return firstTrueIndex;
40}
41Solution Approach
The solution implements a binary search to find the minimum possible maximum products per store.
Implementation Details:
-
Binary Search Setup: We use
bisect_leftfrom Python's bisect module to search in the range[1, 10^6]. The range starts at 1 (minimum possible products per store) and goes up to10^6(a reasonable upper bound for the maximum quantity). -
Feasibility Check Function: The
check(x)function determines if we can distribute all products with at mostxitems per store:def check(x): return sum((v + x - 1) // x for v in quantities) <= n- For each quantity
vinquantities, we calculate(v + x - 1) // x - This formula computes
ceil(v/x)using integer division, giving us the minimum number of stores needed for that product type - We sum the required stores for all product types
- If the total is at most
n, thenxis feasible (returnsTrue)
- For each quantity
-
Binary Search Execution:
return 1 + bisect_left(range(1, 10**6), True, key=check)bisect_leftfinds the leftmost position whereTruecan be inserted in the sorted sequence- The
key=checkparameter means we're searching based on the feasibility check - Since
check(x)returnsFalsefor small values andTruefor large values,bisect_leftfinds the transition point - We add 1 to the result because
bisect_leftreturns an index (0-based), but our range starts at 1
Why This Works:
- The check function creates a boolean sequence:
[False, False, ..., False, True, True, ..., True] - The transition from
FalsetoTruehappens at the minimum feasiblex bisect_leftefficiently finds this transition point inO(log(10^6))time- Each check takes
O(m)time wheremis the length of quantities - Total time complexity:
O(m * log(max_quantity))
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example with quantities = [15, 10, 10] and n = 7 stores.
Step 1: Set up binary search range
- Minimum possible
x: 1 (each store gets at least 1 item) - Maximum possible
x: 15 (the largest quantity) - We'll search in range [1, 15]
Step 2: Binary search iterations
Iteration 1: Try x = 8 (middle value)
- Product type 1 (15 items): needs
ceil(15/8) = 2stores (8 + 7) - Product type 2 (10 items): needs
ceil(10/8) = 2stores (8 + 2) - Product type 3 (10 items): needs
ceil(10/8) = 2stores (8 + 2) - Total stores needed: 2 + 2 + 2 = 6 ≤ 7 ✓ (feasible)
- Since it works, try smaller values (search left half)
Iteration 2: Try x = 4 (middle of [1, 7])
- Product type 1 (15 items): needs
ceil(15/4) = 4stores (4 + 4 + 4 + 3) - Product type 2 (10 items): needs
ceil(10/4) = 3stores (4 + 4 + 2) - Product type 3 (10 items): needs
ceil(10/4) = 3stores (4 + 4 + 2) - Total stores needed: 4 + 3 + 3 = 10 > 7 ✗ (not feasible)
- Since it doesn't work, try larger values (search right half)
Iteration 3: Try x = 6 (middle of [5, 7])
- Product type 1 (15 items): needs
ceil(15/6) = 3stores (6 + 6 + 3) - Product type 2 (10 items): needs
ceil(10/6) = 2stores (6 + 4) - Product type 3 (10 items): needs
ceil(10/6) = 2stores (6 + 4) - Total stores needed: 3 + 2 + 2 = 7 ≤ 7 ✓ (feasible)
- Try smaller values
Iteration 4: Try x = 5
- Product type 1 (15 items): needs
ceil(15/5) = 3stores (5 + 5 + 5) - Product type 2 (10 items): needs
ceil(10/5) = 2stores (5 + 5) - Product type 3 (10 items): needs
ceil(10/5) = 2stores (5 + 5) - Total stores needed: 3 + 2 + 2 = 7 ≤ 7 ✓ (feasible)
- Try smaller values one more time
Since x = 4 failed but x = 5 works, the answer is 5.
Final Distribution:
- Store 1: 5 items (type 1)
- Store 2: 5 items (type 1)
- Store 3: 5 items (type 1)
- Store 4: 5 items (type 2)
- Store 5: 5 items (type 2)
- Store 6: 5 items (type 3)
- Store 7: 5 items (type 3)
Maximum items in any store: 5 ✓
Time and Space Complexity
Time Complexity: O(m * log(max(quantities))) where m is the length of the quantities array.
- The binary search using
bisect_leftsearches over a range from 1 to 10^6, which takesO(log(10^6))iterations. - For each iteration of the binary search, the
checkfunction is called. - The
checkfunction iterates through all elements inquantitiesto calculate the sum, which takesO(m)time. - Therefore, the overall time complexity is
O(m * log(10^6))which simplifies toO(m * log(max(quantities)))since 10^6 is used as an upper bound for the maximum possible value.
Space Complexity: O(1)
- The
checkfunction uses only a constant amount of extra space for the sum calculation and loop variable. - The
bisect_leftfunction with a range object doesn't create the entire range in memory; it generates values on-the-fly, usingO(1)space. - No additional data structures are created that scale with the input size.
- Therefore, the space complexity is
O(1)excluding the input array itself.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using Wrong Binary Search Template Variant
A common pitfall is using a different binary search variant that doesn't match the canonical template. The correct template uses while left <= right with first_true_index tracking.
Incorrect Implementation:
# WRONG: Using while left < right with right = mid left, right = 1, max_value while left < right: mid = (left + right) // 2 if feasible(mid): right = mid else: left = mid + 1 return left
Correct Implementation:
# CORRECT: Using template with first_true_index
left, right = 1, max(quantities)
first_true_index = -1
while left <= right:
mid = (left + right) // 2
if feasible(mid):
first_true_index = mid
right = mid - 1
else:
left = mid + 1
return first_true_index
2. Using Wrong Search Bounds
Another pitfall is setting the upper bound too low or not considering edge cases.
The Problem:
# May fail if max(quantities) > 10000 right = 10000 # Too small
The Fix: Use the actual maximum quantity as the upper bound:
right = max(quantities) # Exact bound
3. Inverted Boolean Logic in Feasible Function
A subtle pitfall is inverting the boolean logic, searching for False instead of True.
The Problem:
def feasible(max_products):
# Inverted: returns True when NOT feasible
return sum((q + max_products - 1) // max_products for q in quantities) > n
# This creates sequence: [True, True, ..., False, False]
# We want [False, False, ..., True, True]
The Fix:
Keep the logic straightforward - return True when the distribution is feasible:
def feasible(max_products):
return sum((q + max_products - 1) // max_products for q in quantities) <= n
4. Integer Overflow in Ceiling Division
While less common in Python, the ceiling division formula can be confusing.
The Problem:
# Incorrect ceiling division attempts stores_needed = v // x + (1 if v % x > 0 else 0) # Verbose stores_needed = math.ceil(v / x) # Requires import, potential float precision issues
The Fix: Use the integer-only formula that's both efficient and precise:
stores_needed = (v + x - 1) // x # Equivalent to ceil(v/x) using only integers
Which of the two traversal algorithms (BFS and DFS) can be used to find whether two nodes are connected?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!