Part 0
这道题就是高斯消元的板子题。
高斯消元,就是用来解一个多项式方程的算法。当然还能判断这个方程是有多组解还是无解。下面来稍微解释一下它的原理。
Part 1
首先先想一想我们是怎么解多元一次方程组的,从中有没有什么通用解法。高斯消元其实就是这样一个通用解法。
有那么多个未知数,我们可以一个一个进行消去。
假设初始有m个n元方程(a数组为系数,x数组为未知数)
a [ 1 ] [ 1 ] ∗ x [ 1 ] + a [ 1 ] [ 2 ] ∗ x [ 3 ] + ⋅ ⋅ ⋅ + a [ 1 ] [ n ] ∗ x [ n ] = b [ 1 ] a[1][1]*x[1]+a[1][2]*x[3]+ \cdot \cdot \cdot +a[1][n]*x[n]=b[1] a[1][1]∗x[1]+a[1][2]∗x[3]+⋅⋅⋅+a[1][n]∗x[n]=b[1]
a [ 2 ] [ 1 ] ∗ x [ 1 ] + a [ 2 ] [ 2 ] ∗ x [ 3 ] + ⋅ ⋅ ⋅ + a [ 2 ] [ n ] ∗ x [ n ] = b [ 2 ] a[2][1]*x[1]+a[2][2]*x[3]+ \cdot \cdot \cdot +a[2][n]*x[n]=b[2] a[2][1]∗x[1]+a[2][2]∗x[3]+⋅⋅⋅+a[2][n]∗x[n]=b[2]
⋅ ⋅ ⋅ \cdot \cdot \cdot ⋅⋅⋅
a [ m ] [ 1 ] ∗ x [ 1 ] + a [ m ] [ 2 ] ∗ x [ 3 ] + ⋅ ⋅ ⋅ + a [ m ] [ n ] ∗ x [ n ] = b [ m ] a[m][1]*x[1]+a[m][2]*x[3]+ \cdot \cdot \cdot +a[m][n]*x[n]=b[m] a[m][1]∗x[1]+a[m][2]∗x[3]+⋅⋅⋅+a[m][n]∗x[n]=b[m]
那么我们对于第二行以下的所有方程都消去x[1]这个未知数,就拿第m条方程为例,其消去x[1]这个未知数就是这样。
0 ∗ x [ 1 ] + ( a [ m ] [ 2 ] − a [ m ] [ 1 ] / a [ 1 ] [ 1 ] ∗ a [ 1 ] [ 2 ] ) ∗ x [ 3 ] + ⋅ ⋅ ⋅ + ( a [ m ] [ n ] − a [ m ] [ 1 ] / a [ 1 ] [ 1 ] ∗ a [ 1 ] [ n ] ) ∗ x [ n ] = b [ m ] − b [ 1 ] ∗ ( a [ m ] [ 1 ] / a [ 1 ] [ 1 ] ) 0*x[1]+(a[m][2]-a[m][1]/a[1][1]*a[1][2])*x[3]+ \cdot \cdot \cdot +(a[m][n]-a[m][1]/a[1][1]*a[1][n])*x[n]=b[m]-b[1]*(a[m][1]/a[1][1]) 0∗x[1]+(a[m][2]−a[m][1]/a[1][1]∗a[1][2])∗x[3]+⋅⋅⋅+(a[m][n]−a[m][1]/a[1][1]∗a[1][n])∗x[n]=b[m]−b[1]∗(a[m][1]/a[1][1])
以此操作n-1次,一次次消去未知数,然后就可以的出x[n]的值,然后代入上一条方程就会得出x[n-1]的值,然后就可以的出所有未知数的值。
对于无解与多解的判定就直接扔在代码里了。
高斯消元的模板就是那个结构体里的东西。
代码及解释如下
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define eps 1e-6
#define M 505
using namespace std;
struct Matrix{
double num[M<<1][M<<1];
double Ans[M<<1];
double val[M<<1];
int n,m;//m行n列
void resize(int x,int y){n=x,m=y;}
void Read(){
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++)scanf("%lf",&num[i][j]);
scanf("%lf",&Ans[i]);
}
}
void swap_line(int x,int y){
if(x==y)return;
for(int i=1;i<=n;i++)swap(num[x][i],num[y][i]);
swap(Ans[x],Ans[y]);
}
void Print(){
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++)printf("%.2lf ",num[i][j]);
printf("%.2lf\n",Ans[i]);
}
}
void solve(){
memset(val,0,sizeof(val));
bool Many_ans=false;//是否有多组解
for(int step=1;step<=n;step++){//当前在处理哪一个物品(也就是消去哪个元)
bool flag=false;
for(int i=step;i<=m;i++){
if(fabs(num[i][step])>eps){
flag=true;
swap_line(step,i);
break;
}
}
//如果剩下的每一个方程的这个元的系数都为0,那么这个元的取值是随意的,那么就有多组解
if(!flag){
Many_ans=true;
continue;
}
for(int i=step+1;i<=m;i++){//将下面的方程进行消元
for(int j=step+1;j<=n;j++)num[i][j]-=num[step][j]*(num[i][step]/num[step][step]);
Ans[i]-=Ans[step]*(num[i][step]/num[step][step]);
num[i][step]=0;
}
}
for(int i=1;i<=m;i++){//先判断无解,在判断多解
bool flag=false;
for(int j=i;j<=n;j++)if(fabs(num[i][j])>eps){flag=true;break;}
if(!flag&&fabs(Ans[i])>eps){//如果有一个方程它的所有未知数的系数都为0但是它们的和却不为0
puts("No solutions");
return;
}
}
if(Many_ans){
puts("Many solutions");
return;
}
for(int i=n;i>=1;i--){
for(int j=i+1;j<=n;j++)Ans[i]-=num[i][j]*val[j];//将本方程的所有未知数都带入数值并消去
val[i]=Ans[i]/num[i][i];//解出当前行的方程的未知数
}
//输出为本题的特殊要求
for(int i=1;i<=n;i++)printf("%d\n",(int)(0.5+val[i]));//四舍五入
}
}A;
int main(){
int n,m;
scanf("%d%d",&n,&m);
A.resize(n,m);
A.Read();
A.solve();
return 0;
}