191103-单调队列练习

191103-单调队列练习

T1 志愿者选拔

题目描述

在这里插入图片描述

解析

在这里插入图片描述

题解

#include<bits/stdc++.h>
#define M 1000009
using namespace std;
int tot,tail,head,last,T,x,tim[M],q[M];
char s[100],s1[100];
int main()
{
	freopen("select.in","r",stdin);
	scanf("%d",&T);
	while(T--)
	{
		scanf("%s",s);
		tot=0;
		last=0;
		tail=0;
		head=1;
		memset(q,0,sizeof(q));
		memset(tim,0,sizeof(tim));
		while(scanf("%s",s)&&s[0]!='E'){
			if(s[0]=='C')
			{
				scanf("%s",s1);
				scanf("%d",&x);
				while(x>=q[tail]&&tail>=head) tail--;
				q[++tail]=x;
				tim[tail]=++tot;
			}
			if(s[0]=='Q')
			{
				if(head<=tail)
					printf("%d\n",q[head]);
				else
					printf("-1\n");
			}
			if(s[0]=='G')
			{
				last++;
				while(tim[head]<=last&&head<=tail) head++; 
			}
		}
	}
	return 0;
} 

T2 广告印刷

题目描述

在这里插入图片描述

解析

在这里插入图片描述

题解

#include<bits/stdc++.h>
#define M 1000006
using namespace std;
long long a[M],num[M],q[M],l[M],r[M],n,head,tail,ans;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	head=1;
	tail=0;
	a[n+1]=a[0]=0;
	for(int i=1;i<=n+1;i++)
	{
		while(a[i]<q[tail]&&head<=tail)
		{
			r[num[tail]]=i-1;
			tail--;
		}
		tail++;
		q[tail]=a[i];
		num[tail]=i;
	}
	head=1;
	tail=0;
	memset(num,0,sizeof(num));
	memset(q,0,sizeof(q));
	for(int i=n;i>=0;i--)
	{
		while(a[i]<q[tail]&&head<=tail)
		{
			l[num[tail]]=i+1;
			tail--;
		}
		tail++;
		q[tail]=a[i];
		num[tail]=i;
	}
	for(int i=1;i<=n;i++)
		ans=max(ans,a[i]*(r[i]-l[i]+1));
	printf("%lld",ans);
	return 0;
}

T3 滑动的窗户

题目描述

在这里插入图片描述

解析

在这里插入图片描述

题解

#include<bits/stdc++.h>
#define M 1000006
using namespace std;
int n,m,k,tot,last,tail,head,a[M],q[M],maxn[M],minn[M],tim[M];
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	head=1;
	tail=0;
	for(int i=1;i<=k;i++)
	{
		while(a[i]>=q[tail]&&tail>=head)
			tail--;
		q[++tail]=a[i];
		tim[tail]=++tot;
	}
	maxn[++m]=q[head];
	for(int i=k+1;i<=n;i++)
	{
		last++;
		while(tim[head]<=last&&head<=tail) head++; 
		while(a[i]>=q[tail]&&tail>=head)
			tail--;
		q[++tail]=a[i];
		tim[tail]=++tot;
		maxn[++m]=q[head];
	}
	memset(q,0,sizeof(q));
	memset(tim,0,sizeof(tim));
	tot=0;
	last=0;
	head=1;
	tail=0;
	m=0;
	for(int i=1;i<=k;i++)
	{
		while(a[i]<=q[tail]&&tail>=head)
			tail--;
		q[++tail]=a[i];
		tim[tail]=++tot;
	}
	minn[++m]=q[head];
	for(int i=k+1;i<=n;i++)
	{
		last++;
		while(tim[head]<=last&&head<=tail) head++; 
		while(a[i]<=q[tail]&&tail>=head)
			tail--;
		q[++tail]=a[i];
		tim[tail]=++tot;
		minn[++m]=q[head];
	}
	for(int i=1;i<=n-k+1;i++)
		printf("%d ",minn[i]);
	printf("\n");
	for(int i=1;i<=n-k+1;i++)
		printf("%d ",maxn[i]);
	return 0;
}

T4 限定长度m的最大和

题目描述

在这里插入图片描述

解析

在这里插入图片描述

题解

#include<bits/stdc++.h>
#define M 300006
using namespace std;
int n,m,sum[M],a[M],head=1,tail,ans,q[M];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	for(int i=1;i<=n;i++)
	{
		while(tail>=head&&i-q[head]>m) head++;
		ans=max(ans,sum[i]-sum[q[head]]);
		while(tail>=head&&sum[i]<=sum[q[tail]]) tail--;
		q[++tail]=i;
	}
	printf("%d",ans);
	return 0;
}

练习题

扫描

逛画展

琪露诺

题解

单调队列优化dp

#include<bits/stdc++.h>//单调队列 
using namespace std;
int a[400009],n,l,r,f[400009],ans,head,tail,q[400009],p;
int main()
{
	scanf("%d%d%d",&n,&l,&r);
	for(int i=0;i<=n;i++)
		scanf("%d",&a[i]);
	memset(f,128,sizeof(f));
	ans=-1e8;
	f[0]=0;
	head=1;
	tail=0;
	q[++tail]=0;
	p=0;
	for(int i=l;i<=n;i++)
	{
		while(f[p]>=f[q[tail]]&&tail>=head) tail--;
		q[++tail]=p;
		while(q[head]+r<i&&head<=tail) head++;
		f[i]=q[head]+a[i];
		p++;
 	}
	for(int i=n-r+1;i<=n;i++)
		ans=max(ans,f[i]);
	printf("%d",ans);
	return 0;
} 

切蛋糕

题解
#include<bits/stdc++.h>//单调队列 
using namespace std;
int n,m,ans=-1e8,head=1,tail,a[200009],q[200009],sum[200009];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	for(int i=1;i<=n;i++)
	{
		while(sum[i]<=sum[q[tail]]&&tail>=head) tail--;
		q[++tail]=i;
		while(q[head]+m<i&&head<=tail) head++;
		ans=max(ans,sum[q[tail]]-sum[q[head]]);
	}
	printf("%d",ans);
	return 0;
}

[USACO09MAR]向右看齐Look Up

题解
#include<bits/stdc++.h>//单调队列 
#define M 100009
using namespace std;
int n,q[M],a[M],ans[M];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	int head=1;
	int tail=0;
	for(int i=1;i<=n;i++)
	{
		while(a[i]>a[q[tail]]&&tail>=head)
		{
			ans[q[tail]]=i;
			tail--;
		}
		q[++tail]=i;
	}
	for(int i=1;i<=n;i++)
		printf("%d\n",ans[i]);
	return 0;
}

Bad Hair Day

题解
#include<bits/stdc++.h>//单调队列 
#define M 100009
using namespace std;
long long n,q[M],a[M],ans;
int main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	int head=1;
	int tail=0;
	a[n+1]=1e17;
	for(int i=1;i<=n+1;i++){
		while(a[i]>=a[q[tail]]&&tail>=head){
			ans+=(i-q[tail]-1);
			tail--;
		}
		q[++tail]=i;
	}
	printf("%lld\n",ans);
	return 0;
}

Feel Good

题解
#include<bits/stdc++.h>//单调队列 
#define M 100009 
using namespace std;
long long n,head,tail,a[M],q[M],ans,sum[M],ans1,ans2;
int main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	head=1;
	tail=0;
	a[n+1]=0;
	for(long long i=1;i<=n+1;i++)
	{
		while(a[i]<a[q[tail]]&&tail>=head)
		{
			if(ans<(a[q[tail]]*(sum[i-1]-sum[q[tail-1]])))
			{
				ans=a[q[tail]]*(sum[i-1]-sum[q[tail-1]]);
				ans1=q[tail-1]+1;
				ans2=i-1;
			}
			if(ans==(a[q[tail]]*(sum[i-1]-sum[q[tail-1]])))
			{
				if(i-1-q[tail-1]==ans2-ans1)
				{
					ans1=min(q[tail-1]+1,ans1);
					ans2=min(i-1,ans2);
				}
				if(i-1-q[tail-1]<ans2-ans1)
				{
					ans1=q[tail-1]+1;
					ans2=i-1;
				}
			}
			tail--;
		}
		q[++tail]=i;
	}
	printf("%lld\n",ans);
	printf("%lld %lld",ans1,ans2);
	return 0;
}
解析

简化题意:给出一组数字,求一区间,使得区间元素和乘以区间最小值最大,结果要求给出这个最大值和区间的左右端点

解题:我们枚举每一个序列中的数,用一个单增的单调队列来维护,当它为区间中最小元素时,该区间的信息

然后每次遇到一个新的数,就把队尾比新数大的数都弹出,计算被弹出的数为最小数时的答案,然后更新。

注意要将n+1入队,从而使得每个数最后都弹出队列,来更新答案

Largest Rectangle in a Histogram

题解
#include<bits/stdc++.h>//单调队列 
#define M 200006
using namespace std;
long long n,a[M],q[M],r[M],l[M],ans,head,tail; 
int main()
{
	while(scanf("%lld",&n)&&n!=0)
	{
		memset(a,0,sizeof(a));
		memset(q,0,sizeof(q));
		memset(l,0,sizeof(l));
		memset(r,0,sizeof(r));
		for(int i=1;i<=n;i++)
			scanf("%lld",&a[i]);
		head=1;
		tail=0;
		a[0]=a[n+1]=-10;
		for(int i=1;i<=n+1;i++)
		{
			while(a[i]<a[q[tail]]&&tail>=head) 
			{
				r[q[tail]]=i-1;
				tail--;
			}
			q[++tail]=i;
		}
		head=1;
		tail=0;
		memset(q,0,sizeof(q));
		for(int i=n;i>=0;i--)
		{
			while(a[i]<a[q[tail]]&&tail>=head) 
			{
				l[q[tail]]=i+1;
				tail--;
			}
			q[++tail]=i;
		}
		ans=0;
		for(int i=1;i<=n;i++)
			ans=max(ans,a[i]*(r[i]-l[i]+1));
		printf("%lld\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值