zoukankan      html  css  js  c++  java
  • Poj 1830 开关问题:高斯消元

    刷了好几道高斯消元的题,感觉掌握的应该差不多了,这道还算比较经典。(感觉高斯消元就是模板题)

    题目描述:多组数据,N个开关,给出N个开关操作前和操作后的状态,再给出有联系的开关,每行两个数I J,表示如果操作第 I 个开关,第J个开关的状态也会变化。

    解法:高斯消元,建立一个N*N的方程组,由开关间的状态可以确定方程组的系数,方程组的bn为初状态和末状态异或得出。

    View Code
     1 Source Code
    2
    3 Problem: 1830 User: celia01
    4 Memory: 704K Time: 0MS
    5 Language: G++ Result: Accepted
    6
    7 Source Code
    8 #include<iostream>
    9 #include<cstdio>
    10 #include<cstring>
    11 #include<cmath>
    12 #include<algorithm>
    13 #define N 35
    14 using namespace std;
    15 int equ, var;
    16 int a[N][N];
    17 bool free_x[N];
    18 int Gauss(){
    19 int i, j, k;
    20 int max_r, col;
    21 int temp;
    22 int free_x_num;
    23 int free_index;
    24 for(k=0,col=0;k<equ&&col<var;k++,col++){
    25 max_r = k;
    26 for(i=k+1;i<equ;i++){
    27 if(abs(a[i][col]>abs(a[max_r][col]))) max_r = i;
    28 }
    29 if(a[max_r][col]==0){
    30 k--;continue;
    31 }
    32 if(max_r!=k){
    33 for(j=col;j<var+1;j++) swap(a[k][j],a[max_r][j]);
    34 }
    35 for(i=k+1;i<equ;i++){
    36 if(a[i][col]){
    37 for(j=col;j<var+1;j++){
    38 a[i][j] ^= a[k][j];//cout<<"ok!\n";
    39 }
    40 }
    41 }
    42 }
    43
    44 for(i=k;i<equ;i++){
    45 if(a[i][col]) return -1;
    46 }
    47 return var-k;
    48 }
    49 int first[N], last[N];
    50
    51 int main(){
    52 int t, i, j, k, n, ans;
    53 scanf("%d",&t);
    54 while(t--){
    55 scanf("%d",&n);
    56 var = equ = n;
    57 memset(a,0,sizeof(a));
    58 for(i=0;i<n;i++) scanf("%d",&first[i]);
    59 for(i=0;i<n;i++){
    60 scanf("%d",&last[i]);
    61 a[i][i] = 1;
    62 a[i][var] = first[i]^last[i];
    63 }
    64 while(scanf("%d%d",&i,&j)&&(i||j)){
    65 a[j-1][i-1] = 1;
    66 }
    67 ans = Gauss();
    68 if(ans==-1){
    69 printf("Oh,it's impossible~!!\n");
    70 }
    71 else{
    72 printf("%.0f\n",pow(2.0,ans));
    73 }
    74 }
    75 return 0;
    76 }

    高斯消元主要还是建立方程组,类比于开关,如果有N个,则建立N*N的方程组,比如poj1222,就要建立30*30的方程组。

    附:常用模板
      1 #include <iostream>
    2 #include <string>
    3 #include <cmath>
    4 using namespace std;
    5
    6 const int maxn = 105;
    7
    8 int equ, var; // 有equ个方程,var个变元。增广阵行数为equ, 分别为0到equ - 1,列数为var + 1,分别为0到var.
    9 int a[maxn][maxn];
    10 int x[maxn]; // 解集.
    11 bool free_x[maxn]; // 判断是否是不确定的变元.
    12 int free_num;
    13
    14 void Debug(void)
    15 {
    16 int i, j;
    17 for (i = 0; i < equ; i++)
    18 {
    19 for (j = 0; j < var + 1; j++)
    20 {
    21 cout << a[i][j] << " ";
    22 }
    23 cout << endl;
    24 }
    25 cout << endl;
    26 }
    27
    28 inline int gcd(int a, int b)
    29 {
    30 int t;
    31 while (b != 0)
    32 {
    33 t = b;
    34 b = a % b;
    35 a = t;
    36 }
    37 return a;
    38 }
    39
    40 inline int lcm(int a, int b)
    41 {
    42 return a * b / gcd(a, b);
    43 }
    44 // 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解,-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数)
    45 int Gauss(void)
    46 {
    47 int i, j, k;
    48 int max_r; // 当前这列绝对值最大的行.
    49 int col; // 当前处理的列.
    50 int ta, tb;
    51 int LCM;
    52 int temp;
    53 int free_x_num;
    54 int free_index;
    55 // 转换为阶梯阵.
    56 col = 0; // 当前处理的列.
    57 for (k = 0; k < equ && col < var; k++, col++)
    58 { // 枚举当前处理的行.
    59 // 找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差)
    60 max_r = k;
    61 for (i = k + 1; i < equ; i++)
    62 {
    63 if (abs(a[i][col]) > abs(a[max_r][col])) max_r = i;
    64 }
    65 if (a[max_r][col] == 0)
    66 { // 说明该col列第k行以下全是0了,则处理当前行的下一列.
    67 k--; continue;
    68 }
    69 if (max_r != k)
    70 { // 与第k行交换.
    71 for (j = col; j < var + 1; j++) swap(a[k][j], a[max_r][j]);
    72 }
    73 for (i = k + 1; i < equ; i++)
    74 { // 枚举要删去的行.
    75 if (a[i][col] != 0)
    76 {
    77 LCM = lcm(abs(a[i][col]), abs(a[k][col]));
    78 ta = LCM / abs(a[i][col]), tb = LCM / abs(a[k][col]);
    79 if (a[i][col] * a[k][col] < 0) tb = -tb; // 异号的情况是两个数相加.
    80 for (j = col; j < var + 1; j++)
    81 {
    82 a[i][j] = a[i][j] * ta - a[k][j] * tb;
    83 }
    84 }
    85 }
    86 }
    87 Debug();
    88 // 1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0).
    89 for (i = k; i < equ; i++)
    90 { // 对于无穷解来说,如果要判断哪些是自由变元,那么初等行变换中的交换就会影响,则要记录交换.
    91 if (a[i][col] != 0) return -1;
    92 }
    93 // 2. 无穷解的情况: 在var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵.
    94 // 且出现的行数即为自由变元的个数.
    95 if (k < var)
    96 {
    97 // 首先,自由变元有var - k个,即不确定的变元至少有var - k个.
    98 for (i = k - 1; i >= 0; i--)
    99 {
    100 // 第i行一定不会是(0, 0, ..., 0)的情况,因为这样的行是在第k行到第equ行.
    101 // 同样,第i行一定不会是(0, 0, ..., a), a != 0的情况,这样的无解的.
    102 free_x_num = 0; // 用于判断该行中的不确定的变元的个数,如果超过1个,则无法求解,它们仍然为不确定的变元.
    103 for (j = 0; j < var; j++)
    104 {
    105 if (a[i][j] != 0 && free_x[j]) free_x_num++, free_index = j;
    106 }
    107 if (free_x_num > 1) continue; // 无法求解出确定的变元.
    108 // 说明就只有一个不确定的变元free_index,那么可以求解出该变元,且该变元是确定的.
    109 temp = a[i][var];
    110 for (j = 0; j < var; j++)
    111 {
    112 if (a[i][j] != 0 && j != free_index) temp -= a[i][j] * x[j];
    113 }
    114 x[free_index] = temp / a[i][free_index]; // 求出该变元.
    115 free_x[free_index] = 0; // 该变元是确定的.
    116 }
    117 return var - k; // 自由变元有var - k个.
    118 }
    119 // 3. 唯一解的情况: 在var * (var + 1)的增广阵中形成严格的上三角阵.
    120 // 计算出Xn-1, Xn-2 ... X0.
    121 for (i = var - 1; i >= 0; i--)
    122 {
    123 temp = a[i][var];
    124 for (j = i + 1; j < var; j++)
    125 {
    126 if (a[i][j] != 0) temp -= a[i][j] * x[j];
    127 }
    128 if (temp % a[i][i] != 0) return -2; // 说明有浮点数解,但无整数解.
    129 x[i] = temp / a[i][i];
    130 }
    131 return 0;
    132 }
    133
    134 int main(void)
    135 {
    136 freopen("Input.txt", "r", stdin);
    137 int i, j;
    138 while (scanf("%d %d", &equ, &var) != EOF)
    139 {
    140 memset(a, 0, sizeof(a));
    141 memset(x, 0, sizeof(x));
    142 memset(free_x, 1, sizeof(free_x)); // 一开始全是不确定的变元.
    143 for (i = 0; i < equ; i++)
    144 {
    145 for (j = 0; j < var + 1; j++)
    146 {
    147 scanf("%d", &a[i][j]);
    148 }
    149 }
    150 // Debug();
    151 free_num = Gauss();
    152 if (free_num == -1) printf("无解!\n");
    153 else if (free_num == -2) printf("有浮点数解,无整数解!\n");
    154 else if (free_num > 0)
    155 {
    156 printf("无穷多解! 自由变元个数为%d\n", free_num);
    157 for (i = 0; i < var; i++)
    158 {
    159 if (free_x[i]) printf("x%d 是不确定的\n", i + 1);
    160 else printf("x%d: %d\n", i + 1, x[i]);
    161 }
    162 }
    163 else
    164 {
    165 for (i = 0; i < var; i++)
    166 {
    167 printf("x%d: %d\n", i + 1, x[i]);
    168 }
    169 }
    170 printf("\n");
    171 }
    172 return 0;
    173 }
  • 相关阅读:
    Java正式day_06——数组排序
    别只知道策略模式+简单工厂,试试更香的策略模式+抽象工厂!
    图解连接阿里云(一)创建阿里云物联网平台产品和设备,使用MQTT.fx快速体验
    嵌入式交叉编译GDB,结合vscode图形化调试C和C++代码 coredump定位段错误
    内核链表之list_for_eacy_entry手绘图解
    makefile实验三 理解make工作的基本原则
    玩转Libmodbus(一) 搭建开发环境
    RT-Thread的C语言多态风格展示
    C++函数默认参数 详解
    杂类-边学边记
  • 原文地址:https://www.cnblogs.com/celia01/p/2435523.html
Copyright © 2011-2022 走看看