1.什么是单调队列
- 通俗的来讲单调队列就是用数组来模拟队列进行数据的插入和删除操作,以保证队列中的元素具有单调性(递增或递减)
2.单调队列例题 : AcWing-滑动窗口
题目大意:给定n个数求所有连续的k个数的最大值和最小值,并按顺序输出。
输入描述:
8 3
1 3 -1 -3 5 3 6 7
输出描述:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
样例解释(以下是最小值的情况,最大值的情况同理):
从左往右开始每个元素都会依次进入队列,在每一次进行队列维护的过程中都会保证队列中的元素个数大于一个小于等于k个。因为每插入一个新的元素ai之前都会进行一次维护保留小于ai的元素,所以队列中的元素一定是单调的,每次输出对头。(因为队列是单调的,所以每一次维护的时候就只用从队列的队尾开始向前遍历将小于ai的元素出队即可)。
3.单调队列优化dp : AcWing-绿色通道
题目描述:
高二数学《绿色通道》总共有 n 道题目要抄,编号 1,2,…,n,抄第 i 题要花 ai 分钟。小 Y 决定只用不超过 t 分钟抄这个,因此必然有空着的题。每道题要么不写,要么抄完,不能写一半。下标连续的一些空题称为一个空题段,它的长度就是所包含的题目数。这样应付自然会引起马老师的愤怒,最长的空题段越长,马老师越生气。现在,小 Y 想知道他在这 t 分钟内写哪些题,才能够尽量减轻马老师的怒火。由于小 Y 很聪明,你只要告诉他最长的空题段至少有多长就可了,不需输出方案。
输入格式:
第一行为两个整数 n,t。
第二行为 n 个整数,依次为 a1,a2,…,an。
输出格式:
输出一个整数,表示最长的空题段至少有多长。
数据范围:
0 < n ≤ ,0 < ai ≤ 3000,0 < t ≤ 。
样例输入:
17 11
6 4 5 2 5 3 4 5 2 3 4 5 2 3 6 3 5
样例输出:
3
这个题是二分查找 + 单调队列优化dp所以分两个代码模块来讲(最后附上AC代码)。
单调队列优化dp部分代码:
//a[]为原数组,q[]下标数组,f[]为记录从f[i]到1的符合条件的最小的a[i]和
int check(int x)//x为二分出来的间隔区间
{
int tt = 0,hh = 0, res = 1e9;
memset(f,0,sizeof f);
for(int i = 1;i <= n;i ++)
{
if(hh <= tt && q[hh] < i - x - 1) hh ++;//维护队列中的个数
f[i] = a[i] + f[q[hh]];//f[q[hh]]表示在i - x - 1 ~ i - 1中的f[i]的最小值
while(hh <= tt && f[q[tt]] > f[i]) tt --;//和需要插入的数组进行比较维护单调递增队列
q[++ tt] = i;//每次都会加入新的元素
}
//在最后x个f[]中选最小值
for(int i = n;i >= n - x;i --)
res = min(res,f[i]);
return res;
}
二分查找部分代码 四种二分模板:
//a[]原数组,q[]下标数组,f[]记录最小数组。
void solve()
{
scanf("%lld%lld", &n, &t);
for(int i = 1;i <= n;i ++)
scanf("%lld",&a[i]);
int l = 0,r = n;
//这个题目满足最大值(指的是二分出来mid)中找最小值,是上面《四种二分模板》中的第一种
while (l < r)
{
int mid = l + r >> 1;
if (check(mid) <= t) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
// cout << "l = "<<l<<" "<<"r = "<<r<<endl;
printf("%lld\n",l);
}