题目描述
原题来自:POJ 2823
给一个长度为 N 的数组,一个长为 K 的滑动窗体从最左端移至最右端,你只能看到窗口中的 K 个数,每次窗体向右移动一位,如下图:
|窗口位置|最小值|最大值|
|:-:|:-:|:-:|
|[1 3 -1] -3 5 3 6 7[1 3 -1] -3 5 3 6 7|−1−1|33|
| 1 [3 -1 -3] 5 3 6 7 1 [3 -1 -3] 5 3 6 7|−3−3|33|
| 1 3 [-1 -3 5] 3 6 7 1 3 [-1 -3 5] 3 6 7|−3−3|55|
| 1 3 -1 [-3 5 3] 6 7 1 3 -1 [-3 5 3] 6 7|−3−3|55|
| 1 3 -1 -3 [5 3 6] 7 1 3 -1 -3 [5 3 6] 7|33|66|
| 1 3 -1 -3 5 [3 6 7] 1 3 -1 -3 5 [3 6 7]|33|77|
你的任务是找出窗体在各个位置时的最大值和最小值。
输入格式
第 1 行:两个整数 N 和 K;
第 2 行:N 个整数,表示数组的 N 个元素(≤2×10^6);
输出格式
第一行为滑动窗口从左向右移动到每个位置时的最小值,每个数之间用一个空格分开;
第二行为滑动窗口从左向右移动到每个位置时的最大值,每个数之间用一个空格分开。
思路:
首先考虑只需求最大值的情况,一个很朴素的想法就是n^2的暴力枚举,然后我们手动模拟就可以发现如果存在s[j]>=s[i]&&j>i,那么在s[j]进入窗口后s[i]不可能成为最大值。所以每当数s进入区间,我们就可以把区间内比它小的全部踢掉。我们用smax数组记录维护区间内每个数在原数组内的下标,top记录区间右端,bot记录区间左端,每次新进来一个数前就一直将top左移直到s[max[top]]>s[i],如此一来区间内的数将是单调递减的,s[smax[bot]]便是区间内最大值,新数进入区间后再将左端右移使得区间长不超过m即可。
最小值的情况同理
#include<iostream>
#include<cstdio>
#define maxn 1010000
using namespace std;
int n,m,smin[maxn],smax[maxn],x,ans[maxn],ansh[maxn],top,bot=1,top1,bot1=1,s[maxn];
int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) {
scanf("%d",&x);
s[i]=x;
if(top!=0)while(s[smin[top]]>=x&&top>=bot)top--;
if(top1!=0)while(s[smax[top1]]<=x&&top1>=bot1)top1--;
smin[++top]=i;
smax[++top1]=i;
while(i-smin[bot]>=m)bot++;
while(i-smax[bot1]>=m)bot1++;
// printf("top:%d %d bot:%d %d\n",top,s[smin[top]],bot,s[smin[bot]]);
// printf("%d %d\n",smax[top],smax[bot]);
ans[i]=smin[bot];
ansh[i]=smax[bot1];
}
for(int i=m;i<=n;i++)printf("%d ",s[ans[i]]);
puts("");
for(int i=m;i<=n;i++)printf("%d ",s[ansh[i]]);
}