时间复杂度O(n3)

高斯消元.pdf

步骤

  1. 枚举每一列
    • 找到本列剩余行中绝对值最大的一行
      • 若所有剩余行该列元素均为0,说明无解或无穷多解,直接退出循环
    • 将本行换到剩余行的最上面
    • 将该行当前列元素变为1
    • 用该行消去其它行该列元素
  2. 判断是否有唯一解
    • 若退出循环后行数小于总行数,说明无解或无穷多解
      • 遍历剩余行,判断是这两种情况的哪一种
  3. 有唯一解,求解
    • 从下往上依次求出xn, xn-1, …, x1

例题

883. 高斯消元解线性方程组
输入一个包含 n 个方程 n 个未知数的线性方程组。
方程组中的系数为实数。
求解这个方程组。

输入格式
第一行包含整数 n。
接下来 n 行,每行包含 n+1 个实数,表示一个方程的 n 个系数以及等号右侧的常数。
输出格式
如果给定线性方程组存在唯一解,则输出共 n 行,其中第 i 行输出第 i 个未知数的解,结果保留两位小数。
如果给定线性方程组存在无数解,则输出 Infinite group solutions
如果给定线性方程组无解,则输出 No solution
数据范围
1≤n≤100,
所有输入系数以及常数均保留两位小数,绝对值均不超过 100。
输入样例:

  1. 3
  2. 1.00 2.00 -1.00 -6.00
  3. 2.00 1.00 -3.00 -9.00
  4. -1.00 -1.00 2.00 7.00

输出样例:

  1. 1.00
  2. -2.00
  3. 3.00
  1. import java.util.*;
  2. public class Main {
  3. static final double esp = 1.0e-6;
  4. public static void main(String[] args) {
  5. Scanner sc = new Scanner(System.in);
  6. int n = sc.nextInt();
  7. double[][] g = new double[n][n+1];
  8. for (int i = 0; i < n; i++) {
  9. for (int j = 0; j <= n; j++) {
  10. g[i][j] = sc.nextDouble();
  11. }
  12. }
  13. int res = gauss(g, n);
  14. if (res == 1) System.out.println("No solution");
  15. else if (res == 2) System.out.println("Infinite group solutions");
  16. else {
  17. for (int i = 0; i < n; i++) {
  18. System.out.printf("%.2f\n", g[i][n]);
  19. }
  20. }
  21. }
  22. static int gauss(double[][] g, int n) {
  23. int r = 0, c = 0;
  24. for (; c < n; c++) {
  25. int t = r;
  26. //找到剩余行中该列值最大的一行
  27. for (int i = r + 1; i < n; i++) {
  28. if (Math.abs(g[i][c]) > Math.abs(g[t][c]))
  29. t = i;
  30. }
  31. //如果当前列剩余所有行最大值为0,说明没有唯一解,继续处理下一行
  32. if (Math.abs(g[t][c]) < esp) continue;
  33. //交换t和r行
  34. if (t != r) {
  35. for (int i = c; i <= n; i++) {
  36. double tmp = g[r][i];
  37. g[r][i] = g[t][i];
  38. g[t][i] = tmp;
  39. }
  40. }
  41. //将第r行归一化
  42. for (int i = n; i >= c; i--) {
  43. g[r][i] /= g[r][c];
  44. }
  45. //消去其他行第c列元素
  46. for (int i = r + 1; i < n; i++) {
  47. if (Math.abs(g[i][c]) > esp) {
  48. for (int j = n; j >= c; j--) {
  49. g[i][j] -= g[r][j] * g[i][c];
  50. }
  51. }
  52. }
  53. //继续下一行处理
  54. r++;
  55. }
  56. //判断是否有唯一解
  57. if (r < n) {
  58. for (int i = r; i < n; i++) {
  59. if (Math.abs(g[i][n]) > esp) return 1;
  60. }
  61. return 2;
  62. }
  63. //有唯一解的处理
  64. for (int i = n - 1; i >= 0; i--) {
  65. for (int j = n - 1; j > i; j--) {
  66. g[i][n] -= g[i][j] * g[j][n];
  67. }
  68. }
  69. return 0;
  70. }
  71. }

注意:浮点数判断是否等于0时与一个特别小的浮点数比较。注意将待比较数转成其绝对值形式

884. 高斯消元解异或线性方程组
输入一个包含 n 个方程 n 个未知数的异或线性方程组。
方程组中的系数和常数为 0 或 1,每个未知数的取值也为 0 或 1。
求解这个方程组。
异或线性方程组示例如下:

  1. M[1][1]x[1] ^ M[1][2]x[2] ^ ^ M[1][n]x[n] = B[1]
  2. M[2][1]x[1] ^ M[2][2]x[2] ^ ^ M[2][n]x[n] = B[2]
  3. 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
输入样例:

  1. 3
  2. 1 1 0 1
  3. 0 1 1 0
  4. 1 0 0 1

输出样例:

  1. 1
  2. 0
  3. 0

思路:
类似于加减乘除意义上的高斯消元,只是运算符有所改变
将亦或 ^ 看作是加号或减号,将 & 位与看作是乘号即可。

  1. import java.util.*;
  2. public class Main {
  3. public static void main(String[] args) {
  4. Scanner sc= new Scanner(System.in);
  5. int n = sc.nextInt();
  6. int[][] g = new int[n][n + 1];
  7. for (int i = 0; i < n; i++) {
  8. for (int j = 0; j <= n; j++) {
  9. g[i][j] = sc.nextInt();
  10. }
  11. }
  12. int res = gauss(g, n);
  13. if (res == 1) System.out.println("No solution");
  14. else if (res == 2) System.out.println("Multiple sets of solutions");
  15. else {
  16. for (int i = 0; i < n; i++) {
  17. System.out.println(g[i][n]);
  18. }
  19. }
  20. }
  21. static int gauss(int[][] g, int n) {
  22. int r = 0, c = 0;
  23. for ( ; c < n; c++) {
  24. int t = r;
  25. for (int i = r + 1; i < n; i++) {
  26. if (g[i][c] == 1) t = i;
  27. }
  28. if (g[t][c] == 0) continue;
  29. if (r != t)
  30. for (int i = c; i <= n; i++) {
  31. int tmp = g[r][i];
  32. g[r][i] = g[t][i];
  33. g[t][i] = tmp;
  34. }
  35. for (int i = r + 1; i < n; i++) {
  36. if (g[i][c] != 0) {
  37. for (int j = n; j >= c; j--) {
  38. g[i][j] ^= g[i][c] & g[r][j];
  39. }
  40. }
  41. }
  42. r++;
  43. }
  44. if (r < n) {
  45. for (int i = r; i < n; i++) {
  46. if (g[i][n] != 0) return 1;
  47. }
  48. return 2;
  49. }
  50. for (int i = n - 1; i >= 0; i--) {
  51. for (int j = i + 1; j < n; j++) {
  52. g[i][n] ^= g[i][j] & g[j][n];
  53. }
  54. }
  55. return 0;
  56. }
  57. }