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;
}

posted @ 2025-03-09 20:45  昊天djh  阅读(171)  评论(0)    收藏  举报