有方程组
\[\begin{cases}k_{1,1}a_1+k_{1,2}a_2+……+k_{1,n}a_n=b_1\\k_{2,1}a_1+k_{2,2}a_2+……+k_{2,n}a_n=b_2\\……\\k_{n,1}a_1+k_{n,2}a_2+……+k_{n,n}a_n=b_n\end{cases}\]
其中\(k_i,b_i\)已知,求\(a_i\)
根据初中芝士,我们可以选择加减/代入消元,但是对于算法来说,我们要有一般性的方法
一般来说,我们选择把主对角线全部消成\(1\),主对角线下的数全部消成\(0\)
\[\begin{cases}1 k^{'}_{1,2} k^{'}_{1,3}=b^{'}_{1}\\0 1 k^{'}_{2,3}=b^{'}_{2}\\0 0 1=b^{'}_{3}\end{cases}\]
类似这样的,如果消不成上三角,那么说明原方程组无解
我们每次选择一个绝对值最大的系数,先将该系数消成\(1\)
用该项将其他式子的对应项直接消为\(0\),其他项对应减去\(\frac{a_{jnow}}{a_{inow}}*a_{iw}\)
至于为什么选最大的,首先如果最大的为\(0\)或小于\(eps\),那么方程组无解,其次选择最大的可以降低精度误差
对于\(n\)元方程组,显然需要消元\(n\)次,每次消元需要\(n^2\)复杂度,所以总复杂度\(O(n^3)\)
好像原理很简单,不过实现需要稍微脑补一下
\(upd:\)个人觉得没讲明白,但是我太菜了好像想不到更细致的解释了……先康康代码吧
#include<bits/stdc++.h> using namespace std; namespace red{ #define int long long #define eps (1e-8) inline int read() { int x=0;char ch,f=1; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n; double a[110][110],ret[110]; inline void main() { n=read(); for(int i=1;i<=n;++i) { for(int j=1;j<=n+1;++j) { scanf("%lf",&a[i][j]); } } for(int i=1;i<=n;++i) { int t=i; for(int j=i+1;j<=n;++j) if(fabs(a[j][i])>fabs(a[t][i])) t=j; if(fabs(a[t][i])<eps) { puts("No Solution"); return; } if(t!=i) swap(a[i],a[t]); double div=a[i][i]; for(int j=i;j<=n+1;++j) a[i][j]/=div; for(int j=i+1;j<=n;++j) { div=a[j][i]; for(int k=i;k<=n+1;++k) a[j][k]-=(a[i][k]*div); } } ret[n]=a[n][n+1]; for(int i=n-1;i;--i) { ret[i]=a[i][n+1]; for(int j=i+1;j<=n;++j) ret[i]-=(a[i][j]*ret[j]); } for(int i=1;i<=n;++i) printf("%.2lf\n",ret[i]); } } signed main() { red::main(); return 0; }
高斯消元解图上期望\(DP\)
bzoj1444: [Jsoi2009]有趣的游戏
洛谷P3232[HNOI2013]游走
来源:https://www.cnblogs.com/knife-rose/p/12025082.html