开关问题 POJ 1830(高斯消元求解的个数)

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

题目描述:中文题,POJ上的描述是:有N个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态如果原来为开就变为关,如果为关就变为开。你的目标是经过若干次开关操作后使得最后N个开关达到一个特定的状态。对于任意一个开关,最多只能进行一次开关操作。你的任务是,计算有多少种可以达到指定状态的方法。( 不计开关操作的顺序)

思路:在做另一道类似的题目时跑来先做这道更好理解的题题了。第一次做出高斯消元这类题目,这两天抽空看了网上很多高斯消元相关介绍,也算是对其有了较浅的认识,今天打算再做三道这方面的题目就睡觉。这道题给了初末时刻所有开关的状态,分别设S[i] 和 E[i] 分别为开关i的初末状态,若E[i] ^ S[i] == 1则说明开关i的状态发生了变化。设A[i][j] == 1表示开关j可以控制开关i,注意A[i][i] == 1(每个开关都可以控制自身),X[i]表示是否打开或关闭开关i。则E[i] ^ S[i] == A[i][1] * X[1] ^ A[i][2] * X[2]  ^ ... ^ A[i][n] * X[n],A[i][j] * X[j]表示只有第j个开关能控制第i个开关并且对第j个开关进行状态修改时第i个开关的状态才会发生变化,所有控制i灯的灯异或就是i等最后的状态。 

这样就列如下等式(每个元素的值只有可能为0或1):

E[1] ^ S[1]  =  A[1][1] * X[1] ^ A[1][2] * X[2]  ^ ... ^ A[1][n] * X[n]

E[2] ^ S[2]  =  A[2][1] * X[1] ^ A[2][2] * X[2]  ^ ... ^ A[2][n] * X[n]

 ......

E[n] ^ S[n]  =  A[n][1] * X[1] ^ A[n][2] * X[2]  ^ ... ^ A[n][n] * X[n]

即:E^S = A^X

这样就变成了求解线性方程组解的个数的问题,这样就可以用高斯消元做了。具体步骤是首先在增广矩阵中找到第一个第一列非0的行,将其与第一行交换,然后将其他行进行消元(本题就是将其他行与第一行对应元素进行异或操作),看下一列(若第一列元素全为0,则直接处理下一列)。直到将增广矩阵的倒数第二列也处理完后,如果有解,此时系数矩阵(增广矩阵最后一列)应该全为0,否则则无解。第一次做有关高斯消元的题目,大一时现代也没好好学,有些地方可能说的不好或不恰当,欢迎指正或赐教。

代码如下:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-6;
const int  maxn = 29 + 10;
const int mod = 10;
const int dx[] = {1, -1, 0, 0};
const int dy[] = {0, 0, -1, 1};
const int Dis[] = {-1, 1, -5, 5};
const int inf = 0x3f3f3f3f;
int n, m, k;
int s[maxn], e[maxn];
int a[maxn][maxn];//a[i][j]表示第j个开关对第i个开关的控制情况,为1则说明j控制i,为0则不控制
int Gauss(){
    int i, j, r, x, y;
    for(i = 1, j = 1; i <= n && j <= n; ++j){
        r = i;
        while(r <= n && !a[r][j]) ++r;//找到第j列第一个非零元素所在行
        if(a[r][j]){//若该列存在非零元素
            swap(a[i], a[r]);//将非零元素所在的行交换到当前行第i行
            for(x = 1; x <= n; ++x){//除当前行第i行外,若其他n-1行中有第j列元素非零的行,利用第i行对该行进行消元(此题中即进行异或操作)
                if(x != i && a[x][j]){
                    for(y = i; y <= n + 1; ++y) a[x][y] ^= a[i][y];
                }
            }
            ++i;//继续处理下一行
        }
    }
    for(x = i; x <= n; ++x){//若最后增广列没有全部变成0,则说明该增广矩阵无解
        if(a[x][n + 1]) return -1;
    }
    return 1 << (n - i + 1);//自由变量个数为n - i + 1个,解的个数就是1<<(n - i - 1)
}
int main(){
    int t;
    scanf("%d", &t);
    while(t--){
        scanf("%d", &n);
        memset(a, 0, sizeof a);
        for(int i = 1; i <= n; ++i) scanf("%d", &s[i]);//开关的初始状态
        for(int i = 1; i <= n; ++i){
            scanf("%d", &e[i]);//开关的末状态
            a[i][i] = 1;//每个开关可以控制其自身
            a[i][n + 1] = s[i] ^ e[i];
        }
        int u, v;
        while(~scanf("%d%d", &u, &v) && u && v){
            a[v][u] = 1;
        }
        int ans = Gauss();
        if(ans == -1) printf("Oh,it's impossible~!!\n");
        else printf("%d\n", ans);
    }
    return 0;
}

/*

2
3
0 0 0
1 1 1
1 2
1 3
2 1
2 3
3 1
3 2
0 0
3
0 0 0
1 0 1
1 2
2 1
0 0

*/


你可能感兴趣的:(数学,——,高斯消元,OJ,——,POJ)