Maximize Win From Two Segments

Maximize Win From Two Segments

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 satisfies 1 <= prizePositions[i] <= 3 or 2 <= 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:

Input: prizePositions = [1,2,3,4], k = 0
Output: 2
Explanation: For this example, one choice for the segments is [3, 3] and [4, 4], and you will be able to get 2 prizes. 

Constraints:

  • $1 \leq \text{prizePositions.length} \leq {10}^5$
  • $1 \leq \text{prizePositions}[i] \leq {10}^9$
  • $0 \leq k \leq {10}^9$
  • prizePositions is sorted in non-decreasing order.

 

解题思路

  比赛的时候贪心思路搞错了,一直在wa,然后对着个样例看了半天都不知道错了哪里。

  一开始的贪心思路是想着线段的其中一个端点一定要覆盖到礼物,然后右端点取到长度限制,这里的贪心思路还是对的。然后我就理所应当的认为下一条线段的左端点应该从上一条线段的右端点的右边开始(不要重合),这样做的后果是把序列分成了连续的若干段,再对这若干段按照能够取到的礼物数量从大到小排序,最后取排序结果的前两个作为答案。

class Solution {
public:
    int maximizeWin(vector<int>& prizePositions, int k) {
        int n = prizePositions.size();
        vector<int> p(1);
        for (int i = 0; i < n; i++) {
            int j = i + 1;
            while (j < n && prizePositions[j] - prizePositions[i] <= k) {
                j++;
            }
            p.push_back(j - i);
            i = j - 1;
        }
        sort(p.begin(), p.end(), greater<int>());
        return p[0] + p[1];
    }
};
wa code

  一开始死都没发现不对的地方,后面看了别人的题解才知道这种做法是有问题的。

  首先容易贪心想到线段端的某个端点(这里假设是左端点)如果包含礼物,那么这条线段能够覆盖的礼物会更多。同时选择的两个线段没有相交的情况能够覆盖到的礼物会更多。

  接着我们通过双指针来预处理得到以$i$为左端点所能够覆盖到的最大礼物数量,得到数组$l[i]$。再预处理得到以$i$为右端点所能够覆盖到的最大礼物数量,得到数组$r[i]$。然后枚举所有$i$,作为第二条线段的左端点,此时第一条线段的右端点应该选择$\max\limits_{1 \leq j \leq i-1}{r[j]}$,因此还需要对$r$数组求前缀最大值。那么这种情况的所能覆盖到的最大礼物数量为$l[i] + \max\limits_{1 \leq j \leq i-1}{r[j]}$,在枚举的过程中对所有情况取个最大值。

  AC代码如下:

 1 class Solution {
 2 public:
 3     int maximizeWin(vector<int>& prizePositions, int k) {
 4         int n = prizePositions.size();
 5         vector<int> l(n + 1), r(n + 1);
 6         for (int i = 1, j = 1; i <= n; i++) {
 7             while (prizePositions[i - 1] - prizePositions[j - 1] > k) { // 右端点
 8                 j++;
 9             }
10             r[i] = i - j + 1;
11         }
12         for (int i = 1; i <= n; i++) {  // 对r数组求前缀最大值
13             r[i] = max(r[i], r[i - 1]);
14         }
15         for (int i = n, j = n; i; i--) {
16             while (prizePositions[j - 1] - prizePositions[i - 1] > k) { // 左端点
17                 j--;
18             }
19             l[i] = j - i + 1;
20         }
21         int ret = 0;
22         for (int i = 1; i <= n; i++) {
23             ret = max(ret, l[i] + r[i - 1]);
24         }
25         return ret;
26     }
27 };

 

参考资料

  two pointers & 枚举:https://leetcode.cn/problems/maximize-win-from-two-segments/solution/two-pointers-mei-ju-by-tsreaper-bui2/

posted @ 2023-02-05 16:08  onlyblues  阅读(21)  评论(0编辑  收藏  举报
Web Analytics