AcWing102 最佳牛围栏 (二分)

题目链接: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\n",(int)(r*1000));
	
	return 0;
}
posted @ 2020-11-01 19:56  Tartarus_li  阅读(79)  评论(0编辑  收藏  举报