一、题目
题目传送门:P1440 求m区间内的最小值 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1440
题目描述
二、题解
1.本题难点
本题用暴力解法时间复杂度为O(n*m)
所以我们就可以考虑用单调队列优化时间复杂度为O(n)
下面我们来了解什么是单调队列:
单调队列,顾名思义,是一种具有单调性的队列。众所周知,单调性有单调递增和单调递减两种,相应的单调队列也分为单调递增队列和单调递减队列两种。
-
单调递增队列:保证队列头元素一定是当前队列的最小值,用于维护区间的最小值。
-
单调递减队列:保证队列头元素一定是当前队列的最大值,用于维护区间的最大值。
实现流程:
实现单调队列,主要分为三个部分:
- 去尾操作 :队尾元素出队列。当队列有新元素待入队,需要从队尾开始,删除影响队列单调性的元素,维护队列的单调性。(删除一个队尾元素后,就重新判断新的队尾元素)
去尾操作结束后,将该新元素入队列。
-
删头操作 :队头元素出队列。判断队头元素是否在待求解的区间之内,如果不在,就将其删除。(这个很好理解呀,因为单调队列的队头元素就是待求解区间的极值)
-
取解操作 :经过上面两个操作,取出 队列的头元素 ,就是 当前区间的极值 。
2.本题注意事项
1.数据量大的时候,用cout输出的时候千万别和endl一起,如果我们的算法思路一致,可能就是用了cout<<endl;而超时建议改用printf或用cout<<"\n";
具体原因你试试就知道,本题用cout<<endl输出就会超时,具体说明请前往传送门
传送门:std::endl为什么导致程序变慢 – 胡超博客 (imhuchao.com)
定义两个超大数组时改用全局变量,不然你的编译器可能运行出错
3.过程讲解
1)定义两个数组,一个数组q[n]用来作为一个简单队列,一个数组a[n]用来接收传进来的元素,记得将数组最大化,确保数据量大的时候全部能存取。q[n]队列里存的是数组的下标,访问元素可以用a[q[n]]来访为第n个元素。
2)因为第一元素的前没有前m项,所以直接输出0
3)写一个单调队列
4.代码
#include <iostream>
using namespace std;
int q[2000005], a[2000005];
int main()
{
int m, k, front = 1, rear = 0;
cin >> m >> k;
for (int i = 1; i <= m; i++)
cin >> a[i];
cout << 0 << endl;
for (int i = 1; i <= m - 1; i++) //构建单调队列
{
while (front <= rear &&q[front] <= i - k) //保证队首在前m区间内
front++;
while (front <= rear && a[q[rear]] >= a[i])//维护一个单调递增队列
rear--;
q[++rear] = i; //入队
cout<<a[q[front]]<<"\n";
}
return 0;
}