题目链接
Solution [JSOI2008]球形空间产生器
题目大意:在\(n\)维空间中给定\(n + 1\)个点,求到\(n + 1\)个点的欧几里得距离相等的点
高斯消元
分析:
假设我们有点\((x_1,x_2,x_3, \dots,x_n)\),到点\((a_1,a_2,a_3, \dots,a_n)\),\((b_1,b_2,b_3, \dots,b_n)\)相等
有\(\sqrt{\sum(x_i-a_i)^2}=\sqrt{\sum(x_i-b_i)^2}\)
显然根号下都是非负数
\(\sum(x_i-a_i)^2=\sum(x_i-b_i)^2\)
平方差
\(\sum(2x_i-a_i-b_i)(b_i-a_i)=0\)
展开式子
\(\sum(-2a_ix_i+2b_ix_i+{a_i}^2-{b_i}^2)=0\)
整理得
\(\sum x_i \cdot (-2a_i+2b_i)=\sum({b_i}^2-{a_i}^2)\)
然后这就是一个\(n\)元一次方程了,我们可以\(n^2\)求出\(n\)元一次方程组,然后对这个方程组进行高斯消元即可,为了数值稳定性每次选取绝对值最大的作为主元,取前\(n\)项回带即可
\(Trick:\)一个\(n\)维的数组实际上每个元素都是指针,指向\(n-1\)维的数组,\(0\)维自然就是元素啦,因此我们可以直接交换指针,做到常数时间内交换方程组两项
又一个\(Trick:\)输出调试信息可以用\(cerr\),以避免忘记删除调试信息而\(GG\)
#include
#include
#include
using namespace std;
typedef double type;
const int maxn = 16;
type val[maxn][maxn],mat[maxn * maxn][maxn];
int n,tot;
inline void add(int a,int b){
tot++;
for(int i = 1;i <= n;i++)
mat[tot][i] = -2 * val[a][i] + 2 * val[b][i],mat[tot][n + 1] += val[b][i] * val[b][i] - val[a][i] * val[a][i];
}
inline void gauss(){
for(int i = 1;i < n;i++){
for(int j = i + 1;j <= tot;j++)
if(fabs(mat[j][i]) > fabs(mat[i][i]))swap(mat[i],mat[j]);
for(int k = i + 1;k <= tot;k++){
type f = mat[k][i] / mat[i][i];
for(int j = i;j <= n + 1;j++)
mat[k][j] -= mat[i][j] * f;
}
}
for(int i = n;i >= 1;i--){
for(int j = i + 1;j <= n;j++)
mat[i][n + 1] -= mat[j][n + 1] * mat[i][j];
mat[i][n + 1] /= mat[i][i];
}
}
int main(){
ios::sync_with_stdio(false);
cin >> n;
for(int i = 1;i <= n + 1;i++)
for(int j = 1;j <= n;j++)
cin >> val[i][j];
for(int i = 1;i <= n + 1;i++)
for(int j = i + 1;j <= n + 1;j++)
add(i,j);
gauss();
cout << setprecision(3) << fixed;
for(int i = 1;i <= n;i++)
cout << mat[i][n + 1] << ' ';
cout << '\n';
return 0;
}