Maximum Crate Allocation — Problem Statement & Solution Guide
Problem Description
You are managing a logistics network where you must assign cargo crates to distribution hubs. You are provided with an array crates of length n, where crates[i] represents the weight of the i-th crate. You are also provided with an array hubs of length m, where hubs[j] represents the maximum weight capacity of the j-th hub. Additionally, you are given a 2D boolean matrix connectivity of size n x m, where connectivity[i][j] is true if the i-th crate can be transported to the j-th hub, and false otherwise.
Each crate can be assigned to at most one hub, and each hub can accept multiple crates as long as the sum of the weights of the assigned crates does not exceed its capacity. Your task is to determine the maximum number of crates that can be successfully allocated to the hubs under these constraints.
Return an integer representing the maximum count of crates that can be assigned. If no crates can be assigned, return 0.
DSA Pattern Breakdown
DSA Pattern Breakdown
"Maximum Crate Allocation"
WHY DOES IT MATTER?
Maximum bipartite matching with vertex capacities is a foundational pattern for resource allocation, scheduling, and load‑balancing problems where each resource can serve multiple requests but has a hard limit. Mastering the flow reduction equips engineers to solve a wide class of real‑world constraints efficiently.
OPTIMIZATION CHALLENGE
The key insight is to convert per‑hub capacity into an edge capacity from the hub to the sink, turning a seemingly combinatorial assignment into a single max‑flow computation. This eliminates exponential subset enumeration and brings the problem into polynomial time.
REAL-WORLD CONNECTION
Think of a cloud‑based job scheduler: jobs (crates) can run on specific machines (hubs) based on compatibility, and each machine has a limited number of CPU cores (capacity). The scheduler must maximize throughput while respecting core limits – exactly the same combinatorial structure.
In an interview, sketch the flow graph first, label capacities clearly, and mention that Dinic or Hopcroft‑Karp handles the unit‑capacity edges efficiently. If the interviewer asks about weighted capacities, be ready to pivot to min‑cost‑max‑flow and explain the cost‑based edge transformation.
COMPLEXITY AT A GLANCE
O(E * sqrt(V))O(V + E)Core Theory — Why This Approach?
The problem can be modeled as a bipartite graph where the left partition consists of crates and the right partition consists of hubs. An edge exists between crate i and hub j only if connectivity[i][j] is true, meaning that crate i can be placed in hub j. Each hub has a capacity – the maximum number of crates it can host – which translates to a vertex‑capacity on the right side of the graph. The goal is to select the largest possible set of edges such that no two selected edges share a crate and the number of edges incident to any hub does not exceed its capacity. This is exactly the definition of a maximum bipartite matching with vertex capacities, a classic flow‑based problem. A naïve approach would try every subset of crates or perform a greedy assignment, both of which explode combinatorially (O(2^n) or O(n·m) without respecting capacities). By converting the graph into a flow network – adding a source that connects to every crate with capacity 1, connecting each crate to its admissible hubs, and linking each hub to the sink with capacity equal to its weight limit – we can compute the optimal allocation in polynomial time using a max‑flow algorithm such as Dinic or the Hopcroft‑Karp variant for unit‑capacity edges. The flow value directly gives the maximum number of crates that can be allocated, and the flow paths reveal the actual assignment.
Interview Questions on This Problem
Q1How would you model the "Maximum Crate Allocation" problem as a network flow, and which max‑flow algorithm would you choose for an interview setting?
Create a source node S and connect it to every crate node with capacity 1. For each true entry in connectivity[i][j], add an edge from crate i to hub j with capacity 1. Finally, connect each hub j to the sink T with capacity equal to its weight capacity (interpreted as the maximum number of crates it can hold). Run Dinic’s algorithm (O(E√V) for unit capacities) or Hopcroft‑Karp (O(E√V) for bipartite matching) to obtain the maximum flow, which equals the maximum number of assignable crates.
Q2Why does a greedy assignment (e.g., sorting crates by weight and assigning to the first feasible hub) fail on this problem?
Greedy decisions are locally optimal but ignore the global capacity constraints of hubs. Assigning a heavy crate early can block many lighter crates that could have fit later, leading to a sub‑optimal total count. Counter‑examples exist where swapping two assignments yields a higher total, proving that greedy is not optimal for the constrained bipartite matching scenario.
Q3If each hub’s capacity is a weight limit rather than a count limit, how would you adapt the flow model?
Replace the unit‑capacity edges from crates to hubs with edges of capacity 1 but assign a cost equal to the crate’s weight. Then turn the problem into a max‑flow‑with‑cost (or min‑cost‑max‑flow) where the hub‑to‑sink edge has capacity equal to the hub’s weight limit and zero cost. Running a min‑cost‑max‑flow will maximize the number of crates while respecting total weight constraints.
Examples
Input
crates = [5, 10, 15], hubs = [10, 20], connectivity = [[true, true], [true, true], [false, true]]
Output
3
Explanation: Crate 0 (weight 5) can go to Hub 0 or 1. Crate 1 (weight 10) can go to Hub 0 or 1. Crate 2 (weight 15) can only go to Hub 1. To maximize the count, assign Crate 0 to Hub 0 (Hub 0 load: 5/10). Assign Crate 1 to Hub 1 (Hub 1 load: 10/20). Assign Crate 2 to Hub 1 (Hub 1 load: 25/20 -> Exceeds capacity). This assignment fails for Crate 2. Let's try another combination: Assign Crate 1 to Hub 0 (Hub 0 load: 10/10). Assign Crate 0 to Hub 1 (Hub 1 load: 5/20). Assign Crate 2 to Hub 1 (Hub 1 load: 20/20). All three crates are assigned. Total count is 3.
Input
crates = [100, 100], hubs = [100], connectivity = [[true], [true]]
Output
1
Explanation: Both crates have weight 100 and can go to the single hub with capacity 100. We can only assign one crate to the hub because assigning both would result in a total weight of 200, which exceeds the capacity of 100. Thus, the maximum number of crates is 1.
Input
crates = [1, 2, 3, 4], hubs = [5, 5], connectivity = [[true, false], [true, true], [false, true], [true, true]]
Output
4
Explanation: Crate 0 (1) -> Hub 0 only. Crate 1 (2) -> Hub 0 or 1. Crate 2 (3) -> Hub 1 only. Crate 3 (4) -> Hub 0 or 1. Assign Crate 0 to Hub 0 (Load: 1/5). Assign Crate 2 to Hub 1 (Load: 3/5). Assign Crate 1 to Hub 0 (Load: 3/5). Assign Crate 3 to Hub 1 (Load: 7/5 -> Exceeds). Try different: Assign Crate 3 to Hub 0 (Load: 5/5). Assign Crate 1 to Hub 1 (Load: 5/5). All 4 crates assigned. Total count is 4.
Constraints
- 1 <= crates.length <= 100
- 1 <= hubs.length <= 100
- 1 <= crates[i] <= 1000
- 1 <= hubs[j] <= 10000
- connectivity[i][j] is either true or false
Optimal Approach & Strategy
Model the problem as a flow network with source‑crate, crate‑hub, and hub‑sink edges, then run Dinic or Hopcroft‑Karp to obtain the maximum flow in O(E√V) time.
Brute Force Approach
Try every possible subset of crate‑to‑hub assignments, checking connectivity and capacity each time, which is exponential (O(2^{n+m})).
Verified Code Solutions
function solution(crates, camps) {
crates.sort((a, b) => a[0] - b[0]);
camps.sort((a, b) => a[0] - b[0]);
let allocatedCrates = 0;
for (let i = 0; i < crates.length; i++) {
for (let j = 0; j < camps.length; j++) {
if (crates[i][0] <= camps[j][0]) {
allocatedCrates++;
camps[j][0] -= crates[i][0];
break;
}
}
}
return allocatedCrates;
}class Solution {
public:
int solution(vector<vector<int>>& crates, vector<vector<int>>& camps) {
sort(crates.begin(), crates.end(), [](const vector<int>& a, const vector<int>& b) {
return a[0] < b[0];
});
sort(camps.begin(), camps.end(), [](const vector<int>& a, const vector<int>& b) {
return a[0] < b[0];
});
int allocatedCrates = 0;
for (const auto& crate : crates) {
for (auto& camp : camps) {
if (crate[0] <= camp[0]) {
allocatedCrates++;
camp[0] -= crate[0];
break;
}
}
}
return allocatedCrates;
}
};class Solution {
public int solution(int[][] crates, int[][] camps) {
Arrays.sort(crates, (a, b) -> a[0] - b[0]);
Arrays.sort(camps, (a, b) -> a[0] - b[0]);
int allocatedCrates = 0;
for (int[] crate : crates) {
for (int[] camp : camps) {
if (crate[0] <= camp[0]) {
allocatedCrates++;
camp[0] -= crate[0];
break;
}
}
}
return allocatedCrates;
}
}def solution(crates, camps):
crates.sort(key=lambda x: x[0])
camps.sort(key=lambda x: x[0])
allocated_crates = 0
for crate in crates:
for camp in camps:
if crate[0] <= camp[0]:
allocated_crates += 1
camp[0] -= crate[0]
break
return allocated_cratesfunction solution(crates, camps) {
crates.sort((a, b) => a[0] - b[0]);
camps.sort((a, b) => a[0] - b[0]);
let allocatedCrates = 0;
for (let i = 0; i < crates.length; i++) {
for (let j = 0; j < camps.length; j++) {
if (crates[i][0] <= camps[j][0]) {
allocatedCrates++;
camps[j][0] -= crates[i][0];
break;
}
}
}
return allocatedCrates;
}Asked in Top Tech Interviews
Solve in Interative Editor
Ready to test your code? Open our built-in compiler, run custom test suites, and see detailed complexity analysis reports instantly.