1423.可获得的最大点数(滑动窗口)

17 篇文章 0 订阅
15 篇文章 0 订阅

可获得的最大点数

几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。
每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。
你的点数就是你拿到手中的所有卡牌的点数之和。
给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数。

示例 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 。

示例 3:
输入:cardPoints = [9,7,7,9,7,7,9], k = 7
输出:55
解释:你必须拿起所有卡牌,可以获得的点数为所有卡牌的点数之和。

示例 4:
输入:cardPoints = [1,1000,1], k = 1
输出:1
解释:你无法拿到中间那张卡牌,所以可以获得的最大点数为 1 。

示例 5:
输入:cardPoints = [1,79,80,1,1,1,200,1], k = 3
输出:202

解题思路

由于题中要求可以从左边或右边任意选择,并且要使得选择的k个元素的和为最大值。
所以,我们只要找出连续的 n - k 个元素的和为最小值时,再用所有元素总和减去该最小值,就可得到题目所有的最大值。(因为,若连续的 n - k 个元素为最小值了,其他的元素和一定会是符合题目要求的 k 个元素和的最大值)

现在原问题转换成:给定 n 个整数,找出长度为 n - k 的连续子数组且和最小。
由于是连续子数组,则可用滑动窗口的方法来求解。
(1) 先求出前 n - k 个元素的和sum,并认为它是最小值minSum。
(2) 设置一个 left 指针,使其一开始指向数组的第一个元素。
(3) 对后 k 个元素进行遍历,每遍历一次就 sum 就减去 left 位置上的元素,并加上 i 位置上的元素,得到新的sum值再与 minSum 比较留下小的那个。(就像是有一个窗口一样从左至右一格一格的移动,直到找到窗口中的和为最小值)

代码

class Solution {

    public int maxScore(int[] cardPoints, int k) {

        // 找k个元素(左或右),只需要找连续n - k个元素取得最小值剩下的就是最大值
        int n = cardPoints.length;
        int sums = 0;
        for (int i=0;i<n;i++) {
        
            sums += cardPoints[i];
        }
        int sum = 0;
        for (int i=0;i<n-k;i++) {

            sum += cardPoints[i];
        }
        int minSum = sum;
        int left = 0;
        for (int i=n-k;i<n;i++) {

            sum = sum - cardPoints[left++] + cardPoints[i];
            minSum = Math.min(sum, minSum);
        }
        int result = sums - minSum;
        return result;
    }
}

时间复杂度:最多一次遍历了n个元素,并且没有循环的嵌套,复杂度为O(n)
空间复杂度:O(1)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值