BZOJ 3039:玉蟾宫 单调栈/悬线法
悬线法,刚学的
转移方程
if(满足^&%$!@#^%){
right[i][j]=min(right[i][j],right[i-1][j]);
left[i][j]=max(left[i][j],left[i-1][j]);
up[i][j]=up[i-1][j]+1;
}
int a=R[i][j]−L[i][j]+1;
int b=min(a,up[i][j]);
若要求矩形
ans=max(ans,a*up[i][j]);
若要求正方形
ans=amx(ans,b*b);
#include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int maxn = 1e3+ 5; int n, m; int a[maxn][maxn]; /* up:up[i][j] 用于表示以(i,j)为低端的悬线的长度 l: l[i][j] 用于表示点(i,j)左端最近的‘R'的纵坐标 r: r[i][j] 用于表示点(i,j)右端最近的‘R'的纵坐标 up记录上下最远的距离 l,r记录点(i,j)左右能到达的最远的距离 */ int up[maxn][maxn], l[maxn][maxn], r[maxn][maxn]; int Getchar() { char ch= getchar(); while (ch != 'F'&&ch != 'R') ch = getchar(); return ch == 'F'; } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { a[i][j] = Getchar(); up[i][j] = 1; l[i][j] = r[i][j] = j; } } for (int i = 1; i <= n; i++) { for (int j = 2; j <= m; j++) { if (a[i][j] == 1 && a[i][j - 1] == 1) l[i][j] = l[i][j - 1]; } for (int j = m - 1; j > 0; j--) { if (a[i][j] == 1 && a[i][j + 1] == 1) r[i][j] = r[i][j + 1]; } } int max_val = -1; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (i>1&&a[i][j] == 1 && a[i - 1][j] == 1) { up[i][j] += up[i - 1][j]; l[i][j] = max(l[i - 1][j], l[i][j]); r[i][j] = min(r[i - 1][j], r[i][j]); } max_val = max(max_val, up[i][j] * (r[i][j] - l[i][j] + 1)); } } cout << 3*max_val; return 0; }
单调栈
首先处理a[i][j],表示点i,j向上拓展的最大高度,然后对于每一行,就可以就按照书上的最大矩形面积处理,仔细想想,有了a数组以后,1-n行每一行的情况是不是和最大矩形面积那题类似
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e3 + 5; int n, m, ans = -1; /* a[i][j] 点(i,j)向上拓展的最大长度 */ int a[maxn][maxn], s[maxn], w[maxn]; int Getchar() { char ch = getchar(); while (ch != 'F'&&ch != 'R') ch = getchar(); return ch == 'F'; } void solve(int x) { //memset(s, 0, sizeof(s)); //memset(w, 0, sizeof(w)); int tot = 0; a[x][m + 1] = 0; //小技巧 保证最后栈是空的 for (int i = 1; i <= m + 1; i++) { if (a[x][i] >= s[tot]) { s[++tot] = a[x][i]; w[tot] = 1; } else{ int width = 0; while (tot&&s[tot] > a[x][i]) { width += w[tot]; ans = max(ans, width*s[tot]); tot--; } s[++tot] = a[x][i]; w[tot] = width + 1; } } } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (Getchar()) a[i][j] = a[i - 1][j] + 1; } } for (int i = 1; i <= n; i++) solve(i); //这里枚举每一行进行处理 cout << 3 * ans; return 0; } ```
再给出一个O(nm2)的做法,这个超时了
#include<iostream> #include<algorithm> using namespace std; const int maxn = 1e3+ 5; int n, m; int a[maxn][maxn]; int Getchar() { char ch= getchar(); while (ch != 'F'&&ch != 'R') ch = getchar(); return ch == 'F'; } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { a[i][j] = Getchar(); a[i][j] += a[i][j - 1]; //列上求和 } } int ans = -1; for (int i = 1; i <=m; i++)//枚举列 { for (int j = i; j <= m; j++)//枚举列 { int sum = 0; for (int k = 1; k <= n; k++) //枚举行 { if (a[k][j] - a[k][i - 1] == j - i + 1) sum += j - i + 1; //满足第k行i-j列全为‘F' else sum = 0; ans = max(ans, sum); //更新答案 } } } cout << 3*ans; return 0; }