POJ 2018 Best Cow Fences 斜率优化/二分

Best Cow Fences POJ - 2018

N ( 1 ≤ N ≤ 100 , 000 ) N(1\le N\le 100,000) N(1N100,000) 块地,每块地有 n ( 1 ≤ n ≤ 2000 ) n(1\le n\le2000) n(1n2000) 头牛。

在这些地之间建两个篱笆,这两个篱笆中间包含的是连续的地,使得该篱笆内平均每块地的牛数最多,每两个篱笆中间至少有 F ( 1 ≤ F ≤ N ) F(1\le F\le N) F(1FN) 块地。

这个题跟 HDU 2993 MAX Average Problem 斜率DP+IO优化 是一样的。

维护一个斜率单增的单调队列,找 i i i 的斜率最大对应点时从队列的开头进行判断。

代码如下(时间复杂度 O ( n ) O(n) O(n)):

#include<iostream>
#include<cstdio>
#include<algorithm>
//#define WINE
#define MAXN 100100
using namespace std;
int n,f,a[MAXN],s[MAXN],h,t,q[MAXN],res;
int up(int j,int k){
    return s[j]-s[k];
}
int down(int j,int k){
    return j-k;
}
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    scanf("%d%d",&n,&f);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),s[i]=s[i-1]+a[i];
    h=t=0;q[t++]=0;
    for(int i=f;i<=n;i++){
        while(h+1<t&&up(i,q[h])*down(i,q[h+1])<up(i,q[h+1])*down(i,q[h]))
            h++;
        res=max(res,(s[i]-s[q[h]])*1000/(i-q[h]));
        int j=i-f+1;
        while(h+1<t&&up(j,q[t-1])*down(q[t-1],q[t-2])<up(q[t-1],q[t-2])*down(j,q[t-1]))
            t--;
        q[t++]=j;
    }
    printf("%d\n",res);
    return 0;
}

在这里插入图片描述
还可以用二分写:

#include<iostream>
#include<cstdio>
#include<algorithm>
//#define WINE
#define INF 0x3f3f3f3f
#define MAXN 100010
using namespace std;
int n,f,a[MAXN],Min=INF,Max=0;
double s[MAXN],l,r,mid,cur;
bool check(double k){
    for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i]-k;
    double m=0;
    for(int i=f;i<=n;i++){
        cur=s[i]-m;
        if(cur>=0)return true;
        m=min(m,s[i-f+1]);
    }
    return false;
}
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    scanf("%d%d",&n,&f);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        Min=min(Min,a[i]);
        Max=max(Max,a[i]);
    }
    l=Min;r=Max;
    double eps=1e-5;
    while(l+eps<r){
        mid=(l+r)/2;
        if(check(mid))l=mid;
        else r=mid;
    }
    printf("%d\n",(int)(r*1000));
    return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值