【算法练习】leetcode每日一题/preSum/滑动窗口/多种方法/1423. 可获得的最大点数

目录

1423. 可获得的最大点数

方法一 递归

方法二 滑动窗口

方法三 环形数组

方法四 preSum


2021年2月6日题目链接:https://leetcode-cn.com/problems/maximum-points-you-can-obtain-from-cards/

1423. 可获得的最大点数

难度中等69

几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 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

 

提示:

  • 1 <= cardPoints.length <= 10^5
  • 1 <= cardPoints[i] <= 10^4
  • 1 <= k <= cardPoints.length

通过次数11,010提交次数22,286

 

 

方法一 递归

首先想到的递归但是 1e5 超时,然后递归写的还不好,多了一些不必要的条件,这个之后要训练训练,

但是递归的过程是把所有的可能组合方式都求了一遍,时间复杂度会达到 O(N*k)

 

方法二 滑动窗口

n=数组的大小  

反向思考,这题用滑动窗口,两边不好滑动,就滑动中间求最小,窗口的size是 n-k,最后sum-min就得到最后要求的结果

注意 滑动窗口的时候窗口大小固定的情况,可以先求0~windowSize这个窗口和,然后之后再滑动

官方题解用了accumulate,算是小技巧吧,直接循环即可求和,

也可以left,right这样滑动,但是这样从0 开始滑动加扩大对于窗口大小固定的这样写反而需要考虑的较多,比如如果没写好循环条件就需要考虑windowSize=0

以下给出两种代码 

固定滑动窗口还用left right 滑动有点没必要的代码一:

int maxScore(vector& cardPoints, int k) {
    int n=cardPoints.size();
    int m=n-k;
    int sum=0;
    for(int i=0;i0){    //必须要考虑m是否大于0
        minSum=sum;
        while(right=m){
                minSum=min(minSum,tmp);
                tmp-=cardPoints[left];
                left++;
            }
            right++;
        }
    } else{
        minSum=0;
    }

    return sum-minSum;
}

参考别人的改进代码

这个方法只用遍历一次数组。

需要注意的是,需要根据 i 的位置,计算滑动窗口是否开始、是否要移除最左边元素:

当 i >= windowSize 时,为了固定窗口的元素是 k 个,每次移动时需要将 i - windowSize 位置的元素移除。
当 i >= windowSize - 1 时,滑动窗口内的元素刚好是 k 个,开始计算滑动窗口的最小和。
最后,用 cardPoints 的所有元素之和,减去滑动窗口内的最小元素和,就是拿走的卡牌的最大点数。

使用 Python2 写的代码如下。


class Solution(object):
    def maxScore(self, cardPoints, k):
        """
        :type cardPoints: List[int]
        :type k: int
        :rtype: int
        """
        N = len(cardPoints)
        windowSize = N - k # 窗口的大小
        sums = 0
        res = float("inf") # 正无穷大
        for i in range(N):
            sums += cardPoints[i]
            if i >= windowSize:
                sums -= cardPoints[i - windowSize]
            if i >= windowSize - 1:
                res = min(res, sums)
        return sum(cardPoints) - res

作者:fuxuemingzhu
链接:https://leetcode-cn.com/problems/maximum-points-you-can-obtain-from-cards/solution/san-chong-fang-fa-tuo-zhan-si-lu-dong-tu-fb9m/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

固定滑动窗口一般写法代码二:

int maxScore(vector& cardPoints, int k) {
    int n=cardPoints.size();
    int windowSize=n-k;
    int sum=0;
    int tmp=0;
    for(int i=0;itmp){
           minSum=tmp;
       }
   }
    return sum-minSum;
}

方法三 环形数组

可以构造一个环形数组 = {后k个元素,前k个元素}),因为卡牌必须是连续的k张卡牌,也就是一个滑动窗口,窗口在环形数组中一次一次右移,找到最大的一个窗口就是结果。

作者:closes
链接:https://leetcode-cn.com/problems/maximum-points-you-can-obtain-from-cards/solution/huan-xing-shu-zu-by-closes-0zk9/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

//参考别人题解思路自己写的
//主要难点就是循环的那个开始以及边界,以及环形数组的处理
int maxScore(vector& cardPoints, int k) {
    int n=cardPoints.size();
    int res=INT_MAX;
    int tmp=0;
    for(int i=n-k;i

 

方法四 preSum

定义preSum[i] 是不包含i的数组 [0,i)的和

然后利用这个求固定窗口的最小值


int maxScore(vector& cardPoints, int k) {
    int n=cardPoints.size();
    int windowSize=n-k;
    vector preSum(n+1,0);
    for(int i=1;i<=n;i++){
        preSum[i]=preSum[i-1]+cardPoints[i-1];
    }
    int sum=preSum[n];
    //[i,j]内的和 = preSum[j+1]-preSum[i]
    int min=INT_MAX;
    int tmp=0;
    for(int i=0;i<=n-windowSize;i++){
        int j=i+windowSize-1;
        tmp=preSum[j+1]-preSum[i];
        if(tmp

参考链接:三种方法拓展思路,动图帮助理解

今天这个题目,难点还是需要把抽取卡牌的问题转变为一个滑动窗口的问题。
问题抽象之后,preSum 和 滑动窗口 两种解法就已经呼之欲出了。
每日一题的作用再次凸显,同一类题目都是换汤不换药,学会思路和套路之后,就很快秒杀!

作者:fuxuemingzhu
链接:https://leetcode-cn.com/problems/maximum-points-you-can-obtain-from-cards/solution/san-chong-fang-fa-tuo-zhan-si-lu-dong-tu-fb9m/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

个人体会:

1、学会转换思路 反向求解 max->min

2、如果用滑动窗口,循环数组等这些需要找好循环条件,还是要多练习

你可能感兴趣的:(算法练习,算法,leetcode)