1. 题目来源
2. 题目解析
挺有意思的一道题目,同向双指针+简单dp 的思想。
思路:
- 题目要求枚举两个线段,其实可以想到经典题目「两数之和」。
- 我们可以枚举靠右的线段位置,然后由于靠右的线段已经枚举了,所以靠左的线段的信息也是知道的。
- 可以枚举,靠右的线段右端点 r,然后左端点即为 l = r - k + 1 所在位置。这个简单使用滑动窗口维护 l 位置即可。
- 此时,我们仅需知道 左侧,取得线段最大值是多少即可得到当前构成的奖品总数。sum = pri[l] + r - l + 1;
- 而 pri 数组又可以在枚举 r 的同时进行维护
- pri[i] 指的是 在 i 位置前,不包括 i 点的情况下,满足条件的线段获取到的奖品最大值。即 [0, i) 位置下的合法线段获取到的最大值。
- 简单依照 dp 的思想,pri[r] = max(pri[r - 1], r-l+1) 即可。
- 当前 r 点所能构成的最大奖品即为 r-l+1。
- r 点前所能构成的最大奖品为 pri[r-1]
- 故 r 点所能构成的最大奖品即为两数的最大值。
代码实现小细节:
- 这里的 pri[r] 类似前缀和数组一样,r 的所处位置是开区间,是不能被取到的。
- 故当数组长度为 n 时,针对 pri 预处理数组来讲,可以将长度开到 n+1。
- 这样的好处是:当枚举到 r 点时,它的奖品应该由 [0, l) 部分 + r-l+1 构成。
- 此时,就可以直接写成 pri[l] + r - l + 1 而不用写成 pri[l-1] + r - l + 1 。即不用考虑 0 这个特殊情况。
- 同时,转移方程下标也同时向前推进 1,变为 pri[r+1]=max(pri[r], r-l+1)
- 这样会让代码简洁很多,并且由同样效果。
本题是个很套路的题目,且是一个经典题目,乍一看有贪心做法,实际上很难证明贪心做法的正确性,也没必要去证明了。
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)
代码:
class Solution {
public:
int maximizeWin(vector<int>& prizePositions, int k) {
int n = prizePositions.size();
int pre[n + 1]; memset(pre, 0, sizeof(pre));
int res = 0;
for (int l = 0, r = 0; r < n; r ++ ) {
while (prizePositions[r] - prizePositions[l] > k) l ++ ;
res = max(res, pre[l] + r - l + 1);
pre[r + 1] = max(pre[r], r - l + 1);
}
return res;
}
};