Silly_3kidZ

思至水穷处,码看云起时

悬线法

悬线法

应用:理解并解决最大子矩形问题

SPOJ 1805

题意:

在一条水平线上有 n(1n105) 个宽为 1,高为 h1,h2,,hn(0hi109) 的矩形,求包含于这些矩形的最大子矩形面积(图中的阴影部分的面积即所求答案)。

样例输入:

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

样例输出:

8
4000

知识点:

悬线法


思路:

悬线,就是一条竖线,这条竖线有初始位置和高度两个性质,可以左右移动至其上端点不超过当前位置矩形高度的任意位置。

对于第 i 条悬线,我们将这条悬线左右移动,求出其最多能向左能移动到的位置 li 和最多能向右移动到的位置 ri ,那么包含该悬线的最大子矩形的面积即为 (rili+1)h[i]。容易发现,最大子矩形必定是包含一条初始位置为 i,高度为 hi 的悬线。枚举实现这个过程的时间复杂度为 O(n2),但是我们可以用悬线法将其优化到 O(n)

定义 li 为当前 i 位置的悬线最多能向左能移动到的位置,容易得到 li 初始为 i,我们需要进一步判断还能不能进一步往左扩展。

  • 如果当前 li=1,则已经扩展到了边界,不可以;
  • 如果当前 hi>hli1,则从当前悬线扩展到的位置不能再往左扩展了;
  • 如果当前 hihli1,则从当前悬线还可以往左扩展,并且由此 li1 位置的悬线能向左扩展到的位置,i 位置的悬线一定也可以扩展到,于是我们将 li 更新为 lli1,并继续执行判断。
for (int i = 0; i < n; i++) {
			while (l[i] > 0 && h[i] <= h[l[i] - 1]) {
				l[i] = l[l[i] - 1];
			}
		}

此即为悬线法

求解 ri 过程同理。

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,可以证明每个 li 最多会被其他的 lj 遍历到一次,因此时间复杂度为 O(n)


代码:

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

题意:

给定一个 n×m 的包含 '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

数据规模:

1n,m1000

知识点:

悬线法


思路:

我们会发现本题的模型和上一题的模型很像。我们可以每次只考虑某一行的所有元素,记下位置 (i,j) 所在元素最多能向上扩展的距离 hi,然后再利用悬线法求解即可。


时间复杂度:

O(n2)


代码:

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;
}

习题

参考:

posted @   Silly_3kidZ  阅读(63)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示