ZOJ 2928 Mathematical contest in modeling(爬山算法)

本文介绍了一种用于评估数学建模竞赛团队实力的算法。算法通过将团队成员的数学、计算机和写作能力量化为分数,并计算与其他团队的相对差距来估算每个团队的实力等级。算法采用爬山算法在三维坐标系中寻找一个点,使得该点与所有团队的模型能力之间的距离之和最小。此方法提供了一个统一的标准来比较不同团队的实力,有助于参赛者更好地了解自己的位置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值