题目内容
有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少. 例如:
数列是 [1 3 -1 -3 5 3 6 7], 其中 k 等于 3.
输入格式
输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示待分析的数列。
输出格式
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。
输入样例
8 3
1 3 -1 -3 5 3 6 7
输出样例
-1 -3 -3 -3 3 3
3 3 5 5 6 7
题目分析
显然,如果对每个区间暴力求解,一旦k大起来,那时间复杂度直接上天了都。
对于此题,我们可以维护一个单调队列,并且是采用的deque双端队列,这样子我们就可以每一次只维护某个区间内的单调性了。
维护这个双端队列,把此队列的左端点的位置看作是起点,那么对于每次这个队列(即窗口)移动的时候,右端维护单调性,左端把边界以外的点丢出去,就能够只扫描一遍就求出每个区间下的最小值了。
最大值同理。
#include <iostream>
#include <vector>
#include <string>
#include <deque>
using namespace std;
int n, k;
struct Point
{
int num;
int index;
Point(int n,int i):num(n),index(i){}
};
deque<Point>minwindow;
deque<Point>maxwindow;
vector<int>minans;
vector<int>maxans;
void insert(Point p)
{
while (!minwindow.empty() && minwindow.back().num > p.num) minwindow.pop_back();
minwindow.push_back(p);
while (!maxwindow.empty() && maxwindow.back().num < p.num) maxwindow.pop_back();
maxwindow.push_back(p);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> k;
int temp;
//初始化
for (int i = 0; i < k - 1; i++)
{
cin >> temp;
Point t(temp, i);
insert(t);
}
for (int i = k - 1; i < n; i++)
{
int index = i - k + 1;//窗口最左边的位置
cin >> temp;
Point t(temp, i);
insert(t);
//弹出窗口最左边之前的数
while (minwindow.front().index < index) minwindow.pop_front();
while (maxwindow.front().index < index) maxwindow.pop_front();
minans.push_back(minwindow.front().num);
maxans.push_back(maxwindow.front().num);
}
for (int i = 0; i < minans.size(); i++)
{
cout << minans[i] << ' ';
}
cout << endl;
for (int i = 0; i < maxans.size(); i++)
{
cout << maxans[i] << ' ';
}
}