題目連接
- 該題是luogu試煉場的2-17:T6
題目大意
- n*m的0,1棋盤中;
- 找最大的正方形,要求:(左或者右)對角線上全是1,其他位置全是0。
題目分析
- 和1387最大子正方形十分類似,但要增加預處理。
- 因爲左右對角線都有可能是最值,所以分開兩次DP:一次左斜,一次右斜,再取最值。
解題思路:左斜
問什麼設什麼: f[i][j]表示以(i,j)爲右下角的正方形,符合條件時的邊長;
- 如果a[i][j]是0,則f[i][j]一定是0;
- 如果a[i][j]是1,則需要考慮左上角,左邊,上邊:
- f[i][j]一定與f[i-1][j-1]的值有關,
- 第i行前面的空格,第j列上面的空格情況;
- 預處理出橙色第i行部分的值:ax[i][j]表示,從(i,j)往左,有多少個空的格子,不包含(i,j);
- 預處理出橙色第j列部分的值:ay[i][j]表示,從(i,j)往上,有多少個空的格子,不包含(i,j);
左邊的搞好了,對稱搞一次右邊的,再求一次最值。
參考代碼
//luogu1736:創意喫魚法
//最大子正方形的對角線
//左斜和右斜各做一次
#include<bits/stdc++.h>
using namespace std;
#define ma 2505
int ax[ma][ma],ay[ma][ma];
int f[ma][ma],a[ma][ma];
int n,m,ans=0;
int main()
{
cin>>n>>m;
//左斜:從左上到右下
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
if(a[i][j]==0)//預處理
{
ax[i][j]=ax[i][j-1]+1;
ay[i][j]=ay[i-1][j]+1;
}
else if(a[i][j]==1)
{
f[i][j]=min(f[i-1][j-1],min(ax[i][j-1],ay[i-1][j]))+1;
ans=max(f[i][j],ans);
}
}
}
//右斜:從右上到左下
memset(f,0,sizeof(f));
memset(ax,0,sizeof(ax));
memset(ay,0,sizeof(ay));
for(int i=1;i<=n;i++)
{
for(int j=m;j>=1;j--)
{
if(a[i][j]==0)
{
ax[i][j]=ax[i][j+1]+1;
ay[i][j]=ay[i-1][j]+1;
}
else if(a[i][j]==1)
{
f[i][j]=min(f[i-1][j+1],min(ax[i][j+1],ay[i-1][j]))+1;
ans=max(f[i][j],ans);
}
}
}
cout<<ans<<endl;
return 0;
}