POJ1830 开关问题【高斯消元法】

题目连接:

http://poj.org/problem?id=1830


题目大意:

有N个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的

时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态

如果原来为开就变为关,如果为关就变为开。你的目标是经过若干次开关操作后使得最

后N个开关达到一个特定的状态。对于任意一个开关,最多只能进行一次开关操作。现

在计算有多少种可以达到指定状态的方法。


解题思路:

对于每个灯有改变或者不改变两种状态,用 1、0 来表示。构造矩阵 A[][],A[i][j] 表示操

作第 i 个开关,第 j 个开关的状态也跟着改变。那么就可以建立 N 个方程组,有开关间

的关系可以确定方程组各变元的系数。N 个开关的开始状态和最终状态的亦或,作为方

程组的常数值,然后用高斯消元法解方程组,求解出是否有解。

无解输出 Oh,it's impossible~!! ,有解计算出变元数目,因为每个变元都有0 和 1两种

可能,所以结果为 1 << ans。


AC代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int MAXN = 40;

int Equ,Var;
int A[MAXN][MAXN];
int X[MAXN];
bool FreeX[MAXN];
int FreeNum;

void Debug()
{
    for(int i = 0; i < Equ; ++i)
    {
        for(int j = 0; j < Var+1; ++j)
            cout << A[i][j] << ' ';
        cout << endl;
    }
}

int GCD(int a,int b)
{
    if(b == 0)
        return a;
    return GCD(b,a%b);
}

int LCM(int a,int b)
{
    return a / GCD(a,b) * b;
}

int Gauss()
{
    int i,j,k;
    int MaxRow;
    int col = 0;
    int Lcm;
    int ta,tb;
    int temp;
    int FreeXNum;
    int FreeIndex;

    for(k = 0; k < Equ && col < Var; ++k,++col)
    {
        MaxRow = k;
        for(i = k+1; i < Equ; ++i)
        {
            if(abs(A[i][col]) > abs(A[MaxRow][col]))
                MaxRow = i;
        }
        if(MaxRow != k)
        {
            for(i = k; i < Var+1; ++i)
                swap(A[k][i],A[MaxRow][i]);
        }
        if(A[k][col] == 0)
        {
            k--;
            continue;
        }
        for(i = k+1; i < Equ; ++i)
        {
            if(A[i][col])
            {
                Lcm = LCM(abs(A[i][col]),abs(A[k][col]));
                ta = Lcm / abs(A[i][col]);
                tb = Lcm / abs(A[k][col]);
                if(A[i][col] * A[k][col] < 0)
                    tb = -tb;
                for(j = col; j < Var+1; ++j)
                    A[i][j] = (A[i][j]*ta - A[k][j]*tb + 2) % 2;
            }
        }
    }

    for(i = k; i < Equ; ++i)
        if(A[i][col])
            return -1;

    if(k < Var)
        return Var - k;
    for(i = Var-1; i >= 0; --i)
    {
        temp = A[i][Var] % 2;
        for(j = i+1; j < Var; ++j)
            if(A[i][j])
                temp = (temp - A[i][j]*X[j]%2 + 2) % 2;
        if(temp % A[i][i])
            return -2;
        X[i] = temp / A[i][i] % 2;
    }
    return 0;
}

void Init(int N)
{
    memset(A,0,sizeof(A));
    memset(X,0,sizeof(X));
    Equ = Var = N;
    for(int i = 0; i < N; ++i)
        A[i][i] = 1;
}

int Start[MAXN],End[MAXN];

int main()
{
    int T,N;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&N);
        for(int i = 0; i < N; ++i)
            scanf("%d",&Start[i]);
        for(int i = 0; i < N; ++i)
            scanf("%d",&End[i]);
        Init(N);
        int u,v;
        while(~scanf("%d%d",&u,&v) && (u||v))
        {
            A[v-1][u-1] = 1;
        }
        for(int i = 0; i < N; ++i)
            A[i][N] = (Start[i] - End[i] + 2) % 2;
        int ans = Gauss();
        if(ans == -1)
            printf("Oh,it's impossible~!!\n");
        else
            printf("%d\n",(1<<ans));
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值