https://www.luogu.org/problemnew/show/P1387
开始自己想的:设f(i,j):以(i,j)为左上角的包含(i,j)的最大子正方形大小,则f(i,j)取决于:设t=f(i+1,j+1),(i,j)右以及下方的t个元素最多连续几个1。由于只有01,所以用前缀和+二分可以o(logn)知道有几个连续的1,二分很麻烦写,不写的话用了前缀和也没什么用。
#include<bits/stdc++.h>
using namespace std;
int n,m,maxn;
int a[105][105],d[105][105];
int row[105][105],col[105][105];
int main()
{
// freopen("input.in","r",stdin);
cin>>n>>m;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>a[i][j];
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
{
row[i][j]=row[i][j-1]+a[i][j];
col[i][j]=col[i-1][j]+a[i][j];
}
for(int i=n;i>=1;i--)
for(int j=m;j>=1;j--)
{
int t=d[i+1][j+1];
if(row[i][j+t]-row[i][j]==t&&col[i+t][j]-col[i][j]==t&&a[i][j])d[i][j]=t+1;
else if(a[i][j])
{
int k1=i+1,k2=j+1;
while(a[k1][j])k1++;
while(a[i][k2])k2++;
d[i][j]=min(k1-i,k2-j);
}
else d[i][j]=0;
}
for(int i=n;i>=1;i--)
for(int j=m;j>=1;j--)
maxn=max(maxn,d[i][j]);
cout<<maxn<<endl;
return 0;
}
看题解,原来状态转移方程这么直接好写。状态表示一样,转移方程
if(a[i][j]==1) f[i][j]=min{f[i][j+1],f[i+1][j],f[i+1][j+1]}+1; else f[i][j]=0;
理解:f(i,j)主要来源于f(i+1,j+1),但是有可能本行或者本列不全为1,带上min{f[i][j+1],f[i+1][j]}就可以限制到这个条件了,因为f[i][j+1],f[i+1][j]与f(i+1,j+1)重叠的部分全是1,只会受本行/列的影响。画图理解。