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

227 篇文章 0 订阅
97 篇文章 0 订阅

传送门

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: ITAK

Motto:

今日的我要超越昨日的我,明日的我要胜过今日的我,
以创作出更好的代码为目标,不断地超越自己。
**/

#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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值