最佳牛围栏题解 二分答案

我真的会二分吗,显然是不,我还必须要学习

对于这个题: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
using namespace std;

const int N = 100010;

int n, f;
int a[N];
double sum[N];
double l = 0, r = 2000;

templateinline 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);
}
View Code

 

转载于:https://www.cnblogs.com/Tyouchie/p/10863481.html

你可能感兴趣的:(最佳牛围栏题解 二分答案)