P8141 [ICPC 2020 WF] What’s Our Vector, Victor? 题解(ChatGPT)

news/2025/3/20 22:39:44/文章来源:https://www.cnblogs.com/zifanoi/p/18784135
  • https://www.zhihu.com/question/581556221/answer/97377273573
  • [ICPC 2020 WF] What’s Our Vector, Victor?

题目理解

题目要求在 \(d\) 维空间中确定一个未知点 \(x\)(你的位置),已知空间中 \(n\) 个蜗牛的坐标 \(p_1, p_2, \dots, p_n \in \mathbb{R}^d\) 以及你与每只蜗牛之间的欧几里得距离 \(r_1, r_2, \dots, r_n\)(数据保证一致,即存在一个 \(x\) 同时满足所有距离方程)。数学上,对于所有 \(i=1,\dots,n\)

\[\|x - p_i\| = r_i. \]

由于数据均由真实位置生成,所以这些方程是一致的。


解题思路

1. 消去二次项构造线性方程组

对每个 \(i\),我们有

\[\|x-p_i\|^2 = r_i^2. \]

选取第一个蜗牛作为参考,将 \(i\ge2\) 的方程与 \(i=1\) 的方程相减:

\[\|x-p_i\|^2 - \|x-p_1\|^2 = r_i^2 - r_1^2. \]

展开后,注意到 \(x^T x\) 相互抵消,得到

\[-2p_i^T x + \|p_i\|^2 + 2p_1^T x - \|p_1\|^2 = r_i^2 - r_1^2, \]

整理后写为

\[(p_i - p_1)^T x = \frac{r_1^2 - r_i^2 + \|p_i\|^2 - \|p_1\|^2}{2}. \]

\[A_i = p_i - p_1,\quad b_i = \frac{r_1^2 - r_i^2 + \|p_i\|^2 - \|p_1\|^2}{2},\quad (i=2,\dots,n). \]

这样就得到了一个 $ (n-1) \times d$ 的线性方程组

\[A x = b. \]

2. 求解线性系统

  • 满秩情况:若方程组满秩(通常当 \(n\ge d+1\) 且点的分布足够“通用”时成立),则线性系统有唯一解。用高斯消元求得特解 \(x_0\)(对自由变量直接置零),理论上应满足所有差分方程,并且利用第一个方程(\(\|x-p_1\|=r_1\))也能成立。

  • 欠定情况或数值误差:当 \(n-1 < d\) 或矩阵 \(A\) 的秩小于 \(d\) 时,系统欠定,得到的特解 \(x_0\)可能不满足第一个方程,即可能有

    \[\|x_0-p_1\| \neq r_1. \]

    但由于数据一致,实际存在解。注意到一般解可写成

    \[x = x_0 + v,\quad \text{其中 } v \in \mathrm{Null}(A). \]

    为了修正 \(x_0\) 使得 \(\|x-p_1\| = r_1\),我们作如下处理:

    • \(w_0 = x_0 - p_1\)。将 \(w_0\) 分解为行空间和零空间两部分。利用对 \(A\) 的行空间做 Gram–Schmidt 正交化,计算 \(w_0\) 在行空间上的投影,然后零空间分量即为

      \[v = w_0 - \mathrm{Proj}_{\mathrm{Row}(A)}(w_0). \]

    • 令修正解为

      \[x = x_0 + \alpha v, \]

      并要求

      \[\|x-p_1\| = \|w_0 + \alpha v\| = r_1. \]

      展开后得到一元二次方程(关于 \(\alpha\)),求解后选择合适的 \(\alpha\) 使得调整最小,从而获得满足所有条件的解。
  • 满秩但由于数值原因误差较大时:若系统满秩但得到的 \(x_0\) 与参考距离 \(r_1\) 仍有较大误差,可以沿着 \(p_1 \to x_0\) 的方向进行缩放修正,使得最终 \(\|x-p_1\|=r_1\)

  • 特殊情况 \(n=1\):只有一个距离信息时,任取 \(p_1 + (r_1,0,\dots,0)\) 即可。

3. 数值精度

由于题目中数据以 17 位精度给出,且 \(d, n\) 的范围最多为 500,为了保证误差不超过 \(10^{-5}\),所有计算均采用 long double 类型,并且在高斯消元、正交化等步骤中设置了较小的容差。


代码时间复杂度分析

代码主要分为以下几个部分:

  1. 高斯消元求解线性系统

    • 对一个 \(m \times d\) 的矩阵(其中 \(m = n-1\))进行消元,复杂度大致为 \(O(m \cdot d^2)\)
    • 最坏情况下 \(n, d \le 500\),故该部分的复杂度为 \(O(500 \times 500^2) = O(125\,000\,000)\) 次基本运算。
  2. Gram–Schmidt 正交化

    • 用于计算 \(A\) 的行空间正交基。设矩阵行数为 \(m\),每个向量维数为 \(d\),此部分的复杂度为 \(O(m \cdot d^2)\)
    • 最坏情况同样为 \(O(125\,000\,000)\) 次基本运算。
  3. 其他向量加减、点积和范数计算

    • 每次操作均为 \(O(d)\),且总次数不会超过 \(O(n \cdot d)\)\(O(m \cdot d)\),在本题范围内开销可以忽略。

综上,总的时间复杂度为

\[O(m \cdot d^2) = O(n \cdot d^2) \]

在最坏情况下 \(n = 500,\, d = 500\) 时约为 \(125\)M 次基本运算,加上常数因子(以及 long double 可能的额外开销),但由于数据规模较小且常数较低,该算法在 C++ 编译器优化(如 -O2)下能够在 1000ms 时限内顺利通过。


总结

本题利用“多边定位”(multilateration)的思想,通过对距离平方方程做差分消去二次项,构造出一个线性方程组,从而求得候选解 \(x_0\)。当系统欠定或数值误差较大时,通过将 \(x_0\) 在零空间上做适当调整,使得最终解满足所有距离条件。整个过程使用高精度 long double 保证结果精度,并且时间复杂度为 \(O(n \cdot d^2)\) ,能够满足题目数据范围要求。

代码:

#include <bits/stdc++.h>
using namespace std;// 使用 long double 以提高计算精度
const long double EPS = 1e-18L;
const long double TOL = 1e-7L;// 计算向量 v 的欧式范数
long double normVec(const vector<long double>& v) {long double s = 0.0L;for (long double x : v) s += x * x;return sqrt(s);
}// 计算两个向量的点积
long double dotVec(const vector<long double>& a, const vector<long double>& b) {long double s = 0.0L;int n = a.size();for (int i = 0; i < n; i++) s += a[i] * b[i];return s;
}// 高斯消元:求解 A*x = b
// A 为 m×d 矩阵,b 为 m 维向量。
// 在欠定系统中,令自由元为 0。
// 同时输出矩阵的秩以及主元所在列(pivot_col)。
vector<long double> gaussElim(vector<vector<long double>> A, vector<long double> b, int d, int m, int &rank, vector<int> &pivot_col) {rank = 0;int row = 0, col = 0;pivot_col.clear();while(row < m && col < d) {// 在 col 列中从 row 到 m-1 寻找主元int pivot = row;for (int i = row; i < m; i++){if (fabsl(A[i][col]) > fabsl(A[pivot][col]))pivot = i;}if(fabsl(A[pivot][col]) < EPS){col++;continue;}if(pivot != row){swap(A[row], A[pivot]);swap(b[row], b[pivot]);}pivot_col.push_back(col);long double pivVal = A[row][col];for (int j = col; j < d; j++){A[row][j] /= pivVal;}b[row] /= pivVal;for (int i = 0; i < m; i++){if(i != row){long double factor = A[i][col];if(fabsl(factor) > EPS){for (int j = col; j < d; j++){A[i][j] -= factor * A[row][j];}b[i] -= factor * b[row];}}}row++;col++;}rank = row;vector<long double> x(d, 0.0L);for (int i = 0; i < rank; i++){int c = pivot_col[i];x[c] = b[i];}return x;
}// 用 Gram–Schmidt 正交化法计算矩阵 A(每一行视为 R^d 中的一个向量)的行空间正交基
vector<vector<long double>> computeOrthonormalBasis(const vector<vector<long double>> &A) {int m = A.size();int d = (m ? A[0].size() : 0);vector<vector<long double>> basis;for (int i = 0; i < m; i++){vector<long double> v = A[i];for(auto &q : basis) {long double proj = dotVec(v,q);for (int j = 0; j < d; j++){v[j] -= proj * q[j];}}long double normv = normVec(v);if(normv > EPS){for (int j = 0; j < d; j++){v[j] /= normv;}basis.push_back(v);}}return basis;
}// 主函数
int main(){ios::sync_with_stdio(false);cin.tie(nullptr);int d, n;cin >> d >> n;vector<vector<long double>> p(n, vector<long double>(d, 0.0L));vector<long double> r(n, 0.0L);for (int i = 0; i < n; i++){for (int j = 0; j < d; j++){double tmp;cin >> tmp;p[i][j] = tmp;}double tmp;cin >> tmp;r[i] = tmp;}// 若只有一个蜗牛,则随便选一个在 p[0] 距离 r[0] 的点if(n == 1){vector<long double> ans = p[0];ans[0] += r[0];cout << fixed << setprecision(12);for (int i = 0; i < d; i++){cout << (long double)ans[i] << ( i==d-1 ? "\n" : " ");}return 0;}// 以第一个蜗牛的位置 p[0] 为参考vector<long double> p1 = p[0];long double r1 = r[0];// 对 i=2..n 建立差分方程: (p[i]-p1)^T x = (r1^2 - r[i]^2 + ||p[i]||^2 - ||p1||^2)/2int m = n - 1;vector<vector<long double>> A_orig(m, vector<long double>(d, 0.0L));for (int i = 1; i < n; i++){for (int j = 0; j < d; j++){A_orig[i-1][j] = p[i][j] - p1[j];}}// 为消元保存一份副本vector<vector<long double>> A = A_orig;vector<long double> b(m, 0.0L);long double normP1Sq = 0.0L;for (int j = 0; j < d; j++){normP1Sq += p1[j]*p1[j];}for (int i = 1; i < n; i++){long double normPiSq = 0.0L;for (int j = 0; j < d; j++){normPiSq += p[i][j]*p[i][j];}long double r1sq = r1*r1, ri_sq = r[i]*r[i];b[i-1] = (r1sq - ri_sq + normPiSq - normP1Sq) / 2.0L;}int rank;vector<int> pivot_col;vector<long double> x0 = gaussElim(A, b, d, m, rank, pivot_col);// 检查 x0 是否满足第一个蜗牛的距离条件: ||x0-p1|| = r1vector<long double> diff(d, 0.0L);for (int j = 0; j < d; j++){diff[j] = x0[j] - p1[j];}long double norm_diff = normVec(diff);long double err = fabsl(norm_diff - r1);if(err < TOL){cout << fixed << setprecision(12);for (int j = 0; j < d; j++){cout << (long double)x0[j] << (j==d-1 ? "\n" : " ");}return 0;}// 如果系统欠定(rank < d),则利用零空间进行修正:// 记 w0 = x0-p1,将其在 A_orig 行空间上的正交投影记为 proj,// 则零空间成分为 v = w0 - proj. 令修正后解为 x_new = x0 + α*v,// 要求 ||(x0 + α*v) - p1|| = r1,从而解出 α。if(rank < d) {vector<vector<long double>> Q = computeOrthonormalBasis(A_orig); // 行空间正交基vector<long double> w0 = diff; // = x0-p1vector<long double> proj(d, 0.0L);for(auto &q : Q) {long double dp = dotVec(w0, q);for (int j = 0; j < d; j++){proj[j] += dp * q[j];}}// v 为 w0 在零空间中的分量vector<long double> v(d, 0.0L);for (int j = 0; j < d; j++){v[j] = w0[j] - proj[j];}long double norm_v = normVec(v);if(norm_v < EPS){// 极少数情况下无可修正分量,输出 x0cout << fixed << setprecision(12);for (int j = 0; j < d; j++){cout << (long double)x0[j] << (j==d-1 ? "\n" : " ");}return 0;}// 求解 α,使得 ||w0 + α*v|| = r1// 即: ||w0||^2 + 2α (w0·v) + α^2||v||^2 = r1^2.long double a_coef = norm_v * norm_v;long double b_coef = 2.0L * dotVec(w0, v);long double c_coef = norm_diff * norm_diff - r1 * r1;long double disc = b_coef * b_coef - 4.0L * a_coef * c_coef;if(disc < 0) disc = 0;long double sqrt_disc = sqrt(disc);long double alpha1 = (-b_coef + sqrt_disc) / (2.0L * a_coef);long double alpha2 = (-b_coef - sqrt_disc) / (2.0L * a_coef);long double alpha = (fabsl(alpha1) < fabsl(alpha2) ? alpha1 : alpha2);vector<long double> x_new(d, 0.0L);for (int j = 0; j < d; j++){x_new[j] = x0[j] + alpha * v[j];}// 检查修正后是否满足要求vector<long double> diff_new(d, 0.0L);for (int j = 0; j < d; j++){diff_new[j] = x_new[j] - p1[j];}long double norm_new = normVec(diff_new);if(fabsl(norm_new - r1) < TOL){cout << fixed << setprecision(12);for (int j = 0; j < d; j++){cout << (long double)x_new[j] << (j==d-1 ? "\n" : " ");}return 0;} else {// 若仍有微小误差,直接输出修正解cout << fixed << setprecision(12);for (int j = 0; j < d; j++){cout << (long double)x_new[j] << (j==d-1 ? "\n" : " ");}return 0;}}// 若系统满秩(rank == d)而误差仍较大,则说明由于数值问题造成误差(理论上唯一解应同时满足所有条件)。// 此时我们用一个一维牛顿法沿 p1->x0 的射线修正(注意:这种调整会破坏差分方程,但数据本身一定一致)。long double t = r1 / norm_diff;vector<long double> x_new(d, 0.0L);for (int j = 0; j < d; j++){x_new[j] = p1[j] + t*(x0[j]-p1[j]);}cout << fixed << setprecision(12);for (int j = 0; j < d; j++){cout << (long double)x_new[j] << (j==d-1 ? "\n" : " ");}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/902251.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

20241105 实验一 《Python程序设计》

课程:《Python程序设计》 班级: 2411 姓名: 王梓墨 学号:20241105 实验教师:王志强 实验日期:2025年3月12日 必修/选修: 公选课 一.实验内容 1.熟悉Python开发环境; 2.练习Python运行、调试技能;(编写书中的程序,并进行调试分析) 3.编写程序,练习变量和类型、字…

英语四级备考第二天

第二天 今天是开始英语备考的第二天,当迈出第二步的时候,就意味着正走在通过考试的路上。到时当你以425分毋庸置疑地通过考试时,过去的90天都不曾虚度。 单词 今天新学的单词加上昨天应复习的单词,在50~60个之间。阅读 今天的阅读还是用扇贝单词推荐的包含学习的单词的文章…

投资日记_道氏理论技术分析

主要用于我自己参考,我感觉我做事情的时候容易上头,忘掉很多事情。技术分析有很多方法,但是我个人相信并实践的还是以道氏理论为根本的方法。方法千千万万只有适合自己价值观,习惯,情绪,性格的方法才是好的方法。 趋势 趋势是技术分析的根本,要是连当前趋势都看不懂,最…

asp.net core webapi 完整Swagger配置

在当前项目下新建Utility文件夹,Utility文件夹下面在创建SwaggerExt文件夹,文档结果如下 CustomSwaggerExt.cs文件如下using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models;namespace xxxxxxxxxx {/// <summary>/// 扩展Swagger/// </summary>pub…

ciscnccb半决赛

AWDP typo 一道2.31的堆题漏洞点位于edit功能,snprintf函数把用户输入作为format,导致了堆溢出以及格式化字符串漏洞fix 从程序的代码不难看出分配出来的堆,前面八个字节是堆的size,后面的空间才是数据域 这里原意是修改heap的size,但是用错了函数,我们修改最大读入的siz…

AI全天候智能助手,为您构建私人数据库

在数字化转型浪潮中,AI与大数据技术已成为企业提升效率、优化服务的核心引擎。思通数科凭借其自主研发的大数据智能系统,以AI为核心,打造了一站式解决方案,覆盖消费者服务、商家赋能与平台运营三大领域,助力用户与合作伙伴实现智能化升级。以下是该系统的核心功能与价值解…

安装 Prometheus监控主机服务

一、安装 Prometheus 下载 Prometheus 首先,访问 Prometheus 官网 获取最新版本的下载链接,然后使用 wget 下载:wget https://github.com/prometheus/prometheus/releases/download/v3.2.1/prometheus-3.2.1.linux-amd64.tar.gz解压并安装解压下载的文件:tar -xvzf prometh…

L1 通讲

好多,好多。L1 通讲 部分知识点速通 技术与产品开发的动机 ​ 这张图展示了两个长期趋势:技术和创新的发展速度逐渐变快; 它对我们的生活影响非常广泛,包括好的(如天花疫苗)和坏的(核弹?) 技术变得越来越强大。 例如,我们的祖先使用石制工具,但现在我们构建跨越全球…

Flink 实战之流式数据去重

流式数据是一种源源不断产生的数据,没有预定的开始与结束,至少理论上来说,它的数据输入永远不会结束。因此流式数据处理与传统的批处理技术不同,必须具备持续不断地对到达的数据进行处理的能力。因为流式数据源源不断地产生,对流式数据做去重就十分困难,因为一条数据重复…

vue3 + springboot 实现模糊查询与增加操作

实现表格查询: <!-- 表格 --><div class="card" style="margin-bottom: 5px"><el-table :data="data.tableData" stripe><el-table-column label="名称" prop="name" /><el-table-column lab…

网络基础与进阶

计算机网络入门与进阶 学习OSI网络模型相关概念(重点掌握) 学习TCP三次握手与四次挥手过程(重点掌握) 学习TCP的11种状态集转化(重点掌握) 学习DNS相关知识概念与原理 linux网关配置(添加网关 网段 以及网络主机路由) 修改网卡配置文件 用户访问www.baidu.com 整个过程…