[单调栈/差分/尺取/单调队列]Exercise Week5 A最大矩形+B魔法猫+C平衡字符串+D滑动窗口
目录
- A.[单调栈]最大矩形
- 题意
- 样例
- 思路
- 总结
- 代码
- B.[差分]TT's Magic Cat
- 题意
- 样例
- 思路
- 总结
- 代码
- C.[尺取]平衡字符串
- 题意
- 样例
- 思路
- 总结
- 代码
- D.[单调队列]滑动窗口
- 题意
- 样例
- 思路
- 总结
- 代码
A.[单调栈]最大矩形
题意
给一个直方图,求直方图中的最大矩形的面积。(如下所示)
样例
样例输入:
输入包含多组数据。每组数据用一个整数n来表示直方图中小矩形的个数n(1 <= n <= 100000) 然后接下来n个整数h1, …, hn(0<= hi <= 1000000000). 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为1。
测试数据以0结尾。
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
样例输出:
对于每组测试数据输出一行一个整数表示答案。
8
4000
思路
1.首先顺序维护一个单减栈,栈中存储下标。每到一个i,先判断栈顶元素与当前元素的大小关系,当栈不为空 且 当前元素小于栈顶元素时,将栈顶元素依次弹出 并置R[s.top()]=i-1 表示 栈顶代表的下标位置的元素最多只能向右扩展到 i-1(即i处的矩形比它要矮) 遍历完一遍后 将所有栈中的元素的R[]赋值为n 代表他能向右扩展到n
2.再倒序维护一个单增栈,与过程1几乎一致,只是在赋值L[s.top()]时 应赋值为 i+1表示只能向左扩展到i+1 遍历完后将栈中所有元素的L[]赋值为1
3.从1到n遍历,找出区间长度(R[i]-L[i]+1)*高度(a[i])的最大值即为答案
总结
1.由于本题需要找到向左、右找到第一个比当前节点小的节点,故可以使用单调递减栈在O(n)的时间复杂度完成
2.单调栈是线性的复杂度,并且可以维护全局上的单调性(维护一端),并且没有大小限制
3.单调栈还可以用来找向左/右的第一个比当前元素大/小的值
代码
#include<iostream>
#include<stdio.h>
#include<stack>
#define MAXN 100010
long long a[MAXN],n,L[MAXN],R[MAXN];
using namespace std;
int main()
{cin>>n;while(n!=0){stack<int> s;//把下标存到s中 维护一个递增栈for(int i=1;i<=n;i++) scanf("%d",&a[i]);//找R[i]for(int i=1;i<=n;i++){while(!s.empty()&&a[s.top()]>a[i])//如果当前矩阵比栈顶矩阵矮 则把当前的下标-1赋值给R[s.top()]{R[s.top()]=i-1;//说明s.top()处的元素只能延伸到i-1处s.pop();}s.push(i);}while(!s.empty()) {R[s.top()]=n;s.pop();}//最终还在栈中的 可以一直延伸到最右端//找L[i] 与上面同理for(int i=n;i>=1;i--){while(!s.empty()&&a[s.top()]>a[i]){L[s.top()]=i+1;s.pop();}s.push(i);}while(!s.empty()) {L[s.top()]=1;s.pop();}long long maxl=0;for(int i=1;i<=n;i++) maxl=max(maxl,(R[i]-L[i]+1)*a[i]);cout<<maxl<<endl;cin>>n;}system("pause");return 0;
}
B.[差分]TT’s Magic Cat
题意
有n个数和q次操作,每一次操作指明了要操作的区间[l,r],以及让该区间内的所有元素全部加c
输出q次操作后所有元素的大小
样例
样例输入:
第一行 n q (1≤n,q≤2⋅105)
第二行 n个数 a1,a2······an (−106≤ai≤106)
接下来q 行 每行3个数
l r c 表示 [l,r]区间内每个数加c(1≤l≤r≤n,−105≤c≤105)
4 2
-3 6 8 4
4 4 -2
3 3 1
样例输出:
进行完q次操作后,所有元素的值
-3 6 9 2
思路
1.对数组a,先进行差分得到数组b 即b[1]=a[1] b[i]=a[i]-a[i-1]
2.对数组a进行区间操作时,转化为对数组b的单点操作 即 b[l]+=c b[r+1]-=c
3.当操作完后 对数组b求前缀和s 再输出前缀和 即得到操作后的a数组 s[1]=b[1] s[i]=s[i-1]+b[i]
总结
1.由于数据范围是1e5,如果我们只是单纯的模拟的话,O(n^2)的复杂度是万万吃不消的 于是我们运用了差分加前缀和的特性
2.差分数组的前缀和=原数组 并且可以吧对原数组a的区间操作变成对差分数组b的单点操作(A[l,r]均加c 在差分数组b中为 b[l]+=c b[r+1]-=c)
3.由于数据范围较大,故需要用scanf提高速度 以及 long long 避免溢出
代码
#include<iostream>
#include<stdio.h>
#define MAXN 300010
using namespace std;
long long a[MAXN],cf[MAXN],s[MAXN];
int main()
{//开long long 还有 scanfint n,q,l,r,c;cin>>n>>q;for(int i=1;i<=n;i++) scanf("%lld",&a[i]);cf[1]=a[1];for(int i=2;i<=n;i++) cf[i]=a[i]-a[i-1];while(q--){scanf("%d%d%d",&l,&r,&c);cf[l]+=c;cf[r+1]-=c;}s[1]=cf[1];for(int i=1;i<=n;i++) s[i]=s[i-1]+cf[i];for(int i=1;i<=n;i++) printf("%lld ",s[i]);return 0;
}
C.[尺取]平衡字符串
题意
一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。
样例
样例输入:
一行字符表示给定的字符串s
QQQQ
样例输出:
一个整数表示答案
3
思路
1.用两个指针l,r表示暂时选定的答案区间
2.由于区间内的字母可以随意更改,我们可以先统计区间外的4种字母的数量,然后找到数量最多的字母的数量maxl
3.然后我们模拟将字母的数量补齐,即计算tmp=(maxl-qsum)-(maxl-wsum)-(maxl-esum)-(maxl-rsum)
tmp代表需要的字母个数([l,r]内) 然后我们计算sy=(r-l+1)-tmp 是否满足大于0 且sy%4==0 若sy<0则说明所选区间内元素不够 sy%4!=0说明无法使区间内的四种字母平衡
4.如果满足条件 则说明当前所选区间可能为答案 我们更新答案同时令l++ 试图找到一个更短的区间来满足条件; 如果不满足条件,说明当前的区间不够长 我们令r++去寻找更长的满足条件的区间
总结
1.由于题目满足 所求解答案为连续区间 区间左右端点移动有明确方向 所以可以用尺取法
2.在计算区间外的字母个数时,可以通过计算前缀和(O(n)的预处理 O(1)的使用) ,即 qsum=Q[len-1]-Q[r]+Q[l-1]
代码
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#define MAXN 200010
using namespace std;
int Q[MAXN],W[MAXN],E[MAXN],R[MAXN];
string s;
int ans = MAXN;
int main()
{cin>>s;int len=s.length();if(s[0]=='Q') Q[0]=1;if(s[0]=='W') W[0]=1;if(s[0]=='E') E[0]=1;if(s[0]=='R') R[0]=1;for(int i=1;i<len;i++)//维护前缀和 便于计算QWER个数{if(s[i]=='Q'){Q[i]=Q[i-1]+1;W[i]=W[i-1];E[i]=E[i-1];R[i]=R[i-1];}if(s[i]=='W'){Q[i]=Q[i-1];W[i]=W[i-1]+1;E[i]=E[i-1];R[i]=R[i-1];}if(s[i]=='E'){Q[i]=Q[i-1];W[i]=W[i-1];E[i]=E[i-1]+1;R[i]=R[i-1];}if(s[i]=='R'){Q[i]=Q[i-1];W[i]=W[i-1];E[i]=E[i-1];R[i]=R[i-1]+1;}}int l=0,r=0;while(l<len&&r<len){int qsum=Q[len-1]-Q[r]+(l==0?0:Q[l-1]);int wsum=W[len-1]-W[r]+(l==0?0:W[l-1]);int esum=E[len-1]-E[r]+(l==0?0:E[l-1]);int rsum=R[len-1]-R[r]+(l==0?0:R[l-1]);int maxl=max(max(qsum,wsum),max(esum,rsum));//将字符补整int tmp_len=r-l+1;int sy=tmp_len-(maxl-qsum)-(maxl-wsum)-(maxl-esum)-(maxl-rsum);if(sy>=0&&sy%4==0) {ans=min(ans,tmp_len);l++;}else r++;}cout<<ans<<endl;system("pause");return 0;
}
D.[单调队列]滑动窗口
题意
有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 求在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少.
样例
样例输入:
第一行 n k 代表 长度为n,窗口大小为k (1<=k<=n<=1000000)
8 3
1 3 -1 -3 5 3 6 7
样例输出:
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。
-1 -3 -3 -3 3 3
3 3 5 5 6 7
思路
1.先顺序维护一个单调递增的双向队列(存储下标)。首先将1~k-1号元素push进队列中维护单增队列,如果队尾元素大于当前元素,则一直pop队尾元素直到不满足条件。然后从k到n号元素开始,每次先维护队列的单调性,然后放入i号元素并且维护窗口的大小,具体做法为判断i-队首元素存储的下标有没有超过k,超过则pop队首元素 然后将队首元素存到minn[i]中
2.然后再顺序维护一个单调递减的双向队列,具体实现与步骤一几乎完全一致,然后将结果存储到maxx[]中
总结
1.由于本题的数据范围,朴素的O(n^2)肯定是超时的。单调队列也是线性复杂度(与单调栈相同),时间复杂度较优
2.单调队列可以用来维护两端,通常用来维护局部的单调性(队首可以出队并且前面的元素一定比后面的元素先到)
代码
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<deque>
#define MAXN 1000010
int a[MAXN];
int maxx[MAXN],minn[MAXN];
using namespace std;
int n,k;
int main()
{cin>>n>>k;for(int i=1;i<=n;i++) scanf("%d",&a[i]);deque<int> q;//储存下标for(int i=1;i<=k-1;i++){ while(!q.empty()&&a[q.back()]>a[i])q.pop_back();q.push_back(i);}for(int i=k;i<=n;i++)//窗口从k开始向右移动 维护一个单增队列{while(!q.empty()&&a[q.back()]>a[i]) q.pop_back();//先维护单调性q.push_back(i);while(!q.empty()&&(i-q.front())>=k) q.pop_front();//然后维护窗口的大小minn[i]=q.front();}q.clear();for(int i=1;i<=k-1;i++){ while(!q.empty()&&a[q.back()]<a[i])q.pop_back();q.push_back(i);}for(int i=k;i<=n;i++)//窗口从k开始向右移动 维护一个单增队列{while(!q.empty()&&a[q.back()]<a[i]) q.pop_back();//先维护单调性q.push_back(i);while(!q.empty()&&(i-q.front())>=k) q.pop_front();//然后维护窗口的大小maxx[i]=q.front();}for(int i=k;i<=n;i++) printf("%d ",a[minn[i]]);cout<<endl;for(int i=k;i<=n;i++) printf("%d ",a[maxx[i]]);cout<<endl;system("pause");return 0;
}
[单调栈/差分/尺取/单调队列]Exercise Week5 A最大矩形+B魔法猫+C平衡字符串+D滑动窗口相关推荐
- 【CodeForces - 514D】R2D2 and Droid Army(二分+滑动窗口ST表,或 尺取+单调队列或STLmultiset)
题干: An army of n droids is lined up in one row. Each droid is described by m integers a1, a2, ..., a ...
- 【51Nod - 1215 】数组的宽度 (单调栈 或 分治 或 单调队列,算贡献,需去重)
题干: N个整数组成的数组,定义子数组aii..ajj的宽度为:max(ai..aj) - min(ai..aj),求所有子数组的宽度和. Input 第1行:1个数N,表示数组的长度.(1 < ...
- 算法学习12: 单调队列和单调栈
算法学习12: 单调队列和单调栈 单调队列 单调队列解决的问题: 窗口内最大/最小值的更新结构 单调队列的结构和操作 单调队列的应用 题目一: 生成窗口最大值数组[leetcode 239](http ...
- 线性结构 —— 单调栈与单调队列
[单调栈] 1.原理 单调栈,就是栈内元素保持一定单调性(单调递增或单调递减)的栈,即从栈底到栈顶单调递增或递减. 对于单调递增的栈,如果栈为空或入栈元素值大于等于栈顶元素值,则入栈:否则,若入栈会破 ...
- 单调栈和单调队列的应用即总结
单调栈 基本概念 单调栈的基础结构是"栈",即元素是LIFO的,只能从栈顶控制数据出入:单调表示从栈的一端到另一端遍历,元素是单调递增或者单调递减的.比如单调递减栈,从栈底到栈顶的 ...
- [力扣刷题总结](栈和单调栈篇)
文章目录 ~~~~~~~~~~~~栈~~~~~~~~~~~~ 155. 最小栈 解法1:链表 剑指 Offer 31. 栈的压入.弹出序列 解法1:模拟栈 20. 有效的括号 解法1:栈 相似题目: ...
- 算法学习 (门徒计划)4-2 单调栈(Monotone-Stack)及经典问题 学习笔记
算法学习 (门徒计划)4-2 单调栈(Monotone-Stack)及经典问题 学习笔记 前言 单调栈 基础 性质 代码实现 总结 经典例题 LeetCode 155. 最小栈 (基础) 解题思路 L ...
- ACM入门之【单调栈】
何为单调栈,顾名思义,单调栈即满足单调性的栈结构. 单调栈解决的问题: 可以找到对于当前位置左边(或右边)最近的大于(或小于)它的值(或者下标). 常用模板: typedef long long in ...
- CF407 E. k-d-sequence(线段树+单调栈)
文章目录 CF407 E. k-d-sequence problem solution code CF407 E. k-d-sequence problem solution special case ...
最新文章
- (原)使用mkl计算特征值和特征向量
- TensorFlow 2.0开发者预览版发布
- oracle基础-基本的查询,以及pl/sql登录
- mqtt连接失败_Netty实战:如何让单机下Netty支持百万长连接?
- 应用视觉设计_Day01
- 前端学习(1772):前端调试之serverworkers的概念和方法二
- 论文浅尝 - AAAI2020 | 通过知识库问答改善知识感知对话生成
- 【Java】JDBC连接MySQL/SQLServer/Oracle三种数据库
- spark 转换算子应用举例
- LU分解法求解方程组(C语言)
- 网易卡搭python_网易卡搭编程
- Android-仿QQ表情库、表情混合文字聊天图文
- Iso中查看Windows版本
- 力士乐触摸屏维修VCP20.2DUN-003-PB-NN-PW
- linux命令详解之cd命令
- Custom Resource Definitions Operator
- 花生壳http更新协议
- java 12306验证码识别_GitHub - sunqipeng-cn/JavaVerify: 用java 编写的验证码识别
- 辽宁师范大学本科毕业论文答辩PPT模板
- 【愚公系列】2022年12月 .NET CORE工具案例-性能监控工具WatchDog的使用
热门文章
- (精华)2020年8月22日 ABP vNext WebAPI应用ABP
- IDEA2018.3中文翻译包,2018.3版本汉化包(百度云链接下载)
- 计算机类审稿快的中文期刊,审稿快的中文期刊_土木审稿快的期刊_最容易发表审稿快的学报...
- 科研日记2——APS讲解论文写作
- 云控系统针对抖音和快手功能是一样的吗?
- SAP GUI750快捷键保存密码
- 不管是大神还是新手,墙裂推荐7个实用C4D插件,成为C4D高效玩家
- 每个程序猿都应该知道的网站,数量有点多,请点收藏慢慢查看
- 机器学习(十三)——机器学习中的矩阵方法(3)病态矩阵、协同过滤的ALS算法(1)...
- 5条快速优化博客的SEO技巧