• 浅谈最大化子矩阵问题


    文章目录
    1. 矩阵
    2. 最大化子矩阵问题
    3. 解法
    -- 3.1. 初级解法

    1 矩阵

    严格地说,这里的矩阵并不是严格要求的;确切地说只是二位数组或者矩形排列的一些物体(items),但是为了方便表达,就使用矩阵这一名词了.

    我们定义一个矩阵$ ext{Matrix}[r,c]$是一个r行c列的二维阵列(不一定是数组),其中将$ ext{Matrix}[r_1]$称为一行,或$ ext{Matrix}[r_1,1..c](1le r_1le r)$,将$ ext{Matrix}[1..r,c_1](1le c_1le c)$称为一列.
    我们定义一个矩阵$ ext{Matrix}[r,c]$中的连续一块$ ext{Sub_Matrix}[r_1..r_2,c_1..c_2](1le r_1le r_2le r,1le c_1le c_2le c)$为$ ext{Matrix}[r,c]$的一个子矩阵$ ext{Sub}left( ext{Matrix},r_1,r_2,c_1,c_2 ight)$
          / 1 0 1 1 0 1
          | 0 1 1 0 0 0 |
    M_1 = | 0 1 0 1 0 0 |
          | 1 1 0 0 1 0 |
          1 1 0 0 1 1 /
    M_2 = / 0 1 0 = Sub(M_1,3,4,3,5)
          0 0 1 /
    M_2 是 M_1 的子矩阵
    (其中灰色背景+下划线部分).

     2 最大子矩阵问题

    设$M_1$=
    1 0 1 1 0 1
    0 0 0 1 0 0
    0 0 1 1 0 0
    1 0 0 0 0 0
    0 1 0 0 0 0
    1 1 1 0 0 0

    其中,我们将1称为障碍点,我们要找到$M_1$最大(面积最大)的一个子矩阵$M_2$,使$M_2$中不含任何障碍点.这个问题,叫做最大子矩阵问题.

    这个例子中,我们简单地看到,用蓝色标出的这一部分就是$M_1$的最大子矩阵.

    3 解法

    3.1 解法1 最直接的解法

    我们可以暴力枚举.

    先枚举左上角的点,再枚举右下角的点,最后在生成的子矩阵中按行枚举每一个点.这种算法的复杂度大约是$ ext{O}left( r^3 cdot c^3 ight)$.

    3.2 解法2 稍微好一些的解法

    首先预处理,用前缀和表示每行的1数,相减若是0那么在一个范围内没有1.$ ext{O}left( r^3 cdot c^2)$.(可以将$c,r$互换随你变= =)

    Matrix MaxSubMatrix(Matrix M){
    	int i,j,k,l,max=0
    	Matrix prefixSum,MaxMatrix,tmpMatrix
    	foreach(M,byrow,(int i,int x,int y)=>void{
    		prefixSum[x][y]=prefix[x][y-1]+i
    	})
    	foreach(M,byrow,(int i,int x,int y)=>void{
    		foreach(rangeMatrix(M,x,y,>r,>c),byrow,(int i,int xx,int yy)=>void{
    			if(foreach(rangeMatrix(M,x,y,xx,yy),rows,(int row)=>void{
    				return OP::MINUS(prefix[row][yy,y-1])
    			}),false,OP::BOOLOR){
    				tmpMatrix=subMatrix(M,x,y,xx,yy)
    				if(area(tmpMatrix)>max){
    					max=area(tmpMatrix)
    					MaxMatrix=tmpMatrix
    				}
    			}
    		})//伪代码风格凶残
    	})
    	return MaxMatrix
    }
    

    3.3 解法3 更好一些的算法

    在找到上面那个算法的时候,我们可以想到一个更好一些的算法,比如我们有这么一个矩阵

    遍历到如图的红点

    第一次扫描一行没遇到障碍点

    记录这个矩形,扫描下一行.注意由于这个点定了,下一行从这行的最后一个扫描到的同样位置那个数,找到第一个前缀和差为0的格子

    那么下一行应该从蓝色各种数起

    下一行这里由于有一个1

    应该再向前一格数

    一直扫到底

    那么这个就是这个点上的最大子矩阵

    时间复杂度$ ext{O}left( rcdot ccdot left(r+c ight) ight)$.为什么呢?

    对于某个点,这个扫描列最多走一遍行一遍列,也就是$r+c$

    三次方级别.

    换个思想,我们还可以发现另一种解法.

    选中两列

    一行一行扫描,用前缀和判断是不是全0

    仔细想一想,很容易在$ ext{O}left( r ight)$时间内找出它这里最长的一些没有障碍的连续的行.DP即可

    时间复杂度$ ext{O}left( c^2cdot r ight)$

    3.4 最佳的办法

    其实这道题是可以在$ ext{O}left( ccdot r ight)$时间内解决的.

    选定一行,让我们想一想如何求出以这行为底边的最大无障碍矩形.

    标绿色的部分是总可以选择的方块.我们要用它们和低下非1的方块构造最大的矩形

    每个柱状图的高度统计在下面(纯手数)

    显而易见,朴素的办法是$ ext{O}left( c^3 ight)$的.加上RMQ可以达到$ ext{O}left( c^2 ight)$

    但是我们要做得更好.我们要把它优化到$ ext{O}left( c ight)$

    我们可以逐个扫描.设扫到第$i$个高度$h[i]$,设一个栈$stack[]$

    foreach i in row:
    if h[i] >= stack.top.second:
    push (i,h[i]) stack[]
    else:
    while stack.top.second>=h[i]:
    t = pop stack
    area = ( i - ( stack.empty ? t.first : stack.top.first + 1 ) ) * t.second
    _max=max(_max,area)
    push (i,h[i]) stack[]
    return _max

    最后发一下我的代码,USACO 6.1.2

    #include <cstdio>
    int R,C,P,i,j,k,l,ma;
    bool board[4000][4000];
    int height[4000];
    struct stackItem{
    	int height,y;
    } stack[4000];
    int slen,tmp;
    int maxArea(){
    	stack[slen].height=height[0];
    	stack[slen].y=0;
    	k=0;
    	for(j=1;j<=4000;++j){
    		if(height[j]>=stack[slen].height){
    			++slen;
    			stack[slen].height=height[j];
    			stack[slen].y=j;
    		}else{
    			while(stack[slen].height>=height[j]&&slen>=0){
    				tmp=(j-(slen?stack[slen-1].y-1:stack[slen].y))*stack[slen].height;
    				--slen;
    				k=k>tmp?k:tmp;
    			}
    		}
    	}
    	return k;
    }
    int main(){
    	scanf("%d%d%d",&R,&C,&P);
    	for(i=0;i<P;++i){
    		scanf("%d%d",&j,&k);
    		board[j-1][k-1]=true;
    	}
    	for(i=0;i<R;++i){
    		for(j=0;j<C;++j){
    			height[j]=board[i][j]?0:height[j]+1;
    		}
    		slen=0;
    		if(maxArea()>ma) ma=k;
    	}
    	printf("%d",ma);
    	return 0;
    }
    
  • 相关阅读:
    .net core 大文件分片上传
    Python 运算符
    CF1398G Running Competition FFT
    Luogu「StOI-2」简单的树 树链剖分+线段树+倍增
    LOJ#3145. 「APIO2019」桥梁 分块+可撤销并查集
    【UNR #4】序列妙妙值 分块+DP
    LuoguP5008 [yLOI2018] 锦鲤抄 tarjan+贪心
    windows提权
    基于 Laravel 框架的内容管理系统
    趣谈、浅析CRLF和LF
  • 原文地址:https://www.cnblogs.com/tmzbot/p/4295906.html
Copyright © 2020-2023  润新知