【总结】单调栈
这篇博文主要讲解《李煜东算法进阶指南》上 0x11 基础数据结构栈中的最后一道例题。
题目链接。
蓝书上讲的感觉十分晦涩,像笔者一样的蒟蒻基本看不懂。
说明一下,在这道题中,我们扫描到第 \(i\) 个矩阵时,我们不是要求第 \(i\) 个矩阵对答案的贡献。
我们扫到第 \(i\) 个矩阵要干两件事:
-
将以第 \(i\) 个矩阵为结尾的矩阵的长度求出。这个实际上就是求出前面有多少个矩阵的高度大于第 \(i\) 矩阵在加上 \(1\)(第 \(i\) 个矩阵的长度)。
-
将前面没有统计答案的统计上答案。
怎莫说呢,就是细思一下,第 \(j\) 个矩阵能不能再扫到他的时候统计答案,答案是否定的。
为什莫呢,因为当你扫到第 \(j\) 个矩阵的时候,你可以联合后面连续的、大于第 \(j\) 个矩阵高度的矩阵组合成一个大矩阵,这个才是 \(j\) 要统计答案。
所以一个矩阵要统计的答案,是前面高度大于他的连续矩阵长度加上后面大于他的连续矩阵长度再加上它本身(也就是 \(1\))。
想一想,在我们统计 \(j\) 前面的大于 \(h_j\) 的矩阵个数时,这个时候此时被我们统计上的这个矩阵,后面已经有了一个高度小于他高度的矩阵 \(j\),所以被统计上的这个矩阵的答案统计可以结束了,它前面的连续大于他的矩阵已经在扫他的时候统计好了,而他的后面的大于他的矩阵数就是再扫他之前 \(j\) 的扫描数啊。
所以好好思考一下。
算法流程:
从前到后扫描 \(n\) 个矩阵,当扫描第 \(i\) 个矩阵时,统计在连续它前头的大于他的矩阵个数,将这个数设置为 \(s_p\),这样的话之后就不用统计 \(i\) 统计过的矩阵,所以将其弹出,为啥?
因为如果 在要统计的话如果 \(h_i\) 大于要统计的,那 \(i\) 统计过的都是要统计上的,否则的话,因为要连续,所以到 \(i\) 这里就停了,\(i\) 只前的就不是要统计的了。
同时统计这些矩阵对答案的贡献。
这个过程时后进先出的过程,是一个栈结构,而栈内的元素大小是单调递增的(想一想,每次将前面连续的大于他的弹出这个过程,所以但凡在他前头的一定是小于他的,而在统计下一个得时候,它没有被弹出,所以但凡在他后面的都是大于他的,以此类推,所以单调递增),所以我们叫它单调栈。
引用一下蓝书:借助单调性处理问题的思想在于及时排除不可能的选项,保持策略集合的高度有效性和秩序性可能的方法。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e5 + 5;
ll a[N];
ll n;
pair<ll, ll> st[N];
ll top;
void solve() {
for (ll i = 1; i <= n; i++) a[i] = 0;
for (ll i = 1; i <= n; i++) st[i].first = st[i].second = 0;
ll ans = 0;
for (ll i = 1; i <= n; i++) cin >> a[i];
top = 0;
a[n + 1] = 0;
for (ll i = 1; i <= n + 1; i++) {
if (top == 0 || st[top].first < a[i])
st[++top] = make_pair(a[i], 1);
else {
ll w = 0;
while (top > 0 && st[top].first >= a[i]) {
w += st[top].second;
ans = max(ans, w * st[top].first);
--top;
}
st[++top] = make_pair(a[i], 1 + w);
}
}
cout << ans << "\n";
cin >> n;
}
int main() {
cin >> n;
while (n != 0) solve();
return 0;
}

浙公网安备 33010602011771号