ZigZagKmp
Think twice, code once.

题目传送门

算法分析

不妨设未知数\(d\)表示每一个点到球心的距离,设未知数\(b_1,b_2,\cdots,b_n\)表示球心的坐标。

我们不难得到\(n+1\)个方程:

\[d^2=\sum_{i=1}^n{(a_i-b_i)^2} \]

发现出现二次项了,这里显然不可以用换元法把二次项换成一次项。

我们把这个式子展开:

\[d^2=\sum_{i=1}^n{a_i^2}+\sum_{i=1}^n{b_i^2}+2\times \sum_{i=1}^n{a_ib_i} \]

未知数写到同一侧:

\[-2\times \sum_{i=1}^n{a_ib_i}-\sum_{i=1}^n{b_i^2}+d^2=\sum_{i=1}^n{a_i^2} \]

这时我们发现,对于每一个方程,所有的二次项系数都是相同的,因此我们考虑邻项相消,得出如下的\(n\)个方程:

\[-2\times \sum_{i=1}^n{(a_i-a_{i-1})b_i}=\sum_{i=1}^n{a_i^2-a_{i-1}^2} \]

这符合高斯消元的条件,直接做即可。

代码实现

#include<bits/stdc++.h>
using namespace std;
#define maxn 105
int n;
double a[maxn][maxn],b[maxn][maxn],x[maxn][maxn];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n+1;i++){
		for(int j=1;j<=n;j++){
			scanf("%lf",&x[i][j]);
		}
	}
	for(int i=1;i<=n+1;i++){
		for(int j=1;j<=n;j++){
			b[i][j]=(-2)*x[i][j];
		}
		for(int j=1;j<=n;j++)b[i][n+1]-=(x[i][j]*x[i][j]);
	}
	for(int i=1;i<=n+1;i++){
		for(int j=2;j<=n+1;j++){
			a[i][j]=b[i][j]-b[i-1][j];
		}
	}
	for(int i=1;i<=n;i++){
		int p=0;
		for(int j=i;j<=n;j++){if(a[j][i]){p=j;break;}}
		if(!p)continue;
		for(int j=1;j<=n+1;j++)swap(a[i][j],a[p][j]);
		double k=a[i][i];
		for(int j=1;j<=n+1;j++)a[i][j]/=k;
		for(int j=1;j<=n;j++){
			if(i==j)continue;
			double kk=a[j][i];
			for(int l=1;l<=n+1;l++)a[j][l]-=kk*a[i][l];
		}
	}
	for(int i=1;i<=n;i++)printf("%.3lf ",a[i][n+1]);
	return 0;
}

posted on 2019-09-09 12:41  ZigZagKmp  阅读(151)  评论(0编辑  收藏  举报