AcWing135. 最大子序和

题目链接:135. 最大子序和 - AcWing题库

输入:

6 4
1 -3 5 1 -2 3

输出:

7

思路1:DP+单调递队列 + 前缀和,f[i]表示以i为端点,长度小于等于m的子区间的和的最大值。

fi = max(si - sj) (i - m) <= j < i

si为固定值,从左到右用固定值减去区间最小值,才能得到答案。

因此我们需要从左往右维护一个长度为m的区间和的最小值,用到了单调递增队列,将前缀和放入队列中,再将右端点和队列中前缀和最小的数相减,不断更新最大值。

代码1:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5 + 5;
const int inf = 0x3f3f3f3f;
int a[N], pre[N], q[N];
int32_t main(){
    int n, m;  cin >> n >> m;
    for(int i = 1; i <= n; i++)  cin >> a[i];
    for(int i = 1; i <= n; i++)  pre[i] = pre[i - 1] + a[i];
    int maxn = -inf, hh = 0, tt = 0;
    for(int i = 1; i <= n; i++){
        maxn = max(pre[i] - pre[q[hh]], maxn);
        if(i - m + 1 > q[hh])  hh++;
        while(pre[i] <= pre[q[tt]] && hh <= tt)  tt--;
        q[++tt] = i;
//        printf("%lld\n", q[hh]);
    }
    printf("%lld\n", maxn);
} 

思路2:DP + 单调递队列 + 前缀和,与思路1不同的是,思路2用的是区间最大值 - 固定值,区间最大值指的是(i - m + 1)~ i中前缀和的最大值,而固定值指的是pre[i - m].

要求区间最大值就用到了单调递减队列。

为了能让左端点(区间最大值)到达n,还需要将前缀和拓展到n + m。

代码2:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 6e5 + 5;
const int inf = 0x3f3f3f3f3f;
int a[N], pre[N], q[N];
int32_t main(){
    int n, m;  cin >> n >> m;
    for(int i = 1; i <= n; i++)  cin >> a[i];
    for(int i = 1; i <= n; i++)  pre[i] = pre[i - 1] + a[i];
    for( int i = n+1 ; i <= n + m ; i ++ ) pre[i] = pre[i-1];
    n += m;
    int maxn = -inf, hh = 0, tt = 0;
    for(int i = 1; i < n; i++){
        if(i - m + 1 > q[hh])  hh++;
        while(pre[i] > pre[q[tt]] && hh <= tt)  tt--;
        q[++tt] = i;
        if(i >= m)  maxn = max(pre[q[hh]] - pre[i - m], maxn);
    }
    printf("%lld\n", maxn);
}

思路3:优先队列+ DP + 前缀和,和思路1差不多,就是把单调队列换成了优先队列

代码3:

#include<bits/stdc++.h>
#define int long long
#define pii pair<int, int>
using namespace std;
const int N = 3e5 + 5;
const int inf = 0x3f3f3f3f3f;
int pre[N];
int32_t main(){
    int n, m, x;  cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> x;
        pre[i] = pre[i - 1] + x;
    }
    int maxn = -inf;
    priority_queue<pii, vector<pii>, greater<pii> > q;
    q.push({0, 0});
    for(int i = 1; i <= n; i++){
        while(q.size() && i - m > q.top().second)  q.pop();
        if(q.size())  maxn = max(maxn, pre[i] - q.top().first);
        q.push({pre[i], i});
    }
    printf("%lld\n", maxn);
}

三个代码过题时间:代码1 > 代码2 > 代码3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值