zoukankan      html  css  js  c++  java
  • (模板)poj1681 高斯消元法求异或方程组(无解、唯一解、多解)

    题目链接:https://vjudge.net/problem/POJ-1681

    题意:类似于poj1222,有n×n的01矩阵,翻转一个点会翻转其上下左右包括自己的点,求最少翻转多少点能使得矩阵全0。

    思路:

      同样的可以枚举第一行的状态,这里不说了。

      用高斯消元法来解这道题,每个点的状态表示一个变量,那么有n*n个方程,n*n个变量的方程组,用高斯消元法来解,可能存在无解,唯一解,多解的情况。多解的时候要枚举自由变元的状态。

    AC代码:

    /*
        poj1681
        开关问题,高斯消元法解异或方程组
        求最少要翻转的开关使得矩阵全0
        存在无解,唯一解,多解的情况
        多解时要枚举自由变元的状态
       */
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    using namespace std;
    
    const int maxn=250;
    const int inf=0x3f3f3f3f;
    int T,n,equ,var,a[maxn][maxn],x[maxn],free_xx[maxn];
    int ans;
    char s[20];
    
    void init(){    //初始化
        memset(a,0,sizeof(a));
        for(int i=0;i<n;++i){
            for(int j=0;j<n;++j){
                int t=i*n+j;
                a[t][t]=1;
                if(i>0) a[t][(i-1)*n+j]=1;
                if(i<n-1) a[t][(i+1)*n+j]=1;
                if(j>0) a[t][i*n+j-1]=1;
                if(j<n-1) a[t][i*n+j+1]=1;
            }
        }
    }
    
    int Gauss(){
        int r=0,cnt=0;  //cnt表示自由变元个数
        for(int c=0;r<equ&&c<var;++r,++c){
            int Maxr=r;
            for(int i=r+1;i<equ;++i)
                if(abs(a[i][c])>abs(a[Maxr][c])) 
                    Maxr=i;
            if(Maxr!=r){
                for(int i=c;i<var+1;++i)
                    swap(a[Maxr][i],a[r][i]);
            }
            if(!a[r][c]){
                --r;
                free_xx[cnt++]=c;
                continue;
            }
            for(int i=r+1;i<equ;++i){
                if(!a[i][c]) continue;
                for(int j=c;j<var+1;++j)
                    a[i][j]^=a[r][j];
            }
        }
        for(int i=r;i<equ;++i)
            if(a[i][var])
                return -1;    //无解
        return var-r;         //返回自由变元的个数,cnt=var-r
    }
    
    int solve(int t){
        ans=inf;
        for(int i=0;i<(1<<t);++i){  //枚举自由变元的状态
            int cnt=0;        //要翻转的个数
            memset(x,0,sizeof(x));
            for(int j=0;j<t;++j){
                if((i>>j)&1){
                    ++cnt;
                    x[free_xx[j]]=1;
                }
            }
            for(int j=var-t-1;j>=0;--j){
                int tmp=a[j][var],tp,ok=1;
                for(int k=j;k<var;++k){
                    if(!a[j][k]) continue;
                    if(ok){   //找主元
                        ok=0;
                        tp=k;
                    }
                    else{ 
                        tmp^=x[k];
                    }
                }
                x[tp]=tmp;
                cnt+=x[tp];
            }
            ans=min(ans,cnt);  //取最小
        }
        return ans;
    }
    
    int main(){
        scanf("%d",&T);
        while(T--){
            scanf("%d",&n);
            init();
            equ=var=n*n;
            for(int i=0;i<n;++i){
                scanf("%s",s);
                for(int j=0;j<n;++j)
                    if(s[j]=='y') a[i*n+j][n*n]=0;
                    else a[i*n+j][n*n]=1;
            }
            int t=Gauss();
            if(t==-1)
                printf("inf
    ");
            else 
                printf("%d
    ",solve(t));
        }
        return 0;
    }
  • 相关阅读:
    3月18
    线段树求后继+环——cf1237D
    排序+stl——cf1237C
    思维+双指针+环——cf1244F
    模拟+双指针——cf1244E
    树的性质——cf1244D
    数学思维——cf1244C
    树的直径变形——cf1238F
    ac自动机暴力跳fail匹配——hdu5880
    状态压缩dp增量统计贡献——cf1238E(好题)
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/11770528.html
Copyright © 2011-2022 走看看