时间复杂度O(n3)
步骤
- 枚举每一列
- 找到本列剩余行中绝对值最大的一行
- 若所有剩余行该列元素均为0,说明无解或无穷多解,直接退出循环
- 将本行换到剩余行的最上面
- 将该行当前列元素变为1
- 用该行消去其它行该列元素
- 找到本列剩余行中绝对值最大的一行
- 判断是否有唯一解
- 若退出循环后行数小于总行数,说明无解或无穷多解
- 遍历剩余行,判断是这两种情况的哪一种
- 若退出循环后行数小于总行数,说明无解或无穷多解
- 有唯一解,求解
- 从下往上依次求出xn, xn-1, …, x1
例题
883. 高斯消元解线性方程组
输入一个包含 n 个方程 n 个未知数的线性方程组。
方程组中的系数为实数。
求解这个方程组。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含 n+1 个实数,表示一个方程的 n 个系数以及等号右侧的常数。
输出格式
如果给定线性方程组存在唯一解,则输出共 n 行,其中第 i 行输出第 i 个未知数的解,结果保留两位小数。
如果给定线性方程组存在无数解,则输出 Infinite group solutions
。
如果给定线性方程组无解,则输出 No solution
。
数据范围
1≤n≤100,
所有输入系数以及常数均保留两位小数,绝对值均不超过 100。
输入样例:
3
1.00 2.00 -1.00 -6.00
2.00 1.00 -3.00 -9.00
-1.00 -1.00 2.00 7.00
输出样例:
1.00
-2.00
3.00
import java.util.*;
public class Main {
static final double esp = 1.0e-6;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
double[][] g = new double[n][n+1];
for (int i = 0; i < n; i++) {
for (int j = 0; j <= n; j++) {
g[i][j] = sc.nextDouble();
}
}
int res = gauss(g, n);
if (res == 1) System.out.println("No solution");
else if (res == 2) System.out.println("Infinite group solutions");
else {
for (int i = 0; i < n; i++) {
System.out.printf("%.2f\n", g[i][n]);
}
}
}
static int gauss(double[][] g, int n) {
int r = 0, c = 0;
for (; c < n; c++) {
int t = r;
//找到剩余行中该列值最大的一行
for (int i = r + 1; i < n; i++) {
if (Math.abs(g[i][c]) > Math.abs(g[t][c]))
t = i;
}
//如果当前列剩余所有行最大值为0,说明没有唯一解,继续处理下一行
if (Math.abs(g[t][c]) < esp) continue;
//交换t和r行
if (t != r) {
for (int i = c; i <= n; i++) {
double tmp = g[r][i];
g[r][i] = g[t][i];
g[t][i] = tmp;
}
}
//将第r行归一化
for (int i = n; i >= c; i--) {
g[r][i] /= g[r][c];
}
//消去其他行第c列元素
for (int i = r + 1; i < n; i++) {
if (Math.abs(g[i][c]) > esp) {
for (int j = n; j >= c; j--) {
g[i][j] -= g[r][j] * g[i][c];
}
}
}
//继续下一行处理
r++;
}
//判断是否有唯一解
if (r < n) {
for (int i = r; i < n; i++) {
if (Math.abs(g[i][n]) > esp) return 1;
}
return 2;
}
//有唯一解的处理
for (int i = n - 1; i >= 0; i--) {
for (int j = n - 1; j > i; j--) {
g[i][n] -= g[i][j] * g[j][n];
}
}
return 0;
}
}
注意:浮点数判断是否等于0时与一个特别小的浮点数比较。注意将待比较数转成其绝对值形式
884. 高斯消元解异或线性方程组
输入一个包含 n 个方程 n 个未知数的异或线性方程组。
方程组中的系数和常数为 0 或 1,每个未知数的取值也为 0 或 1。
求解这个方程组。
异或线性方程组示例如下:
M[1][1]x[1] ^ M[1][2]x[2] ^ … ^ M[1][n]x[n] = B[1]
M[2][1]x[1] ^ M[2][2]x[2] ^ … ^ M[2][n]x[n] = B[2]
…
M[n][1]x[1] ^ M[n][2]x[2] ^ … ^ M[n][n]x[n] = B[n]
其中 ^
表示异或(XOR),M[i][j] 表示第 i 个式子中 x[j] 的系数,B[i] 是第 i 个方程右端的常数,取值均为 0 或 1。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含 n+1 个整数 0 或 1,表示一个方程的 n 个系数以及等号右侧的常数。
输出格式
如果给定线性方程组存在唯一解,则输出共 n 行,其中第 i 行输出第 i 个未知数的解。
如果给定线性方程组存在多组解,则输出 Multiple sets of solutions
。
如果给定线性方程组无解,则输出 No solution
。
数据范围
1≤n≤100
输入样例:
3
1 1 0 1
0 1 1 0
1 0 0 1
输出样例:
1
0
0
思路:
类似于加减乘除意义上的高斯消元,只是运算符有所改变
将亦或 ^
看作是加号或减号,将 &
位与看作是乘号即可。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc= new Scanner(System.in);
int n = sc.nextInt();
int[][] g = new int[n][n + 1];
for (int i = 0; i < n; i++) {
for (int j = 0; j <= n; j++) {
g[i][j] = sc.nextInt();
}
}
int res = gauss(g, n);
if (res == 1) System.out.println("No solution");
else if (res == 2) System.out.println("Multiple sets of solutions");
else {
for (int i = 0; i < n; i++) {
System.out.println(g[i][n]);
}
}
}
static int gauss(int[][] g, int n) {
int r = 0, c = 0;
for ( ; c < n; c++) {
int t = r;
for (int i = r + 1; i < n; i++) {
if (g[i][c] == 1) t = i;
}
if (g[t][c] == 0) continue;
if (r != t)
for (int i = c; i <= n; i++) {
int tmp = g[r][i];
g[r][i] = g[t][i];
g[t][i] = tmp;
}
for (int i = r + 1; i < n; i++) {
if (g[i][c] != 0) {
for (int j = n; j >= c; j--) {
g[i][j] ^= g[i][c] & g[r][j];
}
}
}
r++;
}
if (r < n) {
for (int i = r; i < n; i++) {
if (g[i][n] != 0) return 1;
}
return 2;
}
for (int i = n - 1; i >= 0; i--) {
for (int j = i + 1; j < n; j++) {
g[i][n] ^= g[i][j] & g[j][n];
}
}
return 0;
}
}