扫描线法求最大子矩阵

问题描述:

给定一个N*M的矩阵,其中有一些格子是空地,其他是障碍。

找出一个全部由空地组成的面积/周长最大的子矩阵。


朴素算法:

枚举左上角的坐标O(mn)和右下角的坐标O(mn),判断是否全为空地O(mn),时间复杂度为O(m3*n3)。


扫描线法:

把点(i,j)向上所有连续的空格看做一条悬线。矩阵中的每个点都向上对应了一条悬线。


①用h[i,j]表示点(i,j)对应的悬线长度。

当(i,j)为障碍时,h[i,j]=0;当(i,j)为空格时,h[i,j]=h[i-1,j]+1。


②用left[i,j]表示点(i,j)对应的悬线的左边界。

当(i,j)为障碍时,left[i,j]=左边界;当(i,j)为空格时,left[i,j]=max( left[i-1,j], lo+1 ),lo为格子(i,j)左边最近障碍的列编号。


③用right[i,t]表示点(i,j)对应的悬线的右边界。

当(i,j)为障碍时,right[i,j]=右边界; 当(i,j)为空格时,right[i,j]=min( right[i+1,j], ro-1 ),ro为格子(i,j)右边最近障碍的列编号。


从上到下扫描,对每一行,从左到右计算left[i,j]维护lo;从右到左计算right[i,j]维护ro并更新答案。

在实际实现中,降维节约空间,用h[j],left[j],right[j] 表示当前扫描行的信息。


模板:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn=1111;

int mat[maxn][maxn];
int n,m;

//返回矩形的最大面积,障碍物代号为c
int cat(int c)
{
    int h[maxn],l[maxn],r[maxn];
    int lo,ro;
    int ans=0;
    for (int j=1;j<=m;j++)
    {
        h[j]=0;
        l[j]=1;
        r[j]=m;
    }
    for (int i=1;i<=n;i++)
    {
        lo=0;ro=m+1;
        for (int j=1;j<=m;j++)
        {
            if (mat[i][j]==c){ h[j]=0;l[j]=1;lo=j; }
            else
            {
                h[j]++;
                l[j]=max(l[j],lo+1);
            }
        }
        for (int j=m;j>=1;j--)
        {
            if (mat[i][j]==c){ r[j]=m;ro=j; }
            else
            {
                r[j]=min(r[j],ro-1);
                ans=max(ans,h[j]*(r[j]-l[j]+1));
            }
        }
    }
    return ans;
}


题目:

UVa 1330 - City Game 最大子矩阵

hdu 4328 Cut the cake 最大子矩阵



posted on 2013-06-15 09:58  电子幼体  阅读(243)  评论(0编辑  收藏  举报

导航