zoukankan      html  css  js  c++  java
  • 最小圆覆盖

    最小圆覆盖

    期望复杂度 (mathcal O(n)) 求解面积最小的覆盖 (n) 个点的圆,采用的是随机增量法。随机增量法类似归纳法,但由于随机性能够保证复杂度优秀。

    算法思路:

    • 假设我们找到了能够包含前 (i-1) 个点的最小圆,如果第 (i) 个点在圆中,则前 (i) 个点的最小圆不变
    • 否则最小圆一定经过点 (i),接着我们继续采用这样的思路找出包含前 (i-1) 个点的圆,且满足圆经过点 (i),这一步最终可以归纳到所有点的最小圆
    • 假设满足前 (j-1) 个点在过 (i) 的最小圆上,考虑点 (j),如果在圆中,则圆不变,否则一定经过点 (j),这一步最终可以归纳到前 (i) 个点的最小圆
    • 此时对于前 (j) 个点加上 (i) 号点,我们已经知道必定经过 (i)(j),只需要枚举前 (j-1) 个点做为圆上第三个点。同样地,对于前 (k-1) 个点和 (i,j) 的最小圆,如果 (k) 在里面则圆不变,否则一定经过 (i,j,k),更换圆即可,这里最终能得到前 (j) 个点和 (i) 号点的最小圆。

    所以我们这样做

    • 第一层循环判断点 (i) 是否在现有的圆中(初始时圆为空)
    • 如果不在,接着第二层循环枚举第二个点 (j(j<i))
    • 以这两个点为直径的圆判断点 (k(k<j)) 是否在园里,如果不在,选取以 (i,j,k) 构成的圆

    当然,请注意三点共线的情况。模板题没有考虑。

    分析下复杂度。显然最里面一层循环复杂度是 (mathcal O(j))

    第二层循环当且仅当 (j) 不在前 (j-1)(i) 构成的最小圆中,会进入第三层。这里期望是 (mathcal O(dfrac3j)),因为仅仅三个点(或两个点)确定最小圆,(j) 只有是这三个点才会扩大圆,进入第三层。所以第二层复杂度是 (mathcal O(i))

    同样的道理,第一层复杂度为 (mathcal O(n))

    #include <bits/stdc++.h>
    using std::swap;
    typedef double DB;
    const int N = 1000005;
    const DB eps = 1e-6;
    int n;
    struct Point {
    	DB x, y;
    	Point(DB x = 0, DB y = 0) : x(x), y(y) {}
    	Point operator + (Point a) { return Point(x + a.x, y + a.y); }
    	Point operator - (Point a) { return Point(x - a.x, y - a.y); }
    	friend Point operator * (DB k, Point a) { return Point(k * a.x, k * a.y); }
    	operator DB() { return sqrt(x * x + y * y); }
    } a[N], O;
    DB R;
    Point work(Point P1, Point P2, Point P3) {
    	DB a, b, c, d, e, f, x, y;
    	a = P2.x - P1.x, b = P2.y - P1.y, c = P3.x - P1.x, d = P3.y - P1.y;
    	e = P1.x * P1.x + P1.y * P1.y - P2.x * P2.x - P2.y * P2.y; e /= 2;
    	f = P1.x * P1.x + P1.y * P1.y - P3.x * P3.x - P3.y * P3.y; f /= 2;
    	x = (b * f - d * e) / (a * d - b * c), y = (c * e - a * f) / (a * d - b * c);
    	return Point(x, y);
    }
    int main() {
    	srand(time(0));
    	scanf("%d", &n);
    	for (int i = 1; i <= n; i++)
    		scanf("%lf%lf", &a[i].x, &a[i].y);
    	for (int i = 1; i <= n; i++)
    		swap(a[i], a[rand() % n + 1]);
    	for (int i = 1; i <= n; i++)
    		if ((DB)(a[i] - O) > R + eps) {
    			O = a[i], R = 0;
    			for (int j = 1; j < i; j++)
    				if ((DB)(a[j] - O) > R + eps) {
    					O = 0.5 * (a[i] + a[j]), R = (DB)(a[i] - O);
    					for (int k = 1; k < j; k++)
    						if ((DB)(a[k] - O) > R + eps) O = work(a[i], a[j], a[k]), R = (DB)(a[i] - O);
    				}
    		}
    	printf("%.2lf %.2lf %.2lf
    ", O.x, O.y, R);
    	return 0;
    }
    
  • 相关阅读:
    深入理解Node.js垃圾回收与内存管理
    【File System】Node.js中文件操作模块File System
    【事件流】事件冒泡和事件捕获
    undefined 和null的区别?
    localStorage实现登录注册功能
    解刨for循环
    react中嵌入高德地图并Marker标点
    react页面中嵌入地图,标识出某个地点,使用插件react-amap
    react中使用antd的List组件,以及下载文件,List隔行变色
    公众号页面数据处理
  • 原文地址:https://www.cnblogs.com/ac-evil/p/14548672.html
Copyright © 2011-2022 走看看