#dDDLDPybttg050501. 1597:【 例 1】滑动窗口

1597:【 例 1】滑动窗口

好的,我将这道题整理为清晰的题面格式,并补充样例解释、数据范围与时空限制:


题目描述

原题来自:POJ 2823

给定一个长度为 NN 的数组 aa,一个长度为 KK 的滑动窗口从数组最左端移动到最右端。你只能看到窗口中的 KK 个数,每次窗口向右移动一位。

请找出窗口在每个位置时的最大值和最小值。


输入格式

第一行两个整数 NNKK

第二行 NN 个整数,表示数组的 NN 个元素(值在 [2×109,2×109][-2\times 10^9, 2\times 10^9] 范围内)。


输出格式

第一行输出滑动窗口从左向右移动到每个位置时的最小值,共 NK+1N-K+1 个数,每个数之间用一个空格分开;

第二行输出滑动窗口从左向右移动到每个位置时的最大值,共 NK+1N-K+1 个数,每个数之间用一个空格分开。


样例

样例输入

8 3
1 3 -1 -3 5 3 6 7

样例输出

-1 -3 -3 -3 3 3
3 3 5 5 6 7

样例解释

数组:[1,3,1,3,5,3,6,7][1, 3, -1, -3, 5, 3, 6, 7]K=3K=3

窗口位置及各位置的最小值、最大值:

窗口 最小值 最大值
[1 3 -1] -3 5 3 6 7 -1 3
1 [3 -1 -3] 5 3 6 7 -3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7
1 3 -1 -3 [5 3 6] 7 3 6
1 3 -1 -3 5 [3 6 7] 7

因此:

  • 最小值序列:1,3,3,3,3,3-1, -3, -3, -3, 3, 3
  • 最大值序列:3,3,5,5,6,73, 3, 5, 5, 6, 7

数据范围

  • 对于 20%20\% 的数据,KN1000K \le N \le 1000
  • 对于 50%50\% 的数据,KN105K \le N \le 10^5
  • 对于 100%100\% 的数据,KN106K \le N \le 10^6

时空限制

  • 时间:1000 ms1000 \text{ ms}
  • 内存:524288 KB524288 \text{ KB}

提示
此题是经典的 滑动窗口最值 问题,可以用 单调队列 在线性时间内解决。

方法

  • 最小值:维护一个单调递增的双端队列,队列中存储数组下标。保证队首元素是当前窗口的最小值。
    • 当队首下标超出窗口范围时,弹出队首;
    • 当新元素 a[i]a[i] 入队时,从队尾开始弹出所有大于等于 a[i]a[i] 的元素(因为它们在窗口内不可能成为最小值),然后将 ii 入队;
    • 当窗口形成时(iK1i \ge K-1),队首元素即为当前窗口最小值。
  • 最大值:类似,维护一个单调递减的双端队列,保证队首元素是当前窗口的最大值。

步骤

  1. 先处理最小值:
    • 遍历 ii00N1N-1
      • 如果队首下标 qfrontiKq_{\text{front}} \le i-K,则弹出队首;
      • 从队尾弹出所有 a[qback]a[i]a[q_{\text{back}}] \ge a[i] 的下标;
      • ii 入队;
      • iK1i \ge K-1,输出 a[qfront]a[q_{\text{front}}]
  2. 同理处理最大值(弹出条件改为 a[qback]a[i]a[q_{\text{back}}] \le a[i])。

复杂度:每个元素入队出队一次,O(N)O(N)

注意NN 最大 10610^6,输入输出要用较快的 IO 方式(如 scanf/printf 或关闭同步的 cin/cout)。