1655. Distribute Repeating Integers
Problem Description
You have an array nums
containing n
integers, where there are at most 50 unique values in the array. You also have an array quantity
containing m
customer orders, where quantity[i]
represents how many integers the i
-th customer wants.
You need to distribute the integers from nums
to satisfy all customers with the following conditions:
- Each customer
i
must receive exactlyquantity[i]
integers - All integers given to a single customer must have the same value (for example, if customer 1 gets 3 integers, they must all be the same number like [5, 5, 5])
- Every customer must be satisfied with their allocation
The task is to determine if it's possible to distribute all integers from nums
according to these rules. Return true
if such a distribution exists, otherwise return false
.
For example, if nums = [1, 1, 2, 2, 2]
and quantity = [2, 3]
, you could give customer 0 two 1's and customer 1 three 2's, making it possible to satisfy all customers, so the answer would be true
.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- No: The problem doesn't involve nodes and edges or graph traversal. We're dealing with distributing integers from an array to customers.
Need to solve for kth smallest/largest?
- No: We're not looking for the kth smallest or largest element. We need to determine if a valid distribution exists.
Involves Linked Lists?
- No: The problem works with arrays (
nums
andquantity
), not linked lists.
Does the problem have small constraints?
- Yes: The problem explicitly states there are at most 50 unique values in
nums
, and looking at thequantity
array which represents customers, the constraint is typically small (≤10 customers). The solution uses bit manipulation with1 << m
wherem
is the length ofquantity
, indicating we're dealing with small constraint sizes that allow exploring all subsets.
Brute force / Backtracking?
- Yes: With small constraints, we can explore different ways to assign groups of identical numbers to customers. The solution needs to try different combinations of assigning available counts of each unique number to different subsets of customers, which is a classic backtracking scenario where we explore all possible distributions.
Conclusion: The flowchart correctly leads us to use a Brute force/Backtracking approach. While the actual solution implements this through dynamic programming with state compression (using bitmasks to represent subsets of customers), the core idea is still exploring all possible ways to distribute the available counts of unique numbers to satisfy customer demands - a backtracking pattern optimized through DP.
Intuition
The key insight is recognizing that we only care about the counts of each unique value in nums
, not the actual values themselves. If we have five 2's and three 7's, what matters is that we have a group of 5 identical numbers and a group of 3 identical numbers.
Since each customer wants all their integers to be the same value, we can think of this as: "Can we assign the available groups of identical numbers to satisfy all customers?"
With at most 50 unique values and typically around 10 customers (small constraints), we can explore different ways to distribute these groups. But naive backtracking would be inefficient because we'd repeatedly solve the same subproblems.
Here's where the breakthrough comes: We can represent which customers have been satisfied using a bitmask. If we have 3 customers, the bitmask 101
means customers 0 and 2 are satisfied. This allows us to use dynamic programming to remember: "Can we satisfy this specific subset of customers using the first i
groups of numbers?"
The state f[i][j]
answers: "Can we satisfy the customer subset j
using only the first i
unique numbers from our array?"
For each state, we try all possible ways to use the current group of identical numbers:
- We could use it to satisfy customer subset
k
(if the group is large enough) - The remaining customers (
j XOR k
) should be satisfiable using the previous groups
By trying all possible subsets k
of j
, we're essentially doing optimized backtracking - exploring all distributions but memorizing results to avoid redundant work.
The preprocessing step s[j]
that calculates the total demand for each customer subset helps us quickly check if a group of identical numbers is large enough to satisfy that subset.
Learn more about Dynamic Programming, Backtracking and Bitmask patterns.
Solution Approach
The solution uses state compression dynamic programming combined with subset enumeration to efficiently explore all possible distributions.
Step 1: Count frequencies and prepare data
- Count occurrences of each number in
nums
using a Counter:cnt = Counter(nums)
- Extract just the counts into array
arr
since actual values don't matter - Store
n = len(arr)
(number of unique values) andm = len(quantity)
(number of customers)
Step 2: Precompute subset sums
- Create array
s
wheres[mask]
represents the total quantity demanded by the customer subset represented bymask
- For each mask from 1 to
(1 << m) - 1
:- Find the first set bit
j
in the mask - Calculate
s[mask] = s[mask ^ (1 << j)] + quantity[j]
- Find the first set bit
- This allows O(1) lookup of total demand for any customer subset
Step 3: Initialize DP table
- Create 2D DP table
f[n][1 << m]
wheref[i][j]
represents whether we can satisfy customer subsetj
using only the firsti+1
unique numbers - Initialize
f[i][0] = True
for alli
(empty subset is always satisfiable)
Step 4: Fill DP table
For each unique number i
with count x = arr[i]
:
- For each customer subset
j
from 1 to(1 << m) - 1
:- Option 1: Don't use current number for subset
j
- If
i > 0
andf[i-1][j] = True
, thenf[i][j] = True
- If
- Option 2: Use current number for some subset
k
ofj
- Enumerate all subsets
k
ofj
usingk = (k - 1) & j
- Check if:
- Previous numbers can satisfy remaining customers:
f[i-1][j ^ k] = True
(orj == k
ifi == 0
) - Current number has enough count:
s[k] <= x
- Previous numbers can satisfy remaining customers:
- If both conditions met, set
f[i][j] = True
- Enumerate all subsets
- Option 1: Don't use current number for subset
Step 5: Return result
- Return
f[n-1][(1 << m) - 1]
, which indicates whether all customers can be satisfied using all available unique numbers
The subset enumeration technique k = (k - 1) & j
efficiently iterates through all subsets of j
in decreasing order, ensuring we check all possible ways to use the current group of identical numbers.
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 small example to illustrate the solution approach.
Example: nums = [1, 1, 2, 2, 2]
, quantity = [2, 3]
Step 1: Count frequencies and prepare data
- Count occurrences:
{1: 2, 2: 3}
- Extract counts:
arr = [2, 3]
(we have 2 ones and 3 twos) n = 2
(two unique values),m = 2
(two customers)
Step 2: Precompute subset sums Calculate total demand for each customer subset:
s[00] = 0
(no customers)s[01] = quantity[0] = 2
(customer 0 only)s[10] = quantity[1] = 3
(customer 1 only)s[11] = quantity[0] + quantity[1] = 5
(both customers)
Step 3: Initialize DP table
Create f[2][4]
table (2 unique numbers × 4 possible customer subsets):
f[0][00] = True (empty subset always satisfiable) f[1][00] = True
Step 4: Fill DP table
Processing first unique number (2 ones):
f[0][01]
: Can we satisfy customer 0 using 2 ones?- Check subset
01
: Need 2 items, have 2 ones ✓ - Set
f[0][01] = True
- Check subset
f[0][10]
: Can we satisfy customer 1 using 2 ones?- Check subset
10
: Need 3 items, have 2 ones ✗ - Set
f[0][10] = False
- Check subset
f[0][11]
: Can we satisfy both customers using 2 ones?- Check subset
01
: Need 2 items, have 2 ones ✓, but remaining subset10
needs 3 items - Check subset
10
: Need 3 items, have 2 ones ✗ - Check subset
11
: Need 5 items, have 2 ones ✗ - Set
f[0][11] = False
- Check subset
Processing second unique number (3 twos):
f[1][01]
: Can we satisfy customer 0 using first two groups?- Inherit from
f[0][01] = True
(already satisfied with ones) - Set
f[1][01] = True
- Inherit from
f[1][10]
: Can we satisfy customer 1 using first two groups?- Check if we can use 3 twos for customer 1: Need 3, have 3 ✓
- Previous groups must satisfy empty set:
f[0][00] = True
✓ - Set
f[1][10] = True
f[1][11]
: Can we satisfy both customers using first two groups?- Try using twos for subset
10
(customer 1):- Need 3 items, have 3 twos ✓
- Remaining subset
01
must be satisfied by ones:f[0][01] = True
✓
- Set
f[1][11] = True
- Try using twos for subset
Step 5: Return result
- Check
f[1][11] = True
, meaning we can satisfy all customers using all available numbers - Return
true
The solution correctly identifies that we can give customer 0 the two 1's and customer 1 the three 2's, satisfying everyone's requirements.
Solution Implementation
1class Solution:
2 def canDistribute(self, nums: List[int], quantity: List[int]) -> bool:
3 num_customers = len(quantity)
4
5 # Precompute sum of quantities for each subset of customers
6 # subset_sum[mask] = total quantity needed for customers in mask
7 subset_sum = [0] * (1 << num_customers)
8 for mask in range(1, 1 << num_customers):
9 # Find the first set bit and calculate sum incrementally
10 for customer_idx in range(num_customers):
11 if mask >> customer_idx & 1:
12 # Remove this customer from mask and add their quantity
13 subset_sum[mask] = subset_sum[mask ^ (1 << customer_idx)] + quantity[customer_idx]
14 break
15
16 # Count frequency of each number in nums
17 frequency_map = Counter(nums)
18 unique_values = list(frequency_map.values())
19 num_unique = len(unique_values)
20
21 # dp[i][mask] = True if we can satisfy customers in mask using first i+1 unique numbers
22 dp = [[False] * (1 << num_customers) for _ in range(num_unique)]
23
24 # Base case: empty set of customers can always be satisfied
25 for i in range(num_unique):
26 dp[i][0] = True
27
28 # Fill the DP table
29 for idx, count in enumerate(unique_values):
30 for customer_mask in range(1, 1 << num_customers):
31 # Case 1: If we could satisfy this mask without current number
32 if idx > 0 and dp[idx - 1][customer_mask]:
33 dp[idx][customer_mask] = True
34 continue
35
36 # Case 2: Try to use current number for some subset of customers
37 subset = customer_mask
38 while subset:
39 # Check if we can satisfy 'subset' with current number
40 # and remaining customers with previous numbers
41 can_use_previous = (customer_mask == subset if idx == 0
42 else dp[idx - 1][customer_mask ^ subset])
43 can_satisfy_subset = subset_sum[subset] <= count
44
45 if can_use_previous and can_satisfy_subset:
46 dp[idx][customer_mask] = True
47 break
48
49 # Move to next subset of customer_mask
50 subset = (subset - 1) & customer_mask
51
52 # Return whether we can satisfy all customers using all unique numbers
53 return dp[-1][-1]
54
1class Solution {
2 public boolean canDistribute(int[] nums, int[] quantity) {
3 int numCustomers = quantity.length;
4
5 // Precompute sum of quantities for each subset of customers
6 // subsetSum[mask] = sum of quantities for customers in the mask
7 int[] subsetSum = new int[1 << numCustomers];
8 for (int mask = 1; mask < (1 << numCustomers); ++mask) {
9 // Find the first set bit and calculate sum based on previous subset
10 for (int bit = 0; bit < numCustomers; ++bit) {
11 if ((mask >> bit & 1) != 0) {
12 // Remove current bit from mask and add current quantity
13 subsetSum[mask] = subsetSum[mask ^ (1 << bit)] + quantity[bit];
14 break;
15 }
16 }
17 }
18
19 // Count frequency of each number in nums array
20 Map<Integer, Integer> frequencyMap = new HashMap<>(50);
21 for (int num : nums) {
22 frequencyMap.merge(num, 1, Integer::sum);
23 }
24
25 // Convert frequency map values to array for easier access
26 int numDistinctValues = frequencyMap.size();
27 int[] frequencies = new int[numDistinctValues];
28 int index = 0;
29 for (int frequency : frequencyMap.values()) {
30 frequencies[index++] = frequency;
31 }
32
33 // dp[i][mask] = whether we can satisfy customers in mask using first i+1 distinct values
34 boolean[][] dp = new boolean[numDistinctValues][1 << numCustomers];
35
36 // Base case: empty mask can always be satisfied
37 for (int i = 0; i < numDistinctValues; ++i) {
38 dp[i][0] = true;
39 }
40
41 // Fill DP table
42 for (int i = 0; i < numDistinctValues; ++i) {
43 for (int mask = 1; mask < (1 << numCustomers); ++mask) {
44 // Option 1: Don't use current distinct value for this mask
45 // (inherit result from previous distinct value)
46 if (i > 0 && dp[i - 1][mask]) {
47 dp[i][mask] = true;
48 continue;
49 }
50
51 // Option 2: Use current distinct value for some subset of the mask
52 // Iterate through all non-empty subsets of current mask
53 for (int subset = mask; subset > 0; subset = (subset - 1) & mask) {
54 // Check if we can satisfy remaining customers with previous values
55 boolean canSatisfyRemaining = (i == 0) ? (mask == subset) : dp[i - 1][mask ^ subset];
56
57 // Check if current distinct value has enough frequency for this subset
58 boolean hasEnoughFrequency = subsetSum[subset] <= frequencies[i];
59
60 if (canSatisfyRemaining && hasEnoughFrequency) {
61 dp[i][mask] = true;
62 break;
63 }
64 }
65 }
66 }
67
68 // Return whether we can satisfy all customers using all distinct values
69 return dp[numDistinctValues - 1][(1 << numCustomers) - 1];
70 }
71}
72
1class Solution {
2public:
3 bool canDistribute(vector<int>& nums, vector<int>& quantity) {
4 int customerCount = quantity.size();
5
6 // Precompute sum of quantities for each subset of customers
7 // subsetSum[mask] = sum of quantities for customers in the mask
8 int subsetSum[1 << customerCount];
9 memset(subsetSum, 0, sizeof(subsetSum));
10
11 // Calculate sum for each subset using dynamic programming
12 for (int mask = 1; mask < (1 << customerCount); ++mask) {
13 for (int j = 0; j < customerCount; ++j) {
14 if (mask >> j & 1) { // If j-th customer is in the subset
15 // Remove j-th customer from mask and add their quantity
16 subsetSum[mask] = subsetSum[mask ^ (1 << j)] + quantity[j];
17 break;
18 }
19 }
20 }
21
22 // Count frequency of each value in nums
23 unordered_map<int, int> frequencyMap;
24 for (int& value : nums) {
25 ++frequencyMap[value];
26 }
27
28 // Convert frequency map to array for easier indexing
29 int uniqueValueCount = frequencyMap.size();
30 vector<int> frequencies;
31 for (auto& [value, freq] : frequencyMap) {
32 frequencies.push_back(freq);
33 }
34
35 // dp[i][mask] = true if we can satisfy customers in mask using first i+1 unique values
36 bool dp[uniqueValueCount][1 << customerCount];
37 memset(dp, 0, sizeof(dp));
38
39 // Base case: empty subset can always be satisfied
40 for (int i = 0; i < uniqueValueCount; ++i) {
41 dp[i][0] = true;
42 }
43
44 // Fill the DP table
45 for (int i = 0; i < uniqueValueCount; ++i) {
46 for (int mask = 1; mask < (1 << customerCount); ++mask) {
47 // Case 1: Inherit from previous unique value (don't use current value)
48 if (i > 0 && dp[i - 1][mask]) {
49 dp[i][mask] = true;
50 continue;
51 }
52
53 // Case 2: Try to use current unique value for some subset
54 // Iterate through all subsets of the current mask
55 for (int subset = mask; subset > 0; subset = (subset - 1) & mask) {
56 // Check if we can satisfy remaining customers with previous values
57 bool canSatisfyRemaining = (i == 0) ? (mask == subset) : dp[i - 1][mask ^ subset];
58
59 // Check if current value has enough frequency for this subset
60 bool hasEnoughFrequency = subsetSum[subset] <= frequencies[i];
61
62 if (canSatisfyRemaining && hasEnoughFrequency) {
63 dp[i][mask] = true;
64 break;
65 }
66 }
67 }
68 }
69
70 // Check if we can satisfy all customers using all unique values
71 return dp[uniqueValueCount - 1][(1 << customerCount) - 1];
72 }
73};
74
1/**
2 * Determines if we can distribute nums to satisfy all quantity requirements
3 * @param nums - Array of numbers to distribute
4 * @param quantity - Array of required quantities for each customer
5 * @returns true if distribution is possible, false otherwise
6 */
7function canDistribute(nums: number[], quantity: number[]): boolean {
8 const numCustomers: number = quantity.length;
9
10 // Precompute sum of quantities for each subset of customers
11 // subsetSums[mask] = total quantity needed for customers in subset represented by mask
12 const subsetSums: number[] = new Array(1 << numCustomers).fill(0);
13 for (let mask = 1; mask < (1 << numCustomers); ++mask) {
14 // Find the first set bit and calculate sum based on previous subset
15 for (let customerIdx = 0; customerIdx < numCustomers; ++customerIdx) {
16 if ((mask >> customerIdx) & 1) {
17 // Remove current customer from mask and add their quantity
18 subsetSums[mask] = subsetSums[mask ^ (1 << customerIdx)] + quantity[customerIdx];
19 break;
20 }
21 }
22 }
23
24 // Count frequency of each unique number in nums
25 const frequencyMap: Map<number, number> = new Map();
26 for (const num of nums) {
27 frequencyMap.set(num, (frequencyMap.get(num) || 0) + 1);
28 }
29
30 // Extract frequencies into an array for easier indexing
31 const numUniqueValues: number = frequencyMap.size;
32 const frequencies: number[] = [];
33 for (const [_, frequency] of frequencyMap) {
34 frequencies.push(frequency);
35 }
36
37 // DP table: canSatisfy[i][mask] = true if we can satisfy customers in mask
38 // using only the first i+1 unique values
39 const canSatisfy: boolean[][] = new Array(numUniqueValues)
40 .fill(false)
41 .map(() => new Array(1 << numCustomers).fill(false));
42
43 // Base case: empty set of customers can always be satisfied
44 for (let valueIdx = 0; valueIdx < numUniqueValues; ++valueIdx) {
45 canSatisfy[valueIdx][0] = true;
46 }
47
48 // Fill DP table
49 for (let valueIdx = 0; valueIdx < numUniqueValues; ++valueIdx) {
50 for (let customerMask = 0; customerMask < (1 << numCustomers); ++customerMask) {
51 // Option 1: Don't use current value, inherit from previous values
52 if (valueIdx > 0 && canSatisfy[valueIdx - 1][customerMask]) {
53 canSatisfy[valueIdx][customerMask] = true;
54 continue;
55 }
56
57 // Option 2: Use current value to satisfy some subset of remaining customers
58 // Iterate through all subsets of customerMask
59 for (let subset = customerMask; subset > 0; subset = (subset - 1) & customerMask) {
60 // Check if we can satisfy the remaining customers with previous values
61 const canSatisfyRemaining: boolean =
62 (valueIdx === 0 && customerMask === subset) ||
63 (valueIdx > 0 && canSatisfy[valueIdx - 1][customerMask ^ subset]);
64
65 // Check if current value has enough frequency for this subset
66 const hasEnoughFrequency: boolean = subsetSums[subset] <= frequencies[valueIdx];
67
68 if (canSatisfyRemaining && hasEnoughFrequency) {
69 canSatisfy[valueIdx][customerMask] = true;
70 break;
71 }
72 }
73 }
74 }
75
76 // Check if we can satisfy all customers using all unique values
77 return canSatisfy[numUniqueValues - 1][(1 << numCustomers) - 1];
78}
79
Time and Space Complexity
Time Complexity: O(n * 3^m)
The time complexity analysis breaks down as follows:
- Computing the sum array
s
takesO(2^m * m)
time, where we iterate through all2^m
subsets and for each subset, we may iterate throughm
positions to find the first set bit. - The main dynamic programming section has:
- Outer loop iterating through
n
different values (unique counts innums
) - Middle loop iterating through all
2^m
possible subsetsj
- Inner while loop that iterates through all subsets
k
ofj
- Outer loop iterating through
For the inner loop, the key insight is that for a fixed j
, the number of subsets k
of j
is 2^(number of 1s in j)
. Summing over all possible j
values:
- Each element in
quantity
can be in one of 3 states relative to subsetsj
andk
: not inj
, inj
but not ink
, or in bothj
andk
- This gives us
3^m
total iterations when summing across allj
and their subsetsk
Therefore, the overall time complexity is O(2^m * m + n * 3^m) = O(n * 3^m)
since 3^m
dominates 2^m * m
.
Space Complexity: O(n * 2^m)
The space complexity comes from:
- Array
s
storing sums for all2^m
subsets:O(2^m)
- 2D DP array
f
with dimensionsn × 2^m
:O(n * 2^m)
- Counter and array storage:
O(n)
The dominant term is O(n * 2^m)
from the DP table.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Inefficient Subset Enumeration Leading to TLE
A common mistake is using inefficient methods to enumerate subsets, such as generating all possible subsets and checking if each is a subset of the target mask. This approach has exponential time complexity that becomes prohibitive even for moderate input sizes.
Incorrect approach:
# Inefficient: Checking all 2^m subsets for each mask
for subset in range(1 << num_customers):
if (subset & customer_mask) == subset: # Check if subset of customer_mask
# Process subset
Correct approach:
# Efficient: Only enumerate actual subsets of customer_mask subset = customer_mask while subset: # Process subset subset = (subset - 1) & customer_mask
The key insight is that (subset - 1) & customer_mask
generates the next smaller subset directly, visiting only actual subsets rather than all possible combinations.
2. Incorrect DP State Initialization
Another pitfall is forgetting to properly handle the base case when idx == 0
. When processing the first unique number, there are no "previous numbers" to rely on, so the condition for using this number differs from subsequent iterations.
Incorrect initialization:
# Wrong: Always checking dp[idx-1] even when idx == 0 if dp[idx - 1][customer_mask ^ subset] and subset_sum[subset] <= count: dp[idx][customer_mask] = True
Correct initialization:
# Correct: Special handling for first unique number can_use_previous = (customer_mask == subset if idx == 0 else dp[idx - 1][customer_mask ^ subset]) if can_use_previous and subset_sum[subset] <= count: dp[idx][customer_mask] = True
When idx == 0
, we can only satisfy customer_mask
if we use all of it with the current number (hence customer_mask == subset
), since there are no previous numbers available.
3. Memory Optimization Oversight
While not necessarily incorrect, failing to optimize memory usage can be problematic for larger inputs. Since each DP state only depends on the previous row, we can use rolling arrays to reduce space complexity from O(n × 2^m) to O(2^m).
Memory-optimized version:
# Use two 1D arrays instead of 2D array
prev = [False] * (1 << num_customers)
curr = [False] * (1 << num_customers)
prev[0] = True
for count in unique_values:
curr[0] = True
for customer_mask in range(1, 1 << num_customers):
curr[customer_mask] = prev[customer_mask] # Don't use current number
subset = customer_mask
while subset and not curr[customer_mask]:
if prev[customer_mask ^ subset] and subset_sum[subset] <= count:
curr[customer_mask] = True
subset = (subset - 1) & customer_mask
prev, curr = curr, prev
return prev[-1]
Which technique can we use to find the middle of a linked list?
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Backtracking Template Prereq DFS with States problems dfs_with_states Combinatorial search problems Combinatorial search problems involve finding groupings and assignments of objects that satisfy certain conditions Finding all permutations combinations subsets and solving Sudoku are classic combinatorial problems The time complexity of combinatorial problems often grows rapidly with the size of
Bitmask and Dynamic Programming Bit manipulation is a crucial aspect of computer programming and one of the most powerful tools for bit manipulation is bitmasks Let's first understand what a bit is A bit is a binary digit It's the smallest piece of data in a computer and can be
Want a Structured Path to Master System Design Too? Don’t Miss This!