题目大意:给出平面内的一些点,求在一个矩形的范围内找到一个点,使得这个点到其他所有点的最小距离最大。
思路:最小XX最大好像是二分答案的样子,很可惜没办法二分,这个题里没啥满足二分性质的东西。另寻思路,比如模拟退火。
模拟退火的思想大概是一个比较高级的贪心。正常贪心只是每次都贪到最好的状态,有的时候会困在局部最优解中,而没有找到全局最优解。举个简单的例子。见下图:
小明要穿越这个山峰,从左到右。他想找到一个最高的山峰,以看到最好的风景。于是他从左边向A峰爬去。到了A峰,他发现前面的路是往下走的了,他想:看来前面的山峰肯定不会比A峰再高了。于是小明就停在了A峰看风景。实际上他错过了更高的B峰。
这是一种显然错误的贪心算法,我们暂且叫他爬山算法。爬山算法就是如果下一个状态比现在优,就进行下一个状态,但是没有考虑之后的情况,而被困在局部最优解,使得没有找到全局最优解。
为了改进算法使他不错过全局最优解,我们引入了几个概念。
1.T:温度。随着算法的进行,温度会不断降低,接受比较差的解的概率也会降低。反应结束时,温度会趋近于零,解也趋近于正解。
2.dE:△E。为新状态和当前状态之间的差值。当dE>0是表示新状态比当前状态优,那么更新。如果dE<0,表示新状态比向前状态差,我们会有exp( dE / T )的概率接受这个比较差的解。
CODE:
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define EPS 1e-3 #define INF 1e12 #define MAX 10000 using namespace std; struct Point{ double x,y; double length; Point(double _x,double _y):x(_x),y(_y) {} Point() {} }point[MAX],now,ans; int cases; double m,n; int cnt; double dE; inline double Calc(Point p1,Point p2); inline void Initialize(); inline void SA(); inline double Rand(); inline double Statistic(Point p); int main() { srand(19970804); for(cin >> cases;cases; --cases) { scanf("%lf%lf%d",&m,&n,&cnt); for(int i = 1;i <= cnt; ++i) scanf("%lf%lf",&point[i].x,&point[i].y); Initialize(); SA(); printf("The safest point is (%.1lf, %.1lf).\n",ans.x,ans.y); } return 0; } inline double Calc(Point p1,Point p2) { return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)); } inline void Initialize() { now = Point(m * Rand(),n * Rand()); ans.length = 0.0; } inline void SA() { double T = sqrt(m * m + n * n) * 0.5; for(;T > EPS;T *= 0.998) { double alpha = 2.0 * acos(-1.0) * Rand(); Point temp(now.x + T * cos(alpha) * Rand(),now.y + T * sin(alpha) * Rand()); if(temp.x < 0 || temp.y < 0 || temp.x > m || temp.y > n) continue; dE = Statistic(temp) - Statistic(now); if(dE >= 0 || exp(dE / T) >= Rand()) now = temp; } T = 0.5; for(int i = 1;i <= 500; ++i) { double alpha = 2.0 * acos(-1.0) * Rand(); Point temp(ans.x + T * cos(alpha) * Rand(),ans.y + T * sin(alpha) * Rand()); if(temp.x < 0 || temp.y < 0 || temp.x > m || temp.y > n) continue; Statistic(temp); } } inline double Rand() { return (rand() % 1000 + 1) / 1000.0; } inline double Statistic(Point p) { double re = INF; for(int i = 1;i <= cnt; ++i) re = min(re,Calc(point[i],p)); if(re > ans.length) ans = p,ans.length = re; return re; }