- 标签:数组、前缀和、滑动窗口
- 难度:中等
描述:将卡牌排成一行,给定每张卡片的点数数组
每次行动,可以从行的开头或者末尾拿一张卡牌,最终保证正好拿到了
现在给定一个整数数组
要求:返回可以获得的最大点数。
说明:
-
$1 \le cardPoints.length \le 10^5$ 。 $1 \le cardPoints[i] \le 10^4$ -
$1 \le k \le cardPoints.length$ 。
示例:
- 示例 1:
输入:cardPoints = [1,2,3,4,5,6,1], k = 3
输出:12
解释:第一次行动,不管拿哪张牌,你的点数总是 1 。但是,先拿最右边的卡牌将会最大化你的可获得点数。最优策略是拿右边的三张牌,最终点数为 1 + 6 + 5 = 12。
- 示例 2:
输入:cardPoints = [2,2,2], k = 2
输出:4
解释:无论你拿起哪两张卡牌,可获得的点数总是 4。
可以用固定长度的滑动窗口来做。
由于只能从开头或末尾位置拿
-
$window\underline{\hspace{0.5em}}sum$ 用来维护窗口内的元素和,初始值为$0$ 。$min\underline{\hspace{0.5em}}sum$ 用来维护滑动窗口元素的最小和。初始值为$sum(cardPoints)$ 。滑动窗口的长度为$window\underline{\hspace{0.5em}}size$ ,值为$len(cardPoints) - k$ 。 - 使用双指针
$left$ 、$right$。$left$ 、$right$ 都指向序列的第一个元素,即:left = 0
,right = 0
。 - 向右移动
$right$ ,先将$window\underline{\hspace{0.5em}}size$ 个元素填入窗口中。 - 当窗口元素个数为
$window\underline{\hspace{0.5em}}size$ 时,即:$right - left + 1 \ge window\underline{\hspace{0.5em}}size$ 时,计算窗口内的元素和,并维护子数组最小和$min\underline{\hspace{0.5em}}sum$ 。 - 然后向右移动
$left$ ,从而缩小窗口长度,即left += 1
,使得窗口大小始终保持为$k$ 。 - 重复 4 ~ 5 步,直到
$right$ 到达数组末尾。 - 最后输出
$sum(cardPoints) - min\underline{\hspace{0.5em}}sum$ 即为答案。
注意:如果
class Solution:
def maxScore(self, cardPoints: List[int], k: int) -> int:
window_size = len(cardPoints) - k
window_sum = 0
cards_sum = sum(cardPoints)
min_sum = cards_sum
left, right = 0, 0
if window_size == 0:
return cards_sum
while right < len(cardPoints):
window_sum += cardPoints[right]
if right - left + 1 >= window_size:
min_sum = min(window_sum, min_sum)
window_sum -= cardPoints[left]
left += 1
right += 1
return cards_sum - min_sum
-
时间复杂度:$O(n)$,其中
$n$ 为数组$cardPoints$ 中的元素数量。 - 空间复杂度:$O(1)$。