题意:给出一个序列,删除一个连续的子串后使得剩下的平均值最小。
典型的01分数规划,令f(x)=(sum1[i]+sum2[j])/(i+j).sum1表示前缀和,sum2表示后缀和,那么我们就相当于求出f(x)的最小值。
令f(x)=y,化简则有(sum1[i]-i*y)+(sum2[j]-j*y)=0,我们二分y,找出满足这个式子的y的最小值。
根据这个式子可以把序列都减去一个y,这样就相当于求新序列的前缀和sum1[i]+sum2[j]>=0.
实际上就是求min(sum1[i]+sum2[j])>=0,转化一下就变成了求新序列的最大子串和的经典DP问题。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-7 # define MOD 1024523 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=100005; //Code begin... int a[N], n; double b[N], dp[N], sum; bool check(double x){ sum=0; FOR(i,1,n) b[i]=a[i]-x, sum+=b[i]; double res=-INF; FO(i,2,n) { dp[i]=max(dp[i-1]+b[i],b[i]); res=max(res,dp[i]); } return sum<=res; } int main () { scanf("%d",&n); FOR(i,1,n) scanf("%d",a+i); double l=0, r=INF, mid; FOR(i,1,200) { mid=(l+r)/2.0; if (check(mid)) r=mid; else l=mid; } printf("%.3lf ",mid); return 0; }