标签:竖线 简单的 -- 悬线法 完全 max 最大子矩阵 下界 位置
我们常常会遇到这样的问题:给定一个01矩阵,求其中全0/1的最大子矩阵的面积。
简单的模板题如:[Luogu]P4147,复杂点的有[Luogu]P5300
下面我们介绍一种比较容易的算法:悬线法
其实悬线法更多的是一种思想,许多最大化子矩阵的问题也能用这种思想解决。
我们想象有一条竖线,这条线的上端要么是边界,要么是障碍点,然后我们让这条线向左右拓展,直到碰到边界和障碍点。
求出一个位置的悬线后,我们就能直接计算$S = H(R - L + 1)$。然后对所有的$S$取$max$即为答案,正确性是显而易见的(不会证)
现在的问题是:对于每个位置,我们怎么计算它悬线的位置。
考虑$O(n^3)$的暴力算法:我们枚举每个位置,然后对于这个位置,$O(n)$的向其他方向拓展。
$O(n^2)$的枚举位置已经是下界,无法再优化,于是我们考虑优化计算边界的过程。
注意到在暴力算法中,我们每到一个位置都要重新计算一遍它悬线的边界,其实这是没有必要的,我们完全可以根据它前一个位置的边界来递推出当前位置的边界。
具体地,定义$up[i][j], left[i][j], right[i][j]$分别表示位置$(i, j)$的上边界,左边界,右边界的位置。我们可以这样来进行计算:
1 //n*m的矩阵 2 for(int i = 1; i <= m; ++i) right[0][i] = m; 3 for(int i = 1; i <= n; ++i) 4 { 5 int lmax = 1, rmin = m; 6 for(int j = 1; j <= m; ++j) 7 { 8 if(G[i][j]) //当前位置是障碍点 9 { 10 lmax = j + 1; 11 up[i][j] = left[i][j] = 0; 12 } 13 else 14 { 15 up[i][j] = up[i - 1][j] + 1; //利用上一行的信息来计算 16 left[i][j] = max(lmax, left[i - 1][j]); //还要考虑到上一行的左边界 17 } 18 } 19 for(int j = m; j; --j)s 20 { 21 if(G[i][j]) 22 { 23 rmin = j - 1; 24 right[i][j] = m; //设置成m,避免影响下一行的计算(第2行和第11行的赋值也是同理) 25 } 26 else 27 { 28 right[i][j] = min(rmin, right[i - 1][j]); 29 ans = max(ans, (right[i][j] - left[i][j] + 1) * up[i][j]); //计算面积,取最大值 30 } 31 } 32 }
标签:竖线 简单的 -- 悬线法 完全 max 最大子矩阵 下界 位置
原文地址:https://www.cnblogs.com/Aegir/p/11105131.html