ZOJ2928 Mathematical contest in modeling
This year's mathematical contest in modeling in ZJU will come soon. The students loving the contest have already made their teams. We consider a simple model: A team contains three students, one of them is good at maths, the second of them is good at computer, and the third one is good at writing. Now they want to estimate their team's relative power level in all participant teams. They use their grades in school instead of their real modeling abilities now because before the contest no other criterions more properly can be used. The grade of a student is a nonnegative real number no more than 1000. A team's modeling ability can be represented by three real numbers a, b, and c. a is the grade of the student which is good at maths, b is the grade of the one which is good at computer, and c is the grade of the one which is good at writing. If they know the average modeling ability of all teams, each team can compare their own team's modeling ability with the average modeling ability and then estimates their relative modeling power level in all teams.
Assume two teams' modeling abilities are a1, b1, c1 and a2, b2, c2 respectively, then the difference between their modeling abilities is defined as sqrt((a1-a2)^2+(b1-b2)^2+(c1-c2)^2). The average modeling ability of all teams is three real numbers which can be considered as a team's modeling ability, and the sum of the differences between this modeling ability and all teams' modeling abilities is minimum.
Your task is to calculate the average modeling ability of all teams.
Input
There are multiple test cases. Each teat case begins with a line containing a integer n, 3 <= n <= 100, which is the number of all teams. Then n lines follow, each line contains three real numbers ai, bi and ci, 0.00 <= ai, bi, ci <= 1000.00, which is a team's modeling ability.
Output
For each test case, print the average modeling ability of all teams in a line rounded to three decimal places after the decimal point. It is guaranteed that the answer is unique.
Sample Input
4
100.00 100.00 100.00
1000.00 100.00 100.00
100.00 1000.00 100.00
100.00 100.00 1000.00
Sample Output
250.000 250.000 250.000
【思路分析】
题目的意思是在三维坐标里给你若干个点,要你去在该三维坐标内找到一个点,使得该点与这若干个点的距离之和是最小的。假设该点为p(x0,y0,z0),最小距离和为sum,由距离公式可知,当空间中的点越接近p点时,距离之和也就越接近sum,当空间中的点远离p点时,距离之和便一定大于sum。因此,该距离之和是一个单峰函数,即局部最优解就是全局的最优解,所以可以用爬山算法来解决。
实际操作中,首先定义一个起始点s、步长step和三维坐标的方向数组direction,在step大于eps的范围之内不断的进行s点的各个方向的搜索,如果找到距离和更小的点则更新点s。同时每一个点判断完之后要缩短步长,即step *= r。
这个r是介于0到1之间的小数,如果r小了可能会导致结果不精确,如果r大了可能会导致循环次数过多从而超时。
代码如下
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
#define eps 1e-8
#define maxn 105
const double r = 0.992;
int direction[30][3],n;
struct Point
{
double x,y,z;
Point()
{
}
Point(double x1,double y1,double z1)
{
x = x1;
y = y1;
z = z1;
}
Point operator - (const Point &a) const
{
return Point(x - a.x,y - a.y,z - a.z);
}
double length()
{
return sqrt(x * x + y * y + z * z);
}
} points[maxn],now;
double distances(Point a,Point b)
{
return (a - b).length();
}
void init()
{
int x = 0;
for(int i = -1;i <= 1;i++)
for(int j = -1;j <= 1;j++)
for(int k = -1;k <= 1;k++)
{
direction[x][0] = i;
direction[x][1] = j;
direction[x][2] = k;
x++;
}
}
double sum(Point p)
{
double ans = 0;
for(int i = 0;i < n;i++)
{
ans += distances(points[i],p);
}
return ans;
}
int main()
{
init();//初始化三维坐标内某点的各个方向
while(scanf("%d",&n) != EOF)
{
for(int i = 0;i < n;i++)
{
scanf("%lf %lf %lf",&points[i].x,&points[i].y,&points[i].z);
}
now = Point(0.0,0.0,0.0);//初始化起始点
double step = -1;//步长
double ans = sum(now);
for(int i = 0;i < n;i++)//初始步长为起始点到各点的最大值
{
double x;
x = distances(now,points[i]);
if(x > step)
step = x;
}
while(step > eps)
{
Point pre = now;
for(int i = 0;i < 27;i++)
{
Point p = Point(pre.x + direction[i][0] * step,pre.y + direction[i][1] * step,
pre.z + direction[i][2] * step);//向各个方向寻找
double tmp = sum(p);
if(tmp < ans)//找到与各点距离之和更小的点
{
ans = tmp;
now = p;//更新起始点
}
}
step *= r;//减少步长,使步长趋于稳定
}
printf("%.3lf %.3lf %.3lf\n",now.x,now.y,now.z);
}
return 0;
}