单调栈QWQ

先来看一道例题:

给出一个整数数列 a1…n,求Ai右侧第一个比Ai大的数的下标。

【模板】单调栈 - 洛谷

​朴素算法:(其实看不看无所谓)

#include <bits/stdc++.h>

using namespace std;
const int N = 3e6+5;

int n,a[N];
bool flag = true;

int main()
{
    cin >> n;
    for(int i=1;i<=n;i++) cin >> a[i];
    for(int i=1;i<=n;i++){
        flag = true;
        for(int j=i+1;j<=n;j++){
            if(a[j]>a[i]){
                cout << j << ' ';
                flag = false;
                break;
            }
        }
        if(flag) cout << 0 << ' ';
    }
    return 0;
}

然后就不出意外的出意外了,很明显会TLE

 这时就可以使用单调栈来完成!

先讲一讲思路及原理:

顾名思义,单调栈就是在栈后进先出的基础上,具有单调的特性,又分为单调增栈和单调减栈,这里就以题目中的来进行讲解,当然如果是求与题目不相同的,相信作为大佬的你一定可以举一反三的吧!

                                                    

以题目为样例讲解, 首先有一个空栈,我们从右往左的进行便利。

以下面的步骤对数操作 :

1.如果栈顶的元素大于(或小于,更具题目描述)当前的操作数(在栈不为空的情况下,如果栈为空就直接入栈),就弹出栈顶的元素,重复操作2,直到不满足。

2.记录当前的栈顶元素(在栈不为空的情况下),这个元素就是操作数的目标元素。

3.将当前的数入栈。

来看下样例:

注:如果我们直接将元素的值入栈,就只能知道大于他元素是什么,但在做题中的大多数情况下这是不够的,我们就还需要O(n)查找元素的位置,那单调栈还有什么意义?

于是我们就可以进行优化,入栈元素的下标,在判断时只需要用变量套一层就好了。

看代码:

#include <bits/stdc++.h>

using namespace std;
const int N = 3e6+5;

int a[N];
int ans[N]; // 记录答案的数组
int n;
stack<int> stk_r;
int main()
{
    cin >> n;
    for(int i=1;i<=n;i++) cin >> a[i];
    for(int i=n;i>=1;i--){  // 从右往左进行遍历
        while(!stk_r.empty()&&a[stk_r.top()] <= a[i]) stk_r.pop(); 
        //打破单调性,弹出元素
        //注意1:因为栈中存入的是下标,所以在外面套一层a[]
        //注意2:因为C++是从左往右进行判断,所以判断不为空应该写在前面
        //防止stk_r.top()越界导致RE
        //别问我是怎么知道了:(
        if(!stk_r.empty()){ // 如果不为空记录元素
            //如果为空说明没有比他大的元素,默认为0
            ans[i] = stk_r.top(); // 记录下标
        }
        stk_r.push(i); // 入栈
    }
    for(int i=1;i<=n;i++){
        cout << ans[i] << ' ';
    }
    return 0;
}

下面是没有注释的代码:

#include <bits/stdc++.h>

using namespace std;
const int N = 3e6+5;

int a[N];
int ans[N];
int n;
stack<int> stk_r;
int main()
{
    cin >> n;
    for(int i=1;i<=n;i++) cin >> a[i];
    for(int i=n;i>=1;i--){
        while(!stk_r.empty()&&a[stk_r.top()] <= a[i]) stk_r.pop();
        if(!stk_r.empty()) ans[i] = stk_r.top(); 
        stk_r.push(i);
    }
    for(int i=1;i<=n;i++){
        cout << ans[i] << ' ';
    }
    return 0;
}

这有一道练习题:发射站 - 洛谷

题目思路:明显模板题跑两边,可以从左向右再跑一遍。

AC code:

#include <bits/stdc++.h>

using namespace std;
const int N = 1e6+5;
stack<int>l;
stack<int>r;
struct bb{
    long long h,e;
    long long j = 0;
}a[N];
long long n; 
int main()
{
    cin >> n;
    for(int i=1;i<=n;i++) cin >> a[i].h >> a[i].e;
    for(int i=1;i<=n;i++){
        while(!l.empty() && a[l.top()].h <= a[i].h) l.pop();
        if(!l.empty()){
            a[l.top()].j += a[i].e;
        }
        l.push(i);
    }
    for(int i=n;i>=1;i--){
        while(!r.empty()&&a[r.top()].h<=a[i].h) r.pop();
        if(!r.empty()){
            a[r.top()].j += a[i].e;
        }
        r.push(i);
    }
    long long maxn = INT_MIN;
    for(int i=1;i<=n;i++){
        // cout << a[i].j << ' ';
        maxn = max(maxn,a[i].j);
    }
    // cout << endl;
    cout << maxn;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值