HDU 3364 Lanterns(高斯消元入门题目——开关问题)

来源:互联网 发布:淘宝新娘饰品 编辑:程序博客网 时间:2024/06/05 08:19

传送门

Lanterns

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1582    Accepted Submission(s): 621


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.
Then k integers follow between 1 and n inclusive indicating the lantern controlled by this switch.
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
 

题目大意:

就是给了 n 个灯 m 个开关,每个开关能够控制很多灯,当然了每个灯也可以由多种开关控制。现在给了你每个开关能够控制的灯的标号,

然后给你一个最终状态,让你求的是能够达到这个最终状态的方法数(初始状态都是关着的,开着是 1,关着是 0).

解题思路:

这个题目还是挺有意思的,因为有 n 个灯也就意味着我们要列 n 个方程, m 个开关就是 m 个未知数,那么现在有了 n 个方程 m

未知数就好做了,首先通过输入的开关控制的灯可以确定初始的 a(系数)矩阵,注意的是 a 矩阵的列和行的变化。然后通过给定的最终状态

来确定 ax = b 的 列矩阵 b,然后就正常高消就行了,还有需要注意的一点是:它给定的 Q 个询问的时候,我们要提前将 a 矩阵保存

下来否则就会一直 跟我一样 WA,我调了很久才调出来的。。。因为高消之后 a 矩阵就变了,所以我们就将 a 矩阵保存,这样就没什么问题了。

My Code

/**2016 - 09 - 13 上午Author: ITAKMotto:今日的我要超越昨日的我,明日的我要胜过今日的我,以创作出更好的代码为目标,不断地超越自己。**/#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <vector>#include <queue>#include <algorithm>#include <set>using namespace std;typedef long long LL;typedef unsigned long long ULL;const int INF = 1e9+5;const int MAXN = 400;const int MOD = 1e9+7;const double eps = 1e-7;const double PI = acos(-1);using namespace std;LL Scan_LL()///输入外挂{    LL res=0,ch,flag=0;    if((ch=getchar())=='-')        flag=1;    else if(ch>='0'&&ch<='9')        res=ch-'0';    while((ch=getchar())>='0'&&ch<='9')        res=res*10+ch-'0';    return flag?-res:res;}int Scan_Int()///输入外挂{    int res=0,ch,flag=0;    if((ch=getchar())=='-')        flag=1;    else if(ch>='0'&&ch<='9')        res=ch-'0';    while((ch=getchar())>='0'&&ch<='9')        res=res*10+ch-'0';    return flag?-res:res;}void Out(LL a)///输出外挂{    if(a>9)        Out(a/10);    putchar(a%10+'0');}int equ, var;///equ个方程 var个变量int a[MAXN][MAXN];///增广矩阵int x[MAXN];///解集int x_i[MAXN];bool free_x[MAXN];///判断是不是自由变元int free_num;///自由变元的个数int Gauss(){    int Max_r;///当前列绝对值最大的存在的行    ///col:处理当前的列    int row,col = 0;    int free_x_num;    int free_index;    free_num = 0;    for(int i=0; i<=var; i++)    {        x[i] = 0;        free_x[i] = 1;    }    for(row=0; row<equ&&col<var; row++,col++)    {        Max_r = row;        for(int i=row+1; i<equ; i++)            if(abs(a[i][col]) > abs(a[Max_r][col]))                Max_r = i;        if(a[Max_r][col] == 0)        {            free_x[col] = 1;            x_i[free_num++] = col;            row--;            continue;        }        if(Max_r != row)            for(int i=col; i<var+1; i++)                swap(a[row][i], a[Max_r][i]);        ///消元        for(int i=row+1; i<equ; i++)            if(a[i][col])                for(int j=col; j<var+1; j++)                    a[i][j] ^= a[row][j];    }    for(int i=row; i<equ; i++)        if(a[i][col])            return -1;///无解    if(row < var)        return var - row;///自由变元的个数    ///回代,得到解集    for(int i=var-1; i>=0; i--)    {        x[i] = a[i][var];        for(int j=i+1; j<var; j++)            x[i] ^= (a[i][j] && x[j]);    }    return 0;///唯一解}void Debug(){    puts("");    cout<<"+++++++++++++++++++++++++++分界线++++++++++++++++++++++++++++++"<<endl;    for(int i=0; i<equ; i++)    {        for(int j=0; j<var+1; j++)        {            cout<<a[i][j]<<" ";        }        cout<<endl;    }    cout<<"+++++++++++++++++++++++++++分界线++++++++++++++++++++++++++++++"<<endl;    puts("");}int b[MAXN][MAXN];int main(){    int T;    T = Scan_Int();    for(int cas=1; cas<=T; cas++)    {        int n, m;        n = Scan_Int();        m = Scan_Int();        equ = n;        var = m;        memset(a, 0, sizeof(a));        for(int i=0; i<m; i++)        {            int k;            k = Scan_Int();            while(k--)            {                int val;                val = Scan_Int();                a[val-1][i] = 1;            }        }        /**注意 a 矩阵是改变的!!!**/        for(int i=0; i<equ; i++)            for(int j=0; j<var; j++)                b[i][j] = a[i][j];        int Q;        Q = Scan_Int();        printf("Case %d:\n",cas);        while(Q--)        {            for(int i=0; i<equ; i++)                for(int j=0; j<var; j++)                    a[i][j] = b[i][j];            for(int i=0; i<n; i++)                a[i][var] = Scan_Int();            int tmp = Gauss();            ///Debug();            if(tmp == -1)                puts("0");            else                printf("%I64d\n",(1LL<<tmp));        }    }    return 0;}
0 0
原创粉丝点击