问题描述
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);
}