Leetcode1423. 可获得的最大点数

Every day a Leetcode

题目来源:1423. 可获得的最大点数

解法1:前缀和 + 后缀和

基于贪心的思想,要使得获得的点数最大,每次拿卡牌都应该选点数尽量高的卡牌。

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

设数组 cardPoints 的长度为 n。分别求出它的前缀和数组 prevSum 和后缀和数组 suffixSum,设我们从行的开头拿 i(0<=i<=k) 张卡牌,那么我们只能从行的末尾拿 k-i 张卡牌,设最大点数之和为 max_sum,则有:

for (int i = 0; i <= k; i++)
	max_sum = max(max_sum, prevSum[i] + suffixSum[n - (k - i)]);

也可以将数组 suffixSum 反转,有:

reverse(suffixSum.begin(), suffixSum.end());
for (int i = 0; i <= k; i++)
	max_sum = max(max_sum, prevSum[i] + suffixSum[k - i]);

代码:

/*
 * @lc app=leetcode.cn id=1423 lang=cpp
 *
 * [1423] 可获得的最大点数
 */

// @lc code=start
class Solution
{
public:
    int maxScore(vector<int> &cardPoints, int k)
    {
        if (k == 0)
            return 0;
        if (k == cardPoints.size())
            return accumulate(cardPoints.begin(), cardPoints.end(), 0);
        int n = cardPoints.size();
        vector<int> prevSum(n + 1, 0), suffixSum(n + 1, 0);
        for (int i = 1; i <= n; i++)
            prevSum[i] = prevSum[i - 1] + cardPoints[i - 1];
        for (int i = n - 1; i >= 0; i--)
            suffixSum[i] = suffixSum[i + 1] + cardPoints[i];
        // reverse(suffixSum.begin(), suffixSum.end());
        int max_sum = 0;
        for (int i = 0; i <= k; i++)
        {
            max_sum = max(max_sum, prevSum[i] + suffixSum[n - (k - i)]);
        }
        return max_sum;
    }
};
// @lc code=end

结果:

Leetcode1423. 可获得的最大点数_第1张图片

复杂度分析:

时间复杂度:O(n+k),其中 n 是数组 cardPoints 的长度。

空间复杂度:O(n),其中 n 是数组 cardPoints 的长度。主要是 prevSum 和 suffixSum 的开销,各使用了 n+1 的空间。

解法2:滑动窗口

记数组 cardPoints 的长度为 n,由于只能从开头和末尾拿 k 张卡牌,所以最后剩下的必然是连续的 n−k 张卡牌。

逆向思维:我们可以通过求出剩余卡牌点数之和的最小值,来求出拿走卡牌点数之和的最大值。

算法:

由于剩余卡牌是连续的,使用一个固定长度为 n−k 的滑动窗口对数组 cardPoints 进行遍历,求出滑动窗口最小值,然后用所有卡牌的点数之和减去该最小值,即得到了拿走卡牌点数之和的最大值。

代码:

// 滑动窗口

class Solution
{
public:
    int maxScore(vector<int> &cardPoints, int k)
    {
        if (k == 0)
            return 0;
        if (k == cardPoints.size())
            return accumulate(cardPoints.begin(), cardPoints.end(), 0);
        int n = cardPoints.size();
        int windowSize = n - k;
        int sum = accumulate(cardPoints.begin(), cardPoints.begin() + windowSize, 0);
        int min_sum = sum;
        for (int i = windowSize; i < n; i++)
        {
            sum += cardPoints[i];
            sum -= cardPoints[i - windowSize];
            min_sum = min(min_sum, sum);
        }
        return accumulate(cardPoints.begin(), cardPoints.end(), 0) - min_sum;
    }
};

结果:

Leetcode1423. 可获得的最大点数_第2张图片

复杂度分析:

时间复杂度:O(n),其中 n 是数组 cardPoints 的长度。

空间复杂度:O(1)。

你可能感兴趣的:(Every,day,a,LeetCode,C++,数据结构与算法,leetcode,滑动窗口,前缀和)