[USACO14MAR] Sabotage 二分答案 分数规划

[USACO14MAR] Sabotage 二分答案 分数规划

最终答案的式子:

[frac{sum-sum[l,r]}{n-len[l,r]}le ans ]

转换一下:

[sum[1,l-1]+sum[r+1,n]le ans*(len[l,l-1]+len[r+1,n])\ sum (a[j]-ans)le 0 \ (jin[1,l-1],[r+1,n]) ]

这里我们先都减去(ans),然后求一个前缀和、后缀和、在(i)点最小的前缀和,在(i)点最小的后缀和,(O(n))枚举断点(也不算断点)如果存在则return 1

#include <cstdio>
#include <algorithm>
#define MAXN 100010
using namespace std;
int n;
int m[MAXN];
double psum[MAXN],lsum[MAXN],psum_min[MAXN],lsum_min[MAXN],a[MAXN];
const double eps=1e-6;
inline bool check(double x){
    for(int i=0;i<=n+1;++i) psum_min[i]=lsum_min[i]=1e9;
    for(int i=1;i<=n;++i) a[i]=(double)m[i]-x;
    for(int i=1;i<=n;++i) psum[i]=psum[i-1]+a[i], psum_min[i]=min(psum_min[i-1], psum[i]);
    for(int i=n;i;--i) lsum[i]=lsum[i+1]+a[i], lsum_min[i]=min(lsum_min[i+1], lsum[i]);
    for(int i=1;i<n-1;++i)
        if(psum_min[i]+lsum_min[i+2]<=0) return 1;
    return 0;
}
int main(){
    scanf("%d", &n);
    for(int i=1;i<=n;++i) scanf("%d", &m[i]);
    double l=1,r=10000,ans=-1;
    while(r-l>eps){
        double mid=(l+r)/2;
        if(check(mid)) r=mid,ans=mid;
        else l=mid;
    }
    printf("%.3lf", ans);
}
原文地址:https://www.cnblogs.com/santiego/p/11837627.html