• 玩诈欺的小杉——异或优化的状压dp


    玩诈欺的小杉

    是这样的,在小杉的面前有一个N行M列的棋盘,棋盘上有N*M个有黑白棋的棋子(一面为黑,一面为白),一开始都是白面朝上。
      小杉可以对任意一个格子进行至多一次的操作(最多进行N*M个操作),该操作使得与该格同列的上下各2个格子以及与该格同行的左右各1个格子以及该格子本身翻面。
      例如,对于一个5*5的棋盘,仅对第三行第三列的格子进行该操作,得到如下棋盘(0表示白面向上,1表示黑面向上)。

      00100
      00100
      01110
      00100
      00100

      对一个棋盘进行适当的操作,使得初始棋盘(都是白面朝上)变成已给出的目标棋盘的操作集合称作一个解法。
      小杉的任务是对给出的目标棋盘求出所有解法的总数。

    每组测试数据的第一行有3个正整数,分别是N和M和T(1<=N,M<=20,1<=T<=5)
      接下来T个目标棋盘,每个目标棋盘N行,每行M个整数之前没有空格且非0即1,表示目标棋盘(0表示白面朝上,1表示黑面朝上)
    两个目标棋盘之间有一个空行。
      特别地,对于30%的数据,有1<=N,M<=15

    对每组数据输出T行,每行一个整数,表示能使初始棋盘达到目标棋盘的解法总数

    输入:

    4 4 2

    0010

    0010

    0111

    0010

     

    0010

    0110

    0111

    0010

    输出:

    1

    1

    【样例解释】
    对于输入的数据,两个目标棋盘各有一种解法
    1:
    0000
    0000
    0010
    0000
    2:
    1011
    1101
    0111
    1011
    其中1表示对该格进行操作,0表示不操作

    分析:

    这种类似棋盘的东西其实很容易让我们想到状压,对比两个方向的状压,我们会倾向于选择一列一列地推过去,因为这样问题更加简单。所以我们可以很轻松地和普通的状压一样打出此题,但是显然的时间复杂度O(2nNMT)会超时,此时我们应试着研究转移过程。我们会发现,0与1的翻转其实就是异或的过程,在上述做法中我们一一枚举了每一列的各个数。现在我们来考虑转化为异或来将复杂度缩去一个N,对于"十字架"两端可以直接异或,而对于中间那一长条我们要稍微处理一下,将这一串进行移动再累次异或即可。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define int long long
    #define R register
    #define ld long double
    #define debug printf("zxt
    ")
    inline int read(){
        int a=0,b=1;char c=getchar();
        while(!isdigit(c)){if(c=='-')b=-1;c=getchar();}
        while(isdigit(c)){a=a*10+c-'0';c=getchar();}
        return a*b;
    }
    const int N=50;
    int n,m,T,a[N],b[N],al,ans;
    char s[N];
    signed main(){
        n=read();m=read();
        T=read();
        while(T--){
            for(R int j=1;j<=m;j++){
                a[j]=0;
            }
            for(R int i=1;i<=n;i++){
                scanf("%s",s);
                for(R int j=1;j<=m;j++){
                    a[j]=a[j]*2+s[j-1]-'0';
                }
            }
            al=(1<<n)-1;ans=0;
            for(a[0]=0;a[0]<=al;a[0]++){
                for(R int i=0;i<=m;i++)b[i]=a[i];
                for(R int i=1;i<=m;i++){
                    b[i]=(b[i]^(b[i-1]<<2)^(b[i-1]<<1)^b[i-1]^(b[i-1]>>1)^(b[i-1]>>2))&al;
                    b[i+1]=(b[i-1]^b[i+1])&al;
                }
                if((b[m]&al)==0)ans++;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
     
  • 相关阅读:
    糖尿病周围神经病变有什么表现
    天空之城
    Software Quality Assurance Framework(2)
    组织行为学2
    Software Quality Assurance Framework(1)
    radiculously
    组织行为学1
    software Architecture(1)
    c++运算符重载
    get up~!
  • 原文地址:https://www.cnblogs.com/zjy1412/p/13472379.html
Copyright © 2020-2023  润新知