CodeForces 663E Binary Table
Description
You are given a table consisting of n rows and m columns. Each cell of the table contains either 0 or 1. In one move, you are allowed to pick any row or any column and invert all values, that is, replace 0 by 1 and vice versa.
What is the minimum number of cells with value 1 you can get after applying some number of operations?
Input
The first line of the input contains two integers n and m (1 ≤ n ≤ 20, 1 ≤ m ≤ 100 000) — the number of rows and the number of columns, respectively.
Then n lines follows with the descriptions of the rows. Each line has length m and contains only digits '0' and '1'.
Output
Output a single integer — the minimum possible number of ones you can get after applying some sequence of operations.
Example
Input
3 4
0110
1010
0111
Output
2
题意
每次操作可以取反任意一行或一列,问矩阵中最少的1
题解
我们观察到n只有20,我们完全可以枚举行的翻转状态,然后对于每一列,如果翻转能获得更少的1就翻转,复杂度为\(O(2^nm)\)
我们考虑,设某一列一开始的状态是\(k\),翻转状态为\(i\),翻转后的状态为\(j\),显然有
利用这个性质,我们设a[x]为一开始矩阵中x状态的数量,b[x]为这一列状态为x时,\(min(num[1],num[0])\),因为列可以随意翻转,以获得更少的1,这样这样累加就可以获得翻转状态为i时,矩阵中所含的最少的1的数量,全部求出后取min就可以了.
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void fwt(ll a[], int len, int op) {
for (int h = 2; h <= len; h <<= 1) {
for (int j = 0; j < len; j += h) {
for (int k = j; k < j + h / 2; k++) {
ll u = a[k], t = a[k + h / 2];
a[k] = u + t;
a[k + h / 2] = u - t;
if (op == -1) {
a[k] /= 2; a[k + h / 2] /= 2;
}
}
}
}
}
const int N = 2e6;
char ch[20][100050];
ll a[N], b[N];
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) {
scanf("%s", ch[i]);
}
int len = 1 << n;
for (int i = 0; i < m; i++) {
int x = 0;
for (int j = 0; j < n; j++) {
x += (ch[j][i] - '0') << j;
}
a[x]++;
}
for (int i = 1; i < len; i++) {
b[i] = b[i >> 1] + (i & 1);
}
for (int i = 0; i < len; i++) {
b[i] = min(b[i], n - b[i]);
}
fwt(a, len, 1);
fwt(b, len, 1);
for (int i = 0; i < len; i++) {
a[i] = a[i] * b[i];
}
fwt(a, len, -1);
ll ans = 1e18;
for (int i = 0; i < len; i++) {
ans = min(ans, a[i]);
}
printf("%lld\n", ans);
return 0;
}