P1714 切蛋糕 题解

题目传送门

想做这道题,我们首先需要知道一个容器——deque

deque:双端队列,顾名思义就是可以再两边操作的队列。

deque特点:

  1. 队列元素具有单调性
  2. 元素呈从小到大/从大到小

基本操作: 

//几种操作

//1.定义
deque<数据类型> q;

//2.入队
q.push_front();//前端入队
q.push_back();//尾端入队

//3.出队
q.pop_front();//前端出队
q.pop_back();//尾端出队

//4.调用
q.front();//调用队首
q.back();//调用队尾

//5.判断队空
q.empty();//空返回1,非空返回0

//6.元素个数
q.size();

基础运用:

给定一个长为n的序列a,以及一个大小为k的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。

要想做此题,一般分为三步

1.掐头:将队首不符合区间要求的元素删除

2.去尾:将当前元素加入队列,且保证加入时,队列内部依然是呈单调性

      1.将队尾不符合单调性的元素删除,直到满足单调性或队列为空

      2.队尾加入当前元素

3.队首为最值

      1.单调递增:最小

      2.单调递减:最大

//此处仅写出最小值的处理方法,最大值留给读者思考
#include <iostream>
#include <deque>
using namespace std;
const int N=2e6+5;
deque<int> q;
int n,m;
int a[N];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++)
	{
		//掐头 队首距离超过m的元素删除 
		while(!q.empty() && i-q.front()+1>m) q.pop_front();
		//去尾 将队尾不满足单调性的删除
		while(!q.empty() && a[q.back()]>=a[i]) q.pop_back();
		q.push_back(i);
		if(i<m) continue;
		else cout<<a[q.front()]<<" "; 
	}
	return 0;
}

我们现在再来讨论这道题:

思路

阅读题目,不难发现此题需要求和,所以要用到前缀和

再一结合可得:

#include <iostream>
#include <deque>
using namespace std;
const int N=5e5+5;
deque<int> q;
int s[N<<1],a[N<<1];
int n,m,ans=0;
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		s[i]=s[i-1]+a[i];
	}
	for(int i=1;i<=n;i++)
	{
		while(!q.empty() && i-q.front()+1>m) q.pop_front();
		while(!q.empty() && s[q.back()]<=s[i]) q.pop_back();
		q.push_back(i);
		if(i<m) continue;
		int L=i-m+1,R=q.front();
		int sum=s[R]-s[L-1];
		ans=max(ans,sum);
	}
	cout<<ans;
	return 0;
}

结果:60分

为什么呢?我们测一下样例2,发现过不去

输出中间值看一下:

l=1, r=3, sum=2
l=2, r=3, sum=3
l=3, r=5, sum=4
l=4, r=5, sum=1

答案是在区间[5,5]里,所以得遍历到n+m-1

一提交,尴尬了,80分

这又是为什么呢?问题出在了s[n]以后的数都是0,应该为s[n]

AC Code

#include <iostream>
#include <deque>
using namespace std;
const int N=5e5+5;
deque<int> q;
int s[N<<1],a[N<<1];
int n,m,ans=0;
int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		s[i]=s[i-1]+a[i];
	}
	for(int i=n+1;i<n+m;i++) s[i]=s[i-1];
	for(int i=1;i<n+m;i++)
	{
		while(!q.empty() && i-q.front()+1>m) q.pop_front();
		while(!q.empty() && s[q.back()]<=s[i]) q.pop_back();
		q.push_back(i);
		if(i<m) continue;
		int L=i-m+1,R=q.front();
		int sum=s[R]-s[L-1];
		ans=max(ans,sum);
	}
	cout<<ans;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值