一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表
它按照先进后出的原则存储数据
先进入的数据被压入栈底,最后的数据在栈顶
栈内部的元素是具有单调性的一种数据结构,分为单调递增栈和单调递减栈
对于一个单调递增栈,若当前进栈的元素为 x x x
例如:有一个数列 5 , 4 , 6 , 2 , 4 , 3 , 6 , 5 {5,4,6,2,4,3,6,5} 5,4,6,2,4,3,6,5,以此构造一个单调递增栈
对于每个数实现过程
void push(int now) {
stack<int> s;
while (!s.empty() && s.top() >= now)s.pop();
s.push(now);
}
int s[1010], top = 0;
while (top > 0 && s[top] >= now)top--;
s[++top] = now;
有一个长度为 n 的数组 a ,对于 1 < = i < = n 1<= i <=n 1<=i<=n ,求 L ( i ) = m a x ( j : j < i , a j < a i ) L(i) = max (j : j < i , a_{j} < a_{i}) L(i)=max(j:j<i,aj<ai)
找到每一个元素左边 第一个比它小的元素的位置
农夫约翰有N ( N ≤ 80000 N \leq 80000 N≤80000)N头奶牛正在过乱头发节。每一头牛都站在同一排面朝东方,而且每一头牛的身高为 h i h_{i} hi
第 N N N头牛在最前面,而第 1 1 1头牛在最后面
对于第 i i i头牛, i i i到第一个超过他身高的牛都可以被他看道
令 C [ i ] C[i] C[i]为能被 i i i看到的牛的总数,求 ∑ i = 1 n C [ i ] \sum_{i=1}^{n}C[i] ∑i=1nC[i]
我们可以反过来看,所有超过他高度的牛都可以看到他
我们维护一个单调递减的单调栈,插入一个数后,在该数前面的数的数量都可以被他看到
#include
#include
#include
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
const int maxn = 80005;
int stack[maxn];
int main() {
int top = 0, n, now;
LL ans = 0;
scanf("%d", &n);
while (n--) {
scanf("%d", &now);
while (top > 0 && now >= stack[top])top--;
ans += LL(top);
stack[++top] = now;
}
printf("%lld\n", ans);
}
在一条水平线上有n个宽为1的矩形,求包含于这些矩形的最大子矩形面积(图中的阴影部分的面积即所求答案)
用单调栈记录该矩阵之前大于他的矩阵长度总合,即为前缀长度
出栈时记录该矩阵后面比他高的矩阵长度总和
int width = 0;
while (top > 0 && stack[top].first >= now) {
width += stack[top].second;
ans = max(ans, stack[top].first * width);
top--;
}
stack[++top] = pll(now, width + 1);
#include
#include
#include
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
typedef pair<LL, LL> pll;
const int maxn = 100005;
pll stack[maxn];
LL a[maxn];
int main() {
int top, n;
while (~scanf("%d", &n) && n) {
for (int i = 1; i <= n; i++)scanf("%lld", &a[i]);
a[n + 1] = 0;
top = 0;
LL ans = 0, now, width;
for (int i = 1; i <= n + 1; i++) {
now = a[i]; width = 0;
while (top > 0 && stack[top].first >= now) {
width += stack[top].second;
ans = max(ans, stack[top].first * width);
top--;
}
stack[++top] = pll(now, width + 1);
}
printf("%lld\n", ans);
}
}
给出一个数组
求一个子区间,使该区间和乘区间最小值最大
枚举每个点最为最小值的区间和区间和的乘积(区间和用前缀和算)
区间左区间和右区间,用单调栈找与他最近的比他小的值就是边界
#include
#include
#include
#include
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
typedef pair<LL, LL> pii;
const LL maxn = 100005;
LL Stack[maxn], a[maxn];
LL sum[maxn];
pii l[maxn];
int main() {
LL n, top, now, Case = 0;
while (~scanf("%lld", &n)) {
//memset(sum, 0, sizeof(sum));
sum[0] = 0;
for (LL i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
sum[i] = sum[i - 1] + a[i];
}
top = 0; Stack[0] = 0;
for (LL i = 1; i <= n; i++) {
now = a[i];
while (top > 0 && a[Stack[top]] >= now)top--;
l[i].first = Stack[top] + 1;
Stack[++top] = i;
}
top = 0; Stack[0] = n + 1;
for (LL i = n; i >= 1; i--) {
now = a[i];
while (top > 0 && a[Stack[top]] >= now)top--;
l[i].second = Stack[top] - 1;
Stack[++top] = i;
}
LL ans = -1, g = 0;
for (LL i = 1; i <= n; i++) {
if ((sum[l[i].second] - sum[l[i].first - 1]) * a[i] > ans) {
ans = (sum[l[i].second] - sum[l[i].first - 1]) * a[i];
g = i;
}
}
if (Case++)printf("\n");
printf("%lld\n", ans);
printf("%lld %lld\n", l[g].first, l[g].second);
}
}