【算法每日一练]-单调队列,滑动窗口 篇2 滑动窗口 ,求m区间的最小值 ,理想的正方形

目录

 题目:滑动窗口

思路:

 题目:求m区间的最小值​编辑

思路:

题目:理想的正方形

思路:


今天讲单调队列

一共两种类型:一种是区间中的最值,另一种是区间和的最值,原理一样。

        

 题目:滑动窗口

        

思路:

    放这个题是为了给出模板。

      

单调队列使用注意事项:(遍历i就要维护[i-k+1,i]的单调区间,从而遍历所有的i)

      
入队新元素为i   
队尾出队是a[i]<a[q[t]](取不取等都一样,因为新元素来了都要入队)

队头过期是q[h]+k<i+1

      
1,维持单调:剔除因新来的元素导致的不单调元素
2,队尾入队:新来元素一定入队(以便更新队尾下标,方便和下一个新元素比较)
3,过期出队:队头元素的下标是否越界(和队头指针值无关)

       

#include <bits/stdc++.h>                
using namespace std;
int n,k;
int q1[1000001],q2[1000001],a[1000001];
void min_deque()
{
    int h=1,t=0;//h是头,t是尾(h和t的值没有任何意义,就当成front和back)
    for(int i=1;i<=n;i++)//从第一个数开始滑动
    {                                   //开始对上个元素的队列进行更新
        while(h<=t&&a[i]<a[q1[t]]) t--;//(维持单调)新元素入队后,剔除无效元素
        q1[++t]=i; //新元素入队操作
        if(q1[h]+k<=i) h++;//过期决策从队头出队
        if(i>=k) printf("%d ",a[q1[h]]);//输出最值
    }
    cout<<endl;
}
void max_deque()
{
    int h=1,t=0;
    for(int i=1;i<=n;i++)
    {
        while(h<=t&&a[i]>a[q2[t]]) t--;
        q2[++t]=i;
        if(q2[h]+k<=i) h++;
        if(i>=k) printf("%d ",a[q2[h]]);
    }
}
int main()
{
    cin>>n>>k; //n为长度,k为窗口大小
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    min_deque();//维护两个单调队列
    max_deque();
    return 0;
}

       

        

 题目:求m区间的最小值

         

思路:

      

就是遍历i时候,要创建维护[i-m,i-1]的最小值的单调队列,从而遍历所有的i
入队新元素为i-1     所以h<=t&&a[q[t]]>a[i-1]
过期队头和i-m比较  h<=t&&q[h]+m<i
      

#include <bits/stdc++.h>
using namespace std;
const int N=2e6+5;
int n,m,a[N],q[N];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	int h=1,t=0;
	cout<<0<<'\n';
	for(int i=2;i<=n;i++){
		while(h<=t&&a[q[t]]>a[i-1])t--;
		q[++t]=i-1;
		while(h<=t&&q[h]+m<i)h++;
		cout<<a[q[h]]<<'\n';
	}
	return 0;
}

       

     

题目:理想的正方形

       

思路:

      

之前讲的是一维的区间最值,现在的是二维区间最值,那么维护二维的单调队列即可。

操作:

先对原矩阵进行列压缩,使用单调队列一行一行处理,然后再对压缩后的矩阵进行行压缩,一列一列处理

      
注意事项: 
1,处理完后要从头开始放元素,这时就要注意别让下标越界了!!!
2,注意每个矩阵的行列数,可不一样,别写错了!!!
2,这个写法只针对小矩阵边长不为1 ,当为1时就需要特判处理,在外层循环加上:(memcpy(min2,min1,sizeof(min2));break;)
     

#include <bits/stdc++.h>//P2216:
using namespace std;
int n,m,k,h,H,t,T,ans;
int a[1001][1001],q[1001],Q[1001];//设置两个队列,以便同时完成最大值和最小值,
int x[1001][1001],X[1001][1001];//X,x为第一次压缩后的矩阵
int y[1001][1001],Y[1001][1001];//Y,y为最终最大值和最小值矩阵
void two_deque(){
	for (int i=1;i<=n;i++)//按行来处理(列压缩)同时处理最大值和最小值,不然一共要处理4次
		{
			H=T=h=t=Q[1]=q[1]=1; //同时完成最大值矩阵和最小值矩阵的行压缩
			for (int j=2;j<=m;j++)
				{
					while (a[i][j]>=a[i][Q[T]]&&H<=T) T--;//维持单调性(剔除元素,可以不加等号)
					while (a[i][j]<=a[i][q[t]]&&h<=t) t--;
					Q[++T]=j;q[++t]=j;//新元素一定入队
					while (j-Q[H]>=k) H++;//过期出队
					while (j-q[h]>=k) h++;
					if (j>=k) X[i][j-k+1]=a[i][Q[H]],x[i][j-k+1]=a[i][q[h]];
				}
		}
	for (int j=1;j<=m-k+1;j++)//按列来(注意列变少了)
		{
			H=T=h=t=Q[1]=q[1]=1;   //同时完成最大值矩阵和最小值矩阵的列压缩
			for (int i=2;i<=n;i++)
				{
					while (X[i][j]>=X[Q[T]][j]&&H<=T) T--;
					while (x[i][j]<=x[q[t]][j]&&h<=t) t--;
					Q[++T]=i;q[++t]=i;
					while (i-Q[H]>=k) H++;
					while (i-q[h]>=k) h++;
					if (i>=k) Y[i-k+1][j]=X[Q[H]][j],y[i-k+1][j]=x[q[h]][j];
				}
		}
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);//n,m为矩阵大小,k为需要输出的正方形大小(输出所有方形中极差的最小值)
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			scanf("%d",&a[i][j]);
	
	two_deque();		
    ans=0x3f3f3f3f;
	for (int i=1;i<=n-k+1;i++)
		for (int j=1;j<=m-k+1;j++)
			ans=min(ans,Y[i][j]-y[i][j]);
	printf("%d\n",ans);
	return 0;
}

     

    

  • 9
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值