[ZJOI2007] 棋盘制作

Posted qixingzhi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ZJOI2007] 棋盘制作相关的知识,希望对你有一定的参考价值。

类型:单调栈

传送门:>Here<

题意:给出一个$01$矩阵,求$01$相间的最大子正方形和最大子矩阵

解题思路

首先考虑如果要求的不是$01$相间而是全$1$的怎么做?那就和上一题一样了,预处理$p[i][j]$。特判正方形(取较短边)

那么只需要把问题转化为全$1$矩阵即可。思考黑白相间矩阵的特性:无非两种情况,肯定有一种颜色,满足它的格子的坐标要么全是偶数,要么全是奇数

于是我们只需要在读入的过程中把坐标都是偶数或奇数的格子进行异或。然后对黑色和白色分别做一遍单调栈统计最大子矩形即可

Code

由于$p$数组需要处理两次,第二次不能受到第一次的影响。因此需要把不能取的重新置为0

/*By DennyQi 2018.8.18*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace std;
typedef long long ll;
const int MAXN = 10010;
const int MAXM = 27010;
const int INF = 1061109567;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ - && (c < 0 || c > 9)) c = getchar();
    if(c == -) w = -1, c = getchar();
    while(c >= 0 && c <= 9) x = (x<<3) + (x<<1) + c - 0, c = getchar();return x * w;
}
int N,M,ans1,ans2,top,cnt,tmp;
int a[2010][2010],h[2010],w[2010],p[2010][2010];
inline void Solve(){
    for(int i = 1; i <= N; ++i){
        top = 0;
        for(int j = 1; j <= M; ++j){
            if(!top || p[i][j] > h[top]){
                h[++top] = p[i][j];
                w[top] = 1;
            }
            else{
                cnt = 0;
                while(top > 0 && p[i][j] <= h[top]){
                    cnt += w[top];
                    ans2 = Max(ans2, h[top] * cnt);
                    tmp = Min(cnt, h[top]);
                    ans1 = Max(ans1, tmp*tmp);
                    --top;
                }
                h[++top] = p[i][j];
                w[top] = cnt + 1;
            }
        }
        cnt = 0;
        while(top > 0){
            cnt += w[top];
            ans2 = Max(ans2, h[top] * cnt);
            tmp = Min(cnt, h[top]);
            ans1 = Max(ans1, tmp*tmp);
            --top;
        }
    }
}
int main(){
    N=r,M=r;
    for(int i = 1; i <= N; ++i){
        for(int j = 1; j <= M; ++j){
            a[i][j]=r;
            if(((i&1)&&(j&1)) || ((!(i&1))&&(!(j&1)))){
                a[i][j] ^= 1;
            } 
            if(!a[i][j]){
                p[i][j] = p[i-1][j] + 1;
            }
            else{
                p[i][j] = 0;
            }
        }
    }
    Solve();
    for(int i = 1; i <= N; ++i){
        for(int j = 1; j <= M; ++j){
            if(a[i][j]){
                p[i][j] = p[i-1][j] + 1;
            }
            else{
                p[i][j] = 0;
            }
        }
    }
    Solve();
    printf("%d
%d
",ans1,ans2);
    return 0;
}

以上是关于[ZJOI2007] 棋盘制作的主要内容,如果未能解决你的问题,请参考以下文章

[luoguP1169] [ZJOI2007]棋盘制作(单调栈)

BZOJ1057: [ZJOI2007]棋盘制作

[ZJOI2007]棋盘制作 (单调栈)

[luogu P1169] [ZJOI2007]棋盘制作

洛谷 P1169 [ZJOI2007]棋盘制作

bzoj 1057: [ZJOI2007]棋盘制作