第四章: 栈、队列(7.21)
栈:
NC14893 栈和排序
保证输出字典序最大,预处理maxx[i]为i~n的最大值
如果当前的数比后面的数都要大的话一定就要在这个时候弹出来
#include<bits/stdc++.h> #define LL long long #define INF 0x3f3f3f3f using namespace std; int a[1000003],maxx[1000003]; int stackk[1000003]; int main() { int n;scanf("%d",&n); for(int i=1;i<=n;++i)scanf("%d",&a[i]); for(int i=n;i>=1;--i)maxx[i]=max(maxx[i+1],a[i]); int top=0; for(int i=1;i<=n;++i) { stackk[++top]=a[i]; while(top&&stackk[top]>maxx[i+1]) printf("%d ",stackk[top--]); } }
NC21874 好串
典型栈思想,a则进栈,b就把进了的a出栈
#include<bits/stdc++.h> #define LL long long #define INF 0x3f3f3f3f using namespace std; char s[103]; int main() { scanf("%s",s+1); int l=strlen(s+1); int p=0; for(int i=1;i<=l;++i) { if(s[i]=='a')p++; else if(s[i]=='b'&&p)p--; else {printf("Bad\n");return 0; } } if(p!=0)printf("Bad\n"); else printf("Good\n"); }
队列:
NC13822 Keep In Line
使用unordered_map,在此复习一下map和unordered_map(参考博客)
与map对比
map
优点:
1.有序性,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作;
2.红黑树,内部实现一个红黑树使得map的很多操作在O(lgn)的时间复杂度下就可以实现,因此效率非常的高。
缺点:空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间
应用场景:对于那些有顺序要求的问题,用map会更高效一些
unordered_map
优点:因为内部实现了哈希表,因此其查找速度非常的快
缺点:哈希表的建立比较耗费时间
应用场景:对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map
总结
内存占有率的问题就转化成红黑树 VS Hash表,还是unordered_map占用的内存要高;
但是unordered_map执行效率要比map高很多;
对于unordered_map或unordered_set容器,其遍历顺序与创建该容器时输入的顺序不一定相同,因为遍历是按照哈希表从前往后依次遍历的。
这道题用unordered_map存入字符串到布尔的映射,表示是否访问
#include<bits/stdc++.h> #define LL long long #define INF 0x3f3f3f3f using namespace std; char s[103]; int main() { int T;scanf("%d",&T); while(T--) { int n;scanf("%d",&n); unordered_map<string,bool>mp; queue<string>q1,q2; while(n--) { string op,name; cin>>op>>name; if(op=="in")q1.push(name); else if(op=="out")q2.push(name); } int cnt=0; while(!q2.empty()) { if(q1.front()==q2.front())cnt++; mp[q2.front()]=1; while(!q1.empty()&&mp[q1.front()])q1.pop(); q2.pop(); } printf("%d\n",cnt); } }
NC16663 合并果子
做过很多次了,贪心加优先队列,每次合并最小的两堆
NC16430 蚯蚓
开三个队列
原来的长度
分割后更大的一截
分割后更短的一截
显然队列具有单调性,每次取队列的开头即可
每次都要增加的长度,我们可以先不加上,等到取出来的时候再加(i−1)∗q, 放回去的时候注意要减去i∗q
#include<bits/stdc++.h> #define LL long long #define INF 0x3f3f3f3f using namespace std; LL a[200005]; queue<LL>Q[3]; int main() { LL n,m,q,u,v,t;scanf("%lld%lld%lld%lld%lld%lld",&n,&m,&q,&u,&v,&t); for(int i=1;i<=n;++i)scanf("%lld",&a[i]); sort(a+1,a+1+n); for(int i=n;i>=1;--i)Q[0].push(a[i]); for(int i=1;i<=m;++i) { LL maxx=-0x3f3f3f3f,pos=-1; for(int j=0;j<3;++j) if(Q[j].size()&&Q[j].front()>maxx) { maxx=Q[j].front(); pos=j; } Q[pos].pop(); maxx+=(i-1)*q; LL x=maxx*u/v,y=maxx-x; if(i%t==0)printf("%lld ",maxx); Q[1].push(x-i*q);//Q[1]存长的那截 Q[2].push(y-i*q);//Q[2]存短的那截 } printf("\n"); int cnt=1; while(Q[0].size()||Q[1].size()||Q[2].size()) { // printf("???%d %d %d\n",Q[0].size(),Q[1].size(),Q[2].size()); LL p=-0x3f3f3f3f,pos=-1; for(int j=0;j<3;++j) if(Q[j].size()&&Q[j].front()>p)pos=j,p=Q[j].front(); Q[pos].pop(); if(cnt%t==0)printf("%lld ",p+m*q); cnt++; } printf("\n"); }
双端队列:
NC14661 简单的数据结构
STL中有双端队列deque
push_back(x)/push_front(x) //把x压入后/前端
back()/front() //访问(不删除)后/前端元素
pop_back() pop_front() //删除后/前端元素
empty() //判断deque是否空
size() //返回deque的元素数量
clear() //清空deque
支持通过sort(d.begin(),d.end())进行排序。
#include<bits/stdc++.h> #define LL long long #define INF 0x3f3f3f3f using namespace std; deque<int>q; int main() { int n,T;scanf("%d%d",&n,&T); while(T--) { int op;scanf("%d",&op); if(op==1) { int a;scanf("%d",&a); q.push_front(a); } if(op==2)q.pop_front(); if(op==3) { int a;scanf("%d",&a); q.push_back(a); } if(op==4)q.pop_back(); if(op==5)reverse(q.begin(),q.end()); if(op==6) { printf("%d\n",q.size()); for(int i=0;i<q.size();++i) printf("%d ",q[i]); printf("\n"); } if(op==7)sort(q.begin(),q.end()); } }
单调队列/栈:
NC51001 滑动窗口
区间长度为k,维护区间最大值最小值
方法一:两个单调队列(经典)
方法二:两个双端队列
min:队头删除的当维护的窗口超过k时,a[i]<q.back,删除尾部的元素
NC24840 look up
对每头牛求出向右看的第一头比它高的牛
用栈即可
#include<bits/stdc++.h> #define LL long long #define INF 0x3f3f3f3f using namespace std; int h[100005]; int stackk[100003],ans[100003]; int main() { int n;scanf("%d",&n); for(int i=1;i<=n;++i)scanf("%d",&h[i]); int top=0; stackk[++top]=1; for(int i=2;i<=n;++i) { if(h[i]<=h[stackk[top]])stackk[++top]=i; else { while(top&&h[stackk[top]]<h[i])ans[stackk[top--]]=i; stackk[++top]=i; } } while(top)ans[stackk[top--]]=0; for(int i=1;i<=n;++i)printf("%d\n",ans[i]); }
NC50965 Largest Rectangle in Histogram
求最大矩形覆盖
对于每一列,向左向右找到第一个比它矮的(两个栈处理)
或者更优化一点,就一个从左向右的栈,如果i没有被弹出,说明后面的都比它高,被弹出了说明有一个比它小,也不能往后延伸了,每列在被弹出的时候更新答案。