洛谷:P1714 切蛋糕(玩转单调队列)

一、题目

传送门:P1714 切蛋糕 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 

洛谷:P1714 切蛋糕(玩转单调队列)_第1张图片

二、题解和思路

        这题是要我们在长度为n的区间里找到一个长度小于等于m的子区间并使得区间和最大,解决这题我们可以考虑对于给定的数据,让每一个数据去向前遍历求得最大和,然后依次和每一项数据的小于等于前m项求得的最大和去比较,找到这样的前m项最大和。

如下表对案例n = 5,m=2 {1 2 3 4 5}的演示:

起始项 已起始项去找到的最大和的过程 Max
1 1+2 3
2 2+3 5
3 3+4 7
4 4+5 9

所以最大和为9

如果数据很大,n和m很大时,O(n*m)那便显得有点无力,能否有更简便的方法?

  下面我们来了解什么是单调队列:

单调队列,顾名思义,是一种具有单调性的队列。众所周知,单调性有单调递增和单调递减两种,相应的单调队列也分为单调递增队列和单调递减队列两种。

  • 单调递增队列:保证队列头元素一定是当前队列的最小值,用于维护区间的最小值。

  • 单调递减队列:保证队列头元素一定是当前队列的最大值,用于维护区间的最大值。

 实现流程:

实现单调队列,主要分为三个部分:

  • 去尾操作队尾元素出队列。当队列有新元素待入队,需要从队尾开始,删除影响队列单调性的元素,维护队列的单调性。(删除一个队尾元素后,就重新判断新的队尾元素)

去尾操作结束后,将该新元素入队列。

  • 删头操作队头元素出队列。判断队头元素是否在待求解的区间之内,如果不在,就将其删除。(这个很好理解呀,因为单调队列的队头元素就是待求解区间的极值)

  • 取解操作 :经过上面两个操作,取出 队列的头元素 ,就是 当前区间的极值

我们以案例为例: n=6,m=3 {1 -2 3 -4 5 -6}

它的前缀和为1 -1 2 -2 3 -3

从前缀和我们可以看出数据的单调性,因为对于每一项前缀和来说,它都是对于前n项的和。也就意味这,它的和变小了,它就递减了。同理,它的和变大了,那它就递增了。

这个时候我们引入了一个单调队列。

队首维护一个最小的和。

我们可以逆向的去想,把要求以这项数据为起点去找最大变成以这项数据为终点,去找到达这项数据的前m和最大是多少?对于每一个到达该数据的区间的区间和都有sum[i]-sum[i-x](其中x∈【1,k】,其中因为是对于第i项数据来说,求得最大区间和,sum[i]是固定的但因为x在变。说以只要保证sum[i-x]最小,那区间和不是最大的吗?

我们画图理解对于案例的前缀和创建i 和 sum[i]的图标,去探索理解这个问题的本质。

洛谷:P1714 切蛋糕(玩转单调队列)_第2张图片

 如我们要求到达3的前m项和最大那我们只要用sum[3](2)减去最小的在区间内的-1(i=2时的sum[2])

得到结果3

同理要求到达5的前m项和最大那我们只要用sum[5](3)减去最小的在区间内的-2 (i=4时的sum[4])得到结果5

这样我们就可以一次找到区间并迅速对比找到

要注意的是,由于可能出现初始入队元素全为递增的情况,导致不能算到第一个可能使得和最大的数,所以我们要在队头加一个0,以便计算到第一个元素的值。

如这样一组数据 5 6 7 8 9 -2 -2 -2 -2   ....求 m=5时的最大,当-2要入队前,队列中为 5 6 7 8 9,如果没有在对队头加0,这时的和为 (6+7+8+9)也就是30,不能算到5,因为计算和时是5是为队头的开区间。如果有0,当-2要入队时,此时队列就变成了  0 5 6 7 8 9 最大和就变成了35,这时的正确答案。

接下来我们用表格演示一下实现原理帮组大家理解:

1.           案例一 :1 2 3 4 5   n = 5 , m =2

                前缀和为:1 3 6 10 15

现队列情况 队列操作 要求的Max
0- 初始化队头加个0 0
0 1 1>0入队 1
0 1 3 3>1入队 3-0=3
1 3 6 6>3入队,但队列长度大于3 ,0出队 6-1=5
3 6 10 同上............ 10-3=7
6 10 15 ............. 15-6=9

所以答案为9

2.         案例二           1 -2 3 -4 5 -6   n =6 ,m = 3

                前缀和为:1 -1 2 -2 3 -3

现队列情况 队列操作 要求的Max
0 初始化队头加个0 0
0 1 1>0,1出队 1-0=1
-1 -1<1,1出队 1
-1  2 2>--1,2入队 2-(-1)=3
-1  -2 -2<2,2出队,-2入队 max(-2 - (-1) ,3)=3
-2  3 3>-2,3入队,-1超出区间范围出队 max(3-(-2),3)=5
 -3 --3<3,-3<-2 , -2,3出队,-3进队 5

答案为 5 

可以看出队列是单调递增的,计算和时,对头并不记入范围。队列中的长度为m+1,可以直观的找到区间最大和,我们可以直观的看出为什么队列初始时头部为0的作用。

三、代码区

#include
using namespace std;
int x,sum[500005],que[500005],m,n,front,rear,Max,S;
int main(){
	cin>>n>>m;
	for(int i =1;i<=n;i++){
		cin>>x;
		sum[i]=sum[i-1]+x;//前缀和 
	}
	front=rear=1;//先在队列中添加个sum[0]=0 
	for(int i=1;i<=n;i++){
		while(front<=rear&&que[front]=sum[i])rear--; //保证队列的单调性			
		que[++rear]=i;
	} 
	cout<

附上结果:

洛谷:P1714 切蛋糕(玩转单调队列)_第3张图片

你可能感兴趣的:(算法,c++)