BZOJ 1057. [ZJOI2007]棋盘制作

 

根据奇偶性,把x,y坐标加起来是奇数的位置翻转一下,就变成求最大01子矩阵了。

然后预处理出每个位置往上最长的01路径,每行用一个单调栈解决。

单调栈从栈底到栈顶维护每一个矩阵的高度和宽度,按高度递增维护。

当前高度如果小于栈顶的高度,那就将栈顶的矩阵更新答案,并且出栈,但是它的宽度是可以为当前高度的矩阵做贡献的,就是当前要加入栈的矩阵的宽度得加上这个要出栈的矩阵。

最后一列做完之后再清一下栈就行了。

#include <bits/stdc++.h>
#define pii pair<int, int>
#define fi first
#define se second

const int N = 2222;
int a[N][N], dp[N][N], n, m, ans1, ans2;
std::pii st[N];

void solve() {
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            dp[i][j] = (a[i][j] ? dp[i - 1][j] + 1 : 0);
    for (int i = 1; i <= n; i++) {
        int top = 0;
        for (int j = 1; j <= m + 1; j++) {
            int w = 0;
            while (top && st[top].fi > dp[i][j]) {
                int h = st[top].fi;
                w += st[top].se;
                top--;
                ans1 = std::max(ans1, std::min(w, h) * std::min(w, h));
                ans2 = std::max(ans2, w * h);
            }
            st[++top] = std::pii(dp[i][j], w + 1);
        }
    }
}

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 + j) % 2) a[i][j] ^= 1;
        }
    solve();
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            a[i][j] ^= 1;
    solve();
    printf("%d\n%d\n", ans1, ans2);
    return 0;
}
View Code

 

posted @ 2020-01-25 20:06  Mrzdtz220  阅读(101)  评论(0编辑  收藏  举报