AtCoder Beginner Contest 396-G - Flip Row or Col
https://atcoder.jp/contests/abc396/tasks/abc396_g
下面给出题目的中文思路、详细解释以及带中文注释的 C++ 实现代码。


代码实现
下面给出带中文注释的 C++ 代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
// 快速Walsh-Hadamard变换(FWHT)
// 参数 a 为待变换数组,参数 invert 为 false 时求正变换,为 true 时求反变换(需要除以数组大小)。
void fwht(vector<ll>& a, bool invert) {
int n = a.size();
for (int len = 1; len < n; len <<= 1) {
for (int i = 0; i < n; i += (len << 1)) {
for (int j = 0; j < len; j++) {
ll u = a[i+j], v = a[i+j+len];
a[i+j] = u + v;
a[i+j+len] = u - v;
}
}
}
if (invert) {
for (int i = 0; i < n; i++){
a[i] /= n;
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int H, W;
cin >> H >> W;
int size = 1 << W; // 一共有 2^W 种可能的行模式
// freq[mask] 记录每种行模式出现的次数
vector<ll> freq(size, 0);
for (int i = 0; i < H; i++){
string s;
cin >> s;
int mask = 0;
// 将字符串 s 转换为整数 mask(二进制表示)
for (int j = 0; j < W; j++){
mask = (mask << 1) | (s[j]-'0');
}
freq[mask]++;
}
// 构造数组 G,其中 G[x] = min(popcount(x), W - popcount(x))
// 对于某一行模式 R,固定列翻转方案 C 后,
// 若不翻转该行,贡献为 popcount(R XOR C);
// 若翻转该行,贡献为 W - popcount(R XOR C);
// 取两者中的较小值,即为 min(popcount(R XOR C), W-popcount(R XOR C))。
// 将 R XOR C 换个变量记作 x,则我们令 G(x)=min(popcount(x), W-popcount(x))
vector<ll> G(size, 0);
for (int mask = 0; mask < size; mask++){
int ones = __builtin_popcount(mask);
G[mask] = min(ones, W - ones);
}
// 我们要求的 f(C) = sum_{R} freq[R] * G(R XOR C)
// 这正是 freq 与 G 的 XOR 卷积 (freq ⊕ G)[C] 的定义:
// (freq ⊕ G)[C] = sum_{R} freq[R] * G(C XOR R)
// 因此,我们可以利用 FWHT 来求解所有 f(C)。
// 对 freq 和 G 分别做 FWHT
vector<ll> F = freq; // F 保存 freq 的变换结果
vector<ll> Hvec = G; // Hvec 保存 G 的变换结果
fwht(F, false);
fwht(Hvec, false);
// 逐点相乘
vector<ll> conv(size, 0);
for (int i = 0; i < size; i++){
conv[i] = F[i] * Hvec[i];
}
// 对乘积结果做反变换,即得到 XOR 卷积结果
fwht(conv, true);
// conv[C] 即为 f(C) = sum_{R} freq[R] * G(R XOR C)
// 答案为所有可能列翻转方案 C 中 f(C) 的最小值
ll ans = LLONG_MAX;
for (int i = 0; i < size; i++){
ans = min(ans, conv[i]);
}
cout << ans << "\n";
return 0;
}


浙公网安备 33010602011771号