高斯消元學習筆記——P304題解

LEWISAK發表於2024-04-19

如果你覺得這篇太囉嗦

問題

[SDOI2006] 線性方程組

題目描述

已知 \(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 \\ \cdots \\ a_{n,1} x_1 + a_{n, 2} x_2 + \cdots + a_{n, n} x_n = b_n \end{cases} \]

請根據輸入的資料,程式設計輸出方程組的解的情況。


題解

打眼一看,這不是大版汁嘛😎。立刻把這個題的程式碼直接提交:

#include<bits/stdc++.h>
using namespace std;
double mapp[110][110],ans[110];
int n;
int main(){
	cin>>n;
	int r;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n+1;j++){
			cin>>mapp[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		r=i;
		for(int j=i;j<=n;j++){
			if(fabs(mapp[r][i])<fabs(mapp[j][i])){
				r=j;
			}
		}
		if(fabs(mapp[r][i])<1e-7){
			cout<<"No Solution"<<endl;
			return 0;
		}
		swap(mapp[i],mapp[r]);
		double t=mapp[i][i];
		for(int j=i;j<=n+1;j++){
			mapp[i][j]/=t;
		}
		for(int j=i+1;j<=n;j++){
			t=mapp[j][i];
			for(int k=i;k<=n+1;k++){
				mapp[j][k]-=mapp[i][k]*t;
			}
		}
	}
	ans[n]=mapp[n][n+1];
	for(int i=n-1;i>=1;i--){
		ans[i]=mapp[i][n+1];
		for(int j=i+1;j<=n;j++){
			ans[i]-=mapp[i][j]*ans[j];
		}
	}
	for(int i=1;i<=n;i++){
		printf("x%d=%.2lf\n",i,ans[i]);
	}
}

o,我沒有仔細讀題,還要再判斷是無解還是無窮解。

😕看來並沒有那麼簡單,但注意到,可以透過一個計數器來記錄是什麼時候break掉的,如果計數器不等於n,就要進行判斷:

if(t!=n){
	if(mapp[t][n+1]){
		cout<<-1;
		return 0;
	}
	else{
		cout<<0;
		return 0;
	}
}

那麼加上最佳化,我們的程式碼變成了幾分呢?

🤡這不純純消愁嘛

經過一番排查,發現了一個顯而易見的問題:要一層層的排查t以後的層數(除了我這個蒟蒻都能看出來吧)

if(t<n){
	while(t<n){
		if(mapp[t++][n]<1e-7){
			cout<<-1;
			return 0;
		}
	}
	cout<<0;
	return 0;
}

這個最佳化立竿見影!

🎉️耶!

但有些聰明的人肯定已經發現了問題:測試點12本來是A的,但加上最佳化就戳了:(

但是愚蠢的LEWIAK並沒有發現,拿著錯誤程式碼調了114分鐘

這時,我靈機一動💡,想直接返璞歸真暴力判斷是否為完美梯形:

if(tt<n){
    for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j++){
			if(!mapp[i][j]){
				if(mapp[i][n+1]){
					cout<<-1;
					return 0;
				}
			}
		}
	}
    cout<<0;
	return 0;
}

那麼返璞歸真後,我們的程式碼變成了幾分呢?

🤡

還有翻轉!

但有些聰明的人肯定已經發現了問題:測試點12本來是A的,但加上最佳化就戳了:(

的原因其實是:

變數名衝突

最離譜的是我的婢養的DEV C++竟然沒有檢查出錯(不是哥們,他一個int一個double都不報錯的嗎?)

😁很有希望!

但是愚蠢的LEWIAK調了半天后發現了嚴重的問題

那就是我們的程式碼實際上並沒有處理完成所有的消元👀️

倒是也好處理😎,把break換為continue就好了。

🤓🤓🤓

想了想發現:應該把i換成tt,因為有一些是不算的:

(懶得貼了,自己想象WA的評測資訊吧)

🤓🤓🤓

經過苦思冥想,我發現要用別的多項式消掉continue的,而為了節省空間,我決定無腦的將從i到n的迴圈改為從1到n的迴圈

其實LEWISAK只是懶的用陣列單獨儲存罷了

最後貼上ACcode:

#include<bits/stdc++.h>
using namespace std;
double mapp[110][110],ans[110];
int tt=0;
int n;
int main(){
	cin>>n;
	int r;
	for(int i=0;i<n;i++){
		for(int j=0;j<n+1;j++){
			cin>>mapp[i][j];
		}
	}
	for(int i=0;i<n;i++){
		r=tt;
		for(int j=tt+1;j<n;j++){
			if(fabs(mapp[r][i])<fabs(mapp[j][i])){
				r=j;
			}
		}
		if(fabs(mapp[r][i])<1e-9){
			continue;
		}
		for(int j=0;j<n+1;++j)swap(mapp[tt][j],mapp[r][j]);
		double t;
		for(int j=0;j<n;j++){
			if(j==tt){
				continue;
			}
			t=mapp[j][i]/mapp[tt][i];
			for(int k=i;k<n+1;k++){
				mapp[j][k]-=mapp[tt][k]*t;
			}
		}tt++;
	}
	if(tt<n){
		while(tt<n){
			if(fabs(mapp[tt++][n])>=1e-9){
				cout<<-1;
				return 0;
			}
		}
		cout<<0;
		return 0;
	}
	
	for(int i=0;i<n;i++){
		printf("x%d=%.2lf\n",i,mapp[i][n]/mapp[i][i]);
	}
}

改馬蜂是因為玄學

小彩蛋:

相關文章