Maximize Win From Two Segments
MedPrompt
There are some prizes on the X-axis. You are given an integer array prizePositions that is sorted in non-decreasing order, where prizePositions[i] is the position of the ith prize. There could be different prizes at the same position on the line. You are also given an integer k.
You are allowed to select two segments with integer endpoints. The length of each segment must be k. You will collect all prizes whose position falls within at least one of the two selected segments (including the endpoints of the segments). The two selected segments may intersect.
- For example if
k = 2, you can choose segments[1, 3]and[2, 4], and you will win any prize i that satisfies1 <= prizePositions[i] <= 3or2 <= prizePositions[i] <= 4.
Return the maximum number of prizes you can win if you choose the two segments optimally.
Example 1:
Input: prizePositions = [1,1,2,2,3,3,5], k = 2
Output: 7
Explanation: In this example, you can win all 7 prizes by selecting two segments [1, 3] and [3, 5].Example 2:
[3, 3]
Constraints:
1 <= prizePositions.length <= 1051 <= prizePositions[i] <= 1090 <= k <= 109prizePositionsis sorted in non-decreasing order.
Approaches
2 approaches with complexity analysis and trade-offs.
A straightforward but inefficient approach is to try every possible pair of segments. Since the optimal segments will have their endpoints aligned with prize positions to maximize coverage, we can assume each segment starts at some prizePositions[i]. We can iterate through all pairs of starting indices (i, j) for the two segments. For each pair, we calculate the total number of unique prizes covered by the union of the two segments and keep track of the maximum number found.
Algorithm
- Initialize
maxPrizesto 0. - Iterate through each possible starting position
ifor the first segment, from0ton-1. - Inside this loop, iterate through each possible starting position
jfor the second segment, fromiton-1. - For each pair
(i, j), define the two segments:S1 = [prizePositions[i], prizePositions[i] + k]S2 = [prizePositions[j], prizePositions[j] + k]
- Count the number of prizes in the union of
S1andS2. A simple way to do this is to iterate through all prizes fromp = 0ton-1and check ifprizePositions[p]falls intoS1orS2. AHashSetcan be used to store the indices of the prizes collected to avoid double counting. - Update
maxPrizeswith the maximum count found. - Return
maxPrizes.
Walkthrough
This method exhaustively checks every combination of two segments. The start of each segment is chosen from one of the prize positions. For each pair of segments, we determine the total number of unique prizes they cover.
To implement this, we can use nested loops. The outer loop selects the starting prize for the first segment, and the inner loop selects the starting prize for the second segment. For each pair of segments, we can iterate through all the prizes and count how many fall within at least one of the two segments. To handle overlaps correctly (i.e., not counting a prize twice if it's in both segments), we can use a HashSet to store the indices of the prizes collected.
import java.util.HashSet; class Solution { public int maximizeWin(int[] prizePositions, int k) { int n = prizePositions.length; if (n == 0) { return 0; } int maxPrizes = 0; // If only one segment is used if (n > 0) { for (int i = 0; i < n; i++) { int rightBound = prizePositions[i] + k; int count = 0; for (int p = i; p < n; p++) { if (prizePositions[p] <= rightBound) { count++; } else { break; } } maxPrizes = Math.max(maxPrizes, count); } } for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { long start1 = prizePositions[i]; long end1 = start1 + k; long start2 = prizePositions[j]; long end2 = start2 + k; HashSet<Integer> collectedPrizes = new HashSet<>(); for (int p = 0; p < n; p++) { if ((prizePositions[p] >= start1 && prizePositions[p] <= end1) || (prizePositions[p] >= start2 && prizePositions[p] <= end2)) { collectedPrizes.add(p); } } maxPrizes = Math.max(maxPrizes, collectedPrizes.size()); } } return maxPrizes; }}This version has a time complexity of O(N^3). A slightly better O(N^2 log N) approach would involve calculating the counts for each segment and their intersection using binary search instead of the innermost loop, but it's still too slow for the given constraints.
Complexity
Time
O(N^3), where N is the number of prizes. The three nested loops iterate through all pairs of starting positions and then all prizes.
Space
O(N) in this implementation due to the HashSet. It can be O(1) if we calculate counts without a set.
Trade-offs
Pros
Simple to understand and implement.
Cons
The time complexity is very high, making it unsuitable for the given constraints.
A much more efficient approach uses dynamic programming. The core idea is to split the prizePositions array into two parts and find the best single segment in each part. By trying all possible split points, we can find the global optimum.
Let dp_left[i] be the maximum number of prizes we can win with one segment using only the first i prizes (i.e., prizePositions[0...i-1]).
Let dp_right[i] be the maximum number of prizes we can win with one segment using prizes from index i to the end (i.e., prizePositions[i...n-1]).
Both dp_left and dp_right arrays can be computed in O(N) time using a two-pointer technique. Once we have these arrays, we can iterate through all possible split points i (from 0 to n). For each split point, we consider taking the best segment from the left part [0...i-1] and the best segment from the right part [i...n-1]. The total prizes would be dp_left[i] + dp_right[i]. The maximum of these sums over all i will be our answer. This works because any pair of non-overlapping segments will be separated by some split point i, and even overlapping segments can be shown to be covered by this logic.
Algorithm
- Create an array
dp_leftof sizen + 1.dp_left[i]will store the maximum number of prizes obtainable from a single segment that is entirely contained within the subarrayprizePositions[0...i-1]. - Populate
dp_leftfrom left to right. Use a two-pointerlto find the start of the longest valid segment ending ati-1.dp_left[i] = max(dp_left[i-1], length_of_segment_ending_at_i-1). - Create an array
dp_rightof sizen + 1.dp_right[i]will store the maximum prizes from a single segment entirely withinprizePositions[i...n-1]. - Populate
dp_rightfrom right to left. Use a two-pointerrto find the start of the longest valid segment starting ati.dp_right[i] = max(dp_right[i+1], length_of_segment_starting_at_i). - The problem is now to find a split point
ithat maximizes the sum of prizes from a segment on the left and a segment on the right. InitializemaxPrizes = 0. - Iterate
ifrom0ton, and for eachi, calculatedp_left[i] + dp_right[i]. UpdatemaxPrizeswith the maximum sum found. - The final answer is
maxPrizes.
Walkthrough
The algorithm proceeds in three main steps:
- Compute
dp_left: We iterate fromi = 1ton. We use a sliding window approach with two pointers,landi-1, to find the maximum number of prizes in a segment ending at or beforei-1. The left pointerlis advanced to maintain the window constraintprizePositions[i-1] - prizePositions[l] <= k.dp_left[i]is the maximum ofdp_left[i-1]and the current window size. - Compute
dp_right: Similarly, we iterate fromi = n-1down to0. We use a sliding window with pointersiandrto find the maximum prizes in a segment starting at or afteri. The right pointerris advanced to satisfyprizePositions[r] - prizePositions[i] <= k.dp_right[i]is the maximum ofdp_right[i+1]and the current window size. - Combine results: We iterate through all possible split points
ifrom0ton. The maximum prizes are found bymax(maxPrizes, dp_left[i] + dp_right[i]).
class Solution { public int maximizeWin(int[] prizePositions, int k) { int n = prizePositions.length; if (n == 0) { return 0; } // dp_left[i] = max prizes in a single segment within prizePositions[0...i-1] int[] dp_left = new int[n + 1]; int l = 0; for (int i = 1; i <= n; i++) { while (prizePositions[i - 1] - prizePositions[l] > k) { l++; } dp_left[i] = Math.max(dp_left[i - 1], (i - 1) - l + 1); } // dp_right[i] = max prizes in a single segment within prizePositions[i...n-1] int[] dp_right = new int[n + 1]; int r = n - 1; for (int i = n - 1; i >= 0; i--) { while (prizePositions[r] - prizePositions[i] > k) { r--; } dp_right[i] = Math.max(dp_right[i + 1], r - i + 1); } int maxPrizes = 0; for (int i = 0; i <= n; i++) { maxPrizes = Math.max(maxPrizes, dp_left[i] + dp_right[i]); } return maxPrizes; }}Complexity
Time
O(N), where N is the number of prizes. Each step (computing `dp_left`, `dp_right`, and combining them) takes linear time.
Space
O(N) for the two DP arrays, `dp_left` and `dp_right`.
Trade-offs
Pros
Highly efficient with linear time complexity.
Guaranteed to find the optimal solution.
Cons
Requires extra space for the DP arrays.
Solutions
Solution
class Solution {public int maximizeWin(int[] prizePositions, int k) { int n = prizePositions.length; int[] f = new int[n + 1]; int ans = 0; for (int i = 1; i <= n; ++i) { int x = prizePositions[i - 1]; int j = search(prizePositions, x - k); ans = Math.max(ans, f[j] + i - j); f[i] = Math.max(f[i - 1], i - j); } return ans; }private int search(int[] nums, int x) { int left = 0, right = nums.length; while (left < right) { int mid = (left + right) >> 1; if (nums[mid] >= x) { right = mid; } else { left = mid + 1; } } return left; }}class Solution {public: int maximizeWin(vector<int> &prizePositions, int k) { int n = prizePositions.size(); vector<int> f(n + 1); int ans = 0; for (int i = 1; i <= n; ++i) { int x = prizePositions[i - 1]; int j = lower_bound(prizePositions.begin(), prizePositions.end(), x - k) - prizePositions.begin(); ans = max(ans, f[j] + i - j); f[i] = max(f[i - 1], i - j); } return ans; }};class Solution: def maximizeWin(self, prizePositions: List[int], k: int) -> int: n = len(prizePositions) f = [0] * (n + 1) ans = 0 for i, x in enumerate(prizePositions, 1): j = bisect_left(prizePositions, x - k) ans = max(ans, f[j] + i - j) f[i] = max(f[i - 1], i - j) return ansVideo 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.