题目描述
回到家中的猫猫把三桶鱼全部转移到了她那长方形大池子中,然后开始思考:到底要以何种方法吃鱼呢(猫猫就是这么可爱,吃鱼也要想好吃法 ^_*)。她发现,把大池子视为01矩阵(0表示对应位置无鱼,1表示对应位置有鱼)有助于决定吃鱼策略。
在代表池子的01矩阵中,有很多的正方形子矩阵,如果某个正方形子矩阵的某条对角线上都有鱼,且此正方形子矩阵的其他地方无鱼,猫猫就可以从这个正方形子矩阵“对角线的一端”下口,只一吸,就能把对角线上的那一队鲜鱼吸入口中。
猫猫是个贪婪的家伙,所以她想一口吃掉尽量多的鱼。请你帮猫猫计算一下,她一口下去,最多可以吃掉多少条鱼?
输入输出格式
输入格式:有多组输入数据,每组数据:
第一行有两个整数n和m(n,m≥1),描述池塘规模。接下来的n行,每行有m个数字(非“0”即“1”)。每两个数字之间用空格隔开。
对于30%的数据,有n,m≤100
对于60%的数据,有n,m≤1000
对于100%的数据,有n,m≤2500
输出格式:只有一个整数——猫猫一口下去可以吃掉的鱼的数量,占一行,行末有回车。
输入输出样例
输入样例#1:
4 6 0 1 0 1 0 0 0 0 1 0 1 0 1 1 0 0 0 1 0 1 1 0 1 0
输出样例#1:
3
说明
右上角的
1 0 0 0 1 0 0 0 1
/* 先考虑左上到右下的对角线:f[i][j]表示以(i,j)这一点作为右下角的合法正 方形的最大长度。如果当前位置的数字为1,那么考虑转移: 如果a[i-1][j-1]是0,那么f[i][j]就是1; 如果a[i-1][j-1]是1,那么f[i][j]=min{f[i-1][j-1]+1,(i,j)左边的最长连续0 的长度,(i,j)上面最长连续0的长度}。 那么问题来了,怎么快速求最长连续0的长度呢,这个预处理好就可以了。 为了方便,我们预处理的时候,x[i][j]表示,(i,j)左边最长连续0的长度(包含 (i,j)),y[i][j]同理表示上面。如果a[i][j]==1,直接把x[i][j]和y[i][j]设 为0,否则就x[i][j]=x[i][j-1]+1,y[i][j]=y[i-1][j]+1。这样的话DP转移方程 应该是f[i][j]=min(f[i-1][j-1]+1, x[i][j-1]+1, y[i-1][j]+1)。 如果是右上到左下的对角线,就只要把每一行的数左右反转一下,再做同样的DP 即可。 */ #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define maxn 2510 int n,m,a[maxn][maxn],x[maxn][maxn],y[maxn][maxn],f[maxn][maxn],ans; void dp(){ memset(x,0,sizeof(x)); memset(y,0,sizeof(y)); memset(f,0,sizeof(f)); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(a[i][j])x[i][j]=0; else x[i][j]=x[i][j-1]+1; } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(a[i][j])y[i][j]=0; else y[i][j]=y[i-1][j]+1; } } for(int i=1;i<=n;i++){ f[i][1]=a[i][1]; f[1][i]=a[1][i]; } for(int i=2;i<=n;i++){ for(int j=2;j<=m;j++){ if(a[i][j]==0)continue; if(a[i-1][j-1]==0)f[i][j]=1; else f[i][j]=min(f[i-1][j-1]+1,min(x[i][j-1]+1,y[i-1][j]+1)); } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) ans=max(ans,f[i][j]); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]); dp(); for(int i=1;i<=n;i++) for(int j=1;j<=n/2;j++) swap(a[i][j],a[i][m-j+1]); dp(); printf("%d",ans); return 0; }