#rMQybttg040202. 1542:【例 2】最敏捷的机器人

1542:【例 2】最敏捷的机器人

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


题目描述

Wind 设计了很多机器人。但是它们都认为自己是最强的,于是,一场比赛开始了……

机器人们都想知道谁是最敏捷的,于是它们进行了如下一个比赛。首先,它们面前会有一排共 nn 个数,它们比赛看谁能最先把每连续 kk 个数中最大和最小值写下来,当然,这些机器人运算速度都很快,它们比赛的是谁写得快。

但是 Wind 也想知道答案,你能帮助他吗?


输入格式

第一行为两个整数 n,kn, k,意义如题目描述。

第二行包含 nn 个整数,为数字序列,所有数字均在 Pascal 的 longint 范围内,即所有数均为整数,且在 [231,2311][-2^{31}, 2^{31}-1] 范围内。


输出格式

nk+1n-k+1 行,第 ii 行为第 ii 至第 i+k1i+k-1kk 个数中的最大值和最小值,两个数之间用一个空格隔开。


样例

样例输入

5 3
1 2 3 4 5

样例输出

3 1
4 2
5 3

样例说明

n=5,k=3n=5, k=3,序列 [1,2,3,4,5][1,2,3,4,5]

连续 kk 个数的窗口:

  • 窗口 [1,3][1,3]1,2,31,2,3,最大值 33,最小值 11
  • 窗口 [2,4][2,4]2,3,42,3,4,最大值 44,最小值 22
  • 窗口 [3,5][3,5]3,4,53,4,5,最大值 55,最小值 33

输出共 33 行。


数据范围

对于全部数据:

  • 1kn1051 \le k \le n \le 10^5

时空限制

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

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

方法

  • 维护两个双端队列:一个单调递减(用于最大值),一个单调递增(用于最小值);
  • 遍历 ii11nn
    • 若队首下标超出窗口范围(iqfront+1>ki - q_{\text{front}} + 1 > k),则弹出队首;
    • 在最大值队列中,从队尾弹出所有值 a[i]\le a[i] 的下标,保证队列递减;
    • 在最小值队列中,从队尾弹出所有值 a[i]\ge a[i] 的下标,保证队列递增;
    • ii 加入两个队列;
    • iki \ge k 时,输出最大值队列队首对应的值 和 最小值队列队首对应的值。

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

注意

  • nn 最大 10510^5O(n)O(n) 完全可行;
  • 输入输出量较大,建议使用 scanf/printf 或关闭同步的 cin/cout
  • 注意数据范围,数列元素在 int 范围内,但中间计算不会溢出。

代码框架

deque<int> q_max, q_min;
for (int i = 1; i <= n; i++) {
    // 维护最大值队列(递减)
    while (!q_max.empty() && a[q_max.back()] <= a[i]) q_max.pop_back();
    q_max.push_back(i);
    if (q_max.front() < i - k + 1) q_max.pop_front();
    
    // 维护最小值队列(递增)
    while (!q_min.empty() && a[q_min.back()] >= a[i]) q_min.pop_back();
    q_min.push_back(i);
    if (q_min.front() < i - k + 1) q_min.pop_front();
    
    if (i >= k) {
        printf("%d %d\n", a[q_max.front()], a[q_min.front()]);
    }
}