最大正方形(SOJ 2801)
SOJ 2801: 正方形 http://acm.scu.edu.cn/soj/problem.action?id=2801
问题:给出一个$0-1$矩阵,找出只包含0的最大正方形并输出它的边长。
分析:这道题与最大子矩形(SOJ 3329)类似, 这里我们可以使用相同的方法(参考这里).
方法:
(1)动态规划
定义$dp[i][j]$为右下顶点位于$[i,j]$的正方形的最大边长,则
$dp[i][j]=\min\{dp[i][j-1], dp[i-1][j], dp[i-1][j-1]\}+1$.
时间复杂度为$O(mn)$.
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream>
#include<cstring>
using namespace std;
int mat[1005][1005];
int dp[1005][1005];
int main()
{
int n,m;
int i,j;
int a,b;
int temp;
int ans;
while(scanf("%d%d",&n,&m)==2)
{
memset(mat,0,sizeof(mat));
memset(dp,0,sizeof(dp));
for(i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
mat[a][b]=1;
}
ans=0;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(mat[i][j]==0)
{
temp=dp[i-1][j]<dp[i][j-1] ? dp[i-1][j] : dp[i][j-1];
dp[i][j]=(temp<dp[i-1][j-1] ? temp : dp[i-1][j-1])+1;
ans=dp[i][j]>ans ? dp[i][j] : ans;
}
printf("%d\n",ans);
}
return 0;
}
(2)单调栈
定义$dp[i][j]$为$[i,j]$处前导0的最大个数。假定$dp[k][j], i'\le k\le i$为最小值, 则对应正方形的最大边长为$\min\{dp[k][j],i-i'+1\}$. 时间复杂度比$O(mn)$稍微差一些。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<stack> #include<cstring> using namespace std; struct node { int num; int no; int prevNo; }; int mat[1005][1005]; int main() { int n,m; int i,j; int a,b; stack<node>s; node x; int temp; int ans; while(scanf("%d%d",&n,&m)==2) { memset(mat,0,sizeof(mat)); for(i=0;i<m;i++) { scanf("%d%d",&a,&b); mat[a][b]=1; } for(j=1;j<=n;j++) { mat[0][j]=0; mat[n+1][j]=0; } for(i=1;i<=n;i++) mat[i][0]=0; for(i=1;i<=n;i++) for(j=1;j<=n;j++) mat[i][j]=mat[i][j] ? 0 : mat[i][j-1]+1; ans=0; for(j=1;j<=n;j++) for(i=0;i<=n+1;i++) { while(!s.empty() && mat[i][j]<s.top().num) { temp=s.top().num<i-1-s.top().prevNo ? s.top().num : i-1-s.top().prevNo; ans=temp>ans ? temp : ans; s.pop(); } x.num=mat[i][j]; x.no=i; x.prevNo=s.size() ? s.top().no : 0; s.push(x); } while(!s.empty()) s.pop(); printf("%d\n",ans); } return 0; }