【JZOJ 3966】Sabotage(二分)

问题描述
FJ 的死对头,FP,现在决定了去破坏FJ 的挤奶设备!
这个挤奶设备由一行N(3 <= N<= 100, 000)个挤奶机器,其中第i 个机器生产Mi 单位的牛奶(1<= Mi <= 10, 000)。FP 计划将机器连续的一块断开——从第i 个机器到第j 个机器(2<= i<= j<= N-1);注意第一个和最后一个机器FJ 并不想要断开,因为这会让这次事件太容易被发现。FP 的目标是让剩下的机器的牛奶平均产量最小。FP 打算移去至少一台机器,即使对他来说不进行破坏更好。
幸运的是,FJ 已获悉FP 的邪恶计划,并且他想知道如果计划成功他的挤奶设备会被破坏成什么样。请帮助FJ 找到,如果FP 成功,最小的剩下挤奶机器的平均生产量。
输入
第一行:整数N。
第2 至N + 1 行:第i + 1 行包含Mi。
输出
输出单独一行一个整数——FP 能获得的最小的可能的平均数,四舍五入到小数点后3 位数字,小数点后三位数字都要输出。
样例输入
5
5
1
7
8
2
样例输出
2.667
算法讨论
我们知道,设一个b,将一个序列中所有元素都减b,若这个序列之和小于零,那么这个序列的平均数就比b小,反之若大于零,则序列平均数比b大。
现在我们二分一个b,将挤奶机产奶量依次减b,在2到n-1找到一个最大子区间,这样剩下的就会最小,若剩下的机器产奶量之和小于零,说明平均数小于当前b,向前二分;反之向后二分,直到精度小于0.0001退出。

#include <cstdio>
#define maxn 100006
using namespace std;
int a[maxn],n;
double sum[maxn],l,r,m;

bool work(double b)
{
    for (int i=1;i<=n;i++)
        sum[i]=sum[i-1]+a[i]-b;
    double maxx=a[2]-b,minn=a[1];
    for (int i=2;i<n;i++)
    {
        if (sum[i-1]<minn)
            minn=sum[i-1];
        if (sum[i]-minn>maxx)
            maxx=sum[i]-minn;
    }
    if (sum[n]-maxx<=0)
        return 1;
    return 0;
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if (a[i]>r)
            r=a[i];
    }
    while (l<r)
    {
        if (r-l<0.0001)
            break;
        m=(l+r) / 2;
        if (work(m))
            r=m;
        else
            l=m;
    }
    printf("%0.3lf",m);
}
阅读更多
版权声明:嗯随意转载吧,注明出处就好 https://blog.csdn.net/Fallen_Angel001/article/details/79509029
文章标签: c++ 二分 数学
个人分类: 二分 数学
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭