时间复杂度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。
输入样例:
31.00 2.00 -1.00 -6.002.00 1.00 -3.00 -9.00-1.00 -1.00 2.00 7.00
输出样例:
1.00-2.003.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
输入样例:
31 1 0 10 1 1 01 0 0 1
输出样例:
100
思路:
类似于加减乘除意义上的高斯消元,只是运算符有所改变
将亦或 ^ 看作是加号或减号,将 & 位与看作是乘号即可。
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;}}
