1.单调栈结构
给定一个不含有重复值的数组 arr,找到每一个 i 位置左边和右边离 i 位置最近且值比 arr[i] 小的位置。返回所有位置相应的信息。
输入描述:
第一行输入一个数字 n,表示数组 arr 的长度。
以下一行输出 n个数字,表示数组的值。
输出描述:
输出n行,每行两个数字 L 和 R,如果不存在,则值为-1,下标从0开始。
输入:
7
3 4 1 5 6 2 7
输出:
-1 2
0 2
-1 -1
2 5
3 5
2 -1
5 -1
直接申请a[N], N = 1e6爆内存
找元素i左右最近的较小值,维护单调递增的栈
当栈顶小于当前元素,而次顶元素也比栈顶小,则记录答案
栈顶大于当前元素,直接入栈
最后处理栈内剩余元素
#include<bits/stdc++.h>
using namespace std;
//const int N = 100010;
int n;
int main()
{
cin >> n;
vector<int>a(n);
for(int i = 0; i < n; i ++ )
cin >> a[i];
stack<int> stk;
vector<vector<int>>ans(n, vector<int>(2));
for(int i = 0; i < n; i ++ )
{
while(!stk.empty() && a[stk.top()] > a[i])
{
int t = stk.top();
stk.pop();
int l = stk.empty() ? -1 : stk.top();//左边比a[t]小的
//右边就是i
ans[t][0] = l;
ans[t][1] = i;
}
stk.push(i);
}
//单调栈中剩余元素
while( !stk.empty() )
{
int r = -1; //单调栈中递增,右边一定没有比当前小的元素
int t = stk.top();
stk.pop();
int l = stk.empty() ? -1 : stk.top();
ans[t][0] = l;
ans[t][1] = r;
}
for(int i = 0; i < n; i ++ )
{
cout << ans[i][0] <<" "<<ans[i][1] << endl;
}
return 0;
}
2.单调栈结构(进阶)
给定一个可能含有重复值的数组 arr,找到每一个 i 位置左边和右边离 i 位置最近且值比 arr[i] 小的位置。返回所有位置相应的信息。
#include <bits/stdc++.h>
using namespace std;
// const int N = 100010;
int n;
int main()
{
cin >> n;
vector<int> a(n);
// for (int i = 0; i < n; i++)
// cin >> a[i];
// stack<int> stk;
stack<list<int>> stk;
vector<vector<int>> ans(n, vector<int>(2));
for (int i = 0; i < n; i++)
{
cin >> a[i];
while (!stk.empty() && a[stk.top().front()] > a[i])
{
// int t = stk.top();
list<int> t = stk.top();
stk.pop();
int l = stk.empty() ? -1 : stk.top().back(); // 左边比a[t]小的
// 右边就是i
for (auto x : t) // 遍历链表
{
ans[x][0] = l;
ans[x][1] = i;
}
}
if (!stk.empty() && a[stk.top().front()] == a[i])
{
stk.top().push_back(i); // 相等元素加入链表中
}
else
stk.push({i});
}
// 单调栈中剩余元素
while (!stk.empty())
{
int r = -1; // 单调栈中递增,右边一定没有比当前小的元素
list<int> t = stk.top();
stk.pop();
int l = stk.empty() ? -1 : stk.top().back();
for (auto x : t) // 遍历链表
{
ans[x][0] = l;
ans[x][1] = r;
}
}
for (int i = 0; i < n; i++)
{
printf("%d %d\n",ans[i][0],ans[i][1]);
}
return 0;
}
3.直方图中最大的矩形
直方图是由在公共基线处对齐的一系列矩形组成的多边形。矩形具有相等的宽度,但可以具有不同的高度。例如,图例左侧显示了由高度为 2,1,4,5,1,3,3的矩形组成的直方图,矩形的宽度都为 1
:
通常,直方图用于表示离散分布,例如,文本中字符的频率。现在,请你计算在公共基线处对齐的直方图中最大矩形的面积。图例右图显示了所描绘直方图的最大对齐矩形。
思路
先从考虑枚举,再优化。每个矩阵对应一个高度,宽度向两边延申至高度将要减小为止。因此,找到每个矩阵左右第一个小于当前高度的矩阵,符合单调栈模型
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100010;
int n;
int h[N], l[N], r[N], q[N];
//h存储矩阵高度,l和r存储左右边界,q单调栈
int main()
{
while (scanf("%d", &n), n)
{
for (int i = 1; i <= n; i++)//下标从1开始
scanf("%d", &h[i]);
h[0] = h[n + 1] = -1;
// 在两端添加哨兵元素,方便处理边界情况
int tt = 0;//栈顶指针
q[0] = 0;
for (int i = 1; i <= n; i++)//每个元素左边第一个比它小的元素
{
while (h[i] <= h[q[tt]])
// 如果当前矩形高度小于等于栈顶元素对应的高度,则弹出栈顶元素
tt--;
l[i] = q[tt];
q[++tt] = i;
}
tt = 0;
q[0] = n + 1;
for (int i = n; i; i--)// 从右到左遍历每个矩形
{
while (h[i] <= h[q[tt]])
tt--;
r[i] = q[tt];
q[++tt] = i;
}
ll res = 0;
for (int i = 1; i <= n; i++)
res = max(res, (ll)h[i] * (r[i] - l[i] - 1));
printf("%lld\n", res);
}
return 0;
}