.高斯消元

假如要解n元1次方程组,将n个方程放入一个n*n的矩阵,矩阵的每个位置代表未知数的系数,再写一个答案矩阵,记录每个方程的解,然后将矩阵用方程组的性质(加,乘)化为一个阶梯矩阵,如同下图

\begin{bmatrix} 1 & * & *\\ 0 & 1& *\\ 0& 0 &1 \end{bmatrix}=\begin{bmatrix} x\\ y\\z \end{bmatrix}

这时可以发现,最后一个方程只有最后一个未知数的系数为1,其余都为0,所以可以知道最后的未知数的值,然后用已知的未知数的值一步一步向上递推消元,就可以求出方程组的解

当然,方程组可能无解或有无限多个解

无解:有一行出现了形如0=d(d为非0数)的式子

无数解:有一行为0=0,这是说明有另外一行是ax+by=d的形式,故此时方程有无数解

#include<bits/stdc++.h>
using namespace std;
int n;
double a[110][110],ans[110];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++) scanf("%lf",&a[i][j]);
		scanf("%lf",&ans[i]);
	}
	for(int i=1;i<=n;i++){
		double mx=-1000000.00;
		int id;
		for(int j=i;j<=n;j++){
			if(a[j][i]>mx){
				mx=a[j][i];
				id=j;
			}
		}//a矩阵记录了方程每个未知数的系数,ans记录了方程组的答案
		for(int j=1;j<=n;j++) swap(a[i][j],a[id][j]);
		swap(ans[i],ans[id]);
		if(!a[i][i]){
			if(ans[i]) printf("No Solution");//出现了0=d这种形式
            else printf("wushujie");//出现了0=0这种形式
			return 0;
		}
		ans[i]/=a[i][i];
		for(int j=n;j>=i;j--) a[i][j]/=a[i][i]; 
		for(int j=i+1;j<=n;j++){
			double ty=a[j][i];
			for(int k=1;k<=n;k++) a[j][k]-=ty*a[i][k];
			ans[j]-=ty*ans[i];
		}
	}
	for(int i=1;i<=n;i++){
		bool ok=false;
		for(int j=1;j<=n;j++){
			if(a[i][j]) ok=true;
		}
		if(!ok){
			printf("No Solution");
			return 0;
		}
	}
	for(int i=n-1;i>=1;i--){
		for(int j=i+1;j<=n;j++) ans[i]-=ans[j]*a[i][j];//递推答案
	}
	for(int i=1;i<=n;i++){
		printf("%.2lf\n",ans[i]);
	}
	return 0;
} 

例题 

一个球面上的点到圆心的距离都相等,所以就有

\forall_{i} \sum_{j=1}^{j\leq n}(a_{ij}-x_{j})^{2}=d     

a[i][j]表示球面上第i个点第j维的坐标,x[j]表示圆心第j维的坐标,d是一个固定的数

因为d是固定的,并且有有n+1个这种式子,所以可以让这些式子相减得到n个为0的式子

\because \sum_{j=1}^{j\leq n}(a_{i,j}-x_{j})^{2}-(a_{i,j+1}-x_{j+1})=0

\therefore \sum_{j=1}^{n}a_{i,j}^{2}+2*x_{j}*a_{i,j}+x_{j}^{2}-a_{i,j+1}^{2}+2*x_{j+1}*a_{i,j+1}-x_{j+1}^{2}=0

\therefore \sum_{j=1}^{j\leq n} 2*x_{j}*(a_{i,j}-a_{i,j+1})=\sum_{j=1}^{j\leq n} a_{i,j}^{2}-a_{i,j+1}^{2}

这就是高斯消元的形式了,第i行第j位的值为2*x[j]*(a[i][j]-a[i][j+1]),答案的值为a[i][j]的平方减a[i][j+1]的平方的差的和

#include<bits/stdc++.h>
using namespace std;
int n;
double k[110][110],a[110][110],ans[110];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n+1;i++){
		for(int j=1;j<=n;j++){
			scanf("%lf",&a[i][j]);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			k[i][j]=2.00*(a[i][j]-a[i+1][j]);//每一位的系数
			ans[i]=ans[i]+a[i][j]*a[i][j]-a[i+1][j]*a[i+1][j];
		}	
	}
	for(int i=1;i<=n;i++){
		double mx=-10000000.00;
		int id;
		for(int j=i;j<=n;j++){
			if(mx<k[j][i]){
				mx=k[j][i];
				id=j;
			}
		}
		for(int j=1;j<=n;j++) swap(k[i][j],k[id][j]);
		swap(ans[i],ans[id]);
		ans[i]/=k[i][i];
		for(int j=n;j>=i;j--) k[i][j]/=k[i][i];
		
		for(int j=i+1;j<=n;j++){
			double bs=k[j][i];
			for(int h=1;h<=n;h++) k[j][h]-=bs*k[i][h];	
			ans[j]-=bs*ans[i];
		} 
	}
	for(int i=n-1;i>=1;i--){
		for(int j=i+1;j<=n;j++){
			ans[i]-=ans[j]*k[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		printf("%.3lf ",ans[i]);
	}
	return 0;
}

异或运算的高斯消元

比如说有3个未知数和几个式子,那么矩阵每一位就表示在这个式子中xi是取还是不取

可以将矩阵压成一个数,这样可以缩小时间复杂度

每次找到最大的数来更新

和普通高斯消元一样,使第i位为1,1~i-1位为0


 

题面上说对2取模,相当于就是这一堆数异或后最后一位的值

n达到了1000,只能用bitset来表示每一位的状态

对于第i位,找到第一个第i位为1的式子来更新答案

/*bitset对高斯消元的优化 
对于只有异或运算的高斯消元,可以用bitset将状态压为一个数,使得时间复杂度降倒1/32
找最大值时候就枚举要找的位数就行 
*/
#include<bits/stdc++.h>
using namespace std;
bitset<1010>a[2010];
int m,n,c,ans,pd[2010];
int main(){
	scanf("%d%d",&m,&n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			char ch=getchar();
			while(ch!='0'&&ch!='1') ch=getchar();
			a[i][j]=ch-'0';
		}
		int w;
		scanf("%d",&w);
		a[i][m+1]=w; 
	}
	for(int i=1;i<=m;i++){
		int j=i;
		while(!a[j][i]&&j<=n) j++;
		if(j>n){
			printf("Cannot Determine");
			return 0;
		}
		ans=max(ans,j);
		swap(a[i],a[j]);
		for(int j=1;j<=n;j++){
			if(i!=j&&a[j][i]) a[j]^=a[i];
		}
	}
	for(int i=m;i>=1;i--){
		for(int j=i+1;j<=m;j++){
			if(a[i][j]) a[i]^=a[j];
		}
		pd[i]=a[i][m+1];
	}
	printf("%d\n",ans);
	for(int i=1;i<=m;i++){
		pd[i]%2?printf("?y7M#\n"):printf("Earth\n");
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值