洛谷端文章:https://www.luogu.com.cn/article/n5rjrsdw,如无法访问访问https://www.luogu.com.cn/article/n5rjrsdw。
这个算法可以求 n n n 元一次方程组的解。
举个例子,现在已知有三元一次方程:
{ x + 2 y + 3 z = 36 − x + y + 2 z = 17 x + 2 z = 17 \begin{cases} x + 2y + 3z = 36 \\ -x + y + 2z = 17 \\ x + 2z = 17 \end{cases} ⎩ ⎨ ⎧x+2y+3z=36−x+y+2z=17x+2z=17
解得:
{ x = 3 y = 6 z = 7 \begin{cases} x = 3 \\ y = 6 \\ z = 7 \end{cases} ⎩ ⎨ ⎧x=3y=6z=7
将问题扩展为 n n n 个未知数的方程组:
{ a 1 , 1 x 1 + a 1 , 2 x 2 + ⋯ + a 1 , n x n = b 1 a 2 , 1 x 1 + a 2 , 2 x 2 + ⋯ + a 2 , n x n = b 2 ⋮ a n , 1 x 1 + a n , 2 x 2 + ⋯ + a n , n x n = b n \begin{cases} a_{1,1}x_1 + a_{1,2}x_2 + \cdots + a_{1,n}x_n = b_1 \\ a_{2,1}x_1 + a_{2,2}x_2 + \cdots + a_{2,n}x_n = b_2 \\ \vdots \\ a_{n,1}x_1 + a_{n,2}x_2 + \cdots + a_{n,n}x_n = b_n \end{cases} ⎩ ⎨ ⎧a1,1x1+a1,2x2+⋯+a1,nxn=b1a2,1x1+a2,2x2+⋯+a2,nxn=b2⋮an,1x1+an,2x2+⋯+an,nxn=bn
对应的矩阵表示为:
[ a 1 , 1 a 1 , 2 ⋯ a 1 , n b 1 a 2 , 1 a 2 , 2 ⋯ a 2 , n b 2 ⋮ ⋮ ⋱ ⋮ ⋮ a n , 1 a n , 2 ⋯ a n , n b n ] \begin{bmatrix} a_{1,1} & a_{1,2} & \cdots & a_{1,n} & b_1 \\ a_{2,1} & a_{2,2} & \cdots & a_{2,n} & b_2 \\ \vdots & \vdots & \ddots & \vdots & \vdots \\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} & b_n \\ \end{bmatrix} a1,1a2,1⋮an,1a1,2a2,2⋮an,2⋯⋯⋱⋯a1,na2,n⋮an,nb1b2⋮bn
我们以刚才的例子。
初始矩阵:
[ 1 2 3 36 − 1 1 2 17 1 0 2 17 ] \begin{bmatrix} 1 & 2 & 3 & 36 \\ -1 & 1 & 2 & 17 \\ 1 & 0 & 2 & 17 \\ \end{bmatrix} 1−11210322361717
消去第二、三行的第一列:
[ 1 2 3 36 0 3 5 53 0 − 2 − 1 − 19 ] \begin{bmatrix} 1 & 2 & 3 & 36 \\ 0 & 3 & 5 & 53 \\ 0 & -2 & -1 & -19 \\ \end{bmatrix} 10023−235−13653−19
第二行归一化并消去其他行:
[ 1 0 − 1 3 14 3 0 1 5 3 53 3 0 0 7 3 49 3 ] \begin{bmatrix} 1 & 0 & -\frac{1}{3} & \frac{14}{3} \\ 0 & 1 & \frac{5}{3} & \frac{53}{3} \\ 0 & 0 & \frac{7}{3} & \frac{49}{3} \\ \end{bmatrix} 100010−313537314353349
第三行归一化并回代:
[ 1 0 0 3 0 1 0 6 0 0 1 7 ] \begin{bmatrix} 1 & 0 & 0 & 3 \\ 0 & 1 & 0 & 6 \\ 0 & 0 & 1 & 7 \\ \end{bmatrix} 100010001367
时间复杂度 O ( n 3 ) \text{O}(n^3) O(n3)。
形如这样的,方程一样但是答案不一样,比如:
[ 3 7 5 10 3 7 5 20 ] \begin{bmatrix} 3 & 7 & 5 & 10 \\ 3 & 7 & 5 & 20 \\ \end{bmatrix} [3377551020]
是不行的。
还有形如这样的:
[ 0 0 0 1 − 4 4 1 13 ] \begin{bmatrix} 0 & 0 & 0 & 1 \\ -4 & 4 & 1 & 13 \\ \end{bmatrix} [0−40401113]
存在矛盾方程 0 x + 0 y + 0 z ≠ 0 0x+0y+0z \ne 0 0x+0y+0z=0,方程组无解。
一般情况:一整行的系数都为 0 0 0,答案不为 0 0 0。
[ 0 1 2 18 0 6 − 3 4 ] \begin{bmatrix} 0 & 1 & 2 & 18 \\ 0 & 6 & -3 & 4 \\ \end{bmatrix} [00162−3184]
第一列全零,自由变量存在,方程组有无穷多解。
形如这样的,一整列的系数都为 0 0 0,说明这个数没有参与任何方程。是多少都无所谓,与答案没有关联。
#include
using namespace std;
const int N=160;
const double eps=1e-6;
double a[N][N];
int n;
int gauss()
{
int c,r;//C:当前处理X_c,R:处理了 r 个未知数
for(c=0,r=0;c<n;c++)
{
int t=r;
for(int i=r;i<n;i++)
{
if(fabs(a[i][c])>fabs(a[t][c]))//如果这个系数更大
{
t=i;//更新
}
}
if(fabs(a[t][c])<eps)
{//如果最大的数还是 0,则代表所有方程这个项的系数都是 0。
continue;//就不管
}
for(int i=c;i<n+1;i++)
{
swap(a[r][i],a[t][i]);//将其交换至第 c 行
}
for(int i=n;i>=c;i--)
{
a[r][i]/=a[r][c];//除以系数
}
for(int i=0;i<n;i++)//消消乐
{
if(fabs(a[i][c])>eps && i!=r)//如果这一项系数不为 0 且不是系数最大的一行
{
for(int j=n;j>=c;j--)
{
a[i][j]-=a[i][c]*a[r][j];//消元
}
}
}
r++;//新处理了一个未知数
}
if(r<n)//如果没有处理完所有的未知数
{
for(int i=r;i<n;i++)//检查剩下的方程
{
if(fabs(a[i][n])>eps)
{//如果有最终答案>=0说明无解
return -1;
}
}
return 0;//反之无解
}
return 1;//有唯一解
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<=n;j++)
{
cin>>a[i][j];
}
}
int ans=gauss();
if(ans==1)
{
for(int i=0;i<n;i++)
{
printf("x%d=%.2f\n",i+1,a[i][n]);
}
}
else
{
cout<<ans;
}
return 0;
}
这个 gauss \operatorname{gauss} gauss 是经典void
类型的。
#include
using namespace std;
const int MAXN=100;
int n;
double a[MAXN+5][MAXN+5];
void gauss(int x)
{
for(int i=0;i<x;i++)
{
int row=i;
for(int j=i;j<x;j++)
{
if(fabs(a[row][i])<fabs(a[j][i]))
{
row=j;
}
}
if(row!=i)
{
swap(a[row],a[i]);
}
double div1=a[i][i];
for(int j=0;j<=x;j++)
{
a[i][j]/=div1;
}
for(int j=i+1;j<x;j++)
{
double div2=a[j][i];
for(int k=0;k<=x;k++)
{
a[j][k]-=a[i][k]*div2;
}
}
}
//-----------------------------复原
for(int i=x-1;i>=0;i--)
{
for(int j=i-1;j>=0;j--)
{
a[j][x]-=a[j][i]*a[i][x];
a[j][i]=0;
}
}
//------------------------------
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<=n;j++)
{
cin>>a[i][j];
}
}
gauss(n);
for(int i=0;i<n;i++)
{
bool flag=1;
for(int j=0;j<n;j++)
{
if(fabs(a[i][j])>1e-8)
{
flag=0;
}
}
if(flag)
{
cout<<"No Solution";
return 0;
}
}
for(int i=0;i<n;i++)
{
cout<<fixed<<setprecision(2)<<a[i][n]<<'\n';
}
return 0;
}