Y1课程笔记:第三课时(线性数据结构)

知识点一:双端栈

将两个栈顶对顶使用,可以有出乎意料的效果

先上个题↓

功能强大编辑器

时间限制:1秒        内存限制:128M

题目描述

你要帮助小可创造一个超级数字编辑器!编辑器依旧运行在Linux下,因此你只能通过指令去操控他。指令有五种:
In X 表示在光标左侧插入一个数字
Del 表示删除光标左侧一个数字
Left 表示光标向左移动一下
Right 表示光标向右移动一下
Ask k 表示光标之前的序列为a​1​​,a​2​​,a​3​​…a​k​​,输出max ​1≤i≤k ​​S​i​​,其中S​i​​=a​1​​+a​2​​+..+a​i​​

输入描述

输入第一行包含一个整数Q,表示指令数量。

然后输入Q行,表示Q个指令。

输出描述

对于每个Ask k均输出一行,表示询问结果。

输入样例

  1. 8
  2. In 2
  3. In -1
  4. In 1
  5. Ask 3
  6. Left
  7. Del
  8. Right
  9. Ask 2

输出样例

  1. 2
  2. 3

数据范围

50%的数据,Q不超过1000.

100%的数据,Q不超过1000000,且x不超过1000

解题思路

见到题的第一时间,可能会想到数组模拟,但这样每次操作都会牵动整个数组,极其麻烦且会超时。如果考虑双端栈,就会简单很多。

在这其中,有Ask操作比较特殊,要求出前缀和最大值,直接求会超时,因此我们可以在操作过程中维护最值;

对于in,right操作,都相当于在光标前新加一个数,到这个数的最大值即为上一个数的最值和这个数的前缀和的最大值(即不选此数或选此数)

AC代码

#include<bits/stdc++.h>
using namespace std;
stack<long long>s1,s2;
long long n,x,cnt[1000005],ma[1000005];
string k;
int main(){
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
    cin>>n;
    ma[0]=-0x3f3f3f3f;
    for(int i=1;i<=n;i++){//2  1 
        cin>>k;//         ma2  2
        if(k=="In"){//   cnt2  2
            cin>>x;
            s1.push(x);
            cnt[s1.size()]=cnt[s1.size()-1]+x;
            ma[s1.size()]=max(ma[s1.size()-1],cnt[s1.size()]);
        }
        if(k=="Del"){
            if(!s1.empty()){
                //ma[s1.size()]=-0x3f3f3f3f,cnt[s1.size()]=-0x3f3f3f3f;
                s1.pop();
            }
        }
        if(k=="Left"){
            if(!s1.empty()){
                s2.push(s1.top());
                //ma[s1.size()]=-0x3f3f3f3f,cnt[s1.size()]=-0x3f3f3f3f;
                s1.pop();
            }
        }
        if(k=="Right"){
            if(!s2.empty()){
                s1.push(s2.top());
                cnt[s1.size()]=cnt[s1.size()-1]+s2.top();
                ma[s1.size()]=max(ma[s1.size()-1],cnt[s1.size()]);
                s2.pop();
            }
        }
        if(k=="Ask"){
            cin>>x;
            cout<<ma[x]<<'\n';
        }
    }
}

知识点二:双端队列

单调队列

先上模板

滑动最小值

时间限制:1秒        内存限制:128M

题目描述

有一个序列,共有n个数字,请你求出以每个数字开始的长度为k的区间中的最小值。

输入描述

多组测试数据,输入0 0表示结束。

第一行包含两个整数,n和k。

然后输入n个正整数。

输出描述

输出题面描述的答案,如果i>n-k,则无法查找到最小值,不进行输出。

输入样例

 
  1. 5 2
  2. 1 2 3 4 5
  3. 0 0

输出样例

 
  1. 1 2 3 4

数据范围

30%的数据:n<1000

100%的数据,n<1000000

解题思路

详见注释

AC代码

#include<bits/stdc++.h>
using namespace std;
deque<int>q;
int n,k,a[1000005];
int main(){
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
    while(cin>>n>>k){
        q.clear();
        if(n==0&&k==0){
            return 0;
        }
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        for(int i=1;i<=n;i++){
            if(i>k){//当遍历完第一个数后第k个再开始
                cout<<q.front()<<' ';
                if(q.size()>0&&q.front()==a[i-k]){//控制区间长度
                    q.pop_front();
                }
            }
            while(!q.empty()&&q.back()>a[i]){//维护单调性
                q.pop_back();
            }
            q.push_back(a[i]);
        }
        cout<<q.front()<<'\n';
    }
}

另一例题

让小可睡一个好觉

时间限制:1秒        内存限制:128M

题目描述

小可最近太累了,昨天晚上为了写代码,小可睡觉的时间非常晚。恰好第二天是周六,小可打算好好睡一个懒觉,实现自己一个小小的愿望,睡觉睡到自然醒!为了实现这个愿望,小可肯定不可以被闹钟吵醒!

小可平时为了让自己早起学习,会定n个闹钟,如果长度为m的一段时间内,有K个及以上的闹铃响了,那么小可就会被吵醒,现在请你帮助一下小可,最少关掉多少个闹铃,小可不会被吵醒。

输入描述

输入第一行为n,m,k,含义如题所示。

第二行为每个铃响的时间a​i​​。

输出描述

输出最少关闭几个闹钟,使小可能安稳的睡一个懒觉。

输入样例1

  1. 3 3 2
  2. 3 5 1

输出样例1

  1. 1

输入样例2

  1. 5 10 3
  2. 12 8 18 25 1

输出样例2

  1. 0

提示:

样例一中,可以关闭第3分钟响的闹钟;

样例二中,不需要关闭闹钟。

数据范围:

70%的数据下:1≤k≤n≤200,1≤m≤1000,a​i​​≤3000

100%的数据下:1≤k≤n≤2∗10^​5​​,1≤m≤10^​6​​,a​i​​≤10^​6​​

解题思路

首先排序,使用双端队列,通过队尾-队头来控制时间区间长度,通过队列长度来判断区间内闹钟数量,超出时优先删除靠后的(贪心)

AC代码

#include<bits/stdc++.h>
using namespace std;
deque<int>q;
int n,k,ans,a[1000005];
int main(){
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
    cin>>n>>k>>ans;
        int cnt=0;
        q.clear();
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++){
            if(!q.empty()&&q.back()-q.front()>=k){
                q.pop_front();
            }
            while(!q.empty()&&q.size()>=ans){
                q.pop_back();
                cnt++;
            }
            q.push_back(a[i]);
        }
        if(!q.empty()&&q.back()-q.front()>=k){
            q.pop_front();
        }
        while(!q.empty()&&q.size()>=ans){
            q.pop_back();
            cnt++;
        }
        cout<<cnt;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值