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