1.30数据与结构算法学习日记(单调队列和栈的运用)

目录

滑动窗口/单调队列

题目描述

输入格式

输出格式

输入输出样例

说明/提示

题目分析

代码示例

洛谷p-7505小小的埴轮问题

题目背景

题目描述

输入格式

输出格式

输入输出样例

说明/提示

样例 1 说明

样例 2, 3

数据规模与约定

 题目分析

代码示例

洛谷p-1981表达式求值

题目背景

题目描述

输入格式

输出格式

输入输出样例

说明/提示

题目分析

代码示例


滑动窗口/单调队列

题目描述

有一个长为 n 的序列 a,以及一个大小为 k 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。

1.30数据与结构算法学习日记(单调队列和栈的运用)_第1张图片

输入格式

输入一共有两行,第一行有两个正整数 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");

}

洛谷p-7505小小的埴轮问题

题目背景

杖刀偶磨弓是埴轮兵团的首长。

作为埴轮兵长,训练埴轮兵团是很平常的事情。

题目描述

磨弓下达命令让埴轮们站成一行。不妨认为它们站在了一个数轴上,每个埴轮的位置就是它脚下数轴的数字。磨弓会告诉你,第 i 个埴轮的位置为ai​ 。不保证 ai​ 升序

数轴的长度是有限制的,具体的范围是 [−k,k] 。也就是说,如果某个埴轮移出了这个范围,它就脱离了这个队列了,并且不会再次回到队列当中。

为了训练埴轮,磨弓给埴轮们下达了 m 个指令,有以下 3 种:

  • 指令 1:全体埴轮向数轴的正方向移动 x 个单位长度。
  • 指令 2:全体埴轮往数轴的反方向移动 x 个单位长度。
  • 指令 3:依次报数,统计目前队列里一共有多少个埴轮。

但是磨弓发现,埴轮兵团的大小实在是太大了,以至于执行这些操作变得非常缓慢。尽管如此,磨弓仍然希望你告诉她所有指令 3 的结果。

输入格式

第一行共有 3 个整数 n,m,k,含义如题面所示。

第二行共有 n 个整数 1,2,⋯ ,a1​,a2​,⋯,an​,表示每个埴轮的位置。

接下来 m 行,有 1 或者 22 个正整数,描述一条指令。首先是一个整数 op⁡op,表示这条指令的类型。如果 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,1,2]。

  • 第一次操作后,所有埴轮向左移动 33格,位置变成了 [−4​,−2,−1] 。第一个埴轮被移出了数轴。
  • 第二次操作后,输出当前的埴轮数目,为 2 个。
  • 第三次操作后,所有埴轮向右移动 5 格,位置变成了 [3,4​] ,第二个埴轮被移出了数轴。
  • 第四次操作后,输出当前的埴轮数目,为 11个。
样例 2, 3

见下发附件。

数据规模与约定
  • 对于 30% 的数据,1≤n,m≤5×103;
  • 对于另外 20%20% 的数据,1≤k≤500;
  • 对于 100% 的数据,1≤n,m≤3×105 ,1≤k,x≤2×109,−k≤ai​≤k 。

 题目分析

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;



}

洛谷p-1981表达式求值

题目背景

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<

如以上有任何错误,还请各位大佬指正

你可能感兴趣的:(算法,学习,c++,数据结构)