LeetCode 1423. 可获得的最大点数
题目描述


题意分析
每次只能从当前剩余牌的最左端或最右端拿一张,必须恰好拿走
k张,求点数总和的最大值。拿牌顺序不影响最终点数,真正决定结果的是左边取多少张、右边取多少张。
解法:反向思考保留最小中间窗口
核心思路
[!blue]
从两端拿牌,最后拿走的一定是一段前缀和一段后缀,中间留下长度
remain = n - k的连续区间。原数组总和total固定,所以取走的点数为total - 保留区间的点数和,最大化取走就等价于最小化保留。这个转换覆盖了全部合法取法:若从左边取
a张,就从右边取k - a张,剩余区间从下标a开始、长度为n - k;反过来,任意这个长度的窗口都可以通过取走它左边和右边的牌留下来。于是只需遍历所有长度为remain的窗口,找到最小窗口和。相邻窗口只相差一张移入的牌和一张移出的牌,可以用
windowSum滚动维护。右端移动到right时先加上cardPoints[right];若元素数量超过remain,再减去cardPoints[right - remain],这样完整窗口始终是[right - remain + 1, right]。只有
right >= remain - 1时窗口才凑够长度,此时才能更新minWindow。若k == n,没有需要保留的牌,直接返回总和,不进入滑窗流程。
解题步骤
- 遍历数组求
total,计算保留长度remain = n - k。remain == 0时直接返回total。- 从左到右移动
right,加上新牌,必要时减去窗口左侧多出的一张。- 每当窗口长度达到
remain,用windowSum更新最小保留和minWindow。- 返回
total - minWindow。Java 用整数最大值初始化
minWindow;Go 用total初始化也安全,因为题目中的点数都是正数,任何保留窗口的和都不会超过总和。
代码实现
class Solution {
public int maxScore(int[] cardPoints, int k) {
int total = 0;
for (int point : cardPoints) {
total += point;
}
int remain = cardPoints.length - k;
// 全部取走时直接返回总和,避免继续扫描空的保留窗口。
if (remain == 0) {
return total;
}
int windowSum = 0;
int minWindow = Integer.MAX_VALUE;
for (int right = 0; right < cardPoints.length; right++) {
windowSum += cardPoints[right];
// 窗口元素超过 remain 个就吐出最左边那个,保持定长。
if (right >= remain) {
windowSum -= cardPoints[right - remain];
}
// 只有窗口凑满 remain 个元素后才是合法候选,否则半截窗口会污染最小值。
if (right >= remain - 1) {
minWindow = Math.min(minWindow, windowSum);
}
}
return total - minWindow;
}
}
func maxScore(cardPoints []int, k int) int {
total := 0
for _, point := range cardPoints {
total += point
}
remain := len(cardPoints) - k
// 全部取走时直接返回总和,避免继续扫描空的保留窗口。
if remain == 0 {
return total
}
windowSum := 0
// remain < n 且点数恒为正,任意窗口和都严格小于 total,用它当初值是安全的。
minWindow := total
for right := 0; right < len(cardPoints); right++ {
windowSum += cardPoints[right]
// 窗口元素超过 remain 个就吐出最左边那个,保持定长。
if right >= remain {
windowSum -= cardPoints[right-remain]
}
// 只有窗口凑满 remain 个元素后才是合法候选,否则半截窗口会污染最小值。
if right >= remain-1 && windowSum < minWindow {
minWindow = windowSum
}
}
return total - minWindow
}
复杂度分析
- 时间复杂度:$O(n)$,求总和与滑窗各一次。
- 空间复杂度:$O(1)$,只保存滚动状态。
关键点总结
[!green]
- 两端取走的牌不一定连续,但留下的牌一定连续,适合转化成窗口问题。
- 每个合法取法都对应一个长度为
n - k的保留窗口,反过来也成立。- 总和固定,最大取走和等于总和减去最小保留和。
- 相邻窗口只需一加一减,无需为每个窗口重新求和。
易错点总结
[!yellow]
- 窗口未满就更新,会用短片段的较小和放大答案。
- 窗口长度是
n - k,不是k;要最小化的也是保留部分的和。- 新元素加入后,应移出下标
right - remain,才能留下恰好remain张牌。- 把最小和初值设为零,在正值输入下无法更新。
k == n是合法情况,应直接返回总和,不能按普通非空窗口处理。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 643. 子数组最大平均数 I | 简单 | 拿走两端k项等价于留下中间固定长度n-k的最小和窗口,可复用定长滑动和。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!