最小圆覆盖的三种解法

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

  

原文地址:https://www.cnblogs.com/cleanerhgf/p/12036106.html