zoukankan      html  css  js  c++  java
  • 悬线法学习

    学习资料 王知昆《浅谈用极大化思想解决最大子矩形问题》

    二维上的东西好像一直搞不明白呢。。。这算是挖坟填坑了吧。

    和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。这显然是可以递推的。过于显然连本蒟蒻都秒了不证啦。

    $ Hleft [ i,j ight ]=egin{cases}Hleft [ i-1,j ight ] & aleft [ i-1,j ight ]=0 \1 & aleft [ i-1,j ight ]=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。

    所以递推式即

    $Fleft[i,j ight]=egin{cases}1 & aleft[i,j-1 ight]=1\ Fleft[i,j-1 ight]& aleft[i,j-1 ight]=0 end{cases}$

    $Lleft[i,j ight]=minegin{cases}Fleft[i,j ight] \ Lleft[i-1,j ight] & aleft[i-1,j ight]=0 end{cases}$

    一条悬线对应的极大矩形面积就是$Hleft [ i,j ight ] imes left ( Lleft [ i,j ight ] +Rleft [ i,j ight ]-1 ight )$。

    取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);
        }
    }
  • 相关阅读:
    一步一步实现自己的模拟控件(4)——根控件
    一步一步实现自己的模拟控件(6)——控件树及控件区域
    ATL COM初探(1)
    一步一步实现自己的模拟控件(2)——窗口过程thunk
    一步一步实现自己的模拟控件(3)——Widget驱动
    关于硬盘的一些知识
    Win32中TreeView控件的使用方法,类似于资源管理器中文件树形显示方式
    笔记本双系统XP与Ubuntu,重装XP后如何恢复grup引导,另附操作系统启动过程
    vim常用命令
    MFC中CListCtrl控件的使用方法
  • 原文地址:https://www.cnblogs.com/orzzz/p/7702535.html
Copyright © 2011-2022 走看看