UVA 1451 Average

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4197


题目大意:在01序列中找到一个长度不小于L的序列,使得该序列的平均值最大,输出区间端点,并保证左端点最靠左。


求平均值也就是求(a[l]+a[l+1]+···+a[r])/(r-l+1)

设前缀和为s,则等价于求max((s[r]-s[l-1])/(r-(l-1)))/,即求max((s[r]-s[l])/(r-l))

即(i,s[i])两点的最大斜率,维护凸包(下凸),如图


最优点即为当前点与凸包的切点

对于不满足下凸的点一定不是最优点,所以只需每次维护单调性是图形为下凸

并且可知,当前不为最优解的前端的点,在后面也不可能是最优解,所以同样从队头删除

再用队头更新。


总结一下,单调队列的步骤:

1.删除队尾维护单调性

2.插入队尾

3.删除队头不优的点

4.用队头更新


PS:把有的部分写成函数可以让代码更简洁一些。。。以及,初值要注意啊


代码如下:

#include<cstdio>

using namespace std;

	int T;
	int n,L;
	int s[100050];
	int q[100050];
	int l,r;
	int ansl,ansr;
	char op;
	int now;

int get(int a,int b,int c,int d)
{
	return (s[a]-s[b])*(c-d)-(s[c]-s[d])*(a-b);
}

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d%d",&n,&L);
		getchar();
		s[0]=0;
		for (int i=1;i<=n;i++)
		{
			op=getchar();
			s[i]=op-'0'+s[i-1];
		}
		l=1;
		r=0;
		ansl=0;
		ansr=L;
		for (int i=L;i<=n;i++)
		{
			now=i-L;
			while (l<r && get(q[r-1],q[r],q[r],now)>0)
			{
				r--;
			}
			r++;
			q[r]=now;
			
			while (l<r && get(q[l+1],i,q[l],i)>=0)
			{
				l++;
			}
			
			if (get(q[l],i,ansl,ansr)>0 || (get(q[l],i,ansl,ansr)==0 && ansr-ansl>i-q[l]))
			{
				ansl=q[l];
				ansr=i;
			}
		}
		
		ansl++;
		printf("%d %d\n",ansl,ansr);
	}
	
	return 0;
} 


PS:把有的部分写成函数可以让代码更简洁一些。。。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值