二分+前綴和 - 最佳牛圍欄
農夫約翰的農場由 N 塊田地組成,每塊地裏都有一定數量的牛,其數量不會少於1頭,也不會超過2000頭。
約翰希望用圍欄將一部分連續的田地圍起來,並使得圍起來的區域內每塊地包含的牛的數量的平均值達到最大。
圍起區域內至少需要包含 F 塊地,其中 F 會在輸入中給出。
在給定條件下,計算圍起區域內每塊地包含的牛的數量的平均值可能的最大值是多少。
輸入格式
第一行輸入整數 N 和 F ,數據間用空格隔開。
接下來 N 行,每行輸出一個整數,第i+1行輸出的整數代表,第i片區域內包含的牛的數目。
輸出格式
輸出一個整數,表示平均值的最大值乘以1000再 向下取整 之後得到的結果。
數據範圍:
1≤N≤100000
1≤F≤N
輸入樣例:
10 6
6
4
2
10
3
8
5
9
4
1
輸出樣例:
6500
分析:
二分答案。
check函數:需要計算是否存在連續的長度大於等於f的區間的平均值大於等於mid。
我們枚舉區間的右端點k,計算區間[1,k]中長度大於等於f的子區間的最大平均值。
求區間和我們可以通過前綴和來預處理。
記Sk爲區間[1,k]的前綴和,則左端點至多爲k−f+1,我們需要在[1,k−f+1]中選擇一個左端點L,使得區間[L,k]的平均值最大。
事實上,我們要區間[L,k]和的平均值最大,就是要選擇數量大於平均值的地。
所以我們求前綴和時,將所有地中牛的數量先減去平均值,這樣數量小於平均值的地就變爲負數,
我們的目標就轉化爲求Sk−SL的最大值是否>=mid,即要求SL的最小值。
可用一個變量mins存儲區間[1,k−f+1]內子區間的最小的前綴和SL。
減去平均值avg前:判斷Sk−SL=∑i=L+1kai>=(k−L)×avg是否成立,
減去平均值avg後:判斷Sk−SL>=0是否成立即可。
代碼:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=100010;
int n,f;
double a[N],s[N];
bool check(double avg)
{
for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i]-avg;
double mins=0;
for(int k=f;k<=n;k++)
{
mins=min(mins,s[k-f]);
if(s[k]-mins>=0) return true;
}
return false;
}
int main()
{
cin>>n>>f;
double l=0,r=0;
for(int i=1;i<=n;i++) {scanf("%lf",&a[i]); r=max(r,a[i]);}
while(r-l>1e-5)
{
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%d\n",(int)(r*1000));
return 0;
}