可获得的最大点数

题目

几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。

每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。

你的点数就是你拿到手中的所有卡牌的点数之和。

给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数。

题目来源:https://leetcode.cn/problems/maximum-points-you-can-obtain-from-cards/description/

示例

示例 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
解释:你必须拿起所有卡牌,可以获得的点数为所有卡牌的点数之和。

题解

方法一:逆向思维

分析

拿走 k 张,剩下 n−k 张。这里 n 是 cardPoints 的长度。
由于拿走的点数和 + 剩下的点数和 = 所有点数和 = 常数,所以为了最大化拿走的点数和,应当最小化剩下的点数和。

因为只能在开头和末尾拿走,所以最后剩下的n-k张牌必是连续的。
至此题目就转换为 计算长为 n−k 的连续子数组和的最小值。
利用定滑窗口即可解决。

代码

class Solution {
public:
    int maxScore(vector<int>& cardPoints, int k) {
        int len = cardPoints.size();
        int m = len - k;
        long long sum = 0, min_sum = 0,total_sum = 0;
        int i = 0;
        for (; i < m; i++) {
            sum += cardPoints[i];
        }
        total_sum = min_sum = sum;
        for (; i < len; i++) {
            total_sum += cardPoints[i];
            sum += cardPoints[i] - cardPoints[i-m];
            min_sum = min(sum, min_sum);
        }
        return total_sum - min_sum;
    }
};

复杂度分析

  • 时间复杂度:O(n),其中 n 为 cardPoints 的长度。
  • 空间复杂度:O(1)。Python 忽略切片开销。

方法二:正向思维

分析

由于只能在开头和末尾拿走,假设在开头拿走了 m 张,那么剩下的 k-m 张就只能从末尾拿。
此时所有拿走的 k 张牌点数和就为 cardPoints[0] + cardPoints[1] + ··· + cardPoints[m-1] + cardPoints[n-k+m] + cardPoints[n-k+m+1] + ··· + cardPoints[n-1]

那么所有拿走的点数和最大值为以下结果的最大值:

  • 前 k 个数的和。
  • 前 k−1 个数以及后 1 个数的和。
  • 前 k−2 个数以及后 2 个数的和。
  • ……
  • 前 2 个数以及后 k−2 个数的和。
  • 前 1 个数以及后 k−1 个数的和。
  • 后 k 个数的和。

代码

class Solution {
public:
    int maxScore(vector<int>& cardPoints, int k) {
        int len = cardPoints.size();
        long long sum = 0, max_sum = 0;
        int i = 0;
        for (; i < k; i++) {
            sum += cardPoints[i];
        }
        max_sum = sum;
        for (; i > 0; i--) {
            sum += cardPoints[len-(k-(i-1))] - cardPoints[i-1];
            max_sum = max(sum, max_sum);
        }
        return max_sum;
    }
};

复杂度分析

  • 时间复杂度:O(k)。
  • 空间复杂度:O(1)。Python 忽略切片开销。
© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容