學了將近兩個晚上。突然看懂時感覺自己是個**。
寫個部落格記一下吧。
模板:洛谷p3389 【模板】高斯消元法
有 \(n\) 個形如 \(a_1x_1+a_2x_2+\cdots+a_nx_n=b\) 的方程。解方程組。把方程組用矩陣形式寫出來有唯一解則將其求出,否則輸出
no solution
。
\[\left( \begin
a_ & a_ & \cdots & a_ \\
a_ & a_ & \cdots & a_ \\
\vdots & \vdots & \ddots & \vdots \\
a_ & a_ & \cdots & a_
\end \middle| \begin
b_1 \\ b_2 \\ \vdots \\ b_n
\end \right)\]
(這個東西叫增廣矩陣)
考慮選 \(x_1\) 為主元。
若所有方程中 \(x_1\) 的係數均為 0,則 \(x_1\) 是個自由未知量,方程組無唯一解。直接結束即可。
否則,選出 \(|a_|\) 最大的第 \(i\) 行(這樣精度誤差最小,雖然我也不知道為什麼)
然後用第 \(i\) 個方程和其它所有方程做一遍加減消元,消去 \(x_1\)。
不妨設 \(i=1\),則得到新的增廣矩陣大概是下面這樣
\[\left( \begin
1 & 1 & 1 & \cdots & 1 \\
0 & 1 & 1 & \cdots & 1 \\
0 & 1 & 1 & \cdots & 1 \\
\vdots & \vdots & \vdots & \ddots & \vdots \\
0 & 1 & 1 & \cdots & 1
\end \middle| \begin
1 \\ 1 \\ 1 \\ \vdots \\ 1
\end \right)\]
0 表示該項已被消去,1 表示其它。
對 \(x_2\) 執行相同操作。注意之前選過的行不能再選。
設這次選了第 \(j(j\ne i)\) 行,以 \(j=2\) 為例,新的增廣矩陣是
\[\left( \begin
1 & 0 & 1 & \cdots & 1 \\
0 & 1 & 1 & \cdots & 1 \\
0 & 0 & 1 & \cdots & 1 \\
\vdots & \vdots & \vdots & \ddots & \vdots \\
0 & 0 & 1 & \cdots & 1
\end \middle| \begin
1 \\ 1 \\ 1 \\ \vdots \\ 1
\end \right)\]
重複上述過程,最終得到
\[\left( \begin
1 & 0 & 0 & \cdots & 0 \\
0 & 1 & 0 & \cdots & 0 \\
0 & 0 & 1 & \cdots & 0 \\
\vdots & \vdots & \vdots & \ddots & \vdots \\
0 & 0 & 0 & \cdots & 1
\end \middle| \begin
1 \\ 1 \\ 1 \\ \vdots \\ 1
\end \right)\]
到這裡就算是解完了。
#include#include#define mcpy(a,b) memcpy(a,b,sizeof ta)
const int n=110; int n; double ta[n],a[n][n];
inline double abs(double x)
int main()
}for (int i=1; i<=n; ++i)
printf("%.2lf\n",a[i][n+1]/a[i][i]);
return 0;
}
時間複雜度 \(o(n^3)\)。
另一道模板題:洛谷p2455 [sdoi2006]線性方程組
這次需要區分無解和有無陣列解。
其實也很簡單
正常做消元,如果遇到某一列係數全為 0,就直接擺爛(反正也消不出啥東西),直接跳到下一列
這樣得到的增廣矩陣依然是
\[\left( \begin
1 & 0 & 0 & \cdots & 0 \\
0 & 1 & 0 & \cdots & 0 \\
0 & 0 & 1 & \cdots & 0 \\
\vdots & \vdots & \vdots & \ddots & \vdots \\
0 & 0 & 0 & \cdots & 1
\end \middle| \begin
1 \\ 1 \\ 1 \\ \vdots \\ 1
\end \right)\]
但這次寫著 1 的位置可能會出現 0。
首先檢查一下有沒有 \(0x\ne 0\) 這樣的方程出現。有則說明無解。
否則再檢查有沒有 \(0x=0\) 這樣的方程出現。有則說明有無陣列解。
否則說明有唯一解。
然後我們獲得了 80pts 的好成績。
看一眼報錯資訊,第三個點無窮解判成無解了,第八個點好像把0.00
輸出成了-0.00
。
後者是詭異的 feature,輸出時加上乙個很小的數即可解決。
前者更加離譜,我 debug 了一晚上也沒找到錯。
三天後我終於在題解區翻到一組 hack 資料:
2
0 2 3
0 0 0
答案為0
,而程式會輸出-1
因為第一次消元時選了第一行,然後第二行沒法消了,但原本是可以消的。所以寄了。
原因是消元時只是貪心地選擇了當前列係數最大的一行。沒有考慮到對後面造成的影響。
補救措施:微調一下選擇行的方案,如果兩行的係數絕對值相等,選後面的數更小的。
#include#include#define mcpy(a,b) memcpy(a,b,sizeof ta)
#define rep(i,l,r) for (int i=l; i<=r; ++i)
const int n=52; const double eps=1e-6;
int n,f1,f2; double ta[n],a[n][n];
inline double abs(double x)
inline int f(double x)
bool cmp(int i,int j,int k,bool f)
int main()
}rep(i,1,n) f(a[i][i])||(f1=1,f(a[i][n+1])&&(f2=1));
if (f1) return puts(f2?"-1":"0"),0;
rep(i,1,n) printf("x%d=%.2lf\n",i,a[i][n+1]/a[i][i]+eps);
return 0;
}
做完之後還是感覺這**醜的一批。
不過反正能過,複雜度也沒假,就這樣吧(
高斯 約旦消元法
高斯 約旦消元法 此演算法是基於高斯消元的基礎上改進而成的,唯一不同之處是多進行了幾次矩陣變換使得化為行最簡型矩陣。相比於高斯消元法此方法效率較低,但是不需要回帶求解 此演算法的過程大概為 首先確定每個a i i a i i a i i 不為0 00 如果為0 00就和下面的行替換 接著將每個a i...
數學 高斯 約旦消元法
給定 n 元一次方程組 begin a x 1 a x 2 cdots a x n b 1 a x 1 a x 2 cdots a x n b 2 cdots a x 1 a x 2 cdots a x n b n end 請求出方程組的解的情況 對於這樣的問題,我們可以使用高斯消元法進行求解,當然...
高斯 約旦消元法 理解
高斯消元是一種解方程的很巧妙的方法,核心是把方程轉換成矩陣形式,然後再通過加減消元,求出值後再回帶,就解出了這個方程,這裡我就不贅述了。我一般用高斯 約旦消元法,這種方法是直接轉換成單位矩陣求解,減少回帶次數,提高精確度,實現方式如下 下方是乙個方程 把它轉換成矩陣形式就是 我們可以這樣對其進行變換...