目录
滑动窗口/单调队列
题目描述
输入格式
输出格式
输入输出样例
说明/提示
题目分析
代码示例
洛谷p-7505小小的埴轮问题
题目背景
题目描述
输入格式
输出格式
输入输出样例
说明/提示
样例 1 说明
样例 2, 3
数据规模与约定
题目分析
代码示例
洛谷p-1981表达式求值
题目背景
题目描述
输入格式
输出格式
输入输出样例
说明/提示
题目分析
代码示例
有一个长为 n 的序列 a,以及一个大小为 k 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。
输入一共有两行,第一行有两个正整数 n,k。 第二行 n 个整数,表示序列 a
输出共两行,第一行为每次窗口滑动的最小值
第二行为每次窗口滑动的最大值
输入 #1复制
8 3 1 3 -1 -3 5 3 6 7
输出 #1复制
-1 -3 -3 -3 3 3 3 3 5 5 6 7
【数据范围】
对于 50% 的数据1≤n≤105;
对于 100% 的数据,1≤k≤n≤106,i∈[−231,231)
1.首先题目是每次滑动窗口的最小值和最大值,所以我们可以定义俩个函数来分别求最大值和最小值
2.这题目用单调队列(一般是用双端队列来实现)所以这里我们选择用stl库里的deque来实现双端队列
3.队列实现后,因为是滑动窗口,所以要判断队列头是否在界内(目前所在的位置i减去当前队列头的大于等于k规定的范围大小时,应把队列头往后移,就是相当于把当前队列头删去,然后给后一个充当队列头)
4如果是求最小值的话,为保证每次队列头里存放的都是最小值,所以每次新数据入队时比队列尾大或是等于的话则直接入队,如果小于,则一直弹出队列尾直到遇到比他小的数为止或者队列为空
此时,该数据入队,求最大值的话,则反之。
#include
using namespace std;
int n,k;
static const int maxn=1e6+5;//注意数据范围
int a[maxn];
deques;
dequep;//因为有俩个函数,所以用了俩个队列(貌似有个清空队列的函数)
int main()
{
scanf("%d%d",&n,&k);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
for(int i=1; i<=n; i++)//求最小值
{
if(!p.empty()&&i-k>=p.front())p.pop_front();//不在边界里所以队列头加一,窗口向后滑动
while(!p.empty()&&a[i]=k)printf("%d ",a[p.front()]);//满足边界范围输出队列头(输出第一个窗口)
}
printf("\n");
for(int i=1; i<=n; i++)//与上面相反求的是最大值
{
if(!s.empty()&&i-k>=s.front())s.pop_front();
while(!s.empty()&&a[i]>=a[s.back()])s.pop_back();
s.push_back(i);
if(i>=k)printf("%d ",a[s.front()]);
}
printf("\n");
}
杖刀偶磨弓是埴轮兵团的首长。
作为埴轮兵长,训练埴轮兵团是很平常的事情。
磨弓下达命令让埴轮们站成一行。不妨认为它们站在了一个数轴上,每个埴轮的位置就是它脚下数轴的数字。磨弓会告诉你,第 i 个埴轮的位置为ai 。不保证 ai 升序。
数轴的长度是有限制的,具体的范围是 [−k,k] 。也就是说,如果某个埴轮移出了这个范围,它就脱离了这个队列了,并且不会再次回到队列当中。
为了训练埴轮,磨弓给埴轮们下达了 m 个指令,有以下 3 种:
但是磨弓发现,埴轮兵团的大小实在是太大了,以至于执行这些操作变得非常缓慢。尽管如此,磨弓仍然希望你告诉她所有指令 3 的结果。
第一行共有 3 个整数 n,m,k,含义如题面所示。
第二行共有 n 个整数 1,2,⋯ ,a1,a2,⋯,an,表示每个埴轮的位置。
接下来 m 行,有 1 或者 22 个正整数,描述一条指令。首先是一个整数 opop,表示这条指令的类型。如果 1≤op≤21≤op≤2,接下来还会输入一个整数 x。
对于每条指令 3 ,输出一个整数,表示目前还在队列中的埴轮的数目。
输入 #1复制
3 4 3 -1 1 2 2 3 3 1 5 3
输出 #1复制
2 1
一共有三个埴轮。初始时,它们的站位分别是 [−1,1,2]。
见下发附件。
1.首先题目中的输入不保证升序,所以我们在存完个数组后需对他进行升序后再存放到队列中
2.指令1是往正方向走,指令2是往负方向走,指令3其实就是相当于求当前队列的长度,对于指令1和指令2我们就循环判断每个队列里的元素加上x方向长度后是否在【-k,k】中如果不在就弹出队列因为是可能会在队尾弹出也会在队列头弹出,所以我们选择用单调队列,双端队列
#include
#define ll long long//预编译更简洁
using namespace std;
long long n,m,k,p,to,w,h;
const ll N=300000+10;//特别要注意数据范围
ll a[N];
deques;//开创队列
int main()
{
cin>>n>>m>>k;
for(ll i=1; i<=n; i++)
{
cin>>a[i];
}
sort(a+1,a+1+n);//排序
for(ll i=1; i<=n; i++)
s.push_back(a[i]);//入队
for(ll i=1; i<=m; i++)
{
cin>>p;
if(p==3)cout<>w;
to+=w;//正方向所以是加
while(!s.empty()){//while循环判断队列里是否有元素出界,出界的话从队尾弹出
ll y=s.back();
if(y+to>k)s.pop_back();//,加上去后如果越界就弹出去
else break;
}
}
else if(p==2)
{
cin>>w;
to-=w;
while(!s.empty()){
ll y=s.front();
if(y+to<-k)s.pop_front();//如果是在负方向越界了的话那么应该是从队列头弹出去
else break;
}
}
}
return 0;
}
NOIP2013 普及组 T2
给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值。
一行,为需要你计算的表达式,表达式中只包含数字、加法运算符 +
和乘法运算符 *
,且没有括号,所有参与运算的数字均为 00 到 231−1231−1 之间的整数。
输入数据保证这一行只有 0123456789+*
这 12 种字符。
一个整数,表示这个表达式的值。
注意:当答案长度多于 4 位时,请只输出最后 4 位,前导 0 不输出。
输入 #1复制
1+1*3+4
输出 #1复制
8
输入 #2复制
1+1234567890*1
输出 #2复制
7891
输入 #3复制
1+1000000003*1
输出 #3复制
4
对于 30% 的数据,0≤0≤ 表达式中加法运算符和乘法运算符的总数 ≤100。
对于 80的数据,0≤ 表达式中加法运算符和乘法运算符的总数 ≤1000。
对于 100% 的数据,0≤ 表达式中加法运算符和乘法运算符的总数 ≤100000。
1.注意:当答案长度多于 4 位时,请只输出最后 4 位,前导 0不输出。这是万恶之源
意思大概是每次得出来的答案结果%10000,那么最后得到的值肯定是最后四位,比10000小的数去模10000的话数是不变的
2.入栈的话,是乘号就把乘号前一个数和后一个数相乘后得到的结果入栈,加法就直接入栈
3.最后只需要在栈中进行总的求和就行了
#include
using namespace std;
stackk;//开创栈
int a,c,m=10000,q;
char b;
int main(){
cin>>q;
q=q%m;//%10000取后四位
k.push(q);
while(cin>>b>>c){//把俩数相乘的结果放入栈中,最后在到栈中进行总的求和
if(b=='*'){
q=k.top();//取乘号的前一个数
k.pop();
k.push(q*c%m);//把相乘的结果取后四位放入栈
}
else k.push(c);//不是乘号的话就是加号所以直接把他放入栈中,最后在进行求总和
}
q=0;//此时记得把q清零,这里是作为一个求总和的变量
while(!k.empty()){//进行总和
q+=k.top();
q%=m;//取后四位
k.pop();//每加一次弹出一次栈顶
}
cout<
如以上有任何错误,还请各位大佬指正