• POJ 1830 开关问题 【01矩阵 高斯消元】


    任意门:http://poj.org/problem?id=1830

    开关问题

    Time Limit: 1000MS

     

    Memory Limit: 30000K

    Total Submissions: 10742

     

    Accepted: 4314

    Description

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

    Input

    输入第一行有一个数K,表示以下有K组测试数据。 
    每组测试数据的格式如下: 
    第一行 一个数N(0 < N < 29) 
    第二行 N个0或者1的数,表示开始时N个开关状态。 
    第三行 N个0或者1的数,表示操作结束后N个开关的状态。 
    接下来 每行两个数I J,表示如果操作第 I 个开关,第J个开关的状态也会变化。每组数据以 0 0 结束。 

    Output

    如果有可行方法,输出总数,否则输出“Oh,it's impossible~!!” 不包括引号

    Sample Input

    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
    

    Sample Output

    4
    Oh,it's impossible~!!
    

    Hint

    第一组数据的说明: 
    一共以下四种方法: 
    操作开关1 
    操作开关2 
    操作开关3 
    操作开关1、2、3 (不记顺序) 

    题意概括:

    如题。

    解题思路:

    根据开关之间的关系可以构造一个0,1矩阵,然后通过求解这个矩阵,相加模2(即异或操作),求解线性方程组。

    一个自由元即产生 2 种可能性,假设最后解的方程组存在ans个自由元 ,方案数就是 2 的 ans 次幂;

    如何构造这样一个0,1增广矩阵呢?

    设方程个数为 equ 个, 未知数个数为 var 个,我们最终构造出来的是一个 equ*(var+1)的0,1矩阵。 

    最后一列 a [ i ][ var + 1 ] 很容易 就是 初始状态 st [ i ] ^ 最终状态 ed [ i ];

    而前面的系数矩阵呢?

    其实是一个 N*N 的矩阵,可以把开关之间的关系理解成图的边,这个系数矩阵就是这个图的邻接矩阵。

    构造出了增广矩阵,接下来的就是交给高斯消元去求解这个方程组了。

    Ac code:

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <algorithm>
     4 #include <cstring>
     5 #include <cmath>
     6 #include <map>
     7 #define LL long long
     8 #define INF 0x3f3f3f3f
     9 using namespace std;
    10 const int MAXN = 35;
    11 int N, cnt;
    12 
    13 int a[MAXN][MAXN];      //增广矩阵
    14 int x[MAXN];            //解集
    15 bool freeX[MAXN];       //标记自由元
    16 int free_num;           //自由元个数
    17 int st[MAXN];           //记录初始状态
    18 int ed[MAXN];           //记录最终状态
    19 
    20 int Gauss(int equ, int var)
    21 {
    22     int maxRow, col, k;
    23     free_num = 0;
    24     for(k = 0, col = 0; k < equ && col < var; k++, col++){
    25         maxRow = k;
    26         for(int i = k+1; i < equ; i++){                     //寻找当前列绝对值最大的一行
    27             if(abs(a[i][col]) > abs(a[maxRow][col])){
    28                 maxRow = i;
    29             }
    30         }
    31         if(a[maxRow][col] == 0){                //表示当前列绝对值最大的已经是0了,说明该列下面的全部都是0
    32             k--;
    33             freeX[free_num++] = col;
    34             continue;
    35         }
    36         if(maxRow != k){                       //绝对值最大的一行与当前行交换
    37             for(int j = col; j < var+1; j++){
    38                 swap(a[k][j] , a[maxRow][j]);
    39             }
    40         }
    41         for(int i = k+1; i < equ; i++){         //以绝对值最大的一行为标准对其他行进行消元
    42             if(a[i][col] != 0){
    43                 for(int j = col; j < var+1; j++){
    44                     a[i][j] ^= a[k][j];
    45                 }
    46             }
    47         }
    48     }
    49     for(int i = k; i < equ; i++)        //判断是否无解
    50         if(a[i][col] != 0)
    51         return -1;
    52 
    53     if(k < var){
    54         return var-k;   //自由元的个数
    55     }
    56     //唯一解,回代
    57     for(int i = var-1; i >= 0; i--){
    58         x[i] = a[i][var];
    59         for(int j = i+1; j < var; j++){
    60             x[i] ^= (a[i][j] && x[j]);
    61         }
    62     }
    63     return 0;
    64 }
    65 
    66 int main()
    67 {
    68     int T_case;
    69     int u, v;
    70     scanf("%d", &T_case);
    71     while(T_case--){
    72         scanf("%d", &N);
    73         for(int i = 0; i < N; i++){
    74             scanf("%d", &st[i]);
    75         }
    76         for(int i = 0; i < N; i++){
    77             scanf("%d", &ed[i]);
    78         }
    79         memset(a, 0, sizeof(a));
    80         memset(x, 0, sizeof(x));
    81                                                         //构造增广矩阵
    82         for(int i = 0; i < N; i++){                     //本开关肯定对本开关有影响
    83             a[i][i] = 1;
    84         }
    85 
    86         while(~scanf("%d%d", &u, &v) && (u+v)){         //构造对除自身外开关的影响,自身是系数也是未知量
    87             a[v-1][u-1] = 1;
    88         }
    89         for(int i = 0; i < N; i++){                     //结果
    90             a[i][N] = st[i]^ed[i];                      //这里异或相当于对2取模运算
    91         }
    92         int ans = Gauss(N, N);
    93         if(ans == -1) printf("Oh,it's impossible~!!
    ");    //无解
    94         else printf("%d
    ", 1<<ans);                        //2的ans次方,因为有ans个自由元
    95     }
    96     return 0;
    97 }
    View Code
  • 相关阅读:
    THINKphp学习笔记
    Js获取当前日期时间及其它操作
    Oracle数据导入导出imp/eXP
    SQL中的单记录函数
    Windows服务C#/VS2003
    oracle数据库开发的一些经验积累
    Oracle 数据库的安全策略
    高兴
    无法正确运行的C#程序
    最详细的Visual C++ 2008 Express Edition使用方法(图文)
  • 原文地址:https://www.cnblogs.com/ymzjj/p/9967783.html
Copyright © 2020-2023  润新知