【题目链接】
【思路要点】
- 首先,为了开下数组,我们可以规定一个映射函数将二维数组存在一维的空间里。
- 预处理每个元素向上连续的零的个数,以及答案在某一块拼图内的情况。
- 当\(N\)较小的时候,有一种直观的做法就是枚举最终矩形的上下边界,接下来每一块拼图可以分为全零与非全零两种。我们的最终矩形显然会由某两个非全零的拼图中间夹着所有全零拼图的情况构成,那两个非全零的拼图应当满足左侧/右侧的连续的零尽量多,若左右两侧连续的零最多的是同一块拼图,把其中一个换成次大值即可。
- 上面的做法时间复杂度为\(O(N^2*M)\),无法通过\(N\)较大的情况。
- 我们换一种思路,考虑枚举矩形中的某一个点,将其与之上方连续的一串零作为本次枚举的上下边界,沿用上面的做法,复杂度变为\(O(N*M^2)\)。因为最优解一定被上方的某一个一限制住了,所以这样枚举就是充分的。
- 平衡规划,时间复杂度\(O(Min\{N,M\}*N*M)\),其中\(M=\sum W_i\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 200005; const int Limit = 400; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } bool mp[MAXN]; char st[MAXN]; int l[MAXN], r[MAXN]; int n, m, s, up[MAXN]; int func(int x, int y) {return y * n + x; } void solvem() { int ans = 0; static int vl[MAXN], vr[MAXN]; for (int pi = 1; pi <= n; pi++) for (int pj = 1; pj <= m; pj++) { int len = up[func(pi, pj)], free = 0; int Maxl = 0, Naxl = 0; int Maxr = 0, Naxr = 0; for (int i = 1; i <= s; i++) { bool flg = false; int cnt = 0; for (int j = l[i]; j <= r[i]; j++) if (up[func(pi, j)] < len) { flg = true; chkmax(ans, cnt * len); cnt = 0; } else cnt++; chkmax(ans, cnt * len); if (!flg) free += r[i] - l[i] + 1; else { vl[i] = vr[i] = 0; for (int j = l[i]; j <= r[i]; j++) if (up[func(pi, j)] < len) break; else vl[i]++; for (int j = r[i]; j >= l[i]; j--) if (up[func(pi, j)] < len) break; else vr[i]++; if (vl[i] > vl[Maxl]) { Naxl = Maxl; Maxl = i; } else if (vl[i] > vl[Naxl]) Naxl = i; if (vr[i] > vr[Maxr]) { Naxr = Maxr; Maxr = i; } else if (vr[i] > vr[Naxr]) Naxr = i; } } if (Maxl != Maxr) chkmax(ans, len * (free + vl[Maxl] + vr[Maxr])); else chkmax(ans, len * max(free + vl[Maxl] + vr[Naxr], free + vl[Naxl] + vr[Maxr])); } writeln(ans); } void solven() { int ans = 0; static int vl[MAXN], vr[MAXN]; for (int pi = 1; pi <= n; pi++) for (int pj = pi; pj <= n; pj++) { int len = pj - pi + 1, free = 0; int Maxl = 0, Naxl = 0; int Maxr = 0, Naxr = 0; for (int i = 1; i <= s; i++) { bool flg = false; int cnt = 0; for (int j = l[i]; j <= r[i]; j++) if (up[func(pj, j)] < len) { flg = true; chkmax(ans, cnt * len); cnt = 0; } else cnt++; chkmax(ans, cnt * len); if (!flg) free += r[i] - l[i] + 1; else { vl[i] = vr[i] = 0; for (int j = l[i]; j <= r[i]; j++) if (up[func(pj, j)] < len) break; else vl[i]++; for (int j = r[i]; j >= l[i]; j--) if (up[func(pj, j)] < len) break; else vr[i]++; if (vl[i] > vl[Maxl]) { Naxl = Maxl; Maxl = i; } else if (vl[i] > vl[Naxl]) Naxl = i; if (vr[i] > vr[Maxr]) { Naxr = Maxr; Maxr = i; } else if (vr[i] > vr[Naxr]) Naxr = i; } } if (Maxl != Maxr) chkmax(ans, len * (free + vl[Maxl] + vr[Maxr])); else chkmax(ans, len * max(free + vl[Maxl] + vr[Naxr], free + vl[Naxl] + vr[Maxr])); } writeln(ans); } int main() { int T; read(T); while (T--) { read(s), read(n), m = 0; for (int i = 1; i <= s; i++) { int len; read(len); l[i] = m + 1; r[i] = m + len; for (int j = 1; j <= n; j++) { scanf("\n%s", st + 1); for (int k = 1; k <= len; k++) mp[func(j, m + k)] = st[k] == '1'; } m += len; } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) if (mp[func(i, j)]) up[func(i, j)] = 0; else up[func(i, j)] = up[func(i - 1, j)] + 1; if (n <= Limit) solven(); else solvem(); } return 0; }