知识点 - 最小圆覆盖/最小球覆盖

知识点 - 最小圆覆盖/最小球覆盖

解决问题类型:

找一个点使得到跟顶点最大距离最小。

相当于找一个覆盖所有点的圆。

最小圆覆盖用随机增量(O(n^3)概率均摊到O(n))。

最小球覆盖用退火/三分三分再三分

定义与代码:

圆覆盖

伪代码
圆 C;
for(i=1 to n)
{
    if(P[i] 不在 C 内)
    {
        C = {P[i], 0};
        for(j=1 to i-1)
        {
            if(P[j] 不在 C 内)
            {
                C = {0.5*(P[i]+P[j]), 0.5*dist(P[i], P[j])};
                for(k=1 to j-1)
                {
                    if(P[k] 不在 C 内)
                    {
                        C = 外接圆(P[i], P[j], P[k]);
                    }
                }
            }
        }
    }
}
随机增量
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef double DB;
const int N=100005;
struct node
{
    DB x,y;
}a[N],O;
int n;
DB r;
inline DB power(DB x)
{
    return x*x;
}
inline DB dis(node a,node b)
{
    return sqrt(power(a.x-b.x)+power(a.y-b.y));
}
inline bool in_circle(node a)
{
    return dis(a,O)<=r?1:0;
}
inline int calc(DB a,DB b,DB c,DB d,DB e,DB f)
{
    O.x=(b*f-d*e)/(b*c-a*d);
    O.y=(c*e-a*f)/(b*c-a*d);
}
inline void min_cover_circle(void)
{
    register int i,j,k;
    random_shuffle(a+1,a+n+1);
    for (i=1;i<=n;++i)
    if (!in_circle(a[i]))
    {
        O=a[i]; r=0;
        for (j=1;j<i;++j)
        if (!in_circle(a[j]))
        {
            O.x=(a[i].x+a[j].x)/2.0; O.y=(a[i].y+a[j].y)/2.0; r=dis(a[i],O);
            for (k=1;k<j;++k)
            if (!in_circle(a[k])) calc(a[i].x-a[j].x,a[i].y-a[j].y,a[i].x-a[k].x,a[i].y-a[k].y,
            ((power(a[i].x)-power(a[j].x))-(power(a[j].y)-power(a[i].y)))/2.0,
            ((power(a[i].x)-power(a[k].x))-(power(a[k].y)-power(a[i].y)))/2.0),r=dis(a[i],O);
        }
    }
    printf("%.10lf %.10lf %.10lf",O.x,O.y,r*r/2.);
}
int main()
{
    register int i; scanf("%d",&n);
    for (i=1;i<=n;++i)
    scanf("%lf%lf",&a[i].x,&a[i].y);
    min_cover_circle();
    return 0;
}
退火

(还不懂,正确性不保证)

//******************THE PROGRAM BEGINING******************
struct node
{
	double x, y;
}p[maxn], now;

double dis(node a, node b)
{
	return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
}

double solve(int n)
{
	double ans, cmp;
	double T = 100.0;
	double delat = 0.98;
	now.x = now.y = 0.0;
	int pos = 0;
	while (T > eps)
	{
		pos = 0;
		ans = dis(now, p[pos]);
		per(i, 0, n - 1)
		{
			cmp = dis(now, p[i]);
			if (cmp > ans)
			{
				pos = i;
				ans = cmp;
			}
		}
		now.x += (p[pos].x - now.x) / ans * T;
		now.y += (p[pos].y - now.y) / ans * T;
		T *= delat;
	}
	return ans;
}

int main()
{
	int n;
	scanf("%d", &n);
	
		per(i, 0, n - 1)
			scanf("%lf %lf", &p[i].x, &p[i].y);
		double ans = solve(n);
		printf("%.20lf %.20lf %.20lf\n", now.x, now.y, ans*ans / 2.);
		cin >> n;
	return 0;
}
三分套三分

常数较大且和精度有关,

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>

using namespace std;

const int maxn = 100005;
const double eps = 1e-7;
double minx, miny, maxx, maxy;

struct Point
{
	double x, y;
	Point() {}
	Point(double _x, double _y)
	{
		x = _x;
		y = _y;
	}
	Point operator -(const Point &b)const
	{
		return Point(x - b.x, y - b.y);
	}
	double operator *(const Point &b)const
	{
		return x * b.x + y * b.y;
	}
};

Point p[maxn];
int T;

double dist(Point a, Point b)
{
	Point x = a - b;
	return sqrt(x * x);
}

double f(double x, double y)
{
	Point ans1 = Point(x, y);
	double sum = -1.0;
	for (int i = 0; i<T; i++)
	{
		sum = max(dist(ans1, p[i]), sum);
	}
	return sum;
}
double three_search2(double x)
{
	double l = miny, r = maxy;
	while (r - l>eps)
	{
		double lmid = l + (r - l) / 3;
		double rmid = r - (r - l) / 3;
		if (f(x, lmid) >= f(x, rmid))
		{
			l = lmid;
		}
		else
		{
			r = rmid;
		}
	}
	return l;
}

Point three_search()
{
	double l = minx, r= maxx;
	while (r - l>eps)
	{
		double lmid = l + (r - l) / 3;
		double rmid = r - (r - l) / 3;
		if (f(lmid, three_search2(lmid)) >= f(rmid, three_search2(rmid)))
		{
			l = lmid;
		}
		else
		{
			r = rmid;
		}
	}
	return Point(l, three_search2(l));
}

int main()
{

		scanf("%d", &T);
		minx = 10001.0,maxx= -1.0;
		miny = 10001.0, maxy = -1.0;
		for (int i = 0; i<T; i++)
		{
			scanf("%lf %lf", &p[i].x, &p[i].y);
			minx = min(minx, p[i].x), maxx = max(maxx, p[i].x);
			miny = min(miny, p[i].y), maxy = max(maxy, p[i].y);
		}
		Point ans = three_search();
		printf("%.20lf %.20lf %.20lf\n", ans.x, ans.y, f(ans.x, ans.y)*f(ans.x, ans.y)/2.);


	return 0;
}

球覆盖

只能退火三分。

否则太难了。

例题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值