概述
1.每个元素指向它左右第一个更大的元素(入栈的值找到合适的位置,正是刚好比他大的),通过这个结构可以找到"以每个元素为顶峰的最长合法序列"
Simons and Beating Peaks
题意
一个数组是 cool 的,当且仅当不存在这样的索引 i(1 < i < m)使得:
text
a[i] = max(a[i-1], a[i], a[i+1])
即:不存在任何一个元素,它同时有左右邻居,并且它是这三个数中的最大值。
允许的操作
- 选择一个索引
i(1 < i < n),满足a[i]是它和相邻两个元素中的最大值 - 删除
a[i-1]或a[i+1]中的一个 - 删除后,数组的两部分会拼接在一起
目标
求最少需要多少次操作才能使数组变成 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);
}