做法1:直接套用模拟退火算法
做法2:引入温度系数,启发式寻找圆心
做法3:随机增量算法
做法1直接使用模拟退火,当前能量值f(p)为以p为原点覆盖所有点最小圆半径。
做法2代码最为简单,首先类似于模拟退火,温度越高时,状态之间的跳动越剧烈。以nowp点为原点,计算出与n个点的距离,更新半径ans。然后确定离nowp最远的点P1,在nowp->P1的线段上根据当前温度确定一点,随着温度下降,跳动幅度越来越小,点的位置越来越趋近于准确值。
做法3:我们用 表示题目中的点。考虑使用增量算法,令 , 则代表相对于 的最小外接圆。
定理:设 ,有:
(i) 若 ,则 ;(这个非常显然。)
(ii) 若 ,则 必然落在 的边界上。
(ii)部分“看起来”就是正确的。但是证明部分却不是那么容易。当然这个证明不是本节的重点,有兴趣的同学可以参看附录2。
因此我们很容易设计出基于定理的算法,每次插入 时判断其是否属于 ,不属于则枚举 和 ,并用张角进行判断。
Algorithm MiniDisc Random_shuffle for i ß 3 to n if not (p[o[i]] in D) * for j ß 1 to i-1 * 以o[i],o[j]为圆周上的点进行求解 return D |
如果不使用随机化,复杂度依然是 的(图2)。如果我们使用上文所描述的洗牌算法将 中元素的顺序打乱,复杂度如何呢?
图2 |
取随机变量 ,若 ,则 ,否则 。算法在*处一共需要 的时间。因此,整个最小外接圆的算法复杂度就是
为了界定上述和式的期望值,我们需要利用期望的线性率(linearity of expectation):一组随机变量总和的期望值,等于这些随机变量各自期望值的总和,即便其中的随机变量不是相互独立的,也依然满足这个性质。因此,总的运行时间的期望值为
而 是多少呢?它等于 的概率。但是这个问题比较棘手。
正难则反,我们从反方向看待这个问题。对于 , 在它的边界上的概率是多少呢? 是由 中的3个点构成的,恰好是 的概率则为 ,即 。因此该算法的时间复杂度就为
最后再次声明一点,这里所指出的“期望”,是相对于洗牌算法所给出的随机的顺序而言的,并不是对各种输入数据的平均。确切地讲,这个期望的时间复杂度和输入顺序完全没有关系。
至此,我们通过随机增量算法将原问题 的时间复杂度成功地优化至 。
(文献参考:感受随机的美——浅谈随机化思想在几何问题中的应用 IOI2008冬令营论文 顾研)
做法2代码:
#include <bits/stdc++.h> //#include <unordered_map> using namespace std; typedef long long ll; #define N (50) #define maxn (N + 10) const ll mod = 998244353; const ll INF = (1e18) + 10; const double eps = 1e-3; const double inf = 1e50; const double rt = 0.998; const double K = 1000.0; struct point{ double x, y; }; point p[maxn]; point nowp; double ans; int n; double getdis(point a,point b){ double d1 = a.x - b.x,d2 = a.y - b.y; return sqrt(d1 * d1 + d2 * d2); } void solve(){ double T = 100; while(T > eps){ int idx;double cnt = 0; for(int i = 1;i <= n;i++){ double dis = getdis(nowp,p[i]); if(dis > cnt){ cnt = dis;idx = i; } } ans = min(ans,cnt); nowp.x += ((p[idx].x - nowp.x) / cnt) * T; nowp.y += ((p[idx].y - nowp.y) / cnt) * T; T *= rt; } } int main(){ while(scanf("%d",&n) != EOF){ if(n == 0)break; nowp.x = 0;nowp.y = 0; for(int i = 1;i <= n;i++){ scanf("%lf%lf",&p[i].x,&p[i].y); nowp.x += p[i].x;nowp.y += p[i].y; } nowp.x /= n;nowp.y /= n;ans = inf; solve(); printf("%.2lf %.2lf %.2lf ",nowp.x,nowp.y,ans); } return 0; }