高斯消元好题选讲

例1:[SDOI2006]线性方程组

传送门

例2:[JSOI2008]球形空间产生器

传送门

例3:Barracuda

传送门

例4

传送门

Solution

例1:

看似是一个高斯消元,但普通的程序交上去会被卡成 80 80 80 W A WA WA

我们看下面的一组hack数据:

2
0 2 3
0 0 0

化简 2 y + 3 = 0 , 0 = 0 2y+3=0,0=0 2y+3=0,0=0

易得该方程组有无数解,但是我们的程序会输出 − 1 -1 1

究其原因,我们在考虑第一个方程时有 f [ 1 ] [ 1 ] = 0 , f [ 1 ] [ 3 ] ≠ 0 f[1][1]=0,f[1][3]≠0 f[1][1]=0,f[1][3]=0,程序直接判断无解退出。

所以我们在排序时,我们应该将第一关键字大的排前面,第二关键字小的排前面,这样就可以避免这种情况的发生。

一些细节处理见代码:

#include<bits/stdc++.h>
#define eps 1e-6
using namespace std;
double a[105][105];
int flag1,flag2;int n;
double check(double x){
	if(fabs(x)<=eps)  return 0;
	return x;
}
bool cmp(int k,int x,int y){
	if(fabs(fabs(a[x][k])-fabs(a[y][k]))>eps)  return fabs(a[x][k])>fabs(a[y][k]);
	for(int i=k+1;i<=n;i++){
		if(fabs(fabs(a[x][i])-fabs(a[y][i]))>eps)  return fabs(a[x][i])<fabs(a[y][i]);
	}
}
int main(){
	
	scanf("%d",&n);
	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 maxn=i;
		for(int j=i+1;j<=n;j++){
			if(cmp(i,j,maxn)){
				maxn=j;
			}
		}
		for(int j=1;j<=n+1;j++){
			swap(a[i][j],a[maxn][j]);
		}
		if(!check(a[i][i])){
			continue;
		}
		for(int j=1;j<=n;j++){
			if(j==i)  continue;
			double tmp=a[j][i]/a[i][i];
			for(int k=i+1;k<=n+1;k++){
				a[j][k]-=a[i][k]*tmp;
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(!check(a[i][i])&&check(a[i][n+1])){
			cout<<"-1\n";
			return 0;
		}
	}
	for(int i=1;i<=n;i++){
		if(!check(a[i][i])&&!check(a[i][n+1])){
			cout<<"0\n";
			return 0;
		}
	}
	for(int i=1;i<=n;i++){
		printf("x%d=%.2lf\n",i,check(a[i][n+1]/a[i][i]));
	}
}
例2

设中心点坐标为 ( x 1 , x 2 , . . . , x n ) (x_1,x_2,...,x_n) (x1,x2,...,xn),依照题意列出 n + 1 n+1 n+1个方程,上下两方程相减得出 n n n个方程,有 n n n个未知数,高斯消元求解即可。

#include<bits/stdc++.h>
using namespace std;
int n;
double p[15][15],a[15][15];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n+1;i++){
		for(int j=1;j<=n;j++){
			scanf("%lf",&p[i][j]);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			a[i][j]=2.0*(p[i][j]-p[i+1][j]);
			a[i][n+1]+=(p[i][j]*p[i][j]-p[i+1][j]*p[i+1][j]);
		}
	}
	for(int i=1;i<=n;i++){
		int maxn=i;
		for(int j=i+1;j<=n;j++){
			if(fabs(a[j][i])>fabs(a[maxn][i])){
				maxn=j;
			}
		}
		for(int j=1;j<=n+1;j++){
			swap(a[i][j],a[maxn][j]);
		}
		for(int j=1;j<=n;j++){
			if(j==i)  continue;
			double tmp=a[j][i]/a[i][i];
			for(int k=i+1;k<=n+1;k++){
				a[j][k]-=a[i][k]*tmp;
			}
		}
	}
	for(int i=1;i<=n;i++){
		printf("%.3lf ",a[i][n+1]/a[i][i]);
	}
}
例3

枚举删掉的方程,由剩余的方程联立求解即可。

注意判断浮点数及是否有唯一解。

#include<bits/stdc++.h>
using namespace std;
double a[105][105],b[105][105],w[105];
int flag=0,m[105],f[105][105],n,Num,k,x;
int solve(){
	for(int i=1;i<=n;i++){
		int maxn=i;
		for(int j=i+1;j<=n;j++){
			if(fabs(a[j][i])>fabs(a[maxn][i]))
			  maxn=j;
		}
		for(int j=1;j<=n+1;j++){
			swap(a[i][j],a[maxn][j]);
		}
		if(!a[i][i])  return 0;
		for(int j=1;j<=n;j++){
			if(j==i)  continue;
			double tmp=a[j][i]/a[i][i];
			for(int k=i+1;k<=n+1;k++){
				a[j][k]-=a[i][k]*tmp;
			}
		}
	}
	int maxn=-1,num=0;
	for(int i=1;i<=n;i++){
		double ans=a[i][n+1]/a[i][i];
		if(ans<=0)  return 0;
		if(ans!=(int)ans)  return 0;
		maxn=max(maxn,(int)ans);
	}
	for(int i=1;i<=n;i++){
		int ans=a[i][n+1]/a[i][i];
		if(maxn==ans){
			if(num)  return 0;
			num=i;
		}
	}
	return num;
	
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n+1;i++){
		scanf("%d",&k);
		for(int j=1;j<=k;j++){
			scanf("%d",&x);
			b[i][x]=1;
		}
		scanf("%lf",&b[i][n+1]);
	}
	for(int i=1;i<=n+1;i++){
		swap(b[i],b[n+1]);
		for(int j=1;j<=n;j++){
			for(int k=1;k<=n+1;k++){
				a[j][k]=b[j][k];
			}
		}
		swap(b[i],b[n+1]);
		Num=solve();
		if(Num){
			if(flag){
				cout<<"illegal\n";
				return 0;
			}
			flag=Num;
		}
	}
	if(!flag){
		cout<<"illegal\n";
		return 0;
	}
	else  cout<<flag<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值