Poj 1830 开关问题:高斯消元
刷了好几道高斯消元的题,感觉掌握的应该差不多了,这道还算比较经典。(感觉高斯消元就是模板题)
题目描述:多组数据,N个开关,给出N个开关操作前和操作后的状态,再给出有联系的开关,每行两个数I J,表示如果操作第 I 个开关,第J个开关的状态也会变化。
解法:高斯消元,建立一个N*N的方程组,由开关间的状态可以确定方程组的系数,方程组的bn为初状态和末状态异或得出。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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的方程组。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }