bzoj 1057 单调栈
首先我们可以枚举每个一点,然后向下一直拓展到不能拓展为止,然后向下拓展的同时我们可以算出来向左最多拓展的个数,用单调栈来维护一个上升的序列,这样就类似与悬线法找最大01子矩阵了,但是对于这题01交替来说,好多细节比较麻烦,所以我们可以采用另一种转换方法,对于一个01矩阵来说,一定满足一下两个条件中的一个:
1:矩阵中所有0的点(i,j),i与j的奇偶相同,且对于所有1点(i,j),i与j的奇偶不同。
2:矩阵中所有0的点(i,j),i与j的奇偶不同,且对于所有1点(i,j),i与j的奇偶相同。
那么我们把所有满足1条件的点标号为1,满足2条件的点标号为0,这样,我们就求出标号矩阵的01子矩阵就行了,还是使用单调栈的方法求,但是这里的最大0矩阵和最大1矩阵都有可能成为答案,所以我们先求最大1矩阵,然后将矩阵取非(!),在求一遍更新答案就好了。
至于正方形和矩形的求法是相同的,只是更新答案的方式不同,特别的,最大子方阵也可以用DP来求解,设w[i][j]为以(i,j)点为右下角的方阵的最大边长(面积),转移为w[i][j]=min(w[i-1][j-1],w[i][j-1],w[i-1][j])+1。
反思:没有考虑到DP的方阵局限性。
/************************************************************** Problem: 1057 User: BLADEVIL Language: C++ Result: Accepted Time:2948 ms Memory:48168 kb ****************************************************************/ //By BLADEVIL #include <cstdio> #include <algorithm> #define maxn 2010 #define sqr(x) x*x using namespace std; int a[maxn][maxn],w[maxn],s[maxn],f[maxn][maxn],map[maxn][maxn]; int top,n,m,ans1,ans2; void calc() { for (int j=1;j<=m;j++) { top=0; for (int i=1;i<=n;i++) { int minw=i; while (top&&s[top]>=f[i][j]) { ans1=max(ans1,s[top]*(i-w[top])); ans2=max(ans2,sqr(min(s[top],i-w[top]))); minw=w[top--]; } s[++top]=f[i][j]; w[top]=minw; } } } 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]); if ((i&1)==(j&1)&&a[i][j]||(i&1)!=(j&1)&&!a[i][j]) map[i][j]=1; else map[i][j]=0; } for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (map[i][j]) f[i][j]=f[i][j-1]+1; else f[i][j]=0; calc(); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) map[i][j]=!map[i][j]; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (map[i][j]) f[i][j]=f[i][j-1]+1; else f[i][j]=0; calc(); printf("%d\n%d\n",ans2,ans1); return 0; }