AcWing 207. 球形空间产生器

AcWing 207. 球形空间产生器

一、题目描述

有一个球形空间产生器能够在 n 维空间中产生一个坚硬的球体。

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

注意: 数据保证有唯一解。

输入格式
第一行是一个整数 n

接下来的 n+1 行,每行有 n 个实数,表示球面上一点的 n 维坐标。

每一个实数精确到小数点后 6 位,且其绝对值都不超过 20000

输出格式
有且只有一行,依次给出球心的 n 维坐标(n 个实数),两个实数之间用一个空格隔开。

每个实数精确到小数点后 3 位。

数据范围
1n10

输入样例:

2
0.0 0.0
-1.0 1.0
1.0 0.0

输出样例:

0.500 1.500

二、解题思路

前置知识:高斯消元法求解线性方程组

设球心坐标为(x1,x2,...,xn)
对于给定的n+1个点坐标为(bi1,bi2,...,bin)

根据题意有如下n+1个方程构成的方程组

{(b01x1)2+(b02x2)2+...+(b0nxn)2=R2 (b11x1)2+(b12x2)2+...+(b1nxn)2=R2 (b21x1)2+(b22x2)2+...+(b2nxn)2=R2 ...(bn1x1)2+(bn2x2)2+...+(bnnxn)2=R2 

因为我们学过的知识,只有 高斯消元可以解这样的方程组,但本题的方程是二次方的,肯定是不行,需要想办法把二次方消掉,化简为一次的多元线性方程组,才能使用高斯消元。

进行公式变换,把二项全部消掉:
{...(n+1)
有:

{2(b11b01)x1+2(b12b02)x2+...+2(b1nb0n)xn=b112+b122+...+b1n2b012b022...b0n22(b21b01)x1+2(b22b02)x2+...+2(b2nb0n)xn=b112+b122+...+b1n2b012b022...b0n2...2(bn1b01)x1+2(bn2b02)x2+...+2(bn1b0n)xn=bn12+bn22+...+bnn2b012b022...b0n2

令:

{2(bijb0j)=aijbi12+bi22+...+bin2b012b022...b0n2=ai,n+1 i,j[1,n]

则有:

{a11x1+a12x2+...+a1nxn=a1,n+1a21x1+a22x2+...+a2nxn=a2,n+1...an1x1+an2x2+...+annxn=an,n+1

如此变化后,就得到一个n个变量,n个等式的 线程方程组,可以使用 高斯消元 来处理。

三、实现代码

#include <bits/stdc++.h>

using namespace std;
const int N = 15;
const double eps = 1e-8; // 小数的精度值
int n;
double a[N][N], b[N][N];

int gauss() {                                                    // 高斯消元,答案存于a[i][n]中,0 <= i < n
    int r = 1;                                                   // 先按行后按列进行计算,当前行是第1行
    for (int c = 1; c <= n; c++) {                               // 枚举每一列
        int t = r;                                               // 防破坏r,复制出t
        for (int i = r; i <= n; i++)                             // 当前行需要找它的后续行
            if (abs(a[i][c]) > abs(a[t][c])) t = i;              // t的任务是找出c列中系数最大值是哪一行
        if (abs(a[t][c]) < eps) continue;                        // 如果c列绝对值最大的系数是0, 那么处理下一列
        for (int i = c; i <= n + 1; i++) swap(a[t][i], a[r][i]); // 将绝对值最大的行与当前行交换
        for (int i = n + 1; i >= c; i--) a[r][i] /= a[r][c];     // a[r][c]:行首系数,将当前行的行首通过除法变为1,倒序
        for (int i = r + 1; i <= n; i++)                         // 用当前行r的c列,通过减法将后续行c列消成0
            for (int j = n + 1; j >= c; j--)                     // 倒序,需要保留行首,逻辑和上面是一样的,行首值是变更系数,如果正序就把系数变成1了,后面就不对了
                a[i][j] -= a[r][j] * a[i][c];                    // a[i][c]:需要变化的乘法系数,减法:对位相消
        r++;                                                     // 下一行
    }
    if (r <= n) { // 如果没有成功执行完所有行,意味着中间存在continue,也就是某一列的系数都是0
        for (int i = r; i <= n; i++)
            if (abs(a[i][n + 1]) > eps) return 0; // 系数是0,但结果不是0,无解
        return 2;                                 // 系数是0,结果也是0,x取啥都对,有无穷多组解
    }
    // 代回求每个变量值
    for (int i = n - 1; i; i--)                   // 行,倒序
        for (int j = i + 1; j <= n; j++)          // 列,倒三角,右上角应该都是0,对角线全是1
            a[i][n + 1] -= a[i][j] * a[j][n + 1]; // 系数消为0
    return 1;                                     // 有唯一解
}

int main() {
    cin >> n;
    for (int i = 0; i <= n; i++)     // n+1行,下标从0开始
        for (int j = 1; j <= n; j++) // n列
            cin >> b[i][j];

    // 转换为线性方程组
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            a[i][j] += 2 * (b[i][j] - b[0][j]);
            a[i][n + 1] += b[i][j] * b[i][j] - b[0][j] * b[0][j];
        }
    // Gauss消元
    gauss();
    // 输出答案
    for (int i = 1; i <= n; i++) printf("%.3lf ", a[i][n + 1]);

    return 0;
}
posted @   糖豆爸爸  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2019-06-13 慢查询处理
2018-06-13 C#检查服务状态和启动关闭服务
2018-06-13 判断当前主机是不是阿里云内网
2015-06-13 mysql错误Table ‘./mysql/proc’ is marked as crashed and should be repaired
Live2D
点击右上角即可分享
微信分享提示