单调队列练习题解(切蛋糕&好消息,坏消息)
单调队列的练习题解
前言:
在上一篇学习记录中,单调队列给出了几道练习题,因为这两道题的算法以及思路相差无几(几乎可以算是双倍经验quq),所以就在这里集中写一下相关的题解
前置知识:
见:队列专题(queue、priority_queue、deque) qvq
切蛋糕:
题目简述:
给定n个元素的值Pi,窗口最大限度m,要求找出连续k(0<=k<=m)个元素,使得这些元素和最大,输出这个最大值数据范围:
对100%的数据,M≤N≤500000,|Pi|≤500。 答案保证在2^31-1之内算法:
单调队列deque&前缀和解题思路:
(1)看到求连续一段区间最大值问题,便想到了用前缀和来维护,再循环模拟k的取值,然后每次用ans来比较取最大值
(2)打出纯前缀和代码,会得到40pts,其它意料之中的T掉,说明肯定还有其他算法
(3)我们需要维护长度为k的最大前缀和,所以想到了使用单调队列(好勉强啊...说实话不看算法标签我肯定想不到)
(4)单调队列的实现:当队尾存储的前缀和大于当前前缀和时,将当前前缀和存入队列中(因为计算前缀和需要减去前面的,不好理解可以看代码),再判断当前队首元素是否在窗口限度以内,最后用ans比较存储最大值再输出即可
- 代码Code:
 
(1)40pts的纯前缀和代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,ans,a[1000001],sum[1000001];
int main() {
	scanf("%d%d",&n,&m);
	for(register int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	for(register int k=1;k<=m;k++) {
		for(register int i=k;i<=n;i++) {
			ans=max(ans,sum[i]-sum[i-k]);
		}
	}
	printf("%d",ans);
	return 0;
}
(2)AC的前缀和+单调队列代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,ans,a[5000001],sum[5000001];
deque<int> shan;
int main() {
	scanf("%d%d",&n,&m);
	for(register int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	for(register int i=1;i<=n;i++) {
		while(!shan.empty()&&sum[shan.back()]>sum[i]) shan.pop_back();
		shan.push_back(i);
		while(shan.front()<i-m) shan.pop_front();
		ans=max(ans,sum[i]-sum[shan.front()]);
	}
	printf("%d",ans);
	return 0;
}
好消息,坏消息:
题目简述:
给定n个消息的好坏度Ai,要求找出所有满足在告诉Boss全部消息过程中老板心情一直不低于0的方案总数数据范围:
对于100%数据n<=10^6,-1000 <= Ai <= 1000算法:
前缀和&单调队列deque解题思路:
(1)还是先暴力一发,能得到75pts,因为时间复杂度是O(n^2),所以考虑优化
(2)因为是区间,所以还要使用前缀和来维护区间最值
(3)但是这道题并不是单独的求某一区间的最值,而是要求某一区间内的前缀和都不小于0,所以我们会想到合并石子的思路:破环为链
(4)开两倍数组,复制一遍前n个数,这样方便我们枚举所有情况,然后使用单调队列求出某一区间内的前缀和的最小值,再判断减去前面的前缀和后是否小于0,不是的话方案数++,于是我们就有了A掉这道题的满分思路
- 代码Code:
 
(1)75pts的暴力前缀和:
#include<bits/stdc++.h>
using namespace std;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0' || ch>'9'){ if(ch=='-') f=-1;ch=getchar();}//读取正负号
    while(ch>='0' && ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}//x<<3=x*8,x<<1=x*2,合起来便是x*10了
    return x*f;
}
int n;
int a[2000005];
int sum[2000005];
bool check(int now) {
	for(register int j=0; j<n; j++) {
		if(sum[now+j]-sum[now-1]<0) return false;
	}
	return true;
}
int main() {
	n=read();
//	scanf("%d",&n);
	for(register int i=1; i<=n; i++) {
		a[i]=read();
//		scanf("%d",&a[i]);
		a[i+n]=a[i];
		sum[i]=sum[i-1]+a[i];
	}
	int tot=1;
	for(register int i=n+1; i<=2*n-1; i++) {
		sum[i]=sum[i-1]+a[tot];
		tot++;
	}
	int ans=0;
	for(register int i=1; i<=n; i++) {
		if(check(i)==true) ans++;
	}
	printf("%d",ans);
	return 0;
}
(2)单调队列+前缀和的满分代码:
#include <bits/stdc++.h>
using namespace std;
int n,ans,a[2000010],sum[2000010];
deque<int> shan;
int main() {
	scanf("%d",&n);
	for(register int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		a[i+n]=a[i]; //破环为链
	}
	for(register int i=1;i<2*n;i++) sum[i]=sum[i-1]+a[i]; //记录前缀和
	for(register int i=1;i<2*n;i++) { //枚举
		while(!shan.empty()&&sum[shan.back()]>=sum[i]) shan.pop_back(); //deque来维护前缀和最小值
		shan.push_back(i);
		if(i>=n) {
			while(!shan.empty()&&shan.front()<i-n) shan.pop_front(); //处理队首元素超过窗口最大限度的情况(本题窗口限度就是n)
			if(sum[shan.front()]>=sum[i-n]) ans++; //如果满足条件,方案数++
		}
	}
	printf("%d",ans);
	return 0;
}
吐槽一下:我真的是炒鸡炒鸡蒻啊啊啊啊!!!第二个做法的代码里面前缀和只记录了前n个数的前缀和(晕)...结果...结果...调了半个多小时求助旁边的WS大佬才发现(我去QAQ)
再来一发WS大佬的题解:好消息,坏消息
单调队列练习题解(切蛋糕&好消息,坏消息)的更多相关文章
- 【洛谷】【动态规划+单调队列】P1714 切蛋糕
		
[题目描述:] 今天是小Z的生日,同学们为他带来了一块蛋糕.这块蛋糕是一个长方体,被用不同色彩分成了N个相同的小块,每小块都有对应的幸运值. 小Z作为寿星,自然希望吃到的第一块蛋糕的幸运值总和最大,但 ...
 - 线段树【p2629】 好消息,坏消息
		
顾z 你没有发现两个字里的blog都不一样嘛 qwq 题目描述-->p2629 好消息,坏消息 历程 刚开始看到这个题,发现是需要维护区间和,满心欢喜敲了一通线段树,简单debug之后交上去 \ ...
 - 2019牛客多校第三场F Planting Trees(单调队列)题解
		
题意: 求最大矩阵面积,要求矩阵内数字满足\(max - min < m\) 思路: 枚举上下长度,在枚举的时候可以求出每一列的最大最小值\(cmax,cmin\),这样问题就变成了求一行数,要 ...
 - 洛谷 P1714 切蛋糕  单调队列
		
这个题比较显然,要用前缀和来做.但只用前缀和是过不去的,会TLE,所以需要进行优化. 对于每个前缀和数组 b 中的元素,都可以找到以 b[i] 结尾的子段最大值 p[i],显然,最终的 ans 就是 ...
 - 【单调队列】【P1714】 切蛋糕
		
传送门 Description 今天是小Z的生日,同学们为他带来了一块蛋糕.这块蛋糕是一个长方体,被用不同色彩分成了N个相同的小块,每小块都有对应的幸运值. 小Z作为寿星,自然希望吃到的第一块蛋糕的幸 ...
 - P1714 切蛋糕  dp+单调队列
		
题意: 题目描述 在幻想乡,琪露诺是以笨蛋闻名的冰之妖精. 某一天,琪露诺又在玩速冻青蛙,就是用冰把青蛙瞬间冻起来.但是这只青蛙比以往的要聪明许多,在琪露诺来之前就已经跑到了河的对岸.于是琪露诺决定到 ...
 - 【P1714】切蛋糕(单调队列)
		
实在不明白难度等级,难不成前缀和是个很变态的东西? 说白了就是单调队列裸题,都没加什么别的东西,就是一个前缀和的计算,然而这个题也不是要用它优化,而是必须这么做啊. #include<iostr ...
 - luogu P1714 切蛋糕 单调队列
		
单调队列傻题. 考虑以 $i$ 结尾的答案 : $max(sumv_{i}-sumv_{j}),j \in [i-m,i-1]$ ($sumv_{i}$ 为前缀和) 稍微搞一搞,发现 $sumv_{i ...
 - 洛谷P1714 切蛋糕(单调队列)
		
先放代码...... 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=5e5+10,M=0x3f3f3f3f; ...
 
随机推荐
- Java实现洛谷 P1007独木桥
			
题目背景 战争已经进入到紧要时间.你是运输小队长,正在率领运输部队向前线运送物资.运输任务像做题一样的无聊.你希望找些刺激,于是命令你的士兵们到前方的一座独木桥上欣赏风景,而你留在桥下欣赏士兵们.士兵 ...
 - Java实现 LeetCode 640 求解方程(计算器的加减法计算)
			
640. 求解方程 求解一个给定的方程,将x以字符串"x=#value"的形式返回.该方程仅包含'+',' - '操作,变量 x 和其对应系数. 如果方程没有解,请返回" ...
 - Java实现 LeetCode 391 完美矩形
			
391. 完美矩形 我们有 N 个与坐标轴对齐的矩形, 其中 N > 0, 判断它们是否能精确地覆盖一个矩形区域. 每个矩形用左下角的点和右上角的点的坐标来表示.例如, 一个单位正方形可以表示为 ...
 - Linux 最大有效权限与删除ACL
			
最大有效权限mask mask是用来指定最大有效权限的.如果给用户赋予了acl权限,则需要与mask权限”相与“才能得到用户的真正权限 setfacl -m m:rx 文件名,指定最大有效权限.例如: ...
 - cnblogs 博客爬取 + scrapy + 持久化 + 分布式
			
目录 普通 scrapy 分布式爬取 cnblogs_spider.py 普通 scrapy # -*- coding: utf-8 -*- import scrapy from ..items im ...
 - Java 多线程基础(四)线程安全
			
Java 多线程基础(四)线程安全 在多线程环境下,如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线 ...
 - 宝塔部署时,出现“open_basedir restriction in effect”错误
			
下面是错误代码: Warning: require(): open_basedir restriction in effect. Warning: require(XXXXXXXXXXX): fail ...
 - 华为海思搞定4K60fps!Vmate掌上云台相机国内首发
			
目录 Snoppa Vmate Snoppa Vmate Snoppa Vmate是一款掌上型的高性能4K摄像机,集成了微型机械三轴增稳云台,一体化机身集成可操控式触摸屏,既可以独立使用,也可以无线连 ...
 - 重装conda
			
重装Conda是因为想重装tensorflow,结果竟然报了各种各样稀奇古怪的错误:我贴一下自己见过的报错 The environment is inconsistent, please check ...
 - jwt 工具类
			
public class TokenUtils { private Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 签名 ...