HDU 3276 Star

来源:互联网 发布:你知我知 我 杨过 编辑:程序博客网 时间:2024/06/06 03:50

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 -10000000001double 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;}elsel=mid;}printf("Case %d: %.3lf\n",k++,ret);}}


原创粉丝点击