zoukankan      html  css  js  c++  java
  • 最小圆覆盖的三种解法

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

      

  • 相关阅读:
    面试金典——模式匹配
    浅谈C语言中的联合体
    android recovery模式及ROM制作
    模拟键盘输入 : SendMessage, keybd_event, PostKeybdMessage
    在游戏中使用keybd_event的问题
    keybd_event函数用法
    C语言清空输入缓冲区的N种方法对比
    深究“字节对齐”的原因
    字节对齐
    网络安装CentOS 5.3
  • 原文地址:https://www.cnblogs.com/cleanerhgf/p/12036106.html
Copyright © 2011-2022 走看看