LeetCode 3647 - Maximum Weight in Two Bags
This problem asks us to distribute a set of items, each with a given weight, into two separate bags with fixed capacity limits, in order to maximize the total weight placed across both bags.
Difficulty: 🟡 Medium
Topics: Array, Dynamic Programming
Solution
Problem Understanding
This problem asks us to distribute a set of items, each with a given weight, into two separate bags with fixed capacity limits, in order to maximize the total weight placed across both bags.
Each item in the input array weights can be used at most once, and for each item we must decide one of three possible actions: place it in bag 1, place it in bag 2, or skip it entirely. Bag 1 can hold items whose total weight does not exceed w1, and bag 2 has an independent capacity limit w2. The goal is to maximize the sum of weights successfully placed in either bag.
The output is a single integer representing the maximum achievable total packed weight across both bags.
The constraints indicate that the number of items is at most 100, and both capacities are at most 300. This strongly suggests that a dynamic programming solution with a state depending on both capacities is feasible, since a cubic bound around 100 × 300 × 300 is acceptable.
Important edge cases include situations where no items can fit into either bag, cases where all items fit into one bag but not both, and cases where greedy placement would fail due to capacity tradeoffs between the two bags. Another key observation is that item order does not matter, only assignment decisions do.
Approaches
The brute-force approach considers every possible assignment of each item into one of three states: bag 1, bag 2, or unused. This leads to 3^n possibilities, which becomes infeasible for n up to 100. While correct in theory, it is computationally impossible due to exponential growth.
The optimal solution uses dynamic programming with a two-dimensional state representing remaining or used capacity in both bags. The key insight is that each decision affects two independent capacity dimensions, so we track the best achievable weight for every possible pair of capacities. Each item transitions the DP state by either skipping it or placing it in one of the two bags if capacity allows.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(3^n) | O(n) | Try all assignments of each item into bag1, bag2, or skip |
| Optimal DP | O(n · w1 · w2) | O(w1 · w2) | 2D knapsack-style DP over both bag capacities |
Algorithm Walkthrough
The optimal solution is based on a two-dimensional knapsack dynamic programming formulation, where each dimension corresponds to one bag’s remaining capacity.
- We define a DP table
dp[i][j]as the maximum total weight achievable using any subset of processed items such that bag 1 has used capacityiand bag 2 has used capacityj. This formulation allows us to capture all valid assignment combinations. - We initialize the DP table with all zeros because initially, with no items considered, the maximum weight achievable is zero regardless of capacity usage.
- We iterate through each item in the
weightsarray. For each item with weightw, we consider how it can be assigned into the current DP state. - For each item, we update the DP table in reverse order of capacities, iterating
ifromw1down to0andjfromw2down to0. This reverse traversal ensures that each item is only used once, preventing overwriting states that are needed for correct transitions. - For each capacity pair
(i, j), we consider three possibilities: we skip the item and retain the current best value, we place the item into bag 1 ifi >= w, or we place it into bag 2 ifj >= w. We update the state with the maximum of these valid options. - After processing all items, the answer is the maximum value present in the DP table, since any valid distribution of items across both bags could correspond to any state.
Why it works
This algorithm works because it systematically explores all valid subsets of items while respecting capacity constraints, and it ensures optimal substructure by building solutions for larger item sets from previously computed smaller subsets. The reverse iteration guarantees that each item is considered exactly once per state transition, preserving correctness of the knapsack formulation.
Python Solution
from typing import List
class Solution:
def maxWeight(self, weights: List[int], w1: int, w2: int) -> int:
dp = [[0] * (w2 + 1) for _ in range(w1 + 1)]
for w in weights:
for i in range(w1, -1, -1):
for j in range(w2, -1, -1):
best = dp[i][j]
if i >= w:
best = max(best, dp[i - w][j] + w)
if j >= w:
best = max(best, dp[i][j - w] + w)
dp[i][j] = best
return max(max(row) for row in dp)
The implementation builds a 2D DP table where each entry represents the best achievable total weight for given remaining capacities of the two bags. For each item, we update the table in reverse order to ensure we do not reuse the same item multiple times in a single iteration. The transitions explicitly consider placing the item in either bag or skipping it, ensuring all valid configurations are explored.
Go Solution
func maxWeight(weights []int, w1 int, w2 int) int {
dp := make([][]int, w1+1)
for i := range dp {
dp[i] = make([]int, w2+1)
}
for _, w := range weights {
for i := w1; i >= 0; i-- {
for j := w2; j >= 0; j-- {
best := dp[i][j]
if i >= w {
if dp[i-w][j]+w > best {
best = dp[i-w][j] + w
}
}
if j >= w {
if dp[i][j-w]+w > best {
best = dp[i][j-w] + w
}
}
dp[i][j] = best
}
}
}
ans := 0
for i := 0; i <= w1; i++ {
for j := 0; j <= w2; j++ {
if dp[i][j] > ans {
ans = dp[i][j]
}
}
}
return ans
}
The Go implementation mirrors the Python logic using slices for the 2D DP table. Since Go does not have built-in max functions for integers, comparisons are done explicitly. The structure of the algorithm remains identical, including reverse iteration to ensure correctness of state transitions.
Worked Examples
Example 1
Input: weights = [1,4,3,2], w1 = 5, w2 = 4
We begin with a DP table initialized to zero. We process items one by one.
After processing weight 1, we can place it in either bag, so dp reflects states where either bag has capacity used as 1.
After processing weight 4, we can place it in bag 2 or bag 1 depending on remaining capacity.
After processing weight 3 and 2, the best configuration becomes placing 3 and 2 into bag 1 (total 5), and placing 4 into bag 2.
Final DP maximum value is 9.
Example 2
Input: weights = [3,6,4,8], w1 = 9, w2 = 7
Processing 8 first allows it to go into bag 1. Then 3 and 4 combine into bag 2, reaching 7. The 6 cannot improve the optimal configuration without violating constraints.
Final DP maximum value is 15.
Example 3
Input: weights = [5,7], w1 = 2, w2 = 3
Neither item fits into either bag. DP remains zero throughout all transitions, so result is 0.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n · w1 · w2) | Each item updates a full 2D DP grid |
| Space | O(w1 · w2) | DP table storing best values for each capacity pair |
The complexity arises because for each item we iterate through every possible capacity combination of both bags. This is necessary because each item influences both dimensions independently.
Test Cases
assert Solution().maxWeight([1,4,3,2], 5, 4) == 9 # standard optimal split
assert Solution().maxWeight([3,6,4,8], 9, 7) == 15 # mixed distribution
assert Solution().maxWeight([5,7], 2, 3) == 0 # no item fits
assert Solution().maxWeight([1], 1, 1) == 1 # single item fits either bag
assert Solution().maxWeight([2,2,2], 4, 2) == 4 # split across bags
assert Solution().maxWeight([10,1,1], 10, 1) == 11 # heavy item + small item
assert Solution().maxWeight([3,3,3], 3, 3) == 6 # balanced packing
| Test | Why |
|---|---|
| [1,4,3,2], 5, 4 | verifies optimal split across both bags |
| [3,6,4,8], 9, 7 | tests interaction between large and medium items |
| [5,7], 2, 3 | verifies all items rejected case |
| [1], 1, 1 | single element assignment correctness |
| [2,2,2], 4, 2 | checks distribution across constrained capacities |
| [10,1,1], 10, 1 | ensures greedy failure avoidance |
| [3,3,3], 3, 3 | symmetry and balanced packing |
Edge Cases
One important edge case occurs when no items fit into either bag. In this situation, the DP table remains at its initialized zero values, and the algorithm correctly returns zero without any special handling.
Another edge case is when all items individually fit into only one of the bags but not both combined in an optimal way. The DP formulation correctly handles this because it explicitly considers both placement choices for each item, allowing redistribution between bags rather than forcing greedy assignment.
A further edge case arises when item sizes are large relative to capacities, especially when only one item can fit into a bag. The reverse iteration ensures that we do not reuse items and that each item is considered exactly once per state transition, preserving correctness even in tightly constrained scenarios.