BZOJ3517 翻硬币

原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3517

翻硬币

Description

有一个n行n列的棋盘,每个格子上都有一个硬币,且n为偶数。每个硬币要么是正面朝上,要么是反面朝上。每次操作你可以选定一个格子(x,y),然后将第x行和第y列的所有硬币都翻面。求将所有硬币都变成同一个面最少需要的操作数。

Input

第一行包含一个正整数n。
接下来n行,每行包含一个长度为n的01字符串,表示棋盘上硬币的状态。

Output

仅包含一行,为最少需要的操作数。

Sample Input

4
0101
1000
0010
0101

Sample Output

2

HINT
【样例说明】

对(2,3)和(3,1)进行操作,最后全变成1。

【数据规模】

对于100%的数据,n ≤ 1,000。

题解

最暴力的思路当然是列 n2 n 2 个异或方程组,然后高斯消元去接一个 n2 n 2 个方程, n n 个未知数的方程组,大概是O(n4)的。。。

我们不如拿个 2×2 2 × 2 的矩阵玩玩,设 a a 是否翻转为a取值 0 0 1 a a 的目标状态为a b,c,d b , c , d 以此类推:

那么我们可以列出四个方程:

a xor b xor c=aa xor b xor d=ba xor d xor c=cd xor b xor c=d(1)(2)(3)(4) { a   x o r   b   x o r   c = a ′ ( 1 ) a   x o r   b   x o r   d = b ′ ( 2 ) a   x o r   d   x o r   c = c ′ ( 3 ) d   x o r   b   x o r   c = d ′ ( 4 )

貌似这些式子有点奇妙??

我们把 (1)(2)(3) ( 1 ) ( 2 ) ( 3 ) 式异或起来:

a=a xor b xor c a = a ′   x o r   b ′   x o r   c ′

震惊,其他未知数都被消掉了!!!其他的式子似乎也是如此,好像我们只需要将那某个点所在的一行一列的数全部异或起来,就能知道这个点是否需要反转。

推广一下:因为 n n 是偶数,所以一定可以通过将2n1个方程异或起来得到上面的那种形状。

事情变得简单起来。

代码
#include<bits/stdc++.h>
#define val (sq[i][j]-'0')
using namespace std;
const int M=1e3+5;
int n;
bool x[M],y[M];
char sq[M][M]; 
void in()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",sq[i]+1);
        for(int j=1;j<=n;++j)x[i]^=val,y[j]^=val;
    }
}
void ac()
{
    int ans=0;
    for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)ans+=x[i]^y[j]^val^1;
    printf("%d",min(n*n-ans,ans));
}
int main()
{
    in();ac();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值