悬线法:最大矩阵

我们以最大全0矩阵为例。对于每个点,以这个点为起点往上引一条直线,直到矩形边界或遇到障碍为止,称之为这个点的悬线。悬线的长度记作 U U

对于这个01矩形,答案矩形一定是由某条悬线向左右平移得到。
在这里插入图片描述
U很好维护,如果当前点可以从上面排(Mp[i-1][j])转移,那么 U = U i 1 + 1 U=U_{i-1}+1 ,否则 U = 1 U=1

还有L和R。首先要保证当前排中的端点,即可以转移时有:

  1. L j = L j 1 L_{j}=L_{j-1}
  2. R j = R j + 1 R_{j}=R_{j+1}

然后因为当前悬线 U i U_i 中有一部分是 U i 1 U_{i-1} ,所以需要考虑上面排:

  1. L i = m a x ( L i , L i 1 ) L_{i}=max(L_{i},L_{i-1})
  2. R i = m i n ( R i , R i 1 ) R_{i}=min(R_{i},R_{i-1})

例题:P1169 [ZJOI2007]棋盘制作

题意: 01矩阵,找出最大的正方形和矩形,满足01相见。

//滚动数组优化
#include<bits/stdc++.h>
using namespace std;
const int N = 2002;
int n, m, Mp[N][N];
int u[2][N], l[2][N], r[2][N];

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            scanf("%d", Mp[i] + j);
        }
    }
    int ans1 = 0, ans2 = 0;
    for(int i = 1; i <= n; i++) {
        int now = i & 1, pre = !now;
        l[now][1] = 1;
        for(int j = 2; j <= m; j++) { //L
            if(Mp[i][j] != Mp[i][j - 1])
                l[now][j] = l[now][j - 1];
            else
                l[now][j] = j;
        }
        r[now][m] = m;
        for(int j = m - 1; j >= 1; j--) { //R
            if(Mp[i][j] != Mp[i][j + 1])
                r[now][j] = r[now][j + 1];
            else
                r[now][j] = j;
        }
        for(int j = 1; j <= m; j++) { //如果可以更新
            u[now][j] = 1;
            if(i > 1 && Mp[i][j] != Mp[i - 1][j]) {
                u[now][j] = u[pre][j] + 1;
                l[now][j] = max(l[now][j], l[pre][j]);
                r[now][j] = min(r[now][j], r[pre][j]);
            }
            int H = u[now][j], L = r[now][j] - l[now][j] + 1;
            ans1 = max(ans1, min(H, L) * min(H, L));
            ans2 = max(ans2, H * L);
        }
    }
    printf("%d\n%d\n", ans1, ans2);
}


猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/86750362