[JSOI2008]球形空间产生器

题目描述

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

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1:

2
0.0 0.0
-1.0 1.0
1.0 0.0

输出样例#1:

0.500 1.500

说明

提示:给出两个定义:


高维球长啥样啊 ==

只能拿三维的来想><

我们知道球上的任意一点到球心的距离都等于半径

然后我们又有给定提示的第二个公式

我们就可以列方程辣

我们设给的n+1个点为(A,B,C)

设球心为(x)

显然我们可以列一个方程(以第一个点为例)

(sum_{i=1}^{n}{(A[i]-X[i])^2}=r^2)

然后我们化简一下

(sum_{i=1}^{n}{A[i]^2} - 2*sum_{i=1}^{n}{A[i]*X[i]}+sum_{i=1}^{n}{X[i]^2}=r^2)

然后我们发现这是个二次的方程,而且还不知道r

那高斯消元个锤子

然后我们发现一个问题

由n+1个点组成的方程组第1项是常数,每个方程的第3项都相同

所以我们可以作差

我们还是以第一项和第二项为例

这样式子就成了(sum_{i=1}^{n}{2(A[i]-B[i])X[i]}=sum_{i=1}^{n}{A[i]^2-B[i]^2})

然后就化成了n个方程,并且只有n个未知数,并且最高次项是1次

就可以高斯消元辣

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
const int M = 15 ;
const double eps = 1e-7 ;
using namespace std ;
int n ;
double B[M][M] , p[M][M] , sum[M] ;
inline void Gauss() {
	for(int i = 1 ; i <= n ; i ++) {
		int Tab = i ;
		for(int j = i ; j <= n ; j ++)
		  if(fabs(fabs(B[j][i]) - fabs(B[Tab][i])) <= eps)
		    Tab = j ;
		for(int j = 1 ; j <= n + 1 ; j ++) swap(B[i][j] , B[Tab][j]) ;
	    for(int j = i + 1 ; j <= n + 1 ; j ++) B[i][j] /= B[i][i] ;
	    for(int j = 1 ; j <= n ; j ++) 
	      if(i != j)
	        for(int k = i + 1 ; k <= n + 1 ; k ++)
	          B[j][k] -= B[j][i] * B[i][k] ;
	}
	for(int i = 1 ; i <= n ; i ++) printf("%.3lf ",B[i][n + 1]) ;
}
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]) ;
	    sum[i] += p[i][j] * p[i][j] ;
	  }
	for(int i = 1 ; i <= n ; i ++) {
		for(int j = 1 ; j <= n ; j ++)
			B[i][j] = ( p[i][j] - p[i + 1][j] ) * 2 ;
	    B[i][n + 1] = sum[i] - sum[i + 1] ;
	}
	Gauss() ; 
	return 0 ;
}
原文地址:https://www.cnblogs.com/beretty/p/9539000.html