1. 程式人生 > >ACM/ICPC 2018亞洲區預選賽北京賽站網路賽 K-Dimensional Foil II(思維題)

ACM/ICPC 2018亞洲區預選賽北京賽站網路賽 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維空間,定義一個半徑為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");
        }

    }
}