题意:
给n个点,m条边,一开始点全为白,每次改变一个点及其相邻点的黑白状态,求把所有点变黑的最小步数。
n≤35,m≤395
题解:
首先题目保证了有解,不用考虑无解的情况。
考虑建出异或方程组,每个方程表示相邻点点亮/不点亮异或起来和为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);
}