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

一、题目

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

263bc02f753d485c8f69b0e7db32f85d.png

二、题解和思路

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

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

起始项已起始项去找到的最大和的过程Max
11+23
22+35
33+47
44+59

所以最大和为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]的图标,去探索理解这个问题的本质。

 如我们要求到达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-初始化队头加个00
0 11>0入队1
0 1 33>1入队3-0=3
1 3 66>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初始化队头加个00
0 11>0,1出队1-0=1
-1-1<1,1出队1
-1  22>--1,2入队2-(-1)=3
-1  -2-2<2,2出队,-2入队max(-2 - (-1) ,3)=3
-2  33>-2,3入队,-1超出区间范围出队max(3-(-2),3)=5
 -3--3<3,-3<-2 , -2,3出队,-3进队5

答案为 5 

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

三、代码区

#include<iostream>
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]<i-m)front++;//保证队列在区间内
		S=sum[i]-sum[que[front]]; //求得区间和
		Max=max(Max,S);
		while(front<=rear&&sum[que[rear]]>=sum[i])rear--; //保证队列的单调性			
		que[++rear]=i;
	} 
	cout<<Max;
	return 0;
} 

附上结果:

05e21b8decb24307a09b499f40a7d058.png

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值