题目
图论中的树为一个无环的无向图。给定一棵树,每个节点有一盏指示灯和一个按钮。如果节点的按扭被按了,那么该节点的灯会从熄灭变为点亮(当按之前是熄灭的),或者从点亮到熄灭(当按之前是点亮的)。并且该节点的直接邻居也发生同样的变化。
开始的时候,所有的指示灯都是熄灭的。请编程计算最少要按多少次按钮,才能让所有节点的指示灯变为点亮状态。
分析
可以用x[i]表示第i个按钮按还是不按。
利用边来确定系数,总共得到n个未知数,n个方程。
然后就是用高斯消元求解。
经过高斯消元后,可能会出现自由元,2^S(S为自由元的个数)枚举自由元的选择,计算答案。
ps:数据是真的水,如果有一堆自由元那就超时了,这本来是一道树形dp题。。。
code
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int x[1000];
struct arr{
int x,y;
int next;
}edge[1000000];
int edge_m;
int ls[1000];
int du[1000];
int n,m;
void add(int x,int y)
{
edge_m++;
du[x]++;
edge[edge_m]=(arr){x,y,ls[x]};ls[x]=edge_m;
edge_m++;
du[y]++;
edge[edge_m]=(arr){y,x,ls[y]};ls[y]=edge_m;
}
int b[1000];
int a[1000][1000];
int g[1000];
int ans[1000];
int calc(int x)
{
int cnt=0;
for (int i=1;i<=n;i++) ans[i]=b[i];
for (int i=1;i<=g[0];i++)
if (x&(1<<(i-1)))
{
cnt++;
for (int j=1;j<=n;j++)
if (a[j][g[i]]) ans[j]=(!ans[j]);
}
for (int i=1;i<=n;i++) cnt+=ans[i];
return cnt;
}
int main()
{
scanf("%d",&n);
while (n!=0){
m=n-1;
memset(a,0,sizeof(a));
memset(x,0,sizeof(x));
memset(g,0,sizeof(g));
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
a[x][y]=1; a[y][x]=1;
}
for (int i=1;i<=n;i++)
{
b[i]=1;
a[i][i]=1;
}
for (int i=1;i<=n;i++)
{
int k;
int c=0;
for (k=i;k<=n;k++)
if (a[k][i])
break;
if (k==(n+1))
{
g[++g[0]]=i;
continue;
}
for (int j=1;j<=n;j++)
{
c=a[i][j];
a[i][j]=a[k][j];
a[k][j]=c;
}
c=b[i]; b[i]=b[k]; b[k]=c;
for (int j=1;j<=n;j++)
{
if ((j==i)||(!a[j][i])) continue;
b[j]^=b[i];
for (int l=1;l<=n;l++)
a[j][l]^=a[i][l];
}
}
int ans=200000000;
for (int i=0;i<(1<<g[0]);i++) ans=min(ans,calc(i));
printf("%d\n",ans);
scanf("%d",&n);
}
}