洛谷P1886 滑动窗口

洛谷P1886 滑动窗口

题目大意:

​ 有n个数,每次取其中的k个找最大和最小,输出n-k+1个最大值和最小值

思路:

​ 如果单纯在每一个长度为k的区间里找最大值,时间复杂度是o(n^2)。要将时间复杂度降低到o(n),需要用到单调栈的思想,但也不完全是单调栈。以找最小值为例,维护一个栈底到栈顶单调递增的“单调栈”,栈内每个元素要有两个参数,下标idx和值v。具体操作如下:

1.若栈顶大于等于a[i],持续弹栈,直到将a[i]放在栈顶是可以维持升序的

2.若栈底的idx和i的差超过了k就是说“窗口”需要向右边滑动了,就需要将“栈底”上移

因为有栈底上移的操作,所以这个结构不是正常的单调栈,算是单调栈的变形。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

ll a[1001000];
struct p{
	ll idx,v;
}mi[2001100],ma[2001010];

int main(){
	ll n,k;
	ll lma = 0,lmi = 0;
	ll rma = 0,rmi = 0;
	
	scanf("%lld",&n);
	scanf("%lld",&k);
	
	for(ll i = 1 ; i <= n ; i ++){
		scanf("%lld",&a[i]);
	}
	
	for(ll i = 1 ; i <= n ; i ++){
		//mi升序
	
		while(rmi > 0 && rmi >= lmi && mi[rmi].v >= a[i])rmi -- ;
		
		mi[++rmi] = {i , a[i]};
		
		if(i == 1 || i - mi[lmi].idx == k)lmi++;
		
		if(i >= k)printf("%lld ",mi[lmi].v);
//		cout<<"i="<<i<<" ai="<<a[i]<<" l="<<lmi << " r="<< rmi <<"\n";
//		for(ll j = lmi ; j <= rmi ; j ++)
//		cout<<mi[j].v <<" ";cout<<endl;
	}
	cout<<endl;
	for(ll i = 1 ; i <= n ; i ++){
		//ma降序
	
		while(lma > 0 && rma >= lma && ma[rma].v <= a[i])rma -- ;
		
		ma[++rma] = {i , a[i]};
		
		if(i == 1 || i - ma[lma].idx == k)lma++;
		
		if(i >= k)printf("%lld ",ma[lma].v);

	}
	
}

小结:

​ 1.写栈的时候,最好还是用数组模拟吧,毕竟可以把整个栈打出来,找错比较容易

​ 2.边界条件的考虑很重要

​ 3.用单调栈记录的是一个区间里的上升子序列,并且该上升子序列的第一位是该区间里的最小值

posted @ 2021-09-01 16:33  tyrii  阅读(60)  评论(0编辑  收藏  举报