【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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章