Put Marbles in Bags
HardPrompt
You have k bags. You are given a 0-indexed integer array weights where weights[i] is the weight of the ith marble. You are also given the integer k.
Divide the marbles into the k bags according to the following rules:
- No bag is empty.
- If the
ithmarble andjthmarble are in a bag, then all marbles with an index between theithandjthindices should also be in that same bag. - If a bag consists of all the marbles with an index from
itojinclusively, then the cost of the bag isweights[i] + weights[j].
The score after distributing the marbles is the sum of the costs of all the k bags.
Return the difference between the maximum and minimum scores among marble distributions.
Example 1:
Input: weights = [1,3,5,1], k = 2
Output: 4
Explanation:
The distribution [1],[3,5,1] results in the minimal score of (1+1) + (3+1) = 6.
The distribution [1,3],[5,1], results in the maximal score of (1+3) + (5+1) = 10.
Thus, we return their difference 10 - 6 = 4.Example 2:
Input: weights = [1, 3], k = 2
Output: 0
Explanation: The only distribution possible is [1],[3].
Since both the maximal and minimal score are the same, we return 0.
Constraints:
1 <= k <= weights.length <= 1051 <= weights[i] <= 109
Approaches
2 approaches with complexity analysis and trade-offs.
This approach leverages a key insight: the total score is determined by the k-1 locations where the marbles are divided. Each division point, or 'cut', between adjacent marbles weights[i] and weights[i+1] contributes weights[i] + weights[i+1] to the total score. The base score, weights[0] + weights[n-1], is constant for any partition. Therefore, to find the maximum and minimum scores, we only need to find the k-1 largest and k-1 smallest partition costs. This can be easily done by calculating all n-1 possible partition costs and sorting them.
Algorithm
- If
kis 1 orn, return 0 as there's only one possible distribution. - Create an array
partitionCostsof sizen-1. - Iterate from
i = 0ton-2, calculatingpartitionCosts[i] = weights[i] + weights[i+1]. - Sort the
partitionCostsarray. - Initialize
minSumandmaxSumto 0. - Iterate from
i = 0tok-2:- Add
partitionCosts[i]tominSum. - Add
partitionCosts[n-2-i]tomaxSum.
- Add
- Return
maxSum - minSum.
Walkthrough
The algorithm proceeds as follows:
- First, we recognize that to divide
nmarbles intokbags, we must makek-1cuts. A cut betweenweights[i]andweights[i+1]separates the marbles into two bags, whereweights[i]is the last element of one bag andweights[i+1]is the first element of the next. The cost formulaweights[start] + weights[end]for a bag means these two weights are added to the total score. - The total score can be expressed as
weights[0] + weights[n-1] + sum_of_costs_of_k-1_cuts. - To find the difference between the maximum and minimum scores, the constant term
weights[0] + weights[n-1]cancels out. The problem reduces to finding(sum of k-1 largest cut costs) - (sum of k-1 smallest cut costs). - We create an array,
partitionCosts, of sizen-1to store all possible cut costs, wherepartitionCosts[i] = weights[i] + weights[i+1]. - We sort this
partitionCostsarray. - The sum of the
k-1smallest costs is the sum of the firstk-1elements of the sorted array. - The sum of the
k-1largest costs is the sum of the lastk-1elements of the sorted array. - The final result is the difference between these two sums.
import java.util.Arrays; class Solution { public long putMarbles(int[] weights, int k) { int n = weights.length; if (k == 1 || k == n) { return 0; } // There will be k-1 partitions. // The cost of a partition at index i is weights[i] + weights[i+1]. long[] partitionCosts = new long[n - 1]; for (int i = 0; i < n - 1; i++) { partitionCosts[i] = (long)weights[i] + weights[i+1]; } // Sort the partition costs to easily find the smallest and largest k-1 costs. Arrays.sort(partitionCosts); long minScoreContribution = 0; long maxScoreContribution = 0; // Sum the k-1 smallest and k-1 largest partition costs. for (int i = 0; i < k - 1; i++) { minScoreContribution += partitionCosts[i]; maxScoreContribution += partitionCosts[n - 2 - i]; } return maxScoreContribution - minScoreContribution; }}Complexity
Time
O(n log n), where `n` is the number of marbles. Creating the `partitionCosts` array takes `O(n)` time, sorting it takes `O(n log n)`, and summing the required elements takes `O(k)`. The dominant factor is sorting.
Space
O(n) to store the `partitionCosts` array. If in-place sorting is used, this is the main space overhead besides the input.
Trade-offs
Pros
Conceptually simple and easy to implement.
Correctly solves the problem by identifying the core structure.
Efficient enough to pass within the given constraints.
Cons
The time complexity is dominated by sorting the entire
partitionCostsarray, which is not strictly necessary.Uses
O(n)extra space, which can be improved ifkis much smaller thann.
This approach improves upon the sorting method. Instead of sorting all n-1 partition costs, we only need to identify the k-1 smallest and k-1 largest costs. This is a classic 'Top K' problem, which can be solved efficiently using heaps (Priority Queues). We can find the k-1 smallest costs using a Max-Heap of size k-1 and the k-1 largest costs using a Min-Heap of size k-1. This avoids the O(n log n) sorting cost, leading to a better time complexity.
Algorithm
- If
kis 1 orn, return 0. - Initialize a Max-Heap
maxHeapForSmallestand a Min-HeapminHeapForLargest. - Iterate from
i = 0ton-2:- Calculate
cost = weights[i] + weights[i+1]. - Add
costtomaxHeapForSmallest. If its size exceedsk-1, remove the maximum element. - Add
costtominHeapForLargest. If its size exceedsk-1, remove the minimum element.
- Calculate
- Sum all elements from
maxHeapForSmallestto getminSum. - Sum all elements from
minHeapForLargestto getmaxSum. - Return
maxSum - minSum.
Walkthrough
The core idea remains the same: find the difference between the sum of the k-1 largest and k-1 smallest partition costs. The optimization lies in how we find these sums.
- We iterate through all
n-1possible partition costs,weights[i] + weights[i+1], in a single pass. - To find the
k-1smallest costs, we use a Max-Heap (a priority queue that keeps the largest element at the top). We add each partition cost to this heap. If the heap's size grows larger thank-1, we remove the largest element (poll()). After iterating through alln-1costs, this heap will contain thek-1smallest costs. - Similarly, to find the
k-1largest costs, we use a Min-Heap. We add each cost and if the size exceedsk-1, we remove the smallest element. This leaves us with thek-1largest costs. - Finally, we sum the elements in both heaps and return the difference of the sums. This method is more efficient in both time and space when
kis significantly smaller thann.
import java.util.PriorityQueue;import java.util.Collections; class Solution { public long putMarbles(int[] weights, int k) { int n = weights.length; if (k == 1 || k == n) { return 0; } // A max-heap to find the k-1 smallest costs. PriorityQueue<Long> maxHeapForSmallest = new PriorityQueue<>(Collections.reverseOrder()); // A min-heap to find the k-1 largest costs. PriorityQueue<Long> minHeapForLargest = new PriorityQueue<>(); for (int i = 0; i < n - 1; i++) { long cost = (long)weights[i] + weights[i+1]; // Maintain k-1 smallest costs in the max-heap maxHeapForSmallest.offer(cost); if (maxHeapForSmallest.size() > k - 1) { maxHeapForSmallest.poll(); } // Maintain k-1 largest costs in the min-heap minHeapForLargest.offer(cost); if (minHeapForLargest.size() > k - 1) { minHeapForLargest.poll(); } } long minSum = 0; while (!maxHeapForSmallest.isEmpty()) { minSum += maxHeapForSmallest.poll(); } long maxSum = 0; while (!minHeapForLargest.isEmpty()) { maxSum += minHeapForLargest.poll(); } return maxSum - minSum; }}Complexity
Time
O(n log k), where `n` is the number of marbles and `k` is the number of bags. We iterate through `n-1` partition costs, and for each, we perform two heap operations. Each heap operation takes `O(log k)` time as the heaps' sizes are capped at `k-1`.
Space
O(k) to store the elements in the two heaps. Each heap stores at most `k-1` elements.
Trade-offs
Pros
More efficient time complexity
O(n log k)compared toO(n log n).More efficient space complexity
O(k)compared toO(n).Ideal for scenarios where
kis much smaller thann.
Cons
Slightly more complex to implement than the sorting approach.
The constant factors involved might make it slightly slower than sorting for small
nor whenkis close ton.
Solutions
Solution
class Solution {public long putMarbles(int[] weights, int k) { int n = weights.length; int[] arr = new int[n - 1]; for (int i = 0; i < n - 1; ++i) { arr[i] = weights[i] + weights[i + 1]; } Arrays.sort(arr); long ans = 0; for (int i = 0; i < k - 1; ++i) { ans -= arr[i]; ans += arr[n - 2 - i]; } return ans; }}class Solution {public: long long putMarbles(vector<int> &weights, int k) { int n = weights.size(); vector<int> arr(n - 1); for (int i = 0; i < n - 1; ++i) { arr[i] = weights[i] + weights[i + 1]; } sort(arr.begin(), arr.end()); long long ans = 0; for (int i = 0; i < k - 1; ++i) { ans -= arr[i]; ans += arr[n - 2 - i]; } return ans; }};class Solution: def putMarbles(self, weights: List[int], k: int) -> int: arr = sorted(a + b for a, b in pairwise(weights)) return sum(arr[len(arr) - k + 1:]) - sum(arr[: k - 1])Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.