poj 1830 开关问题 (高斯消元)

题目链接:http://poj.org/problem?id=1830

一,题意:

该题是中文提,题意我就不解释了。

二,解析:

该题我们先的建立一个图,我们以开关为节点,若开关x的变化会影响y开关,则连一条x到y的有向边。

然后我们用邻接矩阵来存储该图。则邻接矩阵中0表示不影响,1表示反转。

例如:样例一, 开始状态为 :begin = [ 0  0   0 ]    最终状态为:end = [ 1  1  1 ]    图的矩阵为:


对角线为1的原因是当你改变某一个节点是除了与他相邻的节点要反转外,其自身也要变。

将矩阵每一列想象为一个开关按下后产生的效果(1表示状态翻转,0表示不变)

即:将每一列最上面想象有一个开关,如图


其中X1,X2,X3  只能取  0或1,因为取2的效果与取0的效果是一样的。

X1=1表示将开关1取反。  X1=0表示不对开关1做操作。

所以我要求的就是 [ x1   x2   x3 ]有多少种情况。

我们令:X=[ x1,x2,x3 ]。关系矩阵  A=[ a1,a2,a3 ]。a1为列向量。



由上面公式可知,b1为开关1的变化次数,b2为开关2变化次数。

而起点 begin 到终点 end 的变化就是  begin^end 即 begin ^ end = b

所以就是解线性方程组(如图):求期解的个数。


解该方程 我们用到高斯消元算法,至于高斯消元就是利用矩阵来解方程组,但是每次

被选取为主元的是一定是最大值。这是为了减少误差。至于利用矩阵来解方程是线性代数

上的内容,,这里就不多说。


代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
int b[35];//状态变化数组
int A[35][35];//运算的矩阵
void Gaussian(int N)
{//对N*(N+1)的增广矩阵做初等行变换
    for (int r=0,col=0; r<N-1&&col<=N;)
    {//行列循环
        if (A[r][col]==0)
        {//交换两行(找主元)
            int i;
            for (i=r+1; i<N; i++)
            {//从下一行开始找
                if (A[i][col] == 1)
                {//由于每个格子只有两种状态0,1
                    for (int j=col; j<=N; j++)
                    {//行交换
                        swap(A[i][j], A[r][j]);
                    }
                    break;
                }
            }
            if (i==N)
            {//如果这一列下面所有的都为0
                col++;
                continue;
            }
        }
        for (int r2=r+1; r2<N; r2++)
        {//用第r行对下面的行消元
            if (A[r2][col]==1)
                for(int j=col; j<=N; j++)
                    A[r2][j]^=A[r][j];
        }
        r++;
        col++;
    }
}
bool Judge(int *x,int start,int End,int val)
{//判断矩阵第i行是否全为0
    for(int i=start; i<End; i++)
        if(x[i]!=val)
            return false;
    return true;
}

int calc(int N)
{//利用行阶梯矩阵解方程
    for (int i=0; i<N; i++)
    {
        if (Judge(A[i],0,N,0))
        {//判断系数矩阵第i行是否为0行
            for (int j=i; j<N; j++)
            {//如果第行为0行则下面全是0
                if (A[j][N]==1) //如果有0=d的情况则无解
                    return -1;
            }
            return 1<<(N-i);//有解,且有N-i个自由变量
        }
    }
    return 1;
}
int main()
{
    int K, N;
    scanf("%d",&K);//K组测试数据
    for (int i=0; i<K; i++)
    {
        scanf("%d", &N);//N个开关
        memset(b,0,sizeof(b));
        memset(A,0,sizeof(A));
        for (int j=0; j<N; j++)
            scanf("%d",&b[j]);
        for (int j=0,tmp; j<N; j++)
        {//b是初始和最终的异或,即状态的变化
            scanf("%d", &tmp);
            b[j]^=tmp;
        }
        int s,t;
        while (scanf("%d%d",&s,&t)!=EOF&&s!=0)
            A[t-1][s-1]=1;//存入加转置
        for (int j=0; j<N; j++)
        {//将b放到最后一列构成增广矩阵
            A[j][j]=1;//对角线
            A[j][N]=b[j];
        }
        Gaussian(N);
        int res=calc(N);
        if (res < 0)
            printf("Oh,it's impossible~!!\n");
        else
            printf("%d\n", res);
    }
    return 0;
}












评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值