前序:来自b站左神,个人整理为笔记,如有错误,感谢指出!、
由于栈内元素始终保持单调递增,因此这种数据结构也叫做单调栈。
运用:// 单调栈求每个位置左右两侧,离当前位置最近、且值严格小于的位置(大于)
// 给定一个可能含有重复值的数组 arr
// 找到每一个 i 位置左边和右边离 i 位置最近且值比 arr[i] 小的位置
// 返回所有位置相应的信息。
难点:遇到相等的时候如何处理
题目:不知道为啥只能通过80%。快读优化后了差1ms,emmm真棒。
基本模板:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e6 + 10;
int arr[N];
int help[N];//数组模拟栈,记录位置下标
int ans[N][2];//二维数组记录答案
int n , r;//r作为入栈记录
void solve()
{
r = 0;
int index;//当前位置下标
//遍历
for(int i = 0 ; i < n ; i++)
{
//通过位置下标就可以获得数组的值,所以记录下标即可
while(r > 0 && arr[i] <= arr[help[r - 1]])//当破坏大压小的原则的时候 前提是栈内还有数 通过r的计数可以判断
{
index = help[--r];//开始找到此时下标左边最近且小于的数,特判一下是否栈为空
ans[index][0] = r > 0 ? help[r - 1]:-1;//如果栈为空index下标就是栈最后一个数,那就没有最左了记为-1;边界的处理
ans[index][1] = i;//当前数再进入
}
//没有破坏正常入栈
help[r++] = i;//记录位置
}
//清算阶段,最后栈内剩下的数,已经不存在最右且小的了
while(r > 0)
{
index = help[--r];
ans[index][0] = r > 0 ? help[r - 1] : -1;
ans[index][1] = -1;
}
//修正阶段,遇到等于的时候的处理 ,从右往左,n - 1位置是最后一个右侧答案一定是-1 ,处理右边界
for(int i = n - 2 ; i >= 0 ; i--)
{
if(ans[i][1] != -1 && arr[ans[i][1]] == arr[i])//先前已经保存的是相等的数
{
ans[i][1] = ans[ans[i][1]][1];//把相等数存的正确答案也是此时的答案
}
}
}
int main()
{
cin >> n;
for(int i = 0 ; i < n ; i++)
cin >> arr[i];
solve();
for(int i = 0 ; i < n ; i++)
cout<<ans[i][0] << " " << ans[i][1] << endl;
return 0;
}
题目二:思路:前面提到的下一个,而且只要考虑右边,等于的时候直接也入栈不影响
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e6 + 10;
int hot[N];
int help[N];//数组模拟栈,记录位置下标
int ans[N];//二维数组记录答案
int n , r;//r作为入栈记录
int main()
{
cin >> n;
for(int i = 0 ; i < n ; i ++)
{
cin >> hot[i];
}
int index;
for(int i =0 ; i < n ; i++)
{
//小压大被破坏
while(r > 0 && hot[help[r - 1]] < hot[i]){
index = help[--r];
ans[index] = i - index;
}
help[r++] = i;
}
for(int i = 0 ; i < n ; i++)
cout << ans[i] << " ";
return 0;
}
题目三:
分析 假设有组数组3 4 5 2,单调栈进行维护当被破坏的时候依次进行枚举,因为5是栈顶元素,所以此时只有一种情况有最小值那就是5本身,继续判断循环再次进入4我们知道因为是单调的此时i到cur就知道结尾的情况,开头只有本身开头,1 * 2 * 4 = 8.emm大概就这意思。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod = 1000000007;
const int N = 3e5 + 10;
int help[N];
int arr[N];
int n,r;
int solve()
{
long long int ans = 0;
r = 0;
for(int i = 0 ; i < n ; i++)
{
while(r > 0 && arr[help[r - 1]] >= arr[i])//当栈内元素大于进栈元素,破坏了大压小()
{
int cur = help[--r];
int left = r == 0 ? -1 : help[r - 1];
ans = (ans + (long)(cur - left) * (i - cur) * arr[cur]) % mod;
}
help[r++] = i;
}
while(r > 0)
{
int cur = help[--r];
int left = r == 0 ? -1 : help[r - 1];
ans = (ans + (long)(cur - left) * (n - cur) * arr[cur]) % mod;
}
return (int)ans;
}
int main()
{
cin >> n;
for(int i = 0; i < n ; i++)
cin >> arr[i];
int res = solve();
cout << res << endl;
return 0;
}