代码如下
#include<stdio.h>
int q[500010] = { 0 }, a[500010] = { 0 };
int main() {
int tt = 0, hh = 0,sum=0,max=0;
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++){
scanf("%d", &a[i]);
a[i]+=a[i-1];
}
for(int i=1;i<=n;i++){
if(hh<=tt&&i-k>q[hh]) hh++;
while(hh<=tt&&a[q[tt]]>=a[i]) tt--;
sum=a[i]-a[q[hh]];
if(sum>max) max=sum;
q[++tt]=i;
}
printf("%d",max);
return 0;
}
这个代码主要用到了两个算法,一个是前缀和,一个是单调队列,在这里我不过多讲述,主要讲一下这一题的思想。
为什么我们要计算前缀和,因为我们是寻找的是这个区间其中某个子区间的最大值,而我们计算前缀和有助于我们计算每个子区间,如果不计算前缀和那么我们计算一个区间还需要一个循环来相加,会浪费时间,而计算了前缀和,我们能用O(1)时间复杂度算出某一个子区间的值。(在这里,我把前缀和的值存在了a数组里)
这题为什么要用到单调队列算法,我们稍微分析一下就可以知道,我们要找到某个子区间和的最大值,而每个子区间我们可以用a数组中某两个下标所储存的值相减,比如我们要计算[2,3]这个区间,那么我们只需要让a[3]-a[1]就可以了,就可以知道这个子区间和的值,既然要找最大值,那么我们需要让被减数越大越好,减数越小越好,我们可以用单调队列找到我们前缀和数组里某一个区间内的最小值。
我举个例子,假设我们要最多能吃3个蛋糕,那么最多是连续的3个,最少是1个,假设原来给了我们5个蛋糕,我们前缀和计算后a数组存储的值是前1个,前2个,前3个,前4个,前5个蛋糕值的总和,而最多只能吃三块,那么我们相当于建立了一个滑动窗口,这个窗口的范围是3+1,为什么要加1,因为如果我们要计算2 3 4的总和,那么我们需要a[4]-a[1],计算3 4的总和,需要a[4]-a[2],涉及到的区间覆盖a[1]到a[4],而我们通过单调队列可以找出这个区间内的最小值,当窗口滑动时,让新加入来的值减去它所涵盖窗口的最小值,通过与max变量比较来找出当前的最大值,当我们遍历了一次后,也就找到了子区间的最大值。
(本人算法也在入门阶段,通过写作来加深自己对学习到的算法的理解,如有解释不清,请谅解,也请指正)