要求:n*m的01矩阵,0表示白色,1表示黑色,求出满足矩阵内部每个位置均与周围位置颜色不同的最大正方形子矩阵和最大矩形子矩阵。
方法:悬线法求最大子矩阵。
这道题真的是学了好久,有一行代码l[i][j]=min(l[i][j],l[i-1][j]);和另一行代码r[i][j]=min(r[i][j],r[i-1][j]);应该有两三个字母不一样,活生生试测例没试出来,最后还是看出来了,果然试测例不是万能的,有时候还是看看代码。
1.悬线:上端的上一个点为障碍点或边界,上端到下端均合法,且下端为(i,j),不考虑下端的下一个点是否合法。
2.这道题没有绝对的障碍点,自身永远合法,周围的点可能合法,也可能不合法。
3.悬线法的原理是找到所有极大子矩阵,面积最大的即为最大子矩阵。
4.分析任意一条下端为(i,j)的悬线,目的是根据矩阵的长和宽得出矩阵的面积,随着i逐渐增大,悬线的长度逐渐增大,因此矩阵的宽会取到合法的极大值。悬线左右移动到无法移动的位置(障碍点或边界)取到长的极大值。
5.求出极大子矩阵的面积,下端为(i,j)的悬线高度为h[i][j],向左走最长为l[i][j],向右走最长为r[i][j],不用向下走到不合法的点是因为包括这条悬线的最长悬线向上走包括这条悬线,省去了一个数组。
6.先求出每个点的h,l,r数组,再整合到一条悬线上。
7.每个点的h,l,r数组可用递推求出,
如果(i,j)在第一行,那么高度一定为1,即h[i][j]=1。
如果(i,j)不在第一行,上一个与(i,j)颜色不同,那么悬线延长1,即h[i][j]=h[i-1][j]+1。
如果(i,j)不在第一行,上一个与(i,j)颜色相同 那么高度为1,即h[i][j]=1。
如果(i,j)在第一列,那么向左走最多为1,即l[i][j]=1。
如果(i,j)不在第一列,左一个与(i,j)颜色不同,那么向左走加1,即l[i][j]=l[i-1][j]+1。
如果(i,j)不在第一列,左一个与(i,j)颜色相同 那么向左走最多为1,即l[i][j]=1。
如果(i,j)在最后一列,那么向右走最多为1,即r[i][j]=1。
如果(i,j)不在最后一列,右一个与(i,j)颜色不同 那么向右走加1,即r[i][j]=r[i-1][j]+1。
如果(i,j)不在最后一列,右一个与(i,j)颜色不同 那么向右走为1,即r[i][j]=1。
8.一条悬线上的h,l,r数组也需递推求出。短板效应:一条悬线的向左向右移动距离是由悬线上的点的最短移动距离决定的。
假设一条悬线长度为5,悬线上的点的h数组已知,则需进行4次min操作求出以当前(i,j)为下端的悬线的l和r数组。
即进行如下操作:l[i][j]=min(l[i][j],l[i-1][j]);和r[i][j]=min(r[i][j],r[i-1][j]);
9.极大矩形子矩阵面积是一条悬线的高度h[i][j]和向左向右移动距离r[i][j]+l[i][j]-1的乘积。
10.正方形一定包含在某个极大矩形子矩阵里面,
故极大正方形子矩阵面积是一条悬线的高度h[i][j]和向左向右移动距离r[i][j]+l[i][j]-1的最小值的平方。
#include<stdio.h>
#include<string.h>
#define maxn 2100
#include<algorithm>
using namespace std;
int map1[maxn][maxn],h[maxn][maxn],l[maxn][maxn],r[maxn][maxn];
int main()
{
int i,j,k,n,m,temp,max1,max2;
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
for(j=0;j<m;j++)
scanf("%d",&map1[i][j]);
memset(h,-1,sizeof(h));
memset(l,-1,sizeof(l));
memset(r,-1,sizeof(r));
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
if(i==0) h[i][j]=1;
//如果i,j在第一行 那么高度一定为1
if(i-1>=0&&map1[i-1][j]!=map1[i][j]) h[i][j]=h[i-1][j]+1;
//如果i,j不在第一行 上一个与i,j颜色不同 那么悬线延长1
if(i-1>=0&&map1[i-1][j]==map1[i][j]) h[i][j]=1;
//如果i,j不在第一行 上一个与i,j颜色相同 那么高度为1
if(j==0) l[i][j]=1;
//如果i,j在第一列 那么向左走最多为1
if(j-1>=0&&map1[i][j-1]!=map1[i][j]) l[i][j]=l[i][j-1]+1;
//如果i,j不在第一列 左一个与i,j颜色不同 那么向左走加1
if(j-1>=0&&map1[i][j-1]==map1[i][j]) l[i][j]=1;
//如果i,j不在第一列 左一个与i,j颜色相同 那么向左走最多为1
}
for(j=m-1;j>=0;j--)
{
if(j==m-1) r[i][j]=1;
//如果i,j在最后一列 那么向右走最多为1
if(j+1<m&&map1[i][j+1]!=map1[i][j]) r[i][j]=r[i][j+1]+1;
//如果i,j不在最后一列 右一个与i,j颜色不同 那么向右走加1
if(j+1<m&&map1[i][j+1]==map1[i][j]) r[i][j]=1;
//如果i,j不在最后一列 右一个与i,j颜色不同 那么向右走为1
}
}
max1=-1,max2=-1;
int len;
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
if(h[i][j]==1) //高度为1就无法递推了
{
max2=max(max2,(r[i][j]+l[i][j]-1)*h[i][j]);
len=min(r[i][j]+l[i][j]-1,h[i][j]);
max1=max(max1,len*len);
}
else
{
l[i][j]=min(l[i][j],l[i-1][j]);
r[i][j]=min(r[i][j],r[i-1][j]);
max2=max(max2,(r[i][j]+l[i][j]-1)*h[i][j]);
len=min(r[i][j]+l[i][j]-1,h[i][j]);
max1=max(max1,len*len);
}
}
}
printf("%d\n%d\n",max1,max2);
}