最佳牛围栏题解 二分答案
我真的会二分吗,显然是不,我还必须要学习
对于这个题:https://www.acwing.com/problem/content/description/104/
我们要找的是 有没有一段不小于F的区间,使这段区间的平均数尽可能的大,如果我们找到了一段连续的区间且区间长度不小于F且平均数大于我们二分的平均数
那么大于这个数且区间也满足不小于F的一定满足 我们直接判断正确即可,这是证明其具有二分性;思考其具有单调性的原因;
因为我们要找一段区间的平均数,根据平均数的小技巧,对于一段序列减去当前的avg,并且比较是否为0,大于0则大于平均数,使用前缀和,判断是否为正,
判断存在平均值大于当前的avg,就能判断一个区间内的平均值是否大于或小于我们二分的平均数了
我们还可以继续优化,因为我们不仅需要找F大小区间内,我们还要找>F大小区间内的,我们如果用二次枚举太费时间了,
我们这里可以使用双指针的做法,我们设i=0,j=F 每次使两个数++ 因为i,j始终满足相距F的距离,但每次i增长,j只会+1,所以我们用一个变量minv来存储ii所遍历到的最小
值,我们进行比较时,只需要将sum[j]与当前遍历到的最小值minv比较,这样我们比较的距离一定是≥F的,并且如果我们用j位的前缀和数减去minv的话,就能得到我们的最优解,如果这个最优解>= 0 那么就满足我们的指定条件。return true即可;
二分答案要满足答案具有单调性,具有可二分性,而并不是这个序列的关系;
#include<bits/stdc++.h> using namespace std; const int N = 100010; int n, f; int a[N]; double sum[N]; double l = 0, r = 2000; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} x*=f; } bool check(double avg) { for (int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + a[i] - avg; double minv = 0; for (int i = 0, j = f; j <= n; ++i, ++j) { minv =min(sum[i], minv); if (sum[j] - minv >= 0) return true; } return false; } int main() { read(n); read(f); for (int i = 1; i <= n; i++) read(a[i]); double eps = 1e-5; while (r - l > eps) { double mid = (r + l) /2; if (check(mid)) l = mid; else r = mid; } cout << (int) (r * 1000); }