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; // }