Maximum Coins From K Consecutive Bags
MedPrompt
There are an infinite amount of bags on a number line, one bag for each coordinate. Some of these bags contain coins.
You are given a 2D array coins, where coins[i] = [li, ri, ci] denotes that every bag from li to ri contains ci coins.
The segments that coins contain are non-overlapping.
You are also given an integer k.
Return the maximum amount of coins you can obtain by collecting k consecutive bags.
Example 1:
Input: coins = [[8,10,1],[1,3,2],[5,6,4]], k = 4
Output: 10
Explanation:
Selecting bags at positions [3, 4, 5, 6] gives the maximum number of coins: 2 + 0 + 4 + 4 = 10.
Example 2:
Input: coins = [[1,10,3]], k = 2
Output: 6
Explanation:
Selecting bags at positions [1, 2] gives the maximum number of coins: 3 + 3 = 6.
Constraints:
1 <= coins.length <= 1051 <= k <= 109coins[i] == [li, ri, ci]1 <= li <= ri <= 1091 <= ci <= 1000- The given segments are non-overlapping.
Approaches
2 approaches with complexity analysis and trade-offs.
A straightforward but inefficient approach is to identify a set of potential optimal starting positions for the k consecutive bags and then calculate the sum for each. The key insight is that the maximum sum is likely achieved when the window of k bags aligns with the start or end of one of the coin intervals. This leads to a set of candidate start positions. For each candidate, we can perform a full calculation of the coins within that window by checking against all given coin intervals.
Algorithm
- Identify Candidate Start Positions: The total number of coins in a window of
kbags changes only when the window's start or end crosses the boundary of a coin interval. This suggests that the optimal window likely starts at a position related to these boundaries. A reasonable set of candidate start positions for the window[s, s+k-1]ares = l_iors = r_i - k + 1for every given interval[l_i, r_i, c_i]. - Iterate and Calculate: The algorithm iterates through each of these
O(N)candidate start positions. - Summation: For each candidate start position
s, it calculates the total coins in the window[s, s+k-1]. This is done by iterating through allNcoin intervals and summing up the contributions from each interval that overlaps with the window. - Overlap Calculation: The contribution of an interval
[l_j, r_j, c_j]isc_jmultiplied by the length of the intersection between[s, s+k-1]and[l_j, r_j]. - Track Maximum: The algorithm keeps track of the maximum sum found across all candidate windows and returns it as the result.
Walkthrough
This method is based on a brute-force check over a reduced search space. Instead of checking every possible start position on the number line (which is infinite), we only check positions that are 'critical'.
-
Generate Candidate Start Positions: We create a list of candidate start positions. For each interval
[l_i, r_i, c_i], the critical start points for a window of sizekarel_i(aligning the window's start with the interval's start) andr_i - k + 1(aligning the window's end with the interval's end). We collect all such unique, positive positions. -
Calculate Sum for Each Candidate: For each candidate start position
s, we calculate the total coins in the window[s, s + k - 1]. This is done by iterating through allNoriginal coin intervals[l_j, r_j, c_j]. For each interval, we find the length of its overlap with our window[s, s + k - 1]. The overlap length ismax(0, min(s + k - 1, r_j) - max(s, l_j) + 1). We multiply this length byc_jand add it to the total for the current window. -
Find Maximum: We maintain a variable
max_coinsand update it with the highest total found among all candidate windows.
import java.util.HashSet;import java.util.Set; class Solution { public long maximumCoins(int[][] coins, int k) { Set<Long> candidates = new HashSet<>(); for (int[] coin : coins) { long l = coin[0]; long r = coin[1]; candidates.add(l); if (r - k + 1 > 0) { candidates.add(r - k + 1); } } long maxTotalCoins = 0; for (long startPos : candidates) { long endPos = startPos + k - 1; long currentTotalCoins = 0; for (int[] coin : coins) { long l = coin[0]; long r = coin[1]; long c = coin[2]; long overlapStart = Math.max(startPos, l); long overlapEnd = Math.min(endPos, r); if (overlapStart <= overlapEnd) { long overlapLength = overlapEnd - overlapStart + 1; currentTotalCoins += overlapLength * c; } } maxTotalCoins = Math.max(maxTotalCoins, currentTotalCoins); } return maxTotalCoins; }}Complexity
Time
O(N^2), where N is the number of coin intervals. There are up to 2N candidate start positions. For each candidate, we iterate through all N intervals to calculate the sum, leading to a quadratic time complexity.
Space
O(N), where N is the number of coin intervals. This space is used to store the set of candidate start positions.
Trade-offs
Pros
Relatively simple to understand and implement.
Correctly identifies a smaller, finite set of candidate solutions to check.
Cons
The time complexity of
O(N^2)is too slow and will not pass the time limits for the given constraints (Nup to 10^5).
A highly efficient approach uses a sweep-line algorithm combined with prefix sums. The core idea is to first discretize the number line based on the start and end points of the coin intervals. This creates a set of elementary segments where the coin count per bag is constant. By pre-calculating prefix sums on these segments, we can quickly find the total coins in any given k-length window. We only need to test a linear number of 'critical' windows to find the maximum.
Algorithm
- Event Points and Segments: First, we process the
coinsarray to find all 'event points'. For each interval[l, r, c],lis an event point where the number of coins per bag increases byc, andr+1is an event point where it decreases byc. We use a map (likeTreeMapin Java) to store the net change at each unique position. - Build Segments: By iterating through the sorted event points, we can construct a list of contiguous segments on the number line. Each segment
[p_i, p_{i+1}-1]has a constant number of coins per bag, which we can calculate by accumulating the changes from the event points. - Prefix Sums: We then build a prefix sum array over these segments.
prefix_sum[i]will store the total number of coins from the beginning of the first segment up to the start of thei-th segment. This allows for quick calculation of the total coins over a large range of segments. - Identify Candidate Windows: The optimal window
[s, s+k-1]must start or end at an event point. Thus, the candidate start positionssarep_jorp_j - k + 1for each event pointp_j. - Query for Sum: For each candidate start
s, we calculate the sum of coins in[s, s+k-1]by using a helper functionquery(p). This function calculates the total coins from the beginning up to positionp. The sum for the window is thenquery(s+k-1) - query(s-1). Thequeryfunction uses binary search on the segment positions and the prefix sum array to compute the result inO(log N)time. - Find Maximum: We find the maximum sum among all candidates.
Walkthrough
This approach optimizes the calculation for each candidate window from O(N) down to O(log N), resulting in a much faster overall algorithm.
-
Create Event Points: We use a
TreeMapto store the change in coins per bag at each interval boundary. For each[l, r, c], we domap[l] += candmap[r+1] -= c. TheTreeMapautomatically keeps the points sorted and aggregates changes at the same position. -
Construct Segments and Prefix Sums: We iterate through the
TreeMapto build an explicit list of segments. Let's say the sorted unique positions arep_0, p_1, ..., p_m. We can determine the constant number of coinsC_iin each segment[p_i, p_{i+1}-1]. Simultaneously, we can build a prefix sum array,prefix_sum[i], storing the total coins fromp_0up top_i-1. -
Identify Candidate Start Positions: The set of critical start positions for a window of size
karep_iandp_i - k + 1for each event pointp_i. -
Efficiently Calculate Window Sum: We create a helper function, say
getSum(p), which calculates the total coins in all bags from position 1 top. This function works by first using binary search to find which segmentpfalls into. Then, it uses the pre-calculated prefix sum to get the total coins up to the start of that segment and adds the contribution from the partial segment. -
Evaluate Candidates: For each candidate start position
s > 0, the window is[s, s+k-1]. The total coins are calculated asgetSum(s+k-1) - getSum(s-1). We track the maximum value found.
import java.util.ArrayList;import java.util.Collections;import java.util.HashSet;import java.util.List;import java.util.Map;import java.util.Set;import java.util.TreeMap; class Solution { public long maximumCoins(int[][] coins, int k) { Map<Long, Long> delta = new TreeMap<>(); for (int[] coin : coins) { long l = coin[0]; long r = coin[1]; long c = coin[2]; delta.put(l, delta.getOrDefault(l, 0L) + c); delta.put(r + 1, delta.getOrDefault(r + 1, 0L) - c); } List<Long> pos = new ArrayList<>(); List<Long> segmentCoins = new ArrayList<>(); long currentCoinsPerBag = 0; long lastPos = 1; for (Map.Entry<Long, Long> entry : delta.entrySet()) { long p = entry.getKey(); if (p > lastPos) { pos.add(lastPos); segmentCoins.add(currentCoinsPerBag); } currentCoinsPerBag += entry.getValue(); lastPos = p; } pos.add(lastPos); segmentCoins.add(currentCoinsPerBag); int n = pos.size(); long[] prefixSum = new long[n + 1]; prefixSum[0] = 0; for (int i = 0; i < n - 1; i++) { prefixSum[i + 1] = prefixSum[i] + (pos.get(i + 1) - pos.get(i)) * segmentCoins.get(i); } prefixSum[n] = prefixSum[n-1]; Set<Long> candidates = new HashSet<>(); for (long p : delta.keySet()) { candidates.add(p); if (p - k > 0) { candidates.add(p - k); } } if (!candidates.contains(1L)) candidates.add(1L); long maxCoins = 0; for (long start : candidates) { if (start <= 0) continue; long end = start + k - 1; maxCoins = Math.max(maxCoins, getSum(end, pos, segmentCoins, prefixSum) - getSum(start - 1, pos, segmentCoins, prefixSum)); } return maxCoins; } private long getSum(long p, List<Long> pos, List<Long> segmentCoins, long[] prefixSum) { if (p <= 0) return 0; int i = Collections.binarySearch(pos, p + 1); i = (i < 0) ? (-i - 1) - 1 : i - 1; if (i < 0) return 0; long sum = prefixSum[i]; sum += (p - pos.get(i) + 1) * segmentCoins.get(i); return sum; }}Complexity
Time
O(N log N), where N is the number of coin intervals. Sorting the event points takes `O(N log N)`. Building segments and prefix sums takes `O(N)`. Evaluating `O(N)` candidates with `O(log N)` per query results in `O(N log N)`. The dominant factor is sorting or the evaluation loop.
Space
O(N), where N is the number of coin intervals. This is for storing the event points, segments, prefix sums, and candidate positions.
Trade-offs
Pros
Efficient, with
O(N log N)time complexity that passes the given constraints.Systematic and robust approach for problems involving intervals on a number line.
Cons
More complex to implement correctly, requiring careful handling of event points, segments, prefix sums, and binary search.
Uses more memory to store segments and prefix sums.
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.