最大全0/1子矩阵

我们常常会遇到这样的问题:给定一个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 }

原文地址:https://www.cnblogs.com/Aegir/p/11105131.html

时间: 2024-08-28 20:06:57

最大全0/1子矩阵的相关文章

[BZOJ 1057][ZJOI 2007]棋盘制作(最大全0/1子矩阵)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1057 这题好像很早之前就看到过...那时候我还只会玩脚丫子,做这题完全像SB一样,记得那时我做了一会就放弃了. 如今看到这题感觉好做多了,此题预处理很巧妙,我们看一个棋盘,它的所有黑点的行标奇偶性都相同,列标的奇偶性也都相同.白点一样. 于是我们就可以预处理下,对于所有行标和列标奇偶性相同的点,保持它们的颜色不变,奇偶性不相同的点,反转它们的颜色,于是预处理后,我们要找的矩形在整

最大全0/1子矩阵的探究

by MedalPluS [问题模型] 给定一个n*n的矩阵,求矩阵中面积最大的一个值全是0或1的子矩阵 [分析] (这里n*n完全可以改为n*m,但由于种种原因,等下代码里是n*n) 首先很容易想到一种解法,枚举这个子矩阵的左上方,和右下方,然后暴力统计,这样时间复杂度O(N6),这种做法很广泛 这肯定是不能满足我们的需求,那么应该怎么办呢?我们发现O(n2)的时间浪费在统计上,我们可以使用前缀和的手段,预处理 这样时间复杂度O(n4),还是很垃圾 在暴力种种优化都不行的时候,想一想贪心或者数

1159 最大全0子矩阵

/*f(i,j)表示以(i,j)为右下角的最大全0子矩阵的边长若a[i][j]==1,f(i,j)=0否则:f(i,j)=min{ f(i-1,j),f(i,j-1),f(i-1,j-1) }+1 这样求得的是最大全0正方形子矩阵要求长方形矩阵,上述思路行不通假设以(i,j)为右下角的最大矩阵=12它可能是3*4.4*3.2*6.6*2.1*12.12*1按上述思路进行状态转移的话,取得最优值的方案不唯一时,所有的方案需要都记下,用于后续的状态转移. 在长方形全0子矩阵中,考察某个位置(i,j)

Codevs 1159 最大全0子矩阵 悬线法!!!!

1159 最大全0子矩阵 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 在一个0,1方阵中找出其中最大的全0子矩阵,所谓最大是指O的个数最多. 输入描述 Input Description 输入文件第一行为整数N,其中1<=N<=2000,为方阵的大小,紧接着N行每行均有N个0或1,相邻两数间严格用一个空格隔开. 输出描述 Output Description 输出文件仅一行包含一个整数表示要求的最大的全零子矩阵中零的个数.

CODEVS1159 最大全0子矩阵

题目描述 Description 在一个0,1方阵中找出其中最大的全0子矩阵,所谓最大是指O的个数最多. 思路:这个题最朴素的n^6的算法,超时美美的...然后想优化,从一个点向上方.左方.右方扩展,首先更新这个点向上能有多少个0h0,然后找左右h比h0大的作为左右边界,然后计算这个矩形的面积,最后输出最大值...这种构造的美丽算法,真心... 比较: 最大全0子正方形:f[i][j](以i,j为右下角的最大正方形的边长)=min(f[i-1][j],f[i][j-1],f[i-1][j-1])

最大子段和||最大子矩阵和||最大全0子矩阵||最大全0子正方形

最大子段和(略) 定义一个最大值dp[i]表示以i结尾的最大子段和: 初始化: dp[0]=A[0]: dp[i]=max(dp[i-1]+A[i],A[i]) 即dp[i-1]+A[i]<0时 dp[i] = A[i]; 否则 dp[i]=dp[i-1]+A[i] 最大全0子矩阵 https://blog.csdn.net/Flere825/article/details/54605662 1159 最大全0子矩阵 /* #define N 2010 int a[N][N],ans,n; in

[codevs1159]最大全0子矩阵(悬线法)

解题关键:悬线法模板题.注意此模板用到了滚动数组. #include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<iostream> #include<cmath> #define maxn 2002 using namespace std; typedef long long ll; int map[maxn][maxn],l[ma

【ZJOI2007】棋盘制作 BZOJ1057

Description 国 际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个8*8大小的黑白相间的方 阵,对应八八六十四卦,黑白对应阴阳.而我们的主人公小Q,正是国际象棋的狂热爱好者.作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好 朋友小W决定将棋盘扩大以适应他们的新规则.小Q找到了一张由N*M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种颜色之一.小Q想在这种纸中裁 减一部分作为新棋盘,当然,他希望这个

最大子序列和最大子矩阵

最大子序列: 问题描述:给定整数序列:a1,a2,a3,...an(可能有负数),求a1~an的一个子序列ai~aj,使其和最大 我们很容易想到一个O(n^2)复杂度的方法,即 i : 1--->n,并令 s = 0,然后 j : i---->n, s<---s + a[j],更新maxsum,如若想得到具体的子序列,我们可设i1,j1,更新maxsum时,同时更新下标,不过为了减少运算,我们在更新时做个判断if i1 ≠ i,i1<--i int sub_sum(int *a,i