单调队列的练习题解

前言:

在上一篇学习记录中,单调队列给出了几道练习题,因为这两道题的算法以及思路相差无几(几乎可以算是双倍经验quq),所以就在这里集中写一下相关的题解

前置知识:

见:队列专题(queue、priority_queue、deque) qvq


切蛋糕:

洛谷P1714

  • 题目简述:

    给定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的纯前缀和代码:

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int n,m,ans,a[1000001],sum[1000001];
  4. int main() {
  5. scanf("%d%d",&n,&m);
  6. for(register int i=1;i<=n;i++) {
  7. scanf("%d",&a[i]);
  8. sum[i]=sum[i-1]+a[i];
  9. }
  10. for(register int k=1;k<=m;k++) {
  11. for(register int i=k;i<=n;i++) {
  12. ans=max(ans,sum[i]-sum[i-k]);
  13. }
  14. }
  15. printf("%d",ans);
  16. return 0;
  17. }

(2)AC的前缀和+单调队列代码:

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int n,m,ans,a[5000001],sum[5000001];
  4. deque<int> shan;
  5. int main() {
  6. scanf("%d%d",&n,&m);
  7. for(register int i=1;i<=n;i++) {
  8. scanf("%d",&a[i]);
  9. sum[i]=sum[i-1]+a[i];
  10. }
  11. for(register int i=1;i<=n;i++) {
  12. while(!shan.empty()&&sum[shan.back()]>sum[i]) shan.pop_back();
  13. shan.push_back(i);
  14. while(shan.front()<i-m) shan.pop_front();
  15. ans=max(ans,sum[i]-sum[shan.front()]);
  16. }
  17. printf("%d",ans);
  18. return 0;
  19. }

好消息,坏消息:

洛谷P2629

  • 题目简述:

    给定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的暴力前缀和:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int read(){
  4. int x=0,f=1;
  5. char ch=getchar();
  6. while(ch<'0' || ch>'9'){ if(ch=='-') f=-1;ch=getchar();}//读取正负号
  7. while(ch>='0' && ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}//x<<3=x*8,x<<1=x*2,合起来便是x*10了
  8. return x*f;
  9. }
  10. int n;
  11. int a[2000005];
  12. int sum[2000005];
  13. bool check(int now) {
  14. for(register int j=0; j<n; j++) {
  15. if(sum[now+j]-sum[now-1]<0) return false;
  16. }
  17. return true;
  18. }
  19. int main() {
  20. n=read();
  21. // scanf("%d",&n);
  22. for(register int i=1; i<=n; i++) {
  23. a[i]=read();
  24. // scanf("%d",&a[i]);
  25. a[i+n]=a[i];
  26. sum[i]=sum[i-1]+a[i];
  27. }
  28. int tot=1;
  29. for(register int i=n+1; i<=2*n-1; i++) {
  30. sum[i]=sum[i-1]+a[tot];
  31. tot++;
  32. }
  33. int ans=0;
  34. for(register int i=1; i<=n; i++) {
  35. if(check(i)==true) ans++;
  36. }
  37. printf("%d",ans);
  38. return 0;
  39. }

(2)单调队列+前缀和的满分代码:

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int n,ans,a[2000010],sum[2000010];
  4. deque<int> shan;
  5. int main() {
  6. scanf("%d",&n);
  7. for(register int i=1;i<=n;i++) {
  8. scanf("%d",&a[i]);
  9. a[i+n]=a[i]; //破环为链
  10. }
  11. for(register int i=1;i<2*n;i++) sum[i]=sum[i-1]+a[i]; //记录前缀和
  12. for(register int i=1;i<2*n;i++) { //枚举
  13. while(!shan.empty()&&sum[shan.back()]>=sum[i]) shan.pop_back(); //deque来维护前缀和最小值
  14. shan.push_back(i);
  15. if(i>=n) {
  16. while(!shan.empty()&&shan.front()<i-n) shan.pop_front(); //处理队首元素超过窗口最大限度的情况(本题窗口限度就是n)
  17. if(sum[shan.front()]>=sum[i-n]) ans++; //如果满足条件,方案数++
  18. }
  19. }
  20. printf("%d",ans);
  21. return 0;
  22. }

吐槽一下:我真的是炒鸡炒鸡蒻啊啊啊啊!!!第二个做法的代码里面前缀和只记录了前n个数的前缀和(晕)...结果...结果...调了半个多小时求助旁边的WS大佬才发现(我去QAQ)

再来一发WS大佬的题解:好消息,坏消息


单调队列练习题解(切蛋糕&好消息,坏消息)的更多相关文章

  1. 【洛谷】【动态规划+单调队列】P1714 切蛋糕

    [题目描述:] 今天是小Z的生日,同学们为他带来了一块蛋糕.这块蛋糕是一个长方体,被用不同色彩分成了N个相同的小块,每小块都有对应的幸运值. 小Z作为寿星,自然希望吃到的第一块蛋糕的幸运值总和最大,但 ...

  2. 线段树【p2629】 好消息,坏消息

    顾z 你没有发现两个字里的blog都不一样嘛 qwq 题目描述-->p2629 好消息,坏消息 历程 刚开始看到这个题,发现是需要维护区间和,满心欢喜敲了一通线段树,简单debug之后交上去 \ ...

  3. 2019牛客多校第三场F Planting Trees(单调队列)题解

    题意: 求最大矩阵面积,要求矩阵内数字满足\(max - min < m\) 思路: 枚举上下长度,在枚举的时候可以求出每一列的最大最小值\(cmax,cmin\),这样问题就变成了求一行数,要 ...

  4. 洛谷 P1714 切蛋糕 单调队列

    这个题比较显然,要用前缀和来做.但只用前缀和是过不去的,会TLE,所以需要进行优化. 对于每个前缀和数组 b 中的元素,都可以找到以 b[i] 结尾的子段最大值 p[i],显然,最终的 ans 就是 ...

  5. 【单调队列】【P1714】 切蛋糕

    传送门 Description 今天是小Z的生日,同学们为他带来了一块蛋糕.这块蛋糕是一个长方体,被用不同色彩分成了N个相同的小块,每小块都有对应的幸运值. 小Z作为寿星,自然希望吃到的第一块蛋糕的幸 ...

  6. P1714 切蛋糕 dp+单调队列

    题意: 题目描述 在幻想乡,琪露诺是以笨蛋闻名的冰之妖精. 某一天,琪露诺又在玩速冻青蛙,就是用冰把青蛙瞬间冻起来.但是这只青蛙比以往的要聪明许多,在琪露诺来之前就已经跑到了河的对岸.于是琪露诺决定到 ...

  7. 【P1714】切蛋糕(单调队列)

    实在不明白难度等级,难不成前缀和是个很变态的东西? 说白了就是单调队列裸题,都没加什么别的东西,就是一个前缀和的计算,然而这个题也不是要用它优化,而是必须这么做啊. #include<iostr ...

  8. luogu P1714 切蛋糕 单调队列

    单调队列傻题. 考虑以 $i$ 结尾的答案 : $max(sumv_{i}-sumv_{j}),j \in [i-m,i-1]$ ($sumv_{i}$ 为前缀和) 稍微搞一搞,发现 $sumv_{i ...

  9. 洛谷P1714 切蛋糕(单调队列)

    先放代码...... 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=5e5+10,M=0x3f3f3f3f; ...

随机推荐

  1. ASP.NET中AJAX的异步加载(Demo演示)

    此次的Demo是一个页面,页面上有两行字,然后后面用AJAX,使用一个下拉框去替换第一行文字 第一个是被替换的网页 <!DOCTYPE html> <html> <hea ...

  2. (Java实现) 装载问题

    2.装载问题 [问题描述] 有一批共n个集装箱要装上艘载重量为c的轮船,其中集装箱i的重量为wi.找出一种最优装载方案,将轮船尽可能装满,即在装载体积不受限制的情况下,将尽可能重的集装箱装上轮船. [ ...

  3. Java实现 LeetCode 537 复数乘法(关于数学唯一的水题)

    537. 复数乘法 给定两个表示复数的字符串. 返回表示它们乘积的字符串.注意,根据定义 i2 = -1 . 示例 1: 输入: "1+1i", "1+1i" ...

  4. Java实现 蓝桥杯VIP 算法提高 士兵排队问题

    算法提高 士兵排队问题 时间限制:1.0s 内存限制:256.0MB 试题 有N个士兵(1≤N≤26),编号依次为A,B,C,-,队列训练时,指挥官要把一些士兵从高到矮一次排成一行,但现在指挥官不能直 ...

  5. Java实现蓝桥杯G将军

    G将军有一支训练有素的军队,这个军队除开G将军外,每名士兵都有一个直接上级(可能是其他士兵,也可能是G将军).现在G将军将接受一个特别的任务,需要派遣一部分士兵(至少一个)组成一个敢死队,为了增加队员 ...

  6. java实现第九届蓝桥杯版本分支

    版本分支 第五题:版本分支 小明负责维护公司一个奇怪的项目.这个项目的代码一直在不断分支(branch)但是从未发生过合并(merge). 现在这个项目的代码一共有N个版本,编号1~N,其中1号版本是 ...

  7. vue+js清除定时器

    注意data数据里面一定要定义Timeout Timeout:Function,//定时器 methods里面 moseovefalse(){//需要执行的方法 var that=this; that ...

  8. php的ts和nts选择

    TS(Thread-Safety)即线程安全,多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用.不会出现数据不一致或者数据 ...

  9. [TopCoder]Seatfriends

    题目   点这里看题目. 分析   可以想到用 DP 解决.   由于把空位放到状态里面太麻烦了,因此我们单独将 " 组 " 提出来进行 DP .   \(f(i,j)\):前\( ...

  10. 基于移动最小二乘法的点云曲面拟合(python)

    1.移动最小二乘法介绍 为了更好地对数据量大且形状复杂的离散数据进行拟合,曾清红等人[1]开发出一种新的算法——移动最小二乘法.这种新的最小二乘算法为点云数据的处理提供了新的方法.使用点云数据拟合曲面 ...