输入:
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