题目链接:https://www.acwing.com/problem/content/104/
先考虑一个经典问题,求最大连续子段和,无长度限制:
扫描序列,不断将新的数加入子段,当子段和为负数时,清空当前子段
若有长度限制:
维护前缀和(sum),减去(i-L)之前的前缀和的最小值
对于这道题,二分平均值,将序列减去(mid),判断最大子段和能否大于(0)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long LL;
const int MAXN = 100010;
const double INF = 1000000007;
const double eps = 1e-5;
int N,L;
double a[MAXN], b[MAXN], sum[MAXN];
bool check(double x){
for(int i=1;i<=N;++i) b[i] = a[i] - x;
for(int i=1;i<=N;++i) sum[i] = (sum[i-1] + b[i]);
double min_val = INF, ans = -INF;
for(int i=L;i<=N;++i){
min_val = min(min_val, sum[i-L]);
ans = max(ans, sum[i] - min_val);
}
if(ans >= 0) return true; else return false;
}
LL read(){ LL s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
int main(){
N = read(), L = read();
for(int i=1;i<=N;++i) scanf("%lf",&a[i]);
double l = 0, r = 2000.0;
while(r - l > eps){
double mid = (l + r) / 2;
if(check(mid)) l = mid;
else r = mid;
}
printf("%d
",(int)(r*1000));
return 0;
}