以前以为单调队列只能求解区间最值的问题,没想到还能用于dp的优化,使循环的维度直接减去1维,将原先O(n³)复杂度的方程优化成O(n²)。最近几天刷了几道相关的题目,遂小小的总结一下以防以后忘掉。
首先,单调队列是一种单调递增或者单调递减的队列(如1、3、5、7、9是单调递增的队列)。对于单调队列的操作主要有三种:插入(从队尾插入一个新元素)、取最值(取队头的元素)、删除(从队头开始删除已经失效的元素)。由于操作较简单,一般采用一维数组的方式来存储队列,用head、tail分别表示队头和队尾。操作的代码如下(以单调递增队列为例,即队首为最小值)。
int q[100];
int head = 0, tail = 0, x;
//插入
while(head < tail && q[tail-1] > x) tail--;
q[tail++] = x;
//取值
printf("%d\n", q[head]);
但是,单调队列不是对所有的状态方程都有效,只有形如dp[i] = max{f[k]}+g[i]或dp[i] = min{f[k]}+g[i],其中(k < i && g[i]与k无关)的方程才能进行优化,优化对象为f[k]。
例如,方程dp[i] = max{dp[j]}+i+j,其中(0<=i < 100, 0 <= j < i)。变换方程式,得dp[i] = max{dp[j]+j}+i。当i=1时,j只可取0,即dp[1] = dp[0]+1+0;当i=n时,j可取0-(n-1)。即i每增加1,j的范围也增加1。当i=n时,向队列中加入元素(dp[n-1]+n-1),这样每次取出的队头元素再加上i,即为dp[i]的值。代码如下
dp[0] = 0;
for(int i = 1;