AcWing102 最佳牛围栏(二分答案)

题目

首先简化问题,给一段长度为n的序列,求长度大等于L的子段,他的平均值最大。

考虑二分答案(因为平均值具有单调性),将求解转化为判定。

当我们二分了一个平均值,让原序列都减去这个平均值,判定是否存在长度大等于L的子序列和非负,即求最大子段和非负。

经典的最大子段和问题,加上长度限制,列出表达式:

max\left \{ A_{j+1} + A_{j+2}...+A_i \right \}\left ( i - j\geqslant L \right ) =max\left \{ sum_i - min\left \{ sum_j \right \}\right \}(L\leqslant i\leqslant n)(0\leqslant j\leqslant i-L)

sumi表示前缀和,观察表达式,随i增加1,j也增加1,故不需枚举j,只需用一个变量记录min{sumj}

#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-6;
int n, L;
double a[100001], b[100001], sum[100001], zx = 2001, zd = 0;
bool check(double x) {
	double ans = -0x3f3f3f3f;
	memset(sum, 0, sizeof(sum));
	memcpy(b, a, sizeof(b));
	for (int i = 1; i <= n; i ++) {
		b[i] -= x;
		sum[i] = sum[i - 1] + b[i];
	}
	double sumj = 0x3f3f3f3f;
	for (int i = L; i <= n; i ++) {
		sumj = min(sumj, sum[i - L]);//记录min{sumj}
		ans = max(ans, sum[i] - sumj);
	}
	if (ans >= 0) return 1;
	else return 0;
}
int main() {
	cin >> n >> L;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
		zx = min(zx, a[i]);
		zd = max(zd, a[i]);
	}
	double l = zx, r = zd;//平均值的上下界
	while (l + eps < r) {
		double mid = (l + r) / 2;
		if (check(mid)) l = mid;
		else r = mid;
	}
	//cout << (int) r * 1000;错误 
	cout << int (r * 1000);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值