ACM学习历程—HDU4717 The Moving Points(模拟退火 || 三分法)

Description

There are N points in total. Every point moves in certain direction and certain speed. We want to know at what time that the largest distance between any two points would be minimum. And also, we require you to calculate that minimum distance. We guarantee that no two points will move in exactly same speed and direction.      
              

Input

The rst line has a number T (T <= 10) , indicating the number of test cases.        For each test case, first line has a single number N (N <= 300), which is the number of points.        For next N lines, each come with four integers X i, Y i, VX i and VY i (-10 6 <= X i, Y i <= 10 6, -10 2 <= VX i , VY i <= 10 2), (X i, Y i) is the position of the i th point, and (VX i , VY i) is its speed with direction. That is to say, after 1 second, this point will move to (X i + VX i , Y i + VY i).      
              

Output

For test case X, output "Case #X: " first, then output two numbers, rounded to 0.01, as the answer of time and distance.      
              

Sample Input

2
2
0 0 1 0
2 0 -1 0
2
0 0 1 0
2 1 -1 0

Sample Output

Case #1: 1.00 0.00
Case #2: 1.00 1.00

 

题目大意就是给定n个点的坐标和它x和y方向的分速度,要求在任意时刻两两点之间距离最大值中的最小值。

根据距离公式可以推断出对于某两个点在t逐渐增大的过程中距离服从二次函数。

于是就是对于n个二次抛物线求任意时刻最高点合成的图像。

可以证明(反证)合成的图像也是由两个单调性相反的图像构成(类似于抛物线)。

于是可以采用模拟退火的退化(类似爬山算法)来查找最值。

从minT从0时刻出发,首先设定步长dt = 1e8。然后对于minT-dt和minT+dt讨论,如果使最大值变小,自然更新minT,然后按比例k衰减dt。

直到dt满足精度要求。

进过测试比例k=0.9是可以满足的,跑了530MS;k = 0.95略慢些,跑了1.3S。

网上也有好多使用的是三分法。

 

这里贴出退火的代码:

#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cmath>

#include <cstring>

#include <algorithm>

#include <set>

#include <map>

#include <vector>

#include <queue>

#include <string>

#define LL long long

#define eps 1e-5



using namespace std;



typedef pair<double, double> pdd;



int x[305], y[305], vx[305], vy[305], n;

double minT, minDis;



double pow2(double k)

{

    return k*k;

}



double calDis(double t)

{

    double dis2 = 0;

    for (int i = 0; i < n; ++i)

    {

        for (int j = i+1; j < n; ++j)

        {

            if (i == j)

                continue;

            dis2 = max(dis2,

                       pow2(x[i]+vx[i]*t-x[j]-vx[j]*t) + pow2(y[i]+vy[i]*t-y[j]-vy[j]*t));

        }

    }

    return sqrt(dis2);

}



void qt()

{

    double dt = 1e8, t, dis, k = 0.9, v;

    minT = 0;

    minDis = calDis(minT);



    while (dt > eps)

    {

        dis = calDis(minT+dt);

        t = minT + dt;

        if (minT-dt >= 0)

        {

            v = calDis(minT-dt);

            if (v < dis)

            {

                dis = v;

                t = minT - dt;

            }

        }

        if (dis < minDis)

        {

            minDis = dis;

            minT = t;

        }

        dt *= k;

    }

}



void Work()

{

    scanf("%d", &n);

    for (int i = 0; i < n; ++i)

        scanf("%d%d%d%d", &x[i], &y[i], &vx[i], &vy[i]);

    qt();

}



int main()

{

    //freopen("test.in", "r", stdin);

    int T;

    scanf("%d", &T);

    for (int times = 1; times <= T; ++times)

    {

        printf("Case #%d: ", times);

        Work();

        printf("%.2lf %.2lf\n", minT, minDis);

    }

    return 0;

}

 

你可能感兴趣的:(ACM)