最大子图形问题

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]),这里用了正方形的特性,所以和矩形的求法不同。
这属于dp中的重要分支,最大子图形问题,详细的讲解可以参考下面的网址。。。真心丧病。。。 
http://www.docin.com/p-46970779.html 

code:

#include<iostream>

#include<cstdio>

using namespace std;

int li[2001]={0},ri[2001]={0},hi[2001]={0},a[2001][2001]={0};

int main()

{

int n,i,j,ans=0;

scanf("%d",&n);

for (i=1;i<=n;++i)

  for (j=1;j<=n;++j)

    scanf("%d",&a[i][j]);

for (i=1;i<=n;++i)

{

for (j=1;j<=n;++j)

{

  if (a[i][j]==0) ++hi[j];

  else hi[j]=0;

  li[j]=ri[j]=j;

    }

    for (j=2;j<=n;++j)

      if (a[i][j]==0)

        while (hi[li[j]-1]>=hi[j])

          li[j]=li[li[j]-1];

for (j=n-1;j>=1;--j)

  if (a[i][j]==0)

        while (hi[ri[j]+1]>=hi[j])

          ri[j]=ri[ri[j]+1];

    for (j=1;j<=n;++j)

      if (a[i][j]==0)

        ans=max(ans,(ri[j]-li[j]+1)*hi[j]);

}

cout<<ans<<endl;

}
RZUC Code

 

 

CODEVS1259最大正方形子矩阵

题目描述 Description

在一个01矩阵中,包含有很多的正方形子矩阵,现在要求出这个01矩阵中,最大的正方形子矩阵,使得这个正方形子矩阵中的某一条对角线上的值全是1,其余的全是0。

 

思路:做了好几个有关的最大子阵的问题,发现还是有些困难,做这个题想了好久,发现其实很简单,利用全0子矩阵的思路和正方形的思路就可以比较简单的写出dp方程。预处理一个点上方hi,左方li和右方ri0的个数(不包含这个点本身)。

  f[i][j]=min(f[i-1][j-1]+1,min(li[i][j]+1,hi[i][j]+1))

第一个比较容易错的地方就来,每次对于能更新的f[i][j]的位置要求在map中为1,否则就不能构成要求的正方形;

其次就是题目中要求对角线为1,一个正方形有两条对角线,都应该考虑到,根据f数组的更新可以同理写出;

在预处理的时候细心,注意这个点和周围点的关系就可以了。

#include<iostream>
#include<cstdio>
using namespace std;
int map[1010][1010]={0},li[1010][1010]={0},ri[1010][1010]={0},hi[1010][1010]={0},f[1010][1010]={0},g[1010][1010]={0};
int main()
{
    int n,m,i,j,ans=0;
    cin>>n>>m;
    for (i=1;i<=n;++i)
    {
        for (j=1;j<=m;++j)
        {
            scanf("%d",&map[i][j]);
            hi[i][j]=hi[i-1][j];
            li[i][j]=li[i][j-1];
            if (map[i-1][j]==0) ++hi[i][j];
            else hi[i][j]=0;
            if (map[i][j-1]==0&&j>1) ++li[i][j];
            else li[i][j]=0; 
        }
        for (j=m-1;j>=1;--j)
        {
            ri[i][j]=ri[i][j+1];
            if (map[i][j+1]==0) ++ri[i][j];
            else ri[i][j]=0;
        }
    }
    for (i=1;i<=n;++i)
    {
        for (j=1;j<=m;++j)
          if (map[i][j]==1)
          {
            f[i][j]=min(f[i-1][j-1]+1,min(li[i][j]+1,hi[i][j]+1));
            if (f[i][j]>ans) 
              ans=f[i][j];
          }
        for (j=m;j>=1;--j)
          if (map[i][j]==1)
          {
            g[i][j]=min(g[i-1][j+1]+1,min(ri[i][j]+1,hi[i][j]+1));
            if (g[i][j]>ans)
              ans=g[i][j];
          }
    }
    cout<<ans<<endl;
}
RZUC Code

从网上看到了一个很全的子图形问题的Word,分享一下:http://www.docin.com/p-351795539.html

 

 

tyvj1563最大正方形

思路:这个题目很特殊,要求最大正方形中相邻两点的颜色不同,所以可以读图的时候进行处理,隔一个变一次(使这个位置上0、1互换),这样就转化成了矩阵中的最大全0或全1子正方形了,非常简单的dp解决

  f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+1;(全0,先判断map[i][j]=0) 全1同理;

最大子图形问题的众多变式都应能转化为基本的求正方形、矩形等问题,得以比较简单的解决。看来noip后要找时间好好研究了。。。

 

bzoj1057 棋盘制作

题目大意:求一个01交错的最大正方形和矩形面积。

思路:把(i+j)%2==0的位置0/1互换一下,就变成了求最大0/1子矩阵问题,正方形可以根据长宽中取较小作为边长就可以了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxnode 2005
using namespace std;
int map[maxnode][maxnode]={0},hi[maxnode]={0},li[maxnode]={0},ri[maxnode]={0};
int fang(int x){return x*x;}
int main()
{
    int i,j,ans1=0,ans2=0,k,n,m;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i)
      for (j=1;j<=m;++j)
      {
         scanf("%d",&map[i][j]);
         if ((i+j)%2) map[i][j]^=1;
      }
    for (k=0;k<=1;++k)
    {
      memset(hi,0,sizeof(hi));
      for (i=1;i<=n;++i)
        {
        for (j=1;j<=m;++j)
        {
            if (map[i][j]==k) ++hi[j];
            else hi[j]=0;
            li[j]=ri[j]=j;
            if (j>1&&map[i][j]==k)
                while(hi[li[j]-1]>=hi[j]) li[j]=li[li[j]-1];
        }
        for (j=m-1;j>=1;--j)
            if (map[i][j]==k)
              while(hi[ri[j]+1]>=hi[j]) ri[j]=ri[ri[j]+1];
        for (j=1;j<=m;++j)
            if (map[i][j]==k)
            {
                ans1=max(ans1,fang(min(hi[j],ri[j]-li[j]+1)));
                ans2=max(ans2,hi[j]*(ri[j]-li[j]+1));
            }
      }
    }
    printf("%d\n%d\n",ans1,ans2);
}
View Code

 

posted @ 2014-11-05 15:28  Rivendell  阅读(453)  评论(0编辑  收藏  举报