题目:http://codeforces.com/contest/579/problem/E
题意:给出n个整数a1,a2,a3...an,你选一个实数x,得到序列a1-x,a2-x,a3-x...an-x,使得可得子序列的连续和的绝对值的最大值 最小。
思路:
x越大,子序列连续和为正时,其绝对值的最大值越小;
x越小,子序列连续和为负时,其绝对值的最大值越小。
所以,随着x由小变大,求得的答案由大变小再变小,使用三分。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <stdlib.h>
#define INF 0x7fffffff
#define MOD 1000000007
#define eps 1e-8
using namespace std;
typedef long long ll;
int n;
double a[200005];
double contisum()
{ //求最大连续和
double maxx = 0, cur = 0;
for(int i = 0; i < n; i++)
{
if(cur > -eps)
cur += a[i];
else cur = a[i];
if(cur - maxx > eps) maxx = cur;
}
return maxx;
}
double check(double x)
{
for(int i = 0; i < n; i++) //假设序列为a-x
a[i] -= x;
double maxx1 = contisum();
for(int i = 0; i < n; i++) //取反求最小连续和
a[i] = -a[i];
double maxx2 = contisum();
for(int i = 0; i < n; i++) //还原
a[i] = -a[i];
for(int i = 0; i < n; i++)
a[i] += x;
if(maxx1 - maxx2 > eps) return maxx1;
else return maxx2;
}
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
#endif
//printf("eps=%.12f\n", eps);
while(~scanf("%d", &n))
{
for(int i = 0; i < n; i++)
scanf("%lf", &a[i]);
double ans = INF;
//printf("ans=%.12f\n", ans);
double l = -10000.0, r = 10000.0;
for(int i = 0; i < 200; i++)
{
double mid = (l + r) / 2;
double rmid = (mid + r) / 2;
double temp1 = check(mid);
double temp2 = check(rmid);
//if(i <= 10) printf("temp1=%.8f temp2=%.8f\n", temp1, temp2);
if(temp1 - temp2 < eps)
r = rmid;
else l = mid;
ans = min(ans, min(temp1, temp2));
}
printf("%.12f\n", ans);
}
return 0;
}