BZOJ1770: [Usaco2009 Nov]lights 燈(异或方程组)

来源:互联网 发布:java token编译原理 编辑:程序博客网 时间:2024/05/19 17:26

传送门

题意:
给n个点,m条边,一开始点全为白,每次改变一个点及其相邻点的黑白状态,求把所有点变黑的最小步数。n35,m395

题解:
首先题目保证了有解,不用考虑无解的情况。

考虑建出异或方程组,每个方程表示相邻点点亮/不点亮异或起来和为1,然后gauss消元,枚举自由变量的所有状态即可。

这里说明为什么暴力枚举是可行的:
考虑先把所有自由变量置为0,那么答案最多就是主元的个数,所以枚举的自由变量个数不超过主元个数个。这里可以看出这个复杂度是折中的(2n2)。

#include<bits/stdc++.h>using namespace std;inline int read(){    char ch=getchar();int i=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}    return i*f; } const int Maxn=40;int n,m,h[Maxn][Maxn],p[Maxn],cp,ans,a[Maxn];inline void gauss(){    for(int i=1;i<=n;i++){        for(int j=cp+1;j<=n;j++){            if(h[j][i]){                p[++cp]=i;                for(int k=i;k<=n+1;k++)swap(h[cp][k],h[j][k]);                for(int k=cp+1;k<=n;k++){                    if(!h[k][i])continue;                    for(int z=i;z<=n+1;++z)h[k][z]^=h[cp][z];                }                break;            }        }    }    for(int i=1;i<=cp;i++)        for(int j=1;j<=cp;j++)            swap(h[j][i],h[j][p[i]]);}inline int calc(){    int res=0;     for(int i=cp;i>=1;i--){        int t=h[i][n+1];        for(int j=i+1;j<=n;j++)t^=(a[j]*h[i][j]);        res+=(a[i]=t);    }    return res;}inline void dfs(int now,int ct){    if(ct>=ans)return;    if(now==n+1){ans=min(ans,calc()+ct);return;}     a[now]=1;dfs(now+1,ct+1);    a[now]=0;dfs(now+1,ct);}int main(){    n=read(),m=read();    for(int i=1;i<=n;i++)h[i][n+1]=(h[i][i]=1);    for(int i=1;i<=m;i++){        int x=read(),y=read();        h[x][y]=(h[y][x]=1);    }    gauss();ans=cp;    dfs(cp+1,0);printf("%d\n",ans);}
原创粉丝点击