leetcode_1423 可获得的最大点数

1. 题意

给定一个数组,每次只能从头和尾进行选择。
选择k次当前头或者尾,问能取到的最大值。
可获得的最大点数

2. 题解

主要难点是意识到这是一个滑动窗口问题。

2.1 滑动窗口

令数组长度为 s z sz sz
s _ w ( p o s , k ) s\_w(pos, k) s_w(pos,k)为其实点为 p o s pos pos,长度为 k k k的滑窗。
则求解的问题为 m a x ( s u m ( s _ w ( i , k ) ) ) , ( s z − k ) ≤ i ≤ s z − 1   o r   i = 0 max(sum(s\_w(i, k))),(sz-k) \le i \le sz -1 \ or\ i=0 max(sum(s_w(i,k))),(szk)isz1 or i=0

  • 正向
    T i m e : O ( k ) , S p a c e : O ( 1 ) Time:O(k),Space: O(1) Time:O(k),Space:O(1)
class Solution {
public:
    int maxScore(vector<int>& cardPoints, int k) {
        
        int sz = cardPoints.size();

        
        int bpos = sz - k;
        int ans = 0;

        int sum = 0;

        for ( int i = sz - k; i < sz - 1; ++i)
            sum += cardPoints[i];


        for ( int i = bpos; i < sz; ++i) {
            int idx = (i + k - 1) % sz;
            sum += cardPoints[idx];
            ans = max(ans, sum);
            sum -= cardPoints[i];
        }

        sum = std::accumulate(cardPoints.begin(), cardPoints.begin() + k, 0);

        ans = max(sum, ans);

        return ans;
    }
};

直接求首尾的滑动窗口复杂点,由于数组固定则总和固定。
求最大的 S u m ( s _ w ( p o s , k ) ) Sum(s\_w(pos, k)) Sum(s_w(pos,k))可以转化为 求最小的 S u m ( s _ w ( p o w , s z − k ) ) Sum(s\_w(pow, sz -k)) Sum(s_w(pow,szk))
最后再用总和减去最小的 s z − k sz-k szk滑窗即可。

注意:由于是首尾滑窗,所以sz-k的滑窗不能出现首尾相连情况

  • 逆向
    T i m e : O ( n ) , S p a c e : O ( 1 ) Time:O(n),Space: O(1) Time:O(n),Space:O(1)
class Solution {
public:
    int maxScore(vector<int>& cardPoints, int k) {
        
        int sz = cardPoints.size();
        int m = sz - k;

        int win_sum = 0;
        int tot_sum = std::accumulate(cardPoints.begin(), cardPoints.end(), 0);

        if ( m == 0)
            return tot_sum;

        if ( m > 1) {
            win_sum = std::accumulate(
                cardPoints.begin(), cardPoints.begin() + m - 1,0
            );
        }

        int win_min = INT_MAX;
        for (int i = 0;i < sz - m + 1; ++i) {
            int idx = (i + m - 1) % sz;
            win_sum += cardPoints[idx];
            win_min = min(win_min, win_sum);
            win_sum -= cardPoints[i];
        }

        return tot_sum - win_min;
    }
};
2.2 前缀和与后缀和

由于最终的情况无非,在前缀中取 k 1 k_1 k1,在后缀中取 k 2 k_2 k2, k 1 + k 2 = k k_1 +k_2=k k1+k2=k
所以我们可以通过计算 P r e f i x ( i ) , 0 ≤ i ≤ k ; S u f f i x ( j ) , 0 ≤ j ≤ k Prefix(i), 0 \le i \le k;Suffix(j),0 \le j \le k Prefix(i),0ik;Suffix(j),0jk
求出每一种情况即: P r e f i x ( x ) + S u f f i x ( k − x ) Prefix(x)+Suffix(k-x) Prefix(x)+Suffix(kx)的值来判断大小。

  • 前后缀分解
    T i m e : O ( k ) , S p a c e : O ( k ) Time:O(k),Space: O(k) Time:O(k),Space:O(k)

注意:前后缀累加只累计k轮

class Solution {
public:
    int maxScore(vector<int>& cardPoints, int k) {
        
        vector<int> prefix;
        vector<int> suffix;
        int preSum = 0;
        int sufSum = 0;
        int sz = cardPoints.size();

        for ( int i = 0; i < k + 1;++i) {
            prefix.push_back(preSum);
            suffix.push_back(sufSum);

            if ( i != k) {
                preSum += cardPoints[i];
                sufSum += cardPoints[sz - 1 - i];
            }
        }

        int ans = 0;
        for ( int i = 0; i < k + 1; ++i) {
            ans = max(ans, prefix[i] + suffix[k  - i]);
        }

        return ans;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值