zoukankan      html  css  js  c++  java
  • 「刷题笔记」单调队列与单调栈

    《兑命》曰:“当一个比你小的(OIer)比你强时,你就打不过他了”,我不信
    其(单调队列和单调栈)此之谓乎!
    (向语文老师谢罪

    单调队列

    滑动窗口

    贴一下板子……给出了(min,max)的处理方法

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    #define MAXN 1000005
    
    ll a[MAXN]={0};
    
    struct QUE
    {
    	ll v,id;
    }Q[MAXN];
    
    ll n,k;
    ll front,back;
    
    int main()
    {
    	scanf("%lld%lld",&n,&k);
    	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    	front=1;
    	back=0;
    	for(int i=1;i<=n;i++)
    	{
    		if(Q[front].id+k<=i)front++;
    		while(back>=front&&Q[back].v>a[i])back--;
    		Q[++back].v=a[i];
    		Q[back].id=i;
    		if(i>=k)printf("%lld ",Q[front].v);
    	}
    	cout<<endl;
    	front=1;
    	back=0;
    	for(int i=1;i<=n;i++)
    	{
    		if(Q[front].id+k<=i)front++;
    		while(back>=front&&Q[back].v<a[i])back--;
    		Q[++back].v=a[i];
    		Q[back].id=i;
    		if(i>=k)printf("%lld ",Q[front].v);
    	}
    	return 0;
    }
    

    Look Up 仰望

    弄清元素退队的过程,这题就很显然,因为每个元素都是被他之后第一个比他大的元素挤出去的,所以这个时候顺便存一下答案就成,感觉还行

    	front=1;
    	back=0;
    	for(int i=1;i<=n;i++)
    	{
    		while(front<=back&&h[i]>Q[back].v)
    		{
    			ans[Q[back].id]=i;
    			back--;
    		}
    		Q[++back].v=h[i];
    		Q[back].id=i; 
    	}
    

    广告印刷

    刚才学到,单调队列可以用来找一个元素前/后第一个比他大/小的元素
    这个题里,广告肯定是在一个地方贴上了楼顶的,否则向上扩展一格更优
    所以枚举每一个高度作为顶端的可能,分别用单调队列求出他左/右能扩展到的最大长度,最后合起来并乘上高度找出最大值即可

    	front=1;
    	back=0;
    	for(int i=1;i<=n+1;i++)
    	{
    		while(front<=back&&h[i]<=Q[back].v)
    		{
    			back--;
    		}
    		temp1[i]=i-Q[back].id;
    		Q[++back].v=h[i];
    		Q[back].id=i;
    	}
    	front=1;
    	back=0;
    	for(int i=n+1;i>=1;i--)
    	{
    		while(front<=back&&h[i]<=Q[back].v)
    		{
    			back--;
    		}
    		temp2[i]=Q[back].id-i;
    		Q[++back].v=h[i];
    		Q[back].id=i;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		ans=max(ans,(temp1[i]+temp2[i]-1)*h[i]);
    	}
    	printf("%lld",ans);
    

    良好的感觉

    明显枚举最不舒服是哪天,然后左右扩展就行
    当时拿广告的代码加了个前缀和直接就过了……

    理想的正方形

    很暴力,直接横纵跑两遍单调队列然后同时维护最大最小值就出来了,也不知道为啥洛谷上是蓝题……码得很乱,就不放了……

    单调栈

    就是不从队首出列的单调队列。

    City Skyline 城市地平线

    维护一个单增栈,插入时每当遇到比当前高的就++(ans)

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define MAXN 1005
    
    struct STA
    {
    	ll w,h;
    }S[MAXN];
    ll top=0;
    
    ll n,w,x,y;
    char ch;
    
    ll h[MAXN][MAXN]={0};
    
    ll ans;
    
    int main()
    {
    	scanf("%lld%lld",&n,&w);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%lld%lld",&h[i][0],&h[i][1]);
    	}
    //	cout<<endl;
    //	for(int i=1;i<=n;i++){for(int j=1;j<=m;j++)cout<<h[i][j]<<' ';cout<<endl;}
    //	cout<<endl;
    	ans=0;
    	top=0;
    	for(int i=1;i<=n+1;i++)
    	{
    		while(top>0&&h[i][1]<=S[top].h)
    		{
    			if(h[i][1]!=S[top].h)ans++;
    			top--;
    		}
    		S[++top].h=h[i][1];
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

    玉蟾宫

    预处理每个点往上能扩展的最大长度,然后每行跑单调栈。
    用一个(temp)记下被弹出所有元素的宽度,加上初始的(1)就是一个元素插进去后最大能向左延伸的宽度。一边跑一边更新答案就行。需要注意的是,为了更新答案,最后要把栈中所有元素都弹出来。记得(×3),就完事了

    #include<iostream>
    using namespace std;
    #define ll long long
    #define MAXN 1005
    
    ll k[MAXN][MAXN]={0};
    
    struct STA
    {
    	ll w,h;
    }S[MAXN];
    ll top=0,tw;
    
    ll n,m;
    char ch;
    
    ll h[MAXN][MAXN]={0};
    
    ll ans;
    
    int main()
    {
    	scanf("%lld%lld",&n,&m);
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=m;j++)
    		{
    			scanf("%s",&ch);
    			if(ch=='R')k[i][j]=0;
    			else if(ch=='F')k[i][j]=1;
    		}	
    	}
    	for(int j=1;j<=m;j++)
    	{
    		h[1][j]=k[1][j];
    		for(int i=2;i<=n;i++)
    		{
    			if(k[i][j])h[i][j]=h[i-1][j]+1;
    			else h[i][j]=0;
    		}
    	}
    //	cout<<endl;
    //	for(int i=1;i<=n;i++){for(int j=1;j<=m;j++)cout<<h[i][j]<<' ';cout<<endl;}
    //	cout<<endl;
    	ans=0;
    	for(int i=1;i<=n;i++)
    	{
    		top=0;
    		for(int j=1;j<=m;j++)
    		{
    			tw=0;
    			while(top>0&&h[i][j]<=S[top].h)
    			{
    				tw+=S[top].w;
    				ans=max(ans,S[top].h*tw);
    				top--;
    			}
    			S[++top].h=h[i][j];
    			S[top].w=tw+1;
    		}
    		tw=0;
    		while(top>0)
    		{
    			tw+=S[top].w;
    			ans=max(ans,S[top].h*tw);
    			top--;
    		}
    	}
    	printf("%lld",ans*3);
    	return 0;
    }
    

    Blocks

    首先明确,这题的答案就是找一个最长的区间,使(avg geq k)
    可以把所有元素-=(k),然后就变成了找最长的(sum geq 0)的区间
    如何找?想到维护个前缀和,那么所有(i,j)满足(pre_i leq pre_j)都是合法答案
    发现最优的答案只要使左端点尽量靠左,右端点尽量靠右
    所以先给左端点弄成单减的(因为尽量靠左,所以这里是(<S[top])的都直接压在栈顶)
    然后从最右边开始,一个一个把数往里丢,找到比这个数小的数中最靠左(也就是最大)的一个,更新答案,因为找到的这个点和新枚举的点组成的答案不会更优,所以这个过程中直接(top)--就行。
    代码很简单,重在思考。

    #include<bits/stdc++.h>
    #define MAXN 1000005
    #define ll long long
    using namespace std;
    
    ll a[MAXN]={0};
    ll p[MAXN]={0};
    ll n,m,t,ans;
    
    struct STA
    {
    	ll v,id;
    }S[MAXN]={0};
    ll top=0;
    
    int main()
    {
    	scanf("%lld%lld",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);	
    	while(m--)
    	{
    		scanf("%lld",&t);
    		for(int i=1;i<=n;i++)p[i]=a[i]+p[i-1]-t;
    		top=0;
    		ans=0;
    		for(int i=1;i<=n;i++)
    		{
    			if(top==0||p[i]<S[top].v)
    			{
    				S[++top].v=p[i];
    				S[top].id=i;
    			}
    		}
    		for(ll i=n;i>=1;i--)
    		{
    			if(p[i]>=0)
    			{
    				ans=max(ans,i);
    				continue;
    			}
    			while(top&&p[i]>=S[top].v)
    			{
    				ans=max(ans,i-S[top].id);
    				top--;
    			}
    		}
    		printf("%lld ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    BizTalk 2010 Adapter for mySAP Business Suite 所支持版本
    BizTalk Adapter Pack for Oracle 实现Oracle 11g的CRUD
    BizTalk 360 功能介绍 CTP版
    以小见大——那些基于 protobuf 的五花八门的 RPC(5 完)
    BizTalk Server 2010 + SQL Server 2008 R2 通过集成创造更大价值 [ 上篇 ]
    double to float
    Office 2003 PIA
    matlab传参数
    Convert.ToInt16
    矩阵转换
  • 原文地址:https://www.cnblogs.com/zzzuozhe-gjy/p/12728755.html
Copyright © 2011-2022 走看看