单调栈
概念:
例题分析:
洛谷p5788
https://www.luogu.com.cn/problem/P5788
思路分析:
此题我们采用单调栈
- 首先对题意分析一波:
我们要求第 i i i个元素之后第一个大于 a i a_i ai的元素的下标,因此我们用单调栈维护一下下标,在栈顶元素值小于操作值的时候,弹出栈顶元素,并记录操作值下标,在栈顶元素大于操作值的时候,我们只需要把操作值下标压入栈中即可。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e6 + 10;
stack<int> s;//单调栈
int a[maxn];
int f[maxn];
int main()
{
int n;
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 1; i <= n; i++)
{
while (!s.empty() && a[s.top()] < a[i])
{
f[s.top()] = i;
s.pop();
}
s.push(i);
}
while(!s.empty())
{
f[s.top()] = 0;
s.pop();
}
for (int i = 1; i <= n; i++)
cout << f[i] << ' ';
cout << endl;
return 0;
}
洛谷p2866
https://www.luogu.com.cn/problem/P2866
思路分析:
- 在吃透p5788后这个题就显得简单一些了,我们在上一题中用单调栈维护的就是下标,而在这一题中,我们也可以用维护下标的方法来解题。
- 为什么呢?
- 因为一头牛可以看到的牛的条数与他的高度有关,也就是上一题中我们说的那种情况,我们需要找到在前面比他高的那头牛的位置啊(还有等于),最后两条牛中间牛的个数即拿 i i i - j j j - 1 即可,这种方法读者请自行实现,其实就是模板。
- 这一题其实可以更简单的来做,也就是说如果对栈顶的牛牛来说,如果当前的牛牛比它高,按照惯例,为了维护栈的单调性,我们会将比它矮的所有牛牛弹出栈中,因为对比它矮的牛牛来说,它们不可能再看到这头比它高的牛牛的前面的牛牛(就是被挡住了)。如果当前的牛牛比他矮,那么我们就压入栈中,并且对当前的牛牛来说,它会比后面所有的牛牛都矮(单调性),所以它能被现在在栈中的所有牛牛看到,也就是当前栈中元素的大小,因此我们在每次维护单调性后将栈中元素个数相加就可得到最终答案。最好还是用这种思路走一遍。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 8e4 + 10;
int x;
//牛牛的高度
stack<int> s;
int ans = 0;
//要求的总和
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
int x;
for (int i = 1; i <= n; i++)
{
cin >> x;
while(!s.empty() && x >= s.top())
s.pop();
ans += s.size();
s.push(x);
}
cout << ans << endl;
return 0;
}
HDU - 6957
https://acm.hdu.edu.cn/showproblem.php?pid=6957
思路分析:
- 这题难度稍微比前面难一点点,建议读者先将前面两题吃透还需要先看看
https://leetcode-cn.com/problems/largest-rectangle-in-histogram/ - 那么这一题我们用到单调栈来储存每一个元素来说(以当前行为底)的高度。
- 首先需要满足题目的条件:单调非递减可以看作高度 + 1 +1 +1
- 在储存好高度之后我们呢就需要对每一行进行行分再和leetcode上的那题一样的做法从而得到答案
代码如下:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn = 2e3 + 10;
int cnt[maxn][maxn];
//储存高度
ll a[maxn][maxn];
//储存矩阵
int main()
{
int t;
ios::sync_with_stdio(0);
cin >> t;
while (t--)
{
memset(cnt, 0, sizeof(cnt));
memset(a, 0, sizeof(a));
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> a[i][j];
if (a[i][j] >= a[i - 1][j])
cnt[i][j] = cnt[i - 1][j] + 1;
//满足条件高度加一
else
cnt[i][j] = 1;
}
}
ll ans = 0;
for (int i = 1; i <= n; i++)
{
ll l[maxn];
ll r[maxn];
stack<int> s;
for (int j = 1; j <= m; j++)
{
while (!s.empty() && cnt[i][j] <= cnt[i][s.top()])
{
s.pop();
}
l[j] = (s.empty() ? 0 : s.top());
s.push(j);
}
while (!s.empty())
s.pop();
for (int j = m; j >= 1; j--)
{
while (!s.empty() && cnt[i][j] <= cnt[i][s.top()])
{
s.pop();
}
r[j] = (s.empty() ? m + 1 : s.top());
s.push(j);
}
for (int j = 1; j <= m; j++)
ans = max(ans, cnt[i][j] * (ll)(r[j] - l[j] - 1));
}
cout << ans << endl;
}
}