HDU3364 Lanterns

来源:互联网 发布:数据整合系统sci 编辑:程序博客网 时间:2024/06/01 09:15

http://blog.sina.com.cn/s/blog_4a0c4e5d01019gwc.html

Problem Description
Alice has received a beautiful present from Bob. The present contains n lanterns and m switches. Each switch controls some lanterns and pushing the switch will change the state of all lanterns it controls from off to on or from on to off. A lantern may be controlled by many switches. At the beginning, all the lanterns are off.
每一个开关都可以控制一些灯笼,并且按下这个开关将会改变所有它控制的灯的状态(从开到关,从关到开)。一个灯会被多个开关控制。在最初的时候,所有的灯都是关着的。

Alice wants to change the state of the lanterns to some specific configurations and she knows that pushing a switch more than once is pointless. Help Alice to find out the number of ways she can achieve the goal. Two ways are different if and only if the sets (including the empty set) of the switches been pushed are different.

Input
The first line contains an integer T (T<=5) indicating the number of test cases.
The first line of each test case contains an integer n (1<=n<=50) and m (1<=m<=50).
Then m lines follow. Each line contains an integer k (k<=n) indicating the number of lanterns this switch controls.
每一行有一个整数k,表示这个开关控制灯的数量。
Then k integers follow between 1 and n inclusive indicating the lantern controlled by this switch.
接下来的k的整数(在1~n之间)表示开关控制的哪个灯。
The next line contains an integer Q (1<=Q<=1000) represent the number of queries of this test case.
询问。
Q lines follows. Each line contains n integers and the i-th integer indicating that the state (1 for on and 0 for off) of the i-th lantern of this query.
灯最后的状态。

Output
For each test case, print the case number in the first line. Then output one line containing the answer for each query.
Please follow the format of the sample output.

Sample Input
2
3 2
2 1 2
2 1 3
2
0 1 1
1 1 1
3 3
0
0
0
2
0 0 0
1 0 0

Sample Output
Case 1:
1
0
Case 2:
8
0

为什么能和高斯消元结合起来?
将每个开关控制每个灯列成行列式?怎么列?
最终的状态是结果列(这句话比较有启发)

(a1x1)^(a2x2)…(amxm)=bi,
bi即最终状态,当第i盏灯与第j个开关相关,aj=1,否则aj=0.(0是异或的单位元)

解出来的x是什么?

这里写图片描述

第一列第一个数是代表第一个灯x1可以控制,为0代表不可以控制;第二列第一个数代表第一个灯可以控制,为0代表不可以控制…所以x1,x2有解代表开关的状态可以使灯达到最后的状态。

接下来,该如何解抑或方程?

如果你要解某个变量 i 就找到一行 i 的系数不为0的交换到当前行(如果当前行都没有这个元素,就解不了了(上面普通方程求解的时候也可以这样)),然后找到其他当前变量的系数不为0的,异或之(每个系数都异或,包括常数),最后也能化成标准或上三角矩阵,从而求解。

矩阵中的系数通常只有0和1了,表示的是如果改变列元素是否会对行元素有影响,例如打开开关1可以让灯2和灯3改变状态,那么a(1,2)=a(1,3)=1,这样就可以通过2,3的状态,求出是不是改变了开关1。(可能还是不太懂….就是我们列的方程成了已知灯的前后状态,求解这些开关是怎么用的,那么我们的已知量就是灯与开关的关系(系数)&灯的状态(常数项),未知量是开关是否使用,大致是一个a(1,1)*x[1] xor a(1,2)*x[2] xor…xor a(1,n)*x[n] 来求解x[1..n]的过程,还是看不懂就看我的另一篇做题的博客好啦)。上面大概介绍了下方程的建立方法,那么我们在解方程的时候我们的消元方法就要变成异或消元了,利用的是1 xor 1=0,如果你要解某个变量 i 就找到一行 i 的系数不为0的交换到当前行(如果当前行都没有这个元素,就解不了了(上面普通方程求解的时候也可以这样)),然后找到其他当前变量的系数不为0的,异或之(每个系数都异或,包括常数),最后也能化成标准或上三角矩阵,从而求解。

还要特殊说明的是在异或方程中如果知道了方程的自由元数量就可以知道总共有多少组解啦!因为只有1或0两种可能嘛,所以答案就是2^n(n为自由元的数量)

#include<iostream>#include<cstdio> #include<cstring>#include<algorithm>#define LL long long using namespace std;int t,n,m,a[55][55],r[55][55];long long gauss(){    int i,row=1;    for(int col=1;col<=m;col++)//高斯消元一列一列的     {        for(i=row;i<=n;i++)            if(a[i][col]) break;//找见一个是1的        if(i>n)continue;        if(i!=row)        {            for(int j=col;j<=m+1;++j)   //基准行和这一行每一列都换了                 swap(a[row][j],a[i][j]);        }         for(i=row+1;i<=n;i++)//从下一行开始高斯消元             if(a[i][col])            {                for(int j=col;j<=m+1;j++)                {                    a[i][j]^=a[row][j];                }            }        ++row;//第二个     }    for(i=row;i<=n;i++)//左边都为0了,右边还>0,则无解         if(a[i][m+1]) return 0;//    return 1LL<<(long long)(m-row+1); //几个未知数-解出几个未知数,因为row是从1开始的,因此m-row+1个自由元 } int main(){    int q,x,c,test=0;     scanf("%d",&t);    while(t--)    {        printf("Case %d:\n",++test);                scanf("%d%d",&n,&m);//m几个开关         memset(a,0,sizeof a);        for(int i=1;i<=m;i++)        {            scanf("%d",&c);            for(int j=1;j<=c;j++)//一共有几列             {                scanf("%d",&x);                a[x][i]=1;             }        }        scanf("%d",&q);        for(int k=1;k<=q;k++)        {            for(int i=1;i<=n;i++)                scanf("%d",&a[i][m+1]);            memcpy(r,a,sizeof a);            printf("%I64d\n",gauss());            memcpy(a,r,sizeof a);        }    }}
0 0
原创粉丝点击