标签:
Description
Input
Output
Sample Input
Sample Output
题目大意就是给定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学习历程—HDU4717 The Moving Points(模拟退火 || 三分法)
标签:
原文地址:http://www.cnblogs.com/andyqsmart/p/4534477.html