[2018北京ICPC网络赛H] HihoCoder - 1835 K-Dimensional Foil II(点到平面的距离)

题目链接:

点击前往


题目:

时间限制: 1000ms
单点时限: 1000ms
内存限制: 256MB

描述

"K-Dimensional Foil" is a dimensional weapon. Its function is quite easy: It can ascend a region in 3D space to K (K≥3) dimension. One can use it to give the enemy unexpected attack. It was called "The Ultimate Weapon".

--"Remembrance of Mars's Past"

You are the chief technology officer in the space fleet, and your fleet was just suffered from the attack of the K-Dimensional Foil. The good news was that you have found the key parameter K, the dimension of the space. But staying in high dimensional space is very dangerous, you must destroy the K-Dimensional Foil as fast as possible.

You have n spaceships, spaceship i locates at si = (si,1, …, si,K), and the K-Dimensional  Foil is a 1-norm ball with center c = (c1, …, cK) and radius r, a 1-norm ball with center c and radius r is a point set defined as
{x |  d(x, c)  ≤ r}, d(x, c) =∑| xi - ci |

In the formula above, the coordinate of point x is (x1, x2 … xK)

Your spaceships will fire laser cannon to destroy the K-Dimensional Foil. The energy decay is very quick with the increase of the distance in the high dimensional space, so for every spaceship, you want to find the closest point (in Euclidean distance) on the K-Dimensional Foil. It's guaranteed that no spaceship is in the K-Dimensional Foil initially.

输入

The first line of the input is an integer T (T ≤ 100), the number of the test cases.

For each test case, the first line contains two integer n, K (1 ≤ n ≤ 50, 1 ≤ K ≤ 100), the number of spaceship in your fleet and the dimension of the space.

Then one line contains an integer r (1 ≤ r ≤ 104 ), the radius of the K-Dimensional Foil.

Then one line contains K integers c1, … cK, meaning the coordinate of the center of the K-Dimensional Foil.

Then n lines follow. Each line contains K integers si,1, …, si,K, meaning the coordinate of a spaceship.

All the absolute values of the coordinate are smaller than 104.

输出

For each test case, output n lines. The ith line contains K numbers representing the coordinate of the closest point on the K-Dimensional Foil to the ith spaceship. The absolute error between your output and the answer should be less than 10-4

提示

The K-Dimensional Foil in the sample was a square with vertex: (1,0), (0,1), (-1,0), (0,-1)

This problem is special judged.

样例输入
1
2 2
1
0 0
1 1
1 3
样例输出
0.50 0.50
0.00 1.00

题目大意:

题目中在 k k k维空间下的“球”,定义如下:
给定球心 c ( c 1 , . . . , c k ) c(c_1,...,c_k) c(c1,...,ck),和半径 r r r。则所有与圆心 c c c的曼哈顿距离 ≤ r \leq r r的点组成的集合就是 k k k维空间下的球。(点 x x x和点 c c c的曼哈顿距离 d i s ( x , c ) = ∑ i = 1 k ( ∣ x i − c i ∣ ) dis(x,c) = \sum_{i=1}^{k}{(|x_i-c_i|)} dis(x,c)=i=1k(xici),则”球“包含的点的集合可以描述为 { x ∣ d i s ( x , c ) ≤ r } \{x|dis(x,c)\leq r\} {xdis(x,c)r}
然后有 n n n个询问,每个询问包含一个 k k k维空间下的点,要求输出距离该点曼哈顿距离最小的,且在给定的“球”上的点。


解题思路:

在二维的时候,作图如下:(球心为 ( 0 , 0 ) (0,0) (0,0),半径 r = 1 r = 1 r=1)
二维
三维的时候作图如下:(球心为 ( 0 , 0 , 0 ) (0,0,0) (0,0,0),半径 r = 1 r = 1 r=1)
三维

方法一:(利用点到平面的投影)

首先,我们只考虑第一象限(推广到 k k k维就是所有的坐标均 ≥ 0 \geq 0 0)(因为其他的点都可以通过对称对称到第一象限)的点,并且假设球心在远点可以平移过来,最后在平移回去)。在第一象限内,“球”只包含唯一的一个平面,那么,如果平面足够大的话,则显然所求答案就是点在平面上的投影。如果点在平面上的投影超过了平面的范围,我们可以先进行投影,然后找到第一个 ≤ 0 \leq 0 0的分量(显然,该分量在第一象限的平面外面,因为第一象限的所有分量都是非负数),贪心的让他等于 0 0 0 (很显然这个分量等于0,最终的距离最小),然后在对剩下的维度重新进行投影(就相当于少了一个维度,进行了降维。)
计算点到平面的投影,可以参照我的这篇博客。下面的内容较为简略,不明白可以查看上述链接。
在本题中,第一象限内的平面方程为:
(1) x 1 + x 2 + . . . + x k − r = 0 x_1+x_2+...+x_k-r=0\tag{1} x1+x2+...+xkr=0(1)
设已知点 P ( p 1 , p 2 , . . . , p k ) P(p_1,p_2,...,p_k) P(p1,p2,...,pk),投影点 ( v 1 , v 2 , . . . , v k ) (v_1,v_2,...,v_k) (v1,v2,...,vk),两者连线为 l 1 l_1 l1,则 l 1 l_1 l1的参数方程为:
(2) ( x 1 − p 1 ) = ( x 2 − p 2 ) = . . . = ( x k − p k ) = t (x_1-p_1)=(x_2-p_2)=...=(x_k-p_k)=t \tag{2} (x1p1)=(x2p2)=...=(xkpk)=t(2)

(3) { v 1 = t + p 1 v 2 = t + p 2 . . . v k = t + p k \begin{cases} v_1 &=& t + p_1 \tag{3} \\ v_2 &=& t + p_2 \\ &...&\\ v_k &=& t + p_k \\ \end{cases} v1v2vk==...=t+p1t+p2t+pk(3)
( 3 ) (3) (3)代入原方程 ( 1 ) (1) (1)解得
(4) t = − ( p 1 + p 2 + . . . + p k − r ) k t = \cfrac{-(p_1+p_2+...+p_k-r)}{k}\tag{4} t=k(p1+p2+...+pkr)(4)
( 4 ) (4) (4)代入 ( 3 ) (3) (3)中得:
(2.5) ( v 1 , v 2 , . . . , v k ) = { v 1 = t + p 1 v 2 = t + p 2 . . . v k = t + p k = { v 1 = − ( p 1 + p 2 + . . . + p k − r ) k + p 1 v 2 = − ( p 1 + p 2 + . . . + p k − r ) k + p 2 . . . v k = − ( p 1 + p 2 + . . . + p k − r ) k + p k \begin{aligned} (v_1,v_2,...,v_k)&= \tag{2.5} \begin{cases} v_1 &=& t + p_1 \\ v_2 &=& t + p_2 \\ &...&\\ v_k &=& t + p_k \\ \end{cases}\\ &= \begin{cases} v_1 &=& \cfrac{-(p_1+p_2+...+p_k-r)}{k} + p_1 \\ v_2 &=& \cfrac{-(p_1+p_2+...+p_k-r)}{k} + p_2 \\ &...&\\ v_k &=& \cfrac{-(p_1+p_2+...+p_k-r)}{k} + p_k \\ \end{cases}\\ \end{aligned} (v1,v2,...,vk)=v1v2vk==...=t+p1t+p2t+pk=v1v2vk==...=k(p1+p2+...+pkr)+p1k(p1+p2+...+pkr)+p2k(p1+p2+...+pkr)+pk(2.5)
按照公式计算即可。具体见代码


方法二:(贪心)

根据题意,给定已知点 P ( p 1 , p 2 , . . . , p k ) P(p_1,p_2,...,p_k) P(p1,p2,...,pk),需要求得 k k k维球面上的点V ( v 1 , v 2 , . . . , v k ) (v_1,v_2,...,v_k) (v1,v2,...,vk)(球心为 ( c 1 , c 2 , . . . , c k ) (c_1,c_2,...,c_k) (c1,c2,...,ck),半径为 r r r),使得 d i s ( P , V ) dis(P,V) dis(P,V)最小,其中:
{ d i s ( P , V ) = ∑ i = 1 k ( p i − v i ) 2 ∑ i = 1 k ( ∣ v i − c i ∣ ) = r \begin{cases} dis(P,V)=\sum_{i=1}^{k}(p_i-v_i)^2 \\ \sum_{i=1}^{k}{(|v_i-c_i|)}=r\\ \end{cases} {dis(P,V)=i=1k(pivi)2i=1k(vici)=r
根据题意,我们可以得到下式
∑ i = 1 k ( ∣ p i − c i ∣ ) = T ( T ≥ r ) \sum_{i=1}^{k}{(|p_i-c_i|)} = T (T\geq r) i=1k(pici)=T(Tr)
设:
d = T − r d = T - r d=Tr
那么,原题目可以等价成:
我们有一个点 P ′ ( p 1 ′ , p 2 ′ , . . . , p k ′ ) P'(p'_1,p'_2,...,p'_k) P(p1,p2,...,pk),初始 P ′ = P P'=P P=P,然后之后的每一次操作都可以选择某一个维度的分量 p i ′ p'_i pi,使得 p i ′ = p i ′ ± 1 p'_i = p'_i\pm1 pi=pi±1,要求在 d d d次操作之后, ∑ i = 1 k ( ∣ p i ′ − c i ∣ ) = r \sum_{i=1}^{k}{(|p'_i-c_i|)}=r i=1k(pici)=r,同时 ∑ i = 1 k ( p i ′ − p i ) 2 \sum_{i=1}^{k}(p'_i-p_i)^2 i=1k(pipi)2最小。
每进行一次操作都可以让 ∑ i = 1 k ( ∣ p i ′ − c i ∣ ) \sum_{i=1}^{k}{(|p'_i-c_i|)} i=1k(pici)减小 1 1 1(即使得某一 ∣ p i ′ − c i ∣ |p'_i-c_i| pici变小 1 1 1),


代码:

方法一:

#include <cstdio>
#include <cmath>

using namespace std;
const int MAXN = 200;
double c[MAXN], temp[MAXN], ans[MAXN];
int flag[MAXN];
bool low[MAXN];

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        int n, k, r;
        scanf("%d %d %d", &n, &k, &r);
        for (int i = 0; i < k; i++) scanf("%lf", &c[i]);
        for (int i = 0; i < n; i++) {
            double sum = 0, tk = k;
            for (int j = 0; j < k; j++) {
                scanf("%lf", &temp[j]);
                temp[j] -= c[j];
                flag[j] = (temp[j] >= 0 ? 1 : -1);
                temp[j] = fabs(temp[j]);
                sum += temp[j];
                low[j] = false;
            }
            while (true) {
                bool tFlag = true;
                for (int j = 0; j < k; j++) {
                    if (low[j]) continue;
                    ans[j] = (r - sum) / tk + temp[j];
                    if (ans[j] < 0) {
                        ans[j] = 0, low[j] = true, tk--, tFlag = false, sum -= temp[j];
                        break;
                    }
                }
                if (tFlag) break;
            }
            for (int j = 0; j < k; j++)
                printf("%.5f ", flag[j] * ans[j] + c[j]);
            putchar('\n');
        }
    }
    return 0;
}

方法二:(贪心)

先挖坑,后填。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值