悬线法
悬线法的适用范围是单调栈的子集。具体来说,悬线法可以应用于满足以下条件的题目:
- 需要在扫描序列时维护单调的信息;
- 可以使用单调栈解决;
- 不需要在单调栈上二分。
看起来悬线法可以被替代,用处不大,但是悬线法概念比单调栈简单,更适合初学 OI 的选手理解并解决最大子矩阵等问题。
SP1805 HISTOGRA - Largest Rectangle in a Histogram
大意:在一条水平线上有
悬线,就是一条竖线,这条竖线有初始位置和高度两个性质,可以在其上端点不超过当前位置的矩形高度的情况下左右移动。
对于一条悬线,我们在这条上端点不超过当前位置的矩形高度且不移出边界的前提下,将这条悬线左右移动,求出其最多能向左和向右扩展到何处,此时这条悬线扫过的面积就是包含这条悬线的尽可能大的矩形。容易发现,最大子矩形必定是包含一条初始位置为
我们考虑如何快速找到悬线可以到达的最左边的位置。
定义
- 如果当前
l_i = 1 - 如果当前
a_i > a_{l_i - 1} - 如果当前
a_i \le a_{l_i - 1} l_i - 1 i l_i l_{l_i - 1}
通过摊还分析,可以证明每个
参考代码
#include <algorithm>
#include <cstdio>
using std::max;
const int N = 100010;
int n, a[N];
int l[N], r[N];
long long ans;
int main() {
while (scanf("%d", &n) != EOF && n) {
ans = 0;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), l[i] = r[i] = i;
for (int i = 1; i <= n; i++)
while (l[i] > 1 && a[i] <= a[l[i] - 1]) l[i] = l[l[i] - 1];
for (int i = n; i >= 1; i--)
while (r[i] < n && a[i] <= a[r[i] + 1]) r[i] = r[r[i] + 1];
for (int i = 1; i <= n; i++)
ans = max(ans, (long long)(r[i] - l[i] + 1) * a[i]);
printf("%lld\n", ans);
}
return 0;
}
UVA1619 感觉不错 Feel Good
对于一个长度为
本题中我们可以考虑枚举最小值,将每个位置的数
参考代码
#include <cstdio>
#include <cstring>
const int N = 100010;
int n, a[N], l[N], r[N];
long long sum[N];
long long ans;
int ansl, ansr;
bool fir = 1;
int main() {
while (scanf("%d", &n) != EOF) {
memset(a, -1, sizeof(a));
if (!fir)
printf("\n");
else
fir = 0;
ans = 0;
ansl = ansr = 1;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
sum[i] = sum[i - 1] + a[i];
l[i] = r[i] = i;
}
for (int i = 1; i <= n; i++)
while (a[l[i] - 1] >= a[i]) l[i] = l[l[i] - 1];
for (int i = n; i >= 1; i--)
while (a[r[i] + 1] >= a[i]) r[i] = r[r[i] + 1];
for (int i = 1; i <= n; i++) {
long long x = a[i] * (sum[r[i]] - sum[l[i] - 1]);
if (ans < x || (ans == x && ansr - ansl > r[i] - l[i]))
ans = x, ansl = l[i], ansr = r[i];
}
printf("%lld\n%d %d\n", ans, ansl, ansr);
}
return 0;
}
最大子矩形¶
P4147 玉蟾宫
给定一个 'F'
和 'R'
的矩阵,求其面积最大的子矩阵的面积 'F'
。
我们会发现本题的模型和第一题的模型很像。仔细分析,发现如果我们每次只考虑某一行的所有元素,将位置
参考代码
#include <algorithm>
#include <cstdio>
int m, n, a[1010], l[1010], r[1010], ans;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
char s[3];
for (int j = 1; j <= m; j++) {
scanf("%s", s);
if (s[0] == 'F')
a[j]++;
else if (s[0] == 'R')
a[j] = 0;
}
for (int j = 1; j <= m; j++)
while (a[l[j] - 1] >= a[j]) l[j] = l[l[j] - 1];
for (int j = m; j >= 1; j--)
while (a[r[j] + 1] >= a[j]) r[j] = r[r[j] + 1];
for (int j = 1; j <= m; j++) ans = std::max(ans, (r[j] + l[j] - 1) * a[j]);
}
printf("%d", ans * 3);
return 0;
}
习题¶
buildLast update and/or translate time of this article,Check the history
editFound smelly bugs? Translation outdated? Wanna contribute with us? Edit this Page on Github
peopleContributor of this article mwsht, sshwy, ouuan, Ir1d, Henry-ZHR, hsfzLZH1
translateTranslator of this article Visit the original article!
copyrightThe article is available under CC BY-SA 4.0 & SATA ; additional terms may apply.