2019牛客多校训练营第二场补题

https://ac.nowcoder.com/acm/contest/882/H

H题:给由01组成的矩阵,问只包含1的子矩阵第二大的面积是多少。

悬线法求极大子矩阵的裸题。 相关资料:https://www.cnblogs.com/-Zzz-/p/11503639.html

由于这个预处理方法会导致一整块的L,R,U的值都同化,所以还要处理出这一块的(L-1)*H 和 L*(H-1)。

还要注意 g[i][j] 等于 0 时不算数,不要拿去算面积

更新ans1时要用大于等于号

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e3+300;
int g[maxn][maxn],Left[maxn][maxn],Right[maxn][maxn],Up[maxn][maxn];
char ch[maxn];

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++){
        scanf("%s",ch+1);
        for(int j=1; j<=m; j++){
            g[i][j] = ch[j]-'0';
            Up[i][j] = 1;
            Left[i][j] = Right[i][j] = j;
        }
    }
    for(int i=1; i<=n; i++){
        for(int j=2; j<=m; j++){
            if(g[i][j]==1 && g[i][j-1]==1){
                Left[i][j] = Left[i][j-1];
            }
        }
    }
    for(int i=1; i<=n; i++){
        for(int j=m-1; j>=1; j--){
            if(g[i][j]==1 && g[i][j+1]==1){
                Right[i][j] = Right[i][j+1];
            }
        }
    }
    int maxh = 0,maxl = 0;
    int L,R,U,ans1=0,ans2=0;
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            if(i>1 && g[i][j]==1 && g[i-1][j]==1){
                Up[i][j] = Up[i-1][j]+1;
                Left[i][j] = max(Left[i-1][j], Left[i][j]);
                Right[i][j] = min(Right[i-1][j], Right[i][j]);  //悬线法
            }
            if(ans1<= (Right[i][j] - Left[i][j] + 1)*Up[i][j] ){
                R = Right[i][j], L = Left[i][j], U = Up[i][j];
                maxl = Right[i][j] - Left[i][j] + 1;
                // printf("l=%d\n", maxl);
                maxh = Up[i][j];
                // printf("h=%d\n", maxh);
                ans1 = maxl*maxh;

            }
        }
    }
    ans2 = max(ans2,(maxl-1)*maxh);
    ans2 = max(ans2,maxl*(maxh-1));

    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            if(g[i][j]==0) continue;
            if(Right[i][j] == R && Left[i][j]==L && Up[i][j]==U) continue;
            ans2 = max( ans2,(Right[i][j] - Left[i][j] + 1)*Up[i][j] );
        }
    }
    printf("%d\n", ans2);
}


// #include <iostream>
// #include <algorithm>
// using namespace std;
 
// const int Max = 2005;
 
// int ves[Max][Max], up[Max][Max], Left[Max][Max], Right[Max][Max];
// int temp1 = 1, temp2 = 1;
 
// int main(void)
// {
//     ios::sync_with_stdio(false);
//     int N, M;
//     cin >> N >> M;
//     for(int i = 1; i <= N; i++)
//         for (int j = 1; j <= M; j++) {
//             cin >> ves[i][j];
//             Left[i][j] = Right[i][j] = j;    //初始化Right和Left,使他们值为点所在纵坐标
//             up[i][j] = 1;    //初始化up使其值为1
//         }
 
//     for (int i = 1; i <= N; i++)
//         for (int j = 2; j <= M; j++)
//             if (ves[i][j] == 1 - ves[i][j - 1])    //判断相邻两个数是否不同
//                 Left[i][j] = Left[i][j - 1];    //是,则
 
//     for (int i = 1; i <= N; i++)
//         for (int j = M - 1; j > 0; j--)
//             if (ves[i][j] == 1 - ves[i][j + 1])
//                 Right[i][j] = Right[i][j + 1];
 
//     for(int i = 1;i <= N; i++)
//         for (int j = 1; j <= M; j++) {
//             if (i > 1 && ves[i][j] == 1 - ves[i - 1][j]) {    //递推公式
//                 Left[i][j] = max(Left[i][j], Left[i - 1][j]);
//                 Right[i][j] = min(Right[i][j], Right[i - 1][j]);
//                 up[i][j] = up[i - 1][j] + 1;
//             }
 
//             int A_instance = Right[i][j] - Left[i][j] + 1;    //计算长度
//             int B_instance = min(A_instance, up[i][j]);    //算出长宽中较小的边,以计算正方形
//             temp1 = max(temp1, B_instance * B_instance);    //正方形面积
//             temp2 = max(temp2, A_instance * up[i][j]);        //长方形面积
//         }
 
//     cout << temp1 << endl << temp2 << endl;
// }

猜你喜欢

转载自www.cnblogs.com/-Zzz-/p/11504077.html