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
结果:
复杂度分析:
时间复杂度: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;
}
};
结果:
复杂度分析:
时间复杂度:O(n),其中 n 是数组 cardPoints 的长度。
空间复杂度:O(1)。