ARC081D Flip and Rectangles
Problem
Analysis
正解思路
对于这类操作性质题目,有两个基本思考方向:
- 操作顺序是否会影响答案。
- 操作过程是否可以构造,进而思考构造的唯一性(充要条件)。
然后我们发现,操作顺序确实不会影响答案,那么操作过程是否可以被构造呢?
考虑如何判定是否能产生一个矩阵:发现当且仅当在原始矩阵中,每一行都与第一行相同或相反,因为列的翻转不影响列上格子的相对性。
那么将原矩阵相邻两行压缩为一行:\(a_{i,j}=[s_{i,j}==s_{i+1,j}]\)。
那么就说明,最大子矩形必然是每行尽可能向左右扩展。这是一个经典问题,有较多解法:悬线法等。
这里利用单调栈维护最高点 \(U_i\) 与最低点 \(D_i\),以及向左能拓展的最远点 \(mx_i\),答案就是 \((D_i-U_i+1+1)mx_i\),这里最后一个的 \(+1\) 是代码实现中线段相邻格子进行压缩,计算时要解压。
注意,单独一行与一列可调控的最大面积为 \(n\) 和 \(m\),需要与它们取 \(\max\)。
错因总结
我考虑到了操作顺序对答案是无影响的,但总拘泥于从正面做出矩形充要条件,有一瞬间想过从判定产生矩形的角度思考(即反面)。
然后一直在思考 dp,但实际证明 dp 确实不行,因为根本无法化简为子问题。
AC Code
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define i128 __int128
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define mk make_pair
#define INF 0x3f3f3f3f
#define INFx 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 2010;
int n, m;
char s[N][N];
int a[N][N];
int mx[N], u[N], d[N];
int stk[N], top;
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
cin >> s[i][j];
}
}
for (int i = 1; i < n; i ++) {
a[i][0] = -1;
for (int j = 1; j <= m; j ++) {
a[i][j] = s[i][j] == s[i + 1][j];
}
}
int ans = max(n, m);
for (int j = 1; j <= m; j ++) {
for (int i = 1; i < n; i ++) {
if (a[i][j] == a[i][j - 1]) mx[i] ++;
else mx[i] = 1;
}
top = 0;
for (int i = 1; i < n; i ++) {
while (top && mx[stk[top]] >= mx[i]) top --;
u[i] = stk[top] + 1;
stk[++ top] = i;
}
top = 0;
stk[++ top] = n;
for (int i = n - 1; i >= 1; i --) {
while (top && mx[stk[top]] >= mx[i]) top --;
d[i] = stk[top] - 1;
stk[++ top] = i;
}
for (int i = 1; i < n; i ++) {
ans = max(ans, (d[i] - u[i] + 2) * mx[i]);
}
}
cout << ans << '\n';
return 0;
}

浙公网安备 33010602011771号