ACM/ICPC 2018亚洲区预选赛北京赛站网络赛 K-Dimensional Foil II(思维题)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37025443/article/details/82924408

时间限制: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.

扫描二维码关注公众号,回复: 4129263 查看本文章

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维空间,定义一个半径为r的物体,圆心在(c1,c2,...,ck)

一个点在该物体内或在物体上需要满足坐标{x |  d(x, c)  ≤ r}, d(x, c) =∑| xi - ci |

然后n个询问,每一个询问该你一个物体外面的点,问你该物体内/上,离该点最近的点的坐标(这里的距离是欧几里得距离)

解析:

首先我们需要把物体的圆心移回到原点,即将xi-ci

然后可以先写出一个公式x(已知询问的点),y(物体上的点)

S=(x1-y1)^2+(x2-y2)^2+...+(xk-yk)^2

S要最小,并且y满足|y1|+|y2|+...+|yk|=r

ai=|xi-yi|

S=\sum _{i=1}^k ai^2,

然后根据上面的式子,我们知道xi与yi的符号一定是相等的,这样才能使ai尽可能小

那么我们就把xi全部取绝对值,,如果之前是负号的,最后再加上去效果是一样的。

因为ai里面的结果一定是同号相减,即他们绝对值相减,那么符号我们可以提前拿出来,

并且这里我们保证|xi|>|yi|

然后这里就有性质,|x1|+|x2|+...+|xk|=sumx>r

|a1|+|a2|+...+|ak|=sumx-r

上面这个性质因为我们已经把S里面的式子确定为绝对值相减,然后ai^2=|ai|^2

所以就可以这么写。知道a的和,我们剩下来的就是尽可能均分ai,这样才能使最后的平方和最小

那么理想的的就是ai=(sumx-r)/k

但是这里我们要保证|xi|>|yi|,因为只有保证这样,|ai|=|xi|-|yi|

然后最后展开|y1|+|y2|+...+|yk|=|x1|+|x2|+...+|xk|-(|a1|+|a2|+...+|ak|)=r

所以当(sumx-r)/k>|xi|的时候,我们只能给它分配|xi|,那么多出来的部分就要其他地方来补

因为要保证|a1|+|a2|+...+|ak|=sumx-r

那么现在问题就变成有n个水杯,每一个水杯有自己的容积,你有sumx-r升的水,你要把水全部倒在这n个

水杯里面,并且使得总价值最少,每一个水杯的价值就是里面水的体积的平方

我的思路就是把这n个水杯从小到大摆好,假定我们倒第i个水杯,我们看如果现在均分的体积

inc=(sumx-r)/(k-i+1),如果inc>=杯子的容积,那么我们就把这杯子倒满,然后到后面杯子

否则的话,我们就把i...k这些杯子,都倒inc升的水

最后就是符号,如果原来是'-'的话最后还要把'-'加上

然后再把他平移回原来的坐标,即+c[i]

这道题还有一个坑点是误差是1e-4,但题目样例的输出只输出了2位小数,这个问题一度让我怀疑算法是不是正确的。。。。

所以你必须输出至少4位

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MK = 100+10;

double c[MK];
int fu[MK];

typedef struct node
{
	double ind;
	int id;
}node;
node s[MK];


bool cmp(node a,node b)
{
	return a.ind<b.ind;
}

bool cmp1(node a,node b)
{
	return a.id<b.id;
}
int main()
{
    int t;
    double r;
	int n,k;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        scanf("%lf",&r);
        for(int i=1;i<=k;i++)
            scanf("%lf",&c[i]);
        for(int i=1;i<=n;i++)
        {
            double sum=0;
            for(int j=1;j<=k;j++)
            {
				scanf("%lf",&s[j].ind);
				s[j].ind-=c[j];
				fu[j]=s[j].ind<0?1:0;
				if(s[j].ind<0) s[j].ind=-s[j].ind;
				sum+=s[j].ind;
				//sum+=s[j].ind<0?-s[j].ind:s[j].ind;
				s[j].id=j;
            }
			sort(s+1,s+1+k,cmp);
            
			sum=sum-r;
			for(int j=1;j<=k;j++)
			{
				double inc=sum/(k-j+1);
				if(inc>=s[j].ind)
				{
					sum-=s[j].ind;
					s[j].ind=0;
				}
				else
				{
					for(int w=j;w<=k;w++)
					{
						s[w].ind-=inc;
					}
					break;
				}
			}
			sort(s+1,s+1+k,cmp1);
			for(int j=1;j<=k;j++)
			{
				if(fu[j]) s[j].ind=-s[j].ind;
				if(j==1) printf("%.5lf",s[j].ind+c[j]);
				else printf(" %.5lf",s[j].ind+c[j]);
			}
			printf("\n");
        }
            
    }
}

下面是我被输出位数坑的时候看别人的代码,他是用二分来找最后均分的那个inc的,而我是贪心遍历n来找最后均分的inc

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MK = 100+10;

double c[MK];
int fu[MK];

typedef struct node
{
	double ind;
	int id;
}node;
node s[MK];


const double eps=1e-9;

bool cmp(node a,node b)
{
	return a.ind<b.ind;
	/*double k1=a.ind<0?-a.ind:a.ind;
	double k2=b.ind<0?-b.ind:b.ind;
	return k1<k2;*/
}

bool cmp1(node a,node b)
{
	return a.id<b.id;
}
int main()
{
    int t;
    double r;
	int n,k;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        scanf("%lf",&r);
        for(int i=1;i<=k;i++)
            scanf("%lf",&c[i]);
        for(int i=1;i<=n;i++)
        {
            double sum=0;
            for(int j=1;j<=k;j++)
            {
				scanf("%lf",&s[j].ind);
				s[j].ind-=c[j];
				fu[j]=s[j].ind<0?1:0;
				if(s[j].ind<0) s[j].ind=-s[j].ind;
				//sum+=s[j].ind<0?-s[j].ind:s[j].ind;
				s[j].id=j;
            }


			double L=0,R=1e18;
			while(R-L>eps)
            {
                double mid=(L+R)/2;
                sum=0;
                for(int j=1;j<=k;j++) sum+=max(0.0,s[j].ind-mid);
                if(sum<=r) R=mid;
                else L=mid+eps;
            }
            for(int j=1;j<=k;j++)
            {
                s[j].ind=max(0.0,s[j].ind-R);
            }
			for(int j=1;j<=k;j++)
			{
				if(fu[j]&&s[j].ind) s[j].ind=-s[j].ind;
				if(j==1) printf("%.5lf",s[j].ind+c[j]);
				else printf(" %.5lf",s[j].ind+c[j]);
			}
			printf("\n");
        }

    }
}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/82924408