悬线法学习
学习资料 王知昆《浅谈用极大化思想解决最大子矩形问题》
二维上的东西好像一直搞不明白呢。。。这算是挖坟填坑了吧。
和Amphetamine说我不会悬线法他都不信qwq。。。可我真的不会啊好菜,,,
以求最大全零子矩阵为例。
悬线说白了就是点(i,j)向上最多能有多少连续的0。我们用H[i,j]记录。
显然每个点都有自己的H,也就是wzk神犇说的
•每个悬线都与它底部的点一一对应。
•矩形中的每一个点(矩形顶部的点除外)都对应了一个悬线。
神犇还说了
由神犇说的一我们知道枚举所有悬线并知道每一条悬线的向左向右扩展的极大距离L,R,一定可以找到最大的那个全零子矩阵。
由神犇说的二,悬线即点,点即悬线,一一对应。
于是我们O(NM)枚举悬线就可以变成O(NM)枚举所有点,L,R也可以变成每个点的值。那么现在瓶颈在于我们能否O(1)计算出每条悬线的H,L,R值。
首先H值。再次说明,H值即悬线长,即点(i,j)向上最多能有多少连续的0。这显然是可以递推的。过于显然连本蒟蒻都秒了不证啦。
$ H\left [ i,j\right ]=\begin{cases}H\left [ i-1,j \right ] & a\left [ i-1,j \right ]=0 \\1 & a\left [ i-1,j \right ]=1 \end{cases} $
而实际上L,R的值也是可以O(1)转移的。考虑一根悬线与它上一根悬线的关系。比如下面这个01矩阵。
$110001$
$111001$
$010000$
L[3,5]=2。因为是由L[2,5]=2转移的。L[2,5]=2,则是因为坐标为(2,5)的0最左只能移动2。所以说可以视L值为整条悬线上的瓶颈,R也是同理的。瓶颈决定了当前悬线的L,R,所以说递推一个min就行了。而每一个点在所在行的最左扩展,可以当做旋转90度的矩阵求悬线不是吗?
所以我们可以先求出对于每个点在所在行1*m的矩阵里的L记为F。然后和上方的悬线的L取min。
所以递推式即
$F\left[i,j \right]=\begin{cases}1 & a\left[i,j-1 \right]=1\\ F\left[i,j-1 \right]& a\left[i,j-1 \right]=0 \end{cases}$
$L\left[i,j \right]=min\begin{cases}F\left[i,j \right] \\ L\left[i-1,j \right] & a\left[i-1,j \right]=0 \end{cases}$
一条悬线对应的极大矩形面积就是$H\left [ i,j \right ]\times \left ( L\left [ i,j \right ] +R\left [ i,j \right ]-1\right )$。
取max即为答案了。
这么做好像常数巨大(大概为5),但好想好写啊、、、
#define inc(i,n) for(int i=1;i<=n;i++) #define dec(i,n) for(int i=n;i;i--) const int N=2010; int a[N][N],h[N][N],l[N][N],r[N][N]; int ans,n,m; void solve(){ inc(i,n){ inc(j,m) h[i][j]=a[i-1][j]?1:h[i-1][j]+1, l[i][j]=a[i][j-1]?1:l[i][j-1]+1; dec(j,m) r[i][j]=a[i][j+1]?1:r[i][j+1]+1; } inc(i,n)inc(j,m){ if(a[i][j]){ h[i][j]=l[i][j]=r[i][j]=0;continue; } if(i>1&&!a[i-1][j]) l[i][j]=min(l[i][j],l[i-1][j]), r[i][j]=min(r[i][j],r[i-1][j]); int x=l[i][j]+r[i][j]-1,y=h[i][j]; ans=max(ans,x*y); } }