http://acm.hdu.edu.cn/showproblem.php?pid=3276
/*
题目给出了值的范围,可以二分一下答案,对每一个二分的均值,将原先序列每一个
都减去这个均值,假设这个均值可以达到,那么一定可以找到两个不连续的区间,它们
的和大于等于0.....这样,我们就可以枚举每个点,L[i]代表第i项以及i项之前选取一
段的最大值,R[i]代表第i项以及第i项之后选取一段的最大值,对每一个i,看是否L[i-1]+R[i+1]>=0即可
dp方程:
左边:L[i]=max( L[i-1], max(sum[i]-sum[j]) ) 主要考虑后一项的决择优化
L[i]代表前i项选取一段的最大值,考虑i这项选或者不选,不选,那就是L[i-1];
选就是 max(sum[i]-sum[j]),由于程度有限制,则i-j>=x&&i-j<=y ,化简一下
就是 i-y<=j<=i-x;
对于i-1项,j的范围是[i-y-1,i-x-1],对比i项,可以发现i项较之多了i-x这项,
同时少了i-y-1这一项,这就提供了队首删除和队尾删除的判断条件,假设代码中
p这个点删了原先队列中的元素,为什么就可以保证那些删了的点不会对之后的决策
造成影响?原因:那些删了的点,sum值肯定是大于p这个点的,而p这个点出现位置
是在那些删了的点之后,可以想象,p这个点对那些删了点来说,不仅具有数值上的
优势,更有位置上的优势,对于以后的点,那些被删的点如果满足长度要求,p这个
点就更满足了,所以被删的那些点不会造成影响;而队首删除,就是看队首元素的
位置是否小于等于i-y-1了,即<i-y,这些点不满足长度上的要求,是无意义的.
右边也是这样思考,只不过R[i]=max( R[i+1], max(sum[i]-sum[j]) ),x+i<=j<=y+i,
要反过来dp,反过来的dp比正向的思考过程可能有点绕,慢慢想,看着代码对照左边的
思路一定可以想明白的,就是考虑R[i]这项比R[i+1]这项多了什么,少了什么...
此外还要注意inf这个值的选择,要足够小
*/
#include<iostream>
using namespace std;
#define N 50005
#define esp 1e-5
#define inf -10000000001
double a[N],sum[N];
double L[N],R[N];
int q[N];
int n,x,y;
int head,tail;
bool find(double ave){
sum[0]=0;
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+a[i]-ave;
head=tail=0;
L[x-1]=inf;
for(int i=x;i<=n;i++){
int p=i-x;
while(head<tail&&sum[p]<sum[q[tail-1]])
tail--;
q[tail++]=p;
while(head<tail&&i-y>q[head])
head++;
L[i]=max(L[i-1],sum[i]-sum[q[head]]);
}
sum[n+1]=0;
for(int i=n;i>0;i--)
sum[i]=sum[i+1]+a[i]-ave;
head=tail=0;
R[n-x+2]=inf;
for(int i=n-x+1;i>0;i--){
int p=i+x;
while(head<tail&&sum[p]<sum[q[tail-1]])
tail--;
q[tail++]=p;
while(head<tail&&i+y<q[head])
head++;
R[i]=max(R[i+1],sum[i]-sum[q[head]]);
}
for(int i=x+1;i<=n-x;i++) //从x+1开始,是因为L[x-1]这项是没有意义的
if(L[i-1]+R[i+1]>=0)
return true;
return false;
}
int main(void){
int k=1;
while(~scanf("%d%d%d",&n,&x,&y)){
for(int i=1;i<=n;i++)
scanf("%lf",&a[i]);
double f=1.0,l=200000.0,ret;
while(l-f>=esp){
double mid=(f+l)/2;
if(find(mid)){
f=mid;
ret=mid;
}
else
l=mid;
}
printf("Case %d: %.3lf\n",k++,ret);
}
}