• [BZOJ 3517]翻硬币


    3517: 翻硬币

    Time Limit: 1 Sec  Memory Limit: 128 MB
    Submit: 289  Solved: 219
    [Submit][Status][Discuss]

    Description

    有一个nn列的棋盘,每个格子上都有一个硬币,且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。

     

    Source

    题解

    第一眼看起来怕是个高斯消元解异或方程组...然而这样会搞出 $n^2$ 个方程, 消元过程又需要 $O(n^3)$ 的时间复杂度, 总时间复杂度会达到 $O(n^6)$ , 在 $nleq 1000$ 的数据下不超时才是见鬼...

    仔细观察方程特征:

    如果我们设格子 $(a,b)$ 翻还是不翻为 $x_{a,b}$ , 初始状态设为 $d_{a,b}$ , 则可以得到形如下的方程:

    [left( igoplus_{j=1}^n x_{a,j} ight)oplus left (igoplus_{i=1}^n x_{i,b} ight)oplus d_{a,b}oplus x_{a,b}=0]

    然后进行变形, 将 $d_{a,b}$ 移到左端, 得到:

    [left( igoplus_{j=1}^n x_{a,j} ight)oplus left (igoplus_{i=1}^n x_{i,b} ight)oplus x_{a,b}=d_{a,b}]

    然后我们发现这 $n^2$ 个方程中有大量的重复项, 再加上异或运算的特殊性, 我们将有重复项的相关方程互相异或抵消掉相同项, 最后剩下的情况大概是形如这个样子的:

    [x_{a,b}=left( igoplus_{j=1}^n d_{a,j} ight)oplus left (igoplus_{i=1}^n d_{i,b} ight)oplus d_{a,b}]

    这样可以优化到单次求值 $O(n)$ , 对于 $n^2$ 个未知数总时间复杂度为 $O(n^3)$ (然而依然跑不过)

    但是我们可以发现都是某行某列的异或和, 读入时顺便求一下异或和存到某个数组就可以了.

    最终总时间复杂度 $O(n^2)$

    参考代码

    GitHub

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <cstdlib>
     4 #include <iostream>
     5 #include <algorithm>
     6 
     7 const int MAXN=1010;
     8 
     9 int n;
    10 char buf[MAXN];
    11 int xorSumx[MAXN];
    12 int xorSumy[MAXN];
    13 int data[MAXN][MAXN];
    14 
    15 int main(){
    16     scanf("%d",&n);
    17     for(int i=0;i<n;i++){
    18         scanf("%s",buf);
    19         for(int j=0;j<n;j++){
    20             data[i][j]=buf[j]-'0';
    21             // printf("%d
    ",data[i][j]);
    22             xorSumx[i]^=data[i][j];
    23             xorSumy[j]^=data[i][j];
    24             // printf("%d %d
    ",xorSumx[i],xorSumy[j]);
    25         }
    26     }
    27     int ans=0;
    28     for(int i=0;i<n;i++){
    29         for(int j=0;j<n;j++){
    30             ans+=data[i][j]^xorSumx[i]^xorSumy[j];
    31         }
    32     }
    33     printf("%d
    ",std::min(n*n-ans,ans));
    34     return 0;
    35 }
    Backup

     

  • 相关阅读:
    IDEA快捷键
    关于redis key命名规范的设计 【转载】
    Mybatis数据源与连接池 【转载】
    通过缓冲区读文件和非缓冲区读文件有什么区别
    BCB6 使用正则表达式的例子
    QT在子窗口外单击关闭子窗口
    QT5 中文乱码
    QT 鼠标右键菜单
    关于线程池的几个问题
    关于ConcurentHashMap的几个问题
  • 原文地址:https://www.cnblogs.com/rvalue/p/7683727.html
Copyright © 2020-2023  润新知