1. 程式人生 > 實用技巧 >洛谷 P1886 滑動視窗 /【模板】單調佇列

洛谷 P1886 滑動視窗 /【模板】單調佇列

題目傳送門

普及演算法都不會,參加個錘子CSP—S

思路

完全剽竊別人的思路,話說回來,我要是會還學它幹啥。單調佇列,顧名思義,佇列裡的元素都是有單調性的,通過維護單調性,優化時間複雜度。幹說其實根本沒用,直接看題。首先根據題解的思路,求最小值的時候將佇列內元素從小到大排列,這樣每次查詢最小值的時候,只需要彈出隊首即可。然後我們考慮怎麼樣維護單調佇列。當來了一個新數,我們就從後往前找(就是從大到小),如果這個新數比佇列中的那些元素都大,那麼那些大的元素就已經不可能作為最小值的候選人了,所以直接彈出佇列即可。每次迴圈還要檢查一遍佇列的跨度,如果隊首和當前的序號相差過遠了,那麼就只好忍痛割愛,彈出最小的元素了。不難發現,普通的佇列是無法實現單調佇列的(我不會告訴你我想了一個小時才發現了這個道理),因為它既要彈出隊首元素也要彈出隊尾元素,這就需要手寫一個雙端佇列來實現。其實並不難寫,就是隊尾也可以彈出元素罷了。但是程式碼實現還是有一些細節的。

程式碼

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
int n,m;
ll a[1000005];
int q[1000005],p[1000005];
void _minn(){
	int head=1,tail=0;//這樣初始化,是因為佇列內一開始是空的,而佇列為空時就相當於是head>tail
	for(int i=1;i<=n;i++){
		while(head<=tail&&q[tail]>=a[i]){//維護單調性
			tail--;
		}
		q[++tail]=a[i];
		p[tail]=i;
		if(p[head]<=i-m) head++;//“視窗”長度過長,被迫拋棄最小元素(如果將這一行加到while迴圈前面的話,則需要加上head<=tail這一個條件。否則的話若區間長度為1,則第一次迴圈後head=2,tail=0,喜提RE)
		if(i>=m) printf("%lld ",a[p[head]]);
	}
	printf("\n");
}
void _maxx(){
	int head=1,tail=0;
	for(int i=1;i<=n;i++){
		while(head<=tail&&q[tail]<=a[i]){
			tail--;
		}
		q[++tail]=a[i];
		p[tail]=i;
		if(p[head]<=i-m) head++;
		if(i>=m) printf("%lld ",a[p[head]]);
	}
	printf("\n");
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	_minn();
	_maxx();
	return 0;
}