最小圆覆盖&最小球覆盖 (增量法和模拟退火法)

     这种题几乎一套板子走天下。

     昨天做最小圆覆盖用的都是增量法,压根没看过退火模拟法,因为退火模拟法并不是很稳定。今天看最小球覆盖时发现用到了退火模拟法,于是看了看最小圆覆盖的退火模拟法,用退火板子提交直接AC,所以一并总结了。

有关题目的链接:传送门

最小圆覆盖:

  • 增量法:

  

借鉴了其他博主的推导图,方便理解

板子如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<ctime>
using namespace std;
typedef long long lt;
#define eps 1e-6
#define sqr(x) ((x)*(x))

const int maxn=1000010;
int n;
struct point{
	double x,y;
}p[maxn],O;
double R;//半径 

double getd(point a,point b){ //求直径 
	return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}
point getO(point p1,point p2,point p3) { //求圆心 
    point res;
    double a=p2.x-p1.x;
	double b=p2.y-p1.y;
	double c=p3.x-p2.x;
	double d=p3.y-p2.y;
    double e=sqr(p2.x)+sqr(p2.y)-sqr(p1.x)-sqr(p1.y);
    double f=sqr(p3.x)+sqr(p3.y)-sqr(p2.x)-sqr(p2.y);
    res.x=(f*b-e*d)/(c*b-a*d)/2.0; 
    res.y=(a*f-e*c)/(a*d-b*c)/2.0;
    return res;
}
void mincir() {
    O=p[1]; 
	R=0;
    for(int i=1;i<=n;++i){
	    if(getd(p[i],O)-R>eps) { //不在圆内 
	        O=p[i]; 
			R=0;
	        for(int j=1;j<i;j++) { 
		        if(getd(p[j],O)-R>eps) {//不在圆内 
		            O=(point){(p[i].x+p[j].x)/2.0,(p[i].y+p[j].y)/2.0};
		            R=getd(p[i],p[j])/2.0;
		            for(int k=1;k<j;++k)
		            if(getd(p[k],O)-R>eps) {//不在圆内 
		                O=getO(p[i],p[j],p[k]);  //外接圆 
		                R=getd(p[i],O);
		            }
		        }
		    } 
	    }
	} 
} 

int main()
{
    cin>>n;
    for(int i=1;i<=n;++i)
    scanf("%lf%lf",&p[i].x,&p[i].y);
    random_shuffle(p+1,p+1+n);// random_shuffle()随机打乱函数 首指针 尾指针 
    mincir();
    printf("%.3f",R);
    return 0;
}

 这里介绍一个随机函数 random_shuffle(p,p+n),用法和sort()一样。    这里为什么要用随机函数是为了降低三层for循环O(n^{3})的复杂度,为O(n),至于为什么...推导我也不懂  (挠头

  • 退火模拟法:

板子: (等我懂了退火模拟法的实现再来补这个算法)

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
const double eps=1e-8;
struct POINT{
    double x,y,z;
}p[510];
POINT op;//最小圆的圆心
int n;
inline double dist(POINT &a,POINT &b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void solve(){
    double ret,delta=100.0;
    double maxDis,tempDis;
    int i,id;
    while(delta>eps){
        id=0;
        maxDis=dist(op,p[id]);
        for(i=1;i<n;i++){
            tempDis=dist(op,p[i]);
            if(tempDis>maxDis){
                maxDis=tempDis;
                id=i;
            }
        }
        ret=maxDis;
        op.x+=(p[id].x-op.x)/maxDis*delta;
        op.y+=(p[id].y-op.y)/maxDis*delta;
        delta*=0.98;
    }
    printf("%.3f\n",ret);
}
int main(){
	scanf("%d",&n);
    op.x=op.y=0;
    for(int i=0;i<n;i++){
        scanf("%lf%lf",&p[i].x,&p[i].y);
        op.x+=p[i].x;
        op.y+=p[i].y;
    }
    op.x/=n;
    op.y/=n;
    solve();
    
    return 0;
}

 

 

最小球覆盖:

  • 退火模拟法

    随机选取一个点作为初始解,然后不断向当前距离它最远的点靠近。

    对于一个点:球心就是这个点,且半径无穷小
    对于两个点:球心就是两点线段的中点,半径就是线段长度的一半
    对于三个点:三点构成的平面必为球的大圆,球心是三角形的外心,半径就是球心到某个点的距离
    对于四个点:若四点共面,则转换到3点共面;若四点不共面,四面体可以唯一确定一个外接球
    对于五个及以上的点:最小球必为某四个点的外接球

板子

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
const double eps=1e-5;
struct POINT{
    double x,y,z;
}p[110],op;//N个点
int n;
double dist(POINT &a,POINT &b){//两点距离
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));
}
double solve(){
    double ret,delta=100.0;//温度
    double maxDis,tempDis;
    int i,id;
    while(delta>eps){
        id=0;
        maxDis=dist(op,p[id]);
        for(i=1;i<n;i++){
            tempDis=dist(op,p[i]);
            if(tempDis>maxDis){
                maxDis=tempDis;
                id=i;
            }
        }
        ret=maxDis;
        op.x+=(p[id].x-op.x)/maxDis*delta;
        op.y+=(p[id].y-op.y)/maxDis*delta;
        op.z+=(p[id].z-op.z)/maxDis*delta;
        delta*=0.98;
    }
    return ret;//最小球半径
}
int main(){
    while(scanf("%d",&n)!=EOF&&n){
        for(int i=0;i<n;i++){
            scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z);
        }
        printf("%.5f\n", solve());
    }
    return 0;
}

 

这个温度delta值的范围随时间而定,时间给的越多那就开的越大,毕竟会越精确嘛

 

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要模拟小的落下并形成高斯分布图,我们可以利用Python来实现这个过程。首先,我们需要定义一个小下落的模型。假设小在每次落下的时候,会随机选择一个方向(左或右)和一个速度(正态分布),然后在下一个时间步中,小会按照所选择的方向和速度进行运动。我们可以使用numpy库中的random模块来生成速度和方向,并使用matplotlib库来绘制高斯分布图。 首先,我们导入必要的库: import numpy as np import matplotlib.pyplot as plt 然后,我们定义模拟的参数: num_balls = 1000 # 模拟小的个数 num_steps = 100 # 模拟的时间步数 speed_mean = 0 # 速度的均值(高斯分布的均值) speed_std = 1 # 速度的标准差(高斯分布的标准差) 接下来,我们创建一个二维数组来保存小的位置信息,其中行表示小的索引,列表示时间步的索引: positions = np.zeros((num_balls, num_steps)) 然后,我们使用一个循环来模拟小的运动: for step in range(1, num_steps): # 生成速度 speeds = np.random.normal(speed_mean, speed_std, num_balls) # 生成方向 directions = np.random.choice([-1, 1], num_balls) # 更新位置 positions[:, step] = positions[:, step - 1] + directions * speeds 最后,我们使用matplotlib库来绘制高斯分布图: # 绘制高斯分布图 plt.hist(positions[:, -1], bins=&#39;auto&#39;, density=True) plt.xlabel(&#39;Final Position&#39;) plt.ylabel(&#39;Probability Density&#39;) plt.title(&#39;Gaussian Distribution of Final Positions&#39;) plt.show() 运行程序后,我们将得到一个表达小最终位置的高斯分布图像。图像的横轴表示小最终的位置,纵轴表示概率密度。根据高斯分布的性质,图像应该呈现出一个钟形曲线状的分布,中间高,两边逐渐变低。 通过以上步骤,我们使用Python成功模拟了小的落下并得到了最终形成的高斯分布图。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值