POJ 1830 开关问题 高斯消元如何求自由变元

题目大意:给出n个开关的初始状态以及目标状态,按下一个开关会相应改变其它某些开关的状态(包括自己),问有多少种方案达到目标状态(每个开关最多按一次)。

 

这道题目是高斯消元求自由变元的模板题。

首先我们可以列出每个开关i的方程,(A1*t1+A2*t2+A3*t3...+An*tn) mod 2=Bi

其中当改变了第j个开关也会改变第i个开关的状态时,Aj=1,否则=0。当第i个开关的初始状态等于结束状态时,Bi=0,否则=1。

ti表示第i个开关最后有没有按,作为方程中的变量。

我们只要求解出这n个方程的自由变元数量t,答案就是2^t了。

自由变元并不是指值不确定的变量数,而是指不确定的“联通块”数。

比如x+y=2,这个时候自由变元只有1个,因为x和y是互相联系的,属于一个“联通块”,只要确定了其中一个,就可以确定另一个。

再比如x+y+z=2,自由变元就有2个。

我是这样求自由变元数的:

一开始和高斯消元步骤一样。但是往回代的时候需要判断一些东西:

如果当前需要求解的这个变量的系数为0,那么有可能增加自由变元,也有可能无解;

否则这个变量就不可能增加自由变元,虽然它可以是任意值,但是它可以由其它自由变量推出,并没有增加方案数。

#include 
#include 
#include 
#include 
#define mem(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for (i=j;i<=k;i++)
#define down(i,j,k) for (i=j;i>=k;i--)
using namespace std;
const int N=30;
int K,n,i,j,u,v,t,fs[N+5],ls[N+5],a[N+5][N+5],c[N+5];
void Swap(int u,int v)
{
	int i;
	rep(i,1,n+1) swap(a[u][i],a[v][i]);
}
void outit()
{
	int i,j;
		rep(i,1,n)
		{
			rep(j,1,n+1) printf("%d ",a[i][j]);
			printf("\n");
		}
	printf("\n");
}
int Gauss()
{
	int i,j,k,ret=0;
	rep(i,1,n-1)
	{
		rep(j,i,n)
			if (a[j][i]) break;
		if (j>n) continue;
		Swap(i,j);
		rep(j,i+1,n)
		{
			if (!a[j][i]) continue;
			rep(k,i,n+1) a[j][k]^=a[i][k];
		}
	}
//	outit();
	down(i,n,1)
	{
		rep(j,i+1,n)
		{
			if (!a[i][j]) continue;
			if (c[j]==-1) {a[i][n+1]=-1; break;}
			else a[i][n+1]^=c[j];
		}
		if (!a[i][i]) {
			if (a[i][n+1]) return -1;
			ret++; c[i]=-1;
		}
		else c[i]=a[i][n+1];
	}
	return ret;
}
int main()
{
	#ifdef ONLINE_JUDGE
	#else
		freopen("button.in","r",stdin);
		freopen("button.out","w",stdout);
	#endif
	scanf("%d",&K);
	while (K--)
	{
		mem(a); mem(c);
		scanf("%d",&n);
		rep(i,1,n) scanf("%d",&fs[i]);
		rep(i,1,n) scanf("%d",&ls[i]);
		rep(i,1,n) a[i][n+1]=fs[i]^ls[i],a[i][i]=1;
		while (1)
		{
			scanf("%d%d",&u,&v);
			if (u==0 && v==0) break;
			a[v][u]=1;
		}
	//	outit();
		t=Gauss();
		if (t==-1) printf("Oh,it's impossible~!!\n");
		else printf("%d\n",(int)pow(2.0,t));
	}
	return 0;
}

 

你可能感兴趣的:(数学)