题目描述 Description
在一个01方阵中找出其中最大的全0子矩阵,所谓最大是指0的个数最多。
输入描述 Input Description
输入文件第一行为整数N,其中1<=N<=2000,为方阵的大小,紧接着N行每行均有N个0或1,相邻两数间严格用一个空格隔开。
输出描述 Output Description
输出文件仅一行包含一个整数表示要求的最大的全零子矩阵中零的个数。
样例输入 Sample Input
5
0 1 0 1 0
0 0 0 0 0
0 0 0 0 1
1 0 0 0 0
0 1 0 0 0
样例输出 Sample Output
9
分析:
dp
虽然是转载了浅谈用极大化思想解决最大子矩阵问题
然而也不是很明白
代码量极小!!!
那么我们就一点一点的解释一下:
认真的研读了论文之后,
我发现两种算法各有长处
而这道题n*m是2000*2000,所以我们可以选择算法二
再简单理一下算法二的思路,枚举每一列
计算这个坐标上的悬线长度,
之后从前往后,从后往前两次扫描,得到从该悬线能够扩展到的最远左右端点,
这样答案统计的时候就记录一下(r-l+1)*h
回归代码,首先是变量的意义:
r,h,l都为滚动数组,l表示向左最远可以扩展的距离,r表示向右最远可以扩展的距离,h表示向上最远可以扩展的距离
la表示当前悬线向左扩展遇到的第一个障碍点的位置,ra表示当前悬线向左扩展的第一个障碍点的位置
在完成读入之后,我们就开始了数组的初始化
显然一开始我们认为所有点都能够扩展到左右边界,悬线长度为0
之后枚举行数
h[j]表示第j列在当前状况下的悬线长度
l[j]表示第j列上的这条悬线尽力向左扩展的坐标
r[j]表示第j列上的这条悬线尽力向右扩展的坐标
维护l
- 如果这个点是障碍点,那么h(能够向上扩展的距离)显然是0
因为j是从小到大枚举的,所以在转移j+1的时候
la至少是当前点,所以在遇到障碍点的时候,la=j
l[j]=1是为了方便之后的计算
(再说了,h已经等于0了,l等于什么就不重要了,我们为何不为以后的状态着想一下呢) - 如果不是障碍点,悬线的长度就可以增加了,
同时维护正确的l
维护r
如果这个点是障碍点,在维护l的时候我们已经把h变成0了,
ra=j
r[j]=m 这也是为了以后的转移着想如果这个点不是障碍点,维护r
答案统计
这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,m,r[2003],l[2003],h[2003],ans=0,la,ra;
int mp[2010][2010];
int main()
{
scanf("%d",&n);
m=n;
int i,j;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
scanf("%d",&mp[i][j]);
for (i=1;i<=m;i++)
h[i]=0,l[i]=1,r[i]=m;
for (i=1;i<=n;i++)
{
la=0;ra=m+1;
for (j=1;j<=m;j++)
if (mp[i][j])
h[j]=0,la=j,l[j]=1;
else
h[j]++,l[j]=max(l[j],la+1);
for (j=m;j>0;j--)
if (mp[i][j])
r[j]=m,ra=j;
else
r[j]=min(ra-1,r[j]),
ans=max(ans,(r[j]-l[j]+1)*h[j]);
}
printf("%d",ans);
return 0;
}