CF 633 E. Binary Table

题目链接

题目大意:给定一个棋盘,棋盘上有0或1,你可以将一整行取反或者一整列取反,要使得最后剩的1最少。\((1\le n\le 20,1\le m\le 100000)\)。

一个容易想到的思路就是先枚举行是否取反,然后列就看1的个数是否大于\(\frac{n}{2}\)考虑是否取反。

我们设函数\(f(x)\)表示\(min(x_0,x_1)\),\(x\)在二进制状态下0或1最少的个数。

我们设行的取反状态为\(k\),每列的最终状态就是\(sta[i]\ xor\ k\),对答案的贡献就是\(f(sta[i]\ xor\ k)\)。

所以我们构造\(g(x)\)表示初始状态为\(x\)的列的数量。答案函数\(A(x)\)表示行的取反状态为\(x\)的答案,则\(A=f*g\)。

代码:

#include<bits/stdc++.h>
#define ll long long

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

int n,m;
int s[25][100005];
ll f[1<<20],g[1<<20];
int Count(int s) {
    int ans=0;
    for(;s;s>>=1) ans+=s&1;
    return ans;
}

void FWT_xor(ll *a,int n,int flag) {
    for(int len=2;len<=n;len<<=1) {
        for(int mid=len>>1,i=0;i<n;i+=len) {
            for(int j=0;j<mid;j++) {
                ll u=a[i+j],v=a[i+j+mid];
                a[i+j]=u+v,a[i+j+mid]=u-v;
                if(flag==-1) a[i+j]/=2,a[i+j+mid]/=2;
            }
        }
    }
}

char t[100005];
int main() {
    n=Get(),m=Get();
    for(int i=1;i<=n;i++) {
        scanf("%s",t+1);
        for(int j=1;j<=m;j++)
            s[i][j]=t[j]-'0';
    }
    for(int i=1;i<=m;i++) {
        int now=0;
        for(int j=1;j<=n;j++) now=(now<<1)|s[j][i];
        g[now]++;
    }
    for(int s=0;s<(1<<n);s++) {
        f[s]=Count(s);
        f[s]=min(f[s],n-f[s]);
    }
    FWT_xor(f,1<<n,1),FWT_xor(g,1<<n,1);
    for(int i=0;i<(1<<n);i++) f[i]*=g[i];
    FWT_xor(f,1<<n,-1);
    ll ans=1e9;
    for(int i=0;i<(1<<n);i++) ans=min(ans,f[i]);
    cout<<ans;
    return 0;
}

原文地址:https://www.cnblogs.com/hchhch233/p/10046801.html

时间: 2024-11-06 09:29:34

CF 633 E. Binary Table的相关文章

【CF662C】Binary Table(FWT)

[CF662C]Binary Table(FWT) 题面 洛谷 CF 翻译: 有一个\(n*m\)的表格(\(n<=20,m<=10^5\)), 每个表格里面有一个\(0/1\), 每次可以将一行或者一列的\(01\)全部翻转 回答表格中最少有多少个\(1\) 题解 发现\(n\)很小,\(m\)很大 状压是跑不掉了 如果我们确定翻转哪些行,那么答案唯一确定(贪心的选每一列中\(0/1\)的较小值) 相同的列显然可以合并, 把每一列按照\(01\)状压,记\(a[i]\)为状态为\(i\)的列

【CF662C】Binary Table 按位处理

[CF662C]Binary Table 题意:给你一个$n\times m$的01网格,你可以进行任意次操作,每次操作是将一行或一列的数都取反,问你最多可以得到多少个1? $n\le 20,m\le 10^5$ 题解:我也不知道叫啥了,说状压也不对,说fwt也不太对,就叫按位处理得了. 显然有$O(2^nm)$暴力,先枚举每行是否取反,然后枚举每列,如果0多就取反,否则不取. 但我们发现我们完全可以将本质相同的列一起处理,什么叫本质相同的列呢?假如我们对每行是否取反的状态为S,则所有$xor

「CF662C」 Binary Table

「CF662C」 Binary Table 题目链接 题目所给的 \(n\) 很小,于是我们可以考虑这样一种朴素做法:暴力枚举第 \(i\) 行是否翻转,这样每一行的状态就确定了,这时取每一列 \(0/1\) 个数较小的数字即可(因为每一列也可以翻转).这样的时间复杂度是 \(O(m\cdot2^n)\). 但是显然这样过不了. 我们发现表格的具体行列对我们的答案是没有影响的.即我们只需要知道状态为 \(x\) 的行或者状态为 \(x\) 的列的个数即可.由于 \(n\le20\),这启发我们对

CF662C Binary Table

LINK:CF662C Binary Table 一个nm的表格 每个元素都是0/1 每次操作可以选择一行或一列 将0/1翻转.可以操作无限次. 问最终局面最少有多少个1.\(n\leq 20,m\leq 100000\) 可以发现 先翻列再翻行等价于先翻行再翻列 先翻行再翻列再翻行 如果行是相同的 等价于翻列 反之同上一种情况. 对于任意一对行列之间的关系只有上述的几种情况 故可以发现 最优操作可以转换成 先翻行再翻列. 之所以这样是发现了行数较少 暴力枚举行的状态. 此时只有列能翻了 每一列

[Codeforces]663E Binary Table

某变换好题.不过听说还有O(2^n*n^2)DP的…… Description 给定一个n*m的01矩阵,你可以选择对任意行和任意列取反,使得最终“1”的数量尽量少. Input 第一行两个整数n,m. 接下来n行,每行m个字符,描述一个01矩阵. Output 一个整数表示最少的1的数量. Sample Input 3 4 0110 1010 0111 Sample Output 2 HINT 1 <= n <= 20,1 <= m <= 100000. Solution 首先发

Codeforces663E. Binary Table

$n \leq 20,m \leq 100000$的01矩阵,可整行整列01翻转,问最少剩几个1. 一个暴力的做法是枚举$2^n$种行翻转然后$m$列扫一遍.但其实在行翻转情况确定的情况下我们只关心两个东西:某一列在行翻转后剩几个1,以及有几个这样的列.$f(i,j)$--在行翻转$j$的情况下,有$i$个1的有多少列.其实就是与$j$有$i$个位不同的有多少列.可以枚举每一个位置$p$,那么这一位上与$j$不同的状态$f(i-1,j \ \ xor \ \ 2^p)$可以加过来,但要挑去其中$

Codeforces 662C Binary Table(快速沃尔什变换)

Problem 给定一个n(≤20)*m(≤100 000)的01矩阵,每次操作可以将一行或一列取反. 求最终1的最少个数. Solution 前置技能:快速沃尔什变换(FWT). 观察到n较小,考虑\(O(2^n)\)枚举每一行选或不选. 不妨设f(x)表示行的操作状态为x时(我们可用一个二进制数表示状态),经过各种列操作后所得到的最少的1的个数. 可以\(O(m)\)再扫一遍所有列.但显然T飞了. 定义\(C_j\)表示有多少列的状态为j:\(E_k\)表示对于某一列而言,若它经过各种行操作

【CF662C】Binary Table

题目 好吧,我连板子都不会了 有一个非常显然的做法就是\(O(2^nm)\)做法就是枚举每一行的状态,之后我们贪心去看看每一列是否需要翻转就好啦 显然这个做法非常垃圾过不去 首先我们发现每一列都不超过\(20\),考虑把每一列都压成一个状态 我们考虑设一些奇怪的东西 设\(g_i\)表示行的翻转状态为\(i\)的最优解,\(f_i\)表示有多少列的状态为\(i\),\(dp_i\)表示\(i\)这个状态最少有多少个\(1\) 显然\(dp_i=min\{bit(i),n-bit(i)\}\) 我

Binary Table CodeForces - 662C (FWT)

大意: 给定$nm$大小的$01$矩阵, $1\le n\le 20,1\le m\le 1e5$, 可以任选行列翻转, 求最终$1$总数最少为多少. 显然有$O(m2^n)$的暴力算法 也就是枚举翻转哪些行, 然后对于一列, 若$1$的个数多于$0$的个数就翻转. 可以发现对于相同的列, 翻转行对它的影响是相同的. 用$a_i$记录状态为$i$的列的个数, $b_i$记录状态为$i$的列的贡献. 假设翻转行状态为$S$时答案为$f_{S}$, 枚举每种状态的列的贡献, 就有 $$f_{S}=\