20191026 专题:高斯消元

总览:

两种方法:回代和约旦。(约旦不知道比回代高到哪里去了)
思路都是加减消元。
注意无解和无穷解。
只要列出方程就是模板…(可是方程列不出) (:逃
神仙博客:传送门

T1 P3389 【模板】高斯消元法

洛谷P3389
题目背景
Gauss消元

题目描述
给定一个线性方程组,对其求解

输入格式
第一行,一个正整数 n n n

第二至 n + 1 n+1 n+1行,每行 n + 1 n+1 n+1个整数,为 a 1 , a 2 . . . a n a_1, a_2 ...a_n a1,a2...an b b b,代表一组方程。

输出格式
n n n行,每行一个数,第 i i i行为 x i x_i xi(保留2位小数)
如果不存在唯一解,在第一行输出"No Solution".

输入输出样例
输入
3
1 3 4 5
1 4 7 3
9 3 2 2
输出
-0.97
5.18
-2.39
说明/提示
1 ≤ n ≤ 100 , ∣ a i ∣ ≤ 1 0 4 , ∣ b ∣ ≤ 1 0 4 1\le n\le 100,|a_i|\le 10^4,|b|\le 10^4 1n100,ai104,b104

注意:
1. d o u b l e double double的地方不能用 i n t int int
2.实数比较需做差。

代码:

#include<bits/stdc++.h>
using namespace std;

const double EPS=1e-7;
int n;
double mapp[105][105];

bool compare(double x,double y){
	if(fabs(x)-fabs(y)>EPS)	return 1;
	return 0;
}

signed main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n+1;j++)
			scanf("%lf",&mapp[i][j]);
	for(int i=1;i<=n;i++){
		int u=i;
		for(int j=i+1;j<=n;j++)
			if(!compare(mapp[u][i],mapp[j][i]))
				u=j;
		if(u!=i)	swap(mapp[u],mapp[i]);
		if(!compare(mapp[i][i],0)){
			printf("No Solution");
			return 0;
		}
		for(int j=1;j<=n;j++){
			if(i==j)	continue;
			double div=mapp[j][i]/mapp[i][i];
			for(int k=i+1;k<=n+1;k++)
				mapp[j][k]-=mapp[i][k]*div;
		}
	}
	for(int i=1;i<=n;i++)
		printf("%0.2lf\n",mapp[i][n+1]/mapp[i][i]);
	return 0;
}

T2 P2455 [SDOI2006]线性方程组

洛谷P2455
题目描述
已知 n n n元线性一次方程组。
在这里插入图片描述
其中: n < = 50 n<=50 n<=50, 系数是整数 < = 100 <=100 <=100(有负数),bi的值都是整数且 < 300 <300 <300(有负数)

编程任务:
根据输入的数据,编程输出方程组的解的情况。

输入格式
第一行:未知数的个数。以下 n n n n + 1 n+1 n+1列:分别表示每一格方程的系数及方程右边的值。

输出格式
如果方程组无实数解输出 − 1 -1 1
如果有无穷多实数解,输出 0 0 0
如果有唯一解,则输出解(小数点后保留两位小数)。

输入输出样例
输入
3
2 -1 1 1
4 1 -1 5
1 1 1 0
输出
x1=1.00
x2=0
x3=-1.00

一道高斯消元的题卡高斯消元…
和模板一样,但需注意判无解和无穷解。
m a p p [ i ] [ i ] = 0 并 且 m a p p [ i ] [ n + 1 ] ≠ 0 mapp[i][i]=0并且mapp[i][n+1]\neq 0 mapp[i][i]=0mapp[i][n+1]=0时,方程无解( 即 为 0 × a = b ( b ≠ 0 ) 即为0\times a=b(b\neq 0) 0×a=b(b=0)
m a p p [ i ] [ i ] = 0 并 且 m a p p [ i ] [ n + 1 ] = 0 mapp[i][i]=0并且mapp[i][n+1]=0 mapp[i][i]=0mapp[i][n+1]=0时,方程有无穷解( 即 为 0 × a = 0 即为0\times a=0 0×a=0
最后注意解出 x = 0 x=0 x=0时,输出 x i = 0 x_i=0 xi=0,而不是 x i = 0.00 x_i=0.00 xi=0.00

代码:

#include<bits/stdc++.h>
using namespace std;

const double EPS=1e-7;
int n;
double mapp[55][55];

bool compare(double x,double y){
	if(fabs(x)-fabs(y)>EPS)	return 1;
	return 0;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n+1;j++)
			scanf("%lf",&mapp[i][j]);
	for(int i=1;i<=n;i++){
		int u=i;
		for(int j=i+1;j<=n;j++)
			if(!compare(mapp[u][i],mapp[j][i]))
				u=j;
		if(u!=i)	swap(mapp[u],mapp[i]);
		if(!compare(mapp[i][i],0))	continue;
		for(int j=1;j<=n;j++){
			if(i==j)	continue;
			double div=mapp[j][i]/mapp[i][i];
			for(int k=i+1;k<=n+1;k++)
				mapp[j][k]-=mapp[i][k]*div;
		}
	}
	for(int i=1;i<=n;i++)
		if(!compare(mapp[i][i],0)&&compare(mapp[i][n+1],0)){
			printf("-1\n");
			return 0;
		}
	for(int i=1;i<=n;i++)
		if(!compare(mapp[i][i],0)&&!compare(mapp[i][n+1],0)){
			printf("0\n");
			return 0;
		}
	for(int i=1;i<=n;i++){
		if(compare(mapp[i][n+1]/mapp[i][i],0))	printf("x%d=%0.2lf\n",i,mapp[i][n+1]/mapp[i][i]);
		else	printf("x%d=0\n",i);
	}
	return 0;
}

T3 P4035 [JSOI2008]球形空间产生器

洛谷P4035
题目描述
有一个球形空间产生器能够在 n n n 维空间中产生一个坚硬的球体。现在,你被困在了这个 n n n 维球体中,你只知道球面上 n + 1 n+1 n+1 个点的坐标,你需要以最快的速度确定这个 n n n 维球体的球心坐标,以便于摧毁这个球形空间产生器。

输入格式
第一行是一个整数 n n n ( 1 < = N = 10 ) (1<=N=10) (1<=N=10)。接下来的 n + 1 n+1 n+1 行,每行有 n n n 个实数,表示球面上一点的 n n n 维坐标。每一个实数精确到小数点后 6 6 6 位,且其绝对值都不超过 20000 20000 20000

输出格式
有且只有一行,依次给出球心的 n n n 维坐标( n n n 个实数),两个实数之间用一个空格隔开。每个实数精确到小数点后 3 3 3 位。数据保证有解。你的答案必须和标准输出一模一样才能够得分。

输入输出样例
输入
2
0.0 0.0
-1.0 1.0
1.0 0.0
输出
0.500 1.500
说明/提示
提示:
给出两个定义:
球心:到球面上任意一点距离都相等的点。
距离:设两个n为空间上的点A, B的坐标为 ( a 1 , a 2 . . . a n ) , ( b 1 , b 2 . . . b n ) (a_1,a_2...a_n),(b_1,b_2...b_n) (a1,a2...an),(b1,b2...bn),则AB的距离定义为: d i s t = ( a 1 − b 1 ) 2 + ( a 2 − b 2 ) 2 + . . . + ( a n − b n ) 2 dist=\sqrt{(a_1-b_1)^2+(a_2-b_2)^2+...+(a_n-b_n)^2} dist=(a1b1)2+(a2b2)2+...+(anbn)2

思路:
导出方程后套模板…
导出方程:
设中心坐标 r 1 , r 2 . . . r n r_1,r_2...r_n r1,r2...rn
球的半径为 s s s

首先列出方程:
( a 1 − r 1 ) 2 + ( a 2 − r 2 ) 2 + . . . + ( a n − r n ) 2 = s \sqrt{(a_1-r_1)^2+(a_2-r_2)^2+...+(a_n-r_n)^2}=s (a1r1)2+(a2r2)2+...+(anrn)2 =s
( b 1 − r 1 ) 2 + ( b 2 − r 2 ) 2 + . . . + ( b n − r n ) 2 = s \sqrt{(b_1-r_1)^2+(b_2-r_2)^2+...+(b_n-r_n)^2}=s (b1r1)2+(b2r2)2+...+(bnrn)2 =s
.
.
.
( k 1 − r 1 ) 2 + ( k 2 − r 2 ) 2 + . . . + ( k n − r n ) 2 = s \sqrt{(k_1-r_1)^2+(k_2-r_2)^2+...+(k_n-r_n)^2}=s (k1r1)2+(k2r2)2+...+(knrn)2 =s

去根号:
( a 1 − r 1 ) 2 + ( a 2 − r 2 ) 2 + . . . + ( a n − r n ) 2 = s 2 (a_1-r_1)^2+(a_2-r_2)^2+...+(a_n-r_n)^2=s^2 (a1r1)2+(a2r2)2+...+(anrn)2=s2
( b 1 − r 1 ) 2 + ( b 2 − r 2 ) 2 + . . . + ( b n − r n ) 2 = s 2 (b_1-r_1)^2+(b_2-r_2)^2+...+(b_n-r_n)^2=s^2 (b1r1)2+(b2r2)2+...+(bnrn)2=s2
.
.
.
( k 1 − r 1 ) 2 + ( k 2 − r 2 ) 2 + . . . + ( k n − r n ) 2 = s 2 (k_1-r_1)^2+(k_2-r_2)^2+...+(k_n-r_n)^2=s^2 (k1r1)2+(k2r2)2+...+(knrn)2=s2

开括号:
( a 1 2 + a 2 2 + . . . + a n 2 ) + ( r 1 2 + r 2 2 . . . + r n 2 ) − 2 × ( a 1 × r 1 + a 2 × r 2 . . . a n × r + n ) (a_1^2+a_2^2+...+a_n^2)+(r_1^2+r_2^2...+r_n^2)-2\times (a_1\times r_1+a_2\times r_2...a_n\times r+n) (a12+a22+...+an2)+(r12+r22...+rn2)2×(a1×r1+a2×r2...an×r+n)
( b 1 2 + b 2 2 + . . . + b n 2 ) + ( r 1 2 + r 2 2 . . . + r n 2 ) − 2 × ( b 1 × r 1 + b 2 × r 2 . . . b n × r + n ) (b_1^2+b_2^2+...+b_n^2)+(r_1^2+r_2^2...+r_n^2)-2\times (b_1\times r_1+b_2\times r_2...b_n\times r+n) (b12+b22+...+bn2)+(r12+r22...+rn2)2×(b1×r1+b2×r2...bn×r+n)
.
.
.
( k 1 2 + k 2 2 + . . . + k n 2 ) + ( r 1 2 + r 2 2 . . . + r n 2 ) − 2 × ( k 1 × r 1 + k 2 × r 2 . . . k n × r + n ) (k_1^2+k_2^2+...+k_n^2)+(r_1^2+r_2^2...+r_n^2)-2\times (k_1\times r_1+k_2\times r_2...k_n\times r+n) (k12+k22+...+kn2)+(r12+r22...+rn2)2×(k1×r1+k2×r2...kn×r+n)

相邻上下两式相减,消去 r 2 r^2 r2 s s s
( b 1 − a 1 ) × r 1 + ( b 2 − a 2 ) × r 2 + . . . + ( b n − a n ) × r n = ( b 1 2 − a 1 2 + b 2 2 − a 2 2 + . . . + b n 2 − a n 2 ) / 2 (b_1-a_1)\times r_1+(b_2-a_2)\times r_2+...+(b_n-a_n)\times r_n=(b_1^2-a_1^2+b_2^2-a_2^2+...+b_n^2-a_n^2)/2 (b1a1)×r1+(b2a2)×r2+...+(bnan)×rn=(b12a12+b22a22+...+bn2an2)/2
( c 1 − b 1 ) × r 1 + ( c 2 − b 2 ) × r 2 + . . . + ( c n − b n ) × r n = ( c 1 2 − b 1 2 + c 2 2 − b 2 2 + . . . + c n 2 − b n 2 ) / 2 (c_1-b_1)\times r_1+(c_2-b_2)\times r_2+...+(c_n-b_n)\times r_n=(c_1^2-b_1^2+c_2^2-b_2^2+...+c_n^2-b_n^2)/2 (c1b1)×r1+(c2b2)×r2+...+(cnbn)×rn=(c12b12+c22b22+...+cn2bn2)/2
.
.
.
( k 1 − j 1 ) × r 1 + ( k 2 − j 2 ) × r 2 + . . . + ( k n − j n ) × r n = ( k 1 2 − j 1 2 + k 2 2 − j 2 2 + . . . + k n 2 − j n 2 ) / 2 (k_1-j_1)\times r_1+(k_2-j_2)\times r_2+...+(k_n-j_n)\times r_n=(k_1^2-j_1^2+k_2^2-j_2^2+...+k_n^2-j_n^2)/2 (k1j1)×r1+(k2j2)×r2+...+(knjn)×rn=(k12j12+k22j22+...+kn2jn2)/2

最后解方程即可!

代码:

#include<bits/stdc++.h>
using namespace std;

const double EPS=1e-7;
int n;
double point[20][20];
double mapp[15][15];

bool compare(double x,double y){
	if(fabs(x)-fabs(y)>EPS)	return 1;
	return 0;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n+1;i++)
		for(int j=1;j<=n;j++)
			scanf("%lf",&point[i][j]);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			mapp[i][j]=point[i+1][j]-point[i][j];
			mapp[i][n+1]=mapp[i][n+1]-point[i][j]*point[i][j]+point[i+1][j]*point[i+1][j];
		}
		mapp[i][n+1]/=2.0;
	}
		
	for(int i=1;i<=n;i++){
		int u=i;
		for(int j=i+1;j<=n;j++)
			if(!compare(mapp[u][i],mapp[j][i]))
				u=j;
		if(u!=i)	swap(mapp[u],mapp[i]);
		for(int j=1;j<=n;j++){
			if(i==j)	continue;
			double div=mapp[j][i]/mapp[i][i];
			for(int k=i+1;k<=n+1;k++)
				mapp[j][k]-=mapp[i][k]*div;
		}
	}
	for(int i=1;i<=n;i++)
		printf("%0.3lf ",mapp[i][n+1]/mapp[i][i]);
	return 0;
}

T4 P5027 Barracuda

P5027 Barracuda
题目背景
小正方形的冒险旅途,并不顺利。
一路上,小正方形看到了壮美秀丽的小岛被污染,看到了雄伟壮观的火山,还碰到了许许多多的敌人。
眼下,小正方形正在对付一个巨大的三角形。

题目描述
大三角形给小正方形讲起自己的过去:过去的它是一个挖宝工,后来被黑暗之主污染才会落到此番境地。
它也希望小正方形去战胜黑暗之主,不过限于黑暗之主的眼线密布,因此必须给小正方形设置障碍才能骗过那些“眼线”。
他给小正方形的问题是:它有 n n n 个小三角形,每个小三角形有一定的质量,它对这些三角形进行了 n + 1 n+1 n+1 次称量,然而由于托盘天平( ? ? ?)的问题,有一次称量的结果是有误的。
现在,大三角形想要知道最重的小三角形的 编号。
一组输入是合法的,当且仅当输入满足以下条件:
不存在一组 i , j i,j i,j,使得当我们假定第 ii 条称量数据有误时能求出一种合法方案且我们假定第 j j j 条称量数据有误时也能求出一种合法方案。
合法方案定义如下:
1、最重的三角形只有一个。
2、不存在重量不确定的三角形。
3、所有三角形的重量均为正整数。

输入格式
输入的第一行为一个正整数 n n n,表示小三角形的数目。
接下来 n + 1 n+1 n+1 行,每行按照以下格式输入:
首先是一个正整数 m m m,表示这次称量抓了几个小三角形。
接下来 m m m 个整数,表示称量的小三角形的编号。
最后一个整数 w e i g h t weight weight ,表示这次称量的结果。

输出格式
若合法,输出最重小三角形的编号,否则输出 " i l l e g a l " "illegal" "illegal"(不含引号)。

输入输出样例
输入
2
1 1 2
2 1 2 5
2 1 2 1
输出
2
输入
2
1 1 2
2 1 2 4
2 1 2 5
输出
2
输入
2
1 1 2
2 1 2 6
2 1 2 5
输出
illegal

说明/提示
样例一:
若第一次称量结果错误,则无法得出正确解。
若第二次称量结果错误,则第二个小三角形重量为负,显然不对。
若第三次称量结果错误,我们得出 1 1 1 号小三角形重量为 2 2 2 2 2 2号小三角形重量为 3 3 3 2 2 2号小三角形最重。

对于 100% 的数据, 1 ≤ m ≤ n ≤ 100 1 \leq m \leq n \leq 100 1mn100

思路:
发现数据 O ( n 4 ) O(n^4) O(n4)可过,于是暴力枚举错误数据,高斯消元后判断。
(模板别错)

代码:

#include<bits/stdc++.h>
using namespace std;

const double EPS=1e-5;
int n;
int mapp[150][150];
double p[150][150];
int wig[150];
int ans=-1;
bool bz=0;

inline void doit(){
	for(int i=1;i<=n;i++){
		int u=i;
		for(int j=i+1;j<=n;j++)
			if(fabs(p[j][i])>fabs(p[u][i]))	u=j;
		if(u!=i)	swap(p[i],p[u]);
		for(int j=1;j<=n;j++){
			if(i==j)	continue;
			double div=p[j][i]/p[i][i];
			for(int k=i+1;k<=n+1;k++)
				p[j][k]-=p[i][k]*div;
		}
	}
	for(int i=1;i<=n;i++){
		if(fabs(p[i][n+1]/p[i][i]-floor(p[i][n+1]/p[i][i]))>EPS)	return;
		if(fabs((int)(p[i][n+1]/p[i][i]))<EPS){
			wig[i]=0;
			continue;
		}	
		if((int)(p[i][n+1]/p[i][i])<0)	return;
		wig[i]=(int)(p[i][n+1]/p[i][i]);
	}
	int maxx=-1,sum=0,res=-1;
	for(int i=1;i<=n;i++){
		if(wig[i]>maxx){
			res=i;
			maxx=wig[i];
			sum=0;
		}
		if(wig[i]==maxx)	sum++;
		if(wig[i]<0)	return;
	}
	if(sum>1)	return;
	if(ans!=-1){
		printf("illegal");
		bz=1;
		return;
	}
	ans=res;
	return;
}

signed main(){
	scanf("%d",&n);
	for(int i=1;i<=n+1;i++){
		int m;
		scanf("%d",&m);
		for(int j=1;j<=m;j++){
			int x;
			scanf("%d",&x);
			mapp[i][x]=1;
		}
		int w;
		scanf("%d",&w);
		mapp[i][n+1]=w;
	}
	for(int i=1;i<=n+1;i++){
		for(int j=1;j<=n+1;j++)
			for(int k=1;k<=n+1;k++){
				if(j<i)	p[j][k]=(double)mapp[j][k];
				if(j>=i)	p[j][k]=(double)mapp[j+1][k];
			}
		doit();
		if(bz)	return 0;
	}
	if(ans==-1){
		printf("illegal");
		return 0;
	}
	printf("%d",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值