1. 程式人生 > 其它 >洛谷 P1886滑動視窗(單調佇列,deque實現)

洛谷 P1886滑動視窗(單調佇列,deque實現)

技術標籤:SDNUOJ佇列演算法c++stl

題目描述
有一個長為 n 的序列 a,以及一個大小為 k 的視窗。現在這個從左邊開始向右滑動,每次滑動一個單位,求出每次滑動後窗口中的最大值和最小值。

例如:
The array is [1,3,-1,-3,5,3,6,7], and k = 3。

在這裡插入圖片描述
輸入格式
輸入一共有兩行,第一行有兩個正整數 n,k。 第二行 n 個整數,表示序列 a

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

輸入輸出樣例:
輸入 #1
8 3
1 3 -1 -3 5 3 6 7
輸出 #1
-1 -3 -3 -3 3 3
3 3 5 5 6 7

在這裡插入圖片描述
關於單調佇列的解釋可以看這篇文章,講的通俗易懂:
連結
自己動手模擬一下過程就懂了。
自己寫的程式碼比較笨重= =

#include<iostream>
#include<deque>
#include<cstdio>
using namespace std;
struct node{
	int loc;
	int num;
};
deque<node> qu;//為了能在特定的時間彈出已經過期的資料,我們要記錄下資料在原陣列中的位置 
node dat[1000005];//儲存每組資料方便之後呼叫 
inline int read()
{
    int s = 0, w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
    return s * w;
} // 這是能判負數的C++快讀模板
inline void write(int x)
{
	if (x < 0) x = ~x + 1, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}//輸出掛 
int main()
{
	int n,k;
	n=read();
	k=read();
	for(int i=1;i<=n;++i)
	{
		dat[i].num=read();
		dat[i].loc=i;//儲存資料 
	}
	for(int i=1;i<=n;++i)//這是找每個區間的最小值 
	{
		if(i==1)//讓第一個元素入隊 
		{
			qu.push_back(dat[i]);
			if(k==1)//一個特判,我這樣寫當k=1時第一個資料就能輸出了 
			{
				 write(qu.front().num);
				 cout<<" ";
			}
		}
		else if(i<k)//還沒到要輸出的時候 
		{
			if(dat[i].num<=qu.front().num)//如果這個數比佇列中最小的(就是隊首)還小,那麼就清空佇列(因為後面的肯定比它大),讓這個資料入隊 
			{
				qu.clear();
				qu.push_back(dat[i]); 
			}
			else//如果這個數比隊首大,那麼我們就要將它放在合適的位置 
			{
				while(qu.back().num>dat[i].num)//將隊尾比這個數大的出隊,直到隊尾的數比它小,那麼我們就找到了這個數應該在的位置 
				qu.pop_back();
				qu.push_back(dat[i]);//將這個數入隊 
			}
		}
		else//滿足輸出條件了,準備輸出 
		{
			if(dat[i].num<=qu.front().num)//這些同上 
			{
				qu.clear();
				qu.push_back(dat[i]); 
			}
			else
			{
				while(qu.back().num>dat[i].num)
				qu.pop_back();
				qu.push_back(dat[i]);
			}
			if(i-qu.front().loc>=k) qu.pop_front();//判斷當前是否超過了隊首元素的生命週期,若超過了就讓隊首元素出隊 
			write(qu.front().num);//輸出 
			cout<<" ";
		}
	}
	cout<<endl;
	qu.clear();
	for(int i=1;i<=n;++i)//這是輸出每個區間中的最大值,原理同上 
	{
		if(i==1)
		{
			qu.push_back(dat[i]);
			if(k==1)//一個特判,我這樣寫當k=1時第一個資料就能輸出了 
			{
				write(qu.front().num);
				cout<<" ";
			}
		}
		else if(i<k)
		{
			if(dat[i].num>=qu.front().num)
			{
				qu.clear();
				qu.push_back(dat[i]); 
			}
			else
			{
				while(qu.back().num<dat[i].num)
				qu.pop_back();
				qu.push_back(dat[i]);
			}
		}
		else
		{
			if(dat[i].num>=qu.front().num)
			{
				qu.clear();
				qu.push_back(dat[i]); 
			}
			else
			{
				while(qu.back().num<dat[i].num)
				qu.pop_back();
				qu.push_back(dat[i]);
			}
			if(i-qu.front().loc>=k) qu.pop_front();
			write(qu.front().num);
			cout<<" ";
		}
	}
	cout<<endl;
	return 0;
}