单调队列
首先,单调队列的作用是维护区间最大值
比如说现在我们看上面这个图吧,现在把这些数,每三个化成一个窗口。在每个窗口后面蓝色部分,从左到右写出窗口中有可能成为区间最大值的元素。然后你会发现,所有的蓝色部分的第一个数字好像就是相应的区间最大值,而且你会发现,每一个蓝色部分好像都是递减的。其实只要一个数后面有一个数比它大的话,那么这个数就不可能会是区间最大值。那么我要怎么维护这样一个数据结构呢?这个地方就需要用到单调队列了。
现在用自然语言描述一遍吧。首先把6,3,2入队列,然后5入队,由于需要维护单调递减,所以把3和2踢出去(这个描述的确有一点诡异),这个所谓的踢出去是往后移么?然后发现已经4个了超出窗口了。所以把6给丢出去。然后把4和1入队。依次类推上面的元素上面都有一个对应的区间最大值。
后面来一题例题,后面其实还是可以再看看别人的博客的。
所以为什么要进行算法的训练呢,像很多之前的学习中,其实我对算法都不够熟练,我后面可能还会花很多时间去熟悉各种算法,其实这题一开始就有点绕,但是自己还是很认真去看了。
首先如果有经过算法训练的话(很显然我这个菜鸡是没有的),看到连续子列和,就要想到转化为前缀和问题。因为一个子列的前缀和就是这个数据的最后一个前缀和减去第一个前缀和。而且这个地方还要有一个s0来记录第0号位置(这个也是有意义的,比如说我要访问前j个元素的话,就是从sj一直到s0)。
后面来看代码把,这个代码我看了很久(脑子笨),但是总算是看懂了,希望后面自己可以多打几遍吧!
#include <iostream> using namespace std; #define MAX_N 300000 int q[MAX_N + 5], head, tail; int arr[MAX_N + 5]; int main() { int n, m,ans; cin >> n >> m; for (int i = 1; i <= n; i++) cin >> arr[i], arr[i] += arr[i - 1]; head = tail = 0; //这个地方有一个技巧,就是在队列里面储存的是arr下标 q[tail++] = 0;//先把第一个位置上放上0号位的下标 ans = arr[1]; for (int i = 1; i <= n; i++) { ans = max(ans, arr[i] - arr[q[head]]); //ans要大的话就要使这个队列是一个递增队列 //这样队头元素就是最小的元素了,相减得到的元素是最大的 while (tail - head && arr[q[tail - 1]] >= arr[i]) tail--; //这里其实就是把tail移动到相应的位置上去 q[tail++] = i; //后面判断会不会超出范围,如果超出的话就把head++就好了,也就是head存的下标里i的距离超过了m if (q[head] == i - m) head++;
} cout << ans << endl; return 0; }
这个地方有一个技巧就是队列中存的是arr数组的下标,这点就很妙了。这样方便对数据的处理。还有就是前缀和这个东西其实就是一个元素加上上一个元素,也就是前n项和了。