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

本文介绍了一道经典的图论问题——如何通过最少的操作次数使图中所有节点的状态变为黑色。利用异或方程组与高斯消元法,文章详细解析了算法的设计思路与实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题意:
给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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值