天天看點

【洛谷 1886】 滑動視窗 /【模闆單調隊列】

Time Limit: 12000MS Memory Limit: 65536K

Total Submissions: 89298 Accepted: 24979

Case Time Limit: 5000MS

題目描述

給定一個數列,從左至右輸出每個長度為m的數列段内的最小數和最大數。

數列長度: N < = 1 0 6 , m < = N N<=10^6,m<=N N<=106,m<=N (以最大值為例)

輸入格式

輸入一共有兩行,第一行有兩個正整數 n , k n,k n,k。 第二行 n n n 個整數,表示序列 a a a。

輸出格式

輸出共兩行,第一行為每次視窗滑動的最小值

第二行為每次視窗滑動的最大值

輸入

8 3

1 3 -1 -3 5 3 6 7

輸出

-1 -3 -3 -3 3 3

3 3 5 5 6 7

說明/提示

【資料範圍】

對于 50 50 50% 的資料, 1 ≤ n ≤ 1 0 5 ; 1≤n≤10^5 ; 1≤n≤105;

對于 100 100 100% 的資料, 1 ≤ k ≤ n ≤ 1 0 6 , a i ∈ [ − 2 3 1 , 2 3 1 ) 。 1≤k≤n≤10^6,a i ∈[−2^31^ ,2^31^ )。 1≤k≤n≤106,ai∈[−231,231)。

解題思路

【洛谷 1886】 滑動視窗 /【模闆單調隊列】
【洛谷 1886】 滑動視窗 /【模闆單調隊列】
【洛谷 1886】 滑動視窗 /【模闆單調隊列】

分析:我們以求最大值為例

使用單調隊列就涉及到去頭和删尾:

單調隊列具有隊列内所有元素不是單調遞增就是單調遞減的性質,是以每次的最小(最大)值一定會在隊首

程式實作過程中先将前 k k k個元素入隊,此後每次在隊尾加入 a [ k + 1... n ] a[k+1...n] a[k+1...n],在插入元素中同時進行以下操作:

1、将隊尾所有大于 a [ i ] a[i] a[i]的值彈出隊列

2、插入 a [ i ] a[i] a[i]到隊尾

3、判斷隊首元素位置是否超出 i − k i-k i−k

代碼

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,m,a[1000010],h,t;
struct c{
	int x,y;
}st[1000010];
struct cc{
	int x,y;
}st1[1000010];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	h=1,t=1;
	for(int i=1;i<=n;i++)
	{
		while(h<t&&st[t-1].x>=a[i])t--;//當st1[t-1].x比隊尾值更優時把隊尾值彈出,删尾
		st[t].x=a[i],st[t].y=i,t++;//記錄目前點的編号,得到最優解并插入
		if(i>=m)
		{
			while(h<t&&st[h].y<=i-m) h++;//删頭
			printf("%d ",st[h].x);	
		}
	}
	printf("\n"); 
	h=1,t=1;
	for(int i=1;i<=n;i++)
	{
		while(h<t&&st1[t-1].x<=a[i])t--;//當st1[t-1].x比隊尾值更優時把隊尾值彈出,删尾
		st1[t].x=a[i],st1[t].y=i,t++;//記錄目前點的編号,得到最優解并插入
		if(i>=m)
		{
			while(h<t&&st1[h].y<=i-m) h++;//删頭
			printf("%d ",st1[h].x);	
		}
	}	
	return 0;
} 


           

繼續閱讀