【算法】【单调栈】

0 阅读2分钟

概述

1.每个元素指向它左右第一个更大的元素(入栈的值找到合适的位置,正是刚好比他大的),通过这个结构可以找到"以每个元素为顶峰的最长合法序列"

Simons and Beating Peaks


题意

一个数组是 cool 的,当且仅当不存在这样的索引 i(1 < i < m)使得:

text

a[i] = max(a[i-1], a[i], a[i+1])

即:不存在任何一个元素,它同时有左右邻居,并且它是这三个数中的最大值。

允许的操作

  1. 选择一个索引 i(1 < i < n),满足 a[i] 是它和相邻两个元素中的最大值
  2. 删除 a[i-1]a[i+1] 中的一个
  3. 删除后,数组的两部分会拼接在一起

目标

最少需要多少次操作才能使数组变成 cool 数组。

代码

#include <bits/stdc++.h>  
using namespace std;  
using ll=long long;  
void so(){  
    //不能出现折线  
    //单调栈中删的就是逆山峰的突起
    int n;cin>>n;  
    vector<int>a(n);  
    for(auto &x:a){cin>>x;}  
    vector<int>l(n,-1);//左侧第一个比a[i]大  
    vector<int>r(n,n);//右侧第一个比它大  
    stack<int>st;//是一个单调递减栈  
    for(int i=0;i<n;i++){  
        while((st.size()!=0)&&a[st.top()]<a[i]){  
            r[st.top()]=i;  
            st.pop();  
        }if(st.empty()==0){l[i]=st.top();}  
        st.push(i);  
    }vector<int>xl(n),xr(n);  
    int ans=0;  
    for(int i=n-1;i>=0;i--){  
        if(r[i]!=n)xr[i]=xr[r[i]]+1;//从右向左多少连续递增  
    }for(int i=0;i<n;i++){  
        if(l[i]!=-1)xl[i]=xl[l[i]]+1;  
        ans=max(ans,xl[i]+xr[i]+1);//(逆着)山峰的最长  
    }ans-=n;  
    cout<<-ans<<'\n';  
}int main(){  
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);  
    int t;cin>>t;  
    while(t--){so();}  
}

思路

1.不能有突起,所以只能有一个逆山峰,所以用单调栈来做 2.单调栈中删的就是逆山峰的突起

单调栈

1. 单调栈维护什么

cpp

stack<int> st; // 单调递减栈(存的是索引)

栈内元素对应的值 a[st.top()]递减的:栈底最大,栈顶最小。

2. 单调栈的作用过程

右边大是被踢出的时候计,左边是进入时候计

for(int i=0; i<n; i++) {
    while(!st.empty() && a[st.top()] < a[i]) {
        r[st.top()] = i;  // 当前元素a[i]比栈顶大
        st.pop();          // 栈顶元素找到了右边第一个更大的
    }
    if(!st.empty()) {
        l[i] = st.top();   // 栈顶是左边第一个比a[i]大的
    }
    st.push(i);
}