悬线法
悬线法
应用:理解并解决最大子矩形问题
SPOJ 1805
题意:
在一条水平线上有 个宽为 ,高为 的矩形,求包含于这些矩形的最大子矩形面积(图中的阴影部分的面积即所求答案)。
样例输入:
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
样例输出:
8
4000
知识点:
悬线法
思路:
悬线,就是一条竖线,这条竖线有初始位置和高度两个性质,可以左右移动至其上端点不超过当前位置矩形高度的任意位置。
对于第 条悬线,我们将这条悬线左右移动,求出其最多能向左能移动到的位置 和最多能向右移动到的位置 ,那么包含该悬线的最大子矩形的面积即为 。容易发现,最大子矩形必定是包含一条初始位置为 ,高度为 的悬线。枚举实现这个过程的时间复杂度为 ,但是我们可以用悬线法将其优化到 。
定义 为当前 位置的悬线最多能向左能移动到的位置,容易得到 初始为 ,我们需要进一步判断还能不能进一步往左扩展。
- 如果当前 ,则已经扩展到了边界,不可以;
- 如果当前 ,则从当前悬线扩展到的位置不能再往左扩展了;
- 如果当前 ,则从当前悬线还可以往左扩展,并且由此 位置的悬线能向左扩展到的位置, 位置的悬线一定也可以扩展到,于是我们将 更新为 ,并继续执行判断。
for (int i = 0; i < n; i++) {
while (l[i] > 0 && h[i] <= h[l[i] - 1]) {
l[i] = l[l[i] - 1];
}
}
此即为悬线法。
求解 过程同理。
for (int i = n - 1; i >= 0; i--) {
while (r[i] < n - 1 && h[i] <= h[r[i] + 1]) {
r[i] = r[r[i] + 1];
}
}
时间复杂度:
通过摊还分析教教QvQ,可以证明每个 最多会被其他的 遍历到一次,因此时间复杂度为 。
代码:
typedef long long ll;
const int N = 1e5 + 10;
int l[N], r[N], h[N];
int n;
ll ans;
void solve() {
while (cin >> n && n) {
for (int i = 0; i < n; i++) {
l[i] = r[i] = i;
cin >> h[i];
}
for (int i = 0; i < n; i++) {
while (l[i] > 0 && h[i] <= h[l[i] - 1]) {
l[i] = l[l[i] - 1];
}
}
for (int i = n - 1; i >= 0; i--) {
while (r[i] < n - 1 && h[i] <= h[r[i] + 1]) {
r[i] = r[r[i] + 1];
}
}
for (int i = 0; i < n; i++) {
ans = max(ans, 1ll * (r[i] - l[i] + 1) * h[i]);
}
cout << ans << endl;
}
}
P4147
题意:
给定一个 的包含 'F' 和 'R' 的矩阵,求其面积最大的子矩阵的面积,使得这个子矩阵中的每一位的值都为 'F'。
样例输入:
5 6
R F F F F F
F F F F F F
R R R F F F
F F F F F F
F F F F F F
样例输出:
45
数据规模:
知识点:
悬线法
思路:
我们会发现本题的模型和上一题的模型很像。我们可以每次只考虑某一行的所有元素,记下位置 所在元素最多能向上扩展的距离 ,然后再利用悬线法求解即可。
时间复杂度:
代码:
typedef long long ll;
const int N = 1010;
int l[N], r[N], h[N];
int n, m;
char ch;
ll ans;
void solve() {
cin >> n >> m;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
l[j] = j, r[j] = j;
cin >> ch;
if (ch == 'R') h[j] = 0;
else h[j]++;
}
for (int j = 0; j < m; j++) {
while (l[j] > 0 && h[j] <= h[l[j] - 1]) {
l[j] = l[l[j] - 1];
}
}
for (int j = m - 1; j >= 0; j--) {
while (r[j] < m - 1 && h[j] <= h[r[j] + 1]) {
r[j] = r[r[j] + 1];
}
}
for (int j = 0; j < m; j++) {
ans = max(ans, 1ll * (r[j] - l[j] + 1) * h[j] * 3);
}
}
cout << ans;
}
习题
参考:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】