题面
题目描述
小 $P$ 现在有一个 $n\times m$ 的 $01$ 矩阵,某一天他突发奇想:假设矩阵行与行可以任意交换,其中最大的全 $1$ 子矩阵有多大。
他想了很久都没有得出答案,于是找到了你。
输入格式
第一行两个数 $n, m$,描述矩阵的大小。
接下来 $n$ 行,每行一个长度为 $m$ 的 $01$ 字符串,描述 $01$ 矩阵。
输出格式
一个数,及最大全 $1$ 子矩阵面积。
样例
样例 $1$
Output
样例 $2$
Output
样例 $3$
1 2 3 4 5 6 7 8 9 10 11 12
| 11 16 0111110101100011 1000101100010000 0010110110010101 0110110010110010 0011101101110000 1001100011010111 0010011111111000 0100100100111110 1001000000100111 0110000011001000 1011111011010000
|
Output
数据范围
对于 $30\%$ 的数据,$n,m\leq 10$
对于 $70\%$ 的数据,$n, m \leq 1000$
对于 $100\%$ 的数据,$n,m\leq 5000$
题解
看到这种题目首先想到的是 $dp$ ,可能是中了最大子矩形的毒,但是很快发现那个交换的操作很灵活,不方便记成状态,况且数据很大,不允许很复杂的 $dp$ 。
大概一算,时间复杂度最多只能是 $O(nm)$ 得了,对于矩阵来说就是线性。那么既然行和行之间可以交换,自然想到我们可以单独处理每一行的信息。于是:
很傻很正确的模拟
我们可以对于每一行每个位置记下这样的东西 $f_i$ :
如果第 $i $ 位是 $0$ ,$f_i=0$;
如果第 $i$ 位是 $0$ ,$f_i$ 为 $f_i$ 前面最多有多少个 $1.$
举个例子:
$A=11100110001110\\F=12300120001230$
进一步的,如果我们把所有位结合起来,就会发现这个 $f$ 其实可以表示这一行最大能对边长为多少的矩阵做贡献。
自然的,我们可以把每一行的 $f$ 算出来,再对于每一列(准确的说是一个前缀矩阵)算一下答案。即我们记 $g_i$ 表示在这一列每一行的 $f$ 中有 $g_i$ 个是 $i$ 。然后我们发现 $f$ 记的是最大的长度,所以小于 $f$ 的长度都可以取到,所以需要对 $g$ 做一个后缀和,那么最终的答案是每一列的:$\min\limits^n_{i=1}g_i\times i$ ,因为 $g_i$ 表示有多少行,$i$ 表示行的长度(也就是列数),乘起来就是体积了。
那么处理 $f$ 时间复杂度是 $O(nm)$,处理 $g$ 是 $O(nm)$,总的就也是 $O(nm)$ 了。
注意要优化读入不然会卡常。
$code:$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <deque> #include <queue> #include <cmath> #include <algorithm> #include <vector> #include <ctime> #define INF 0x3f3f3f3fll #define NO 5005 #define MO 300005 #define mp(x, y) make_pair(x, y) #define fi first #define se second #define fup(i, x, y) for(register int i = x; i <= y; ++i) #define fdn(i, x, y) for(register int i = x; i >= y; --i) #define lln putchar('\n') #define blk putchar(' ') using namespace std; typedef long long ll; typedef double db; typedef pair<int, int> pii; typedef pair<int, pii> piii; ll read() { ll ret = 0; char ch = ' ', last; while (ch < '0' || ch > '9') last = ch, ch = getchar(); while (ch >= '0' && ch <= '9') ret = ((ret + (ret << 2)) << 1) + (ch ^ '0'), ch = getchar(); return last == '-' ? -ret : ret; } void write(ll x) { if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
int n, m, a[NO][NO], book[NO]; string x;
void init() { freopen("matrix.in", "r", stdin); freopen("matrix.out", "w", stdout); n = read(), m = read(); fup (i, 1, n) fup (j, 1, m) { char ch = getchar(); while (ch == ' ' || ch == '\n' || ch == '\r') ch = getchar(); a[i][j] = ch - '0', a[i][j] = (a[i][j] ? a[i][j - 1] + 1 : 0); } int ans = 0; fup (i, 1, m) { memset(book, 0, sizeof(int) * (m + 1)); fup (j, 1, n) book[a[j][i]]++; fdn (j, m, 1) book[j] += book[j + 1]; fup (j, 1, m) ans = max(ans, book[j] * j); } write(ans), lln; }
int main() { init(); return 0; }
|
题解说的计数排序(??)
放张图吧:(效率和上面是一样的。所以了解上面就好)
Data
download