首页 > 技术文章 > 最佳牛围栏 及 寻找段落

guiyou 2021-08-07 11:54 原文

因其两题具有相似的地方因此合为一篇题解 ----------题记

最佳牛围栏

思路:
二分答案
答案即为平均值

如何取check答案是否满足题意,判断一个满足题意的区间是否>=这个平均值
一般可以把序列都减去平均值,然后求出前缀和,判断区间是否大于0(转化为判定性问题)

if(sum[i]-min(sum[0]~sum[i-f])>0) return true;//如果成立此时区间平均值较小
  • code
#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;
int n,f;const int maxn=1e5+10;
int m[maxn];double sum[maxn];
bool check(double mid){
    sum[0]=0;
    for(int i=1;i<=n;++i) sum[i]=sum[i-1]+m[i]-mid;
    double minv=0;
    for(int i=0,j=f;j<=n;++i,++j){
        minv=min(minv,sum[i]);
        if(sum[j]>=minv) return true;
    }
    return false;
}
int main(){
    scanf("%d%d",&n,&f);
    for(int i=1;i<=n;++i) scanf("%d",&m[i]);
    double l=0,r=2000;
    while(r-l>1e-5){
        double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }printf("%d",int(r*1000));
}

寻找最小线段

其实思路有上述大同小异

唯一的差别
就是区间 有上限(类似于滑动窗口)

判断的时候
应该是这样

if(sum[i]-min(sum[i-t]~sum[i-s]) return 1;

维护一定区间的最小值可以考虑用到单调队列

bool check(double mid){
	for(int i=1;i<=n;++i) sum[i]=sum[i-1]+m[i]-mid;
	int head=1,tail=0;
	for(int i=1;i<=n;++i){
		if(i>=s){
			while(head<=tail && sum[i-s]<sum[q[tail]]) --tail;//挤掉比它大的数
			q[++tail]=i-s;
		}
		while(q[head]<i-t && head<=tail) ++head;
		if(sum[i]-sum[q[head]]>=0 && head<=tail) return 1; 
	} return 0; 
}  

ZFY AK IOI

推荐阅读