HDU 3276 Star

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);
	}
}
			


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值