http://acm.hdu.edu.cn/showproblem.php?pid=3007
相关题型连接:
http://acm.hdu.edu.cn/showproblem.php?pid=3932
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=450
3 1.00 1.00 2.00 2.00 3.00 3.00 0
2.00 2.00 1.41
方法一:随机增量法:
分析:用最小的圆覆盖住所有的点:随机增量法复杂度是O(n);
2、算法及原理算法介绍:我们本次算法的设计是基于这样一个简单直观的性质:
在既定的给定点条件下,如果引入一张新的半平面,只要此前的最优解顶点(即唯一确定最小包围圆的几个关键顶点)能够包含于其中,则不必对此最优解进行修改,亦即此亦为新点集的最优解;否则,新的最优解顶点必然位于这个新的半空间的边界上。定理可以通过反证法证明。于是,基于此性质,我们便可得到一个类似于线性规划算法的随机增量式算法。定义Di为相对于pi的最小包围圆。此算法实现的关键在于对于pi∉Di-1时的处理。显然,如果pi∈Di-1,则Di= Di-1;否则,需要对Di另外更新。而且,Di的组成必然包含了pi;因此,此种情况下的最小包围圆是过pi点且覆盖点集{ p1 ,p2 ,p3 ……pi-1}的最小包围圆。则仿照上述处理的思路,Di={ p1 ,pi },逐个判断点集{ p2 ,p3 ……pi-1 },如果存在pj∉ Di,则Di={pj,pi }。同时,再依次对点集{ p1 ,p2 ,p3 ……pj-1 }判断是否满足pk∈Di,若有不满足,则Di={pk ,pj,pi }。由于,三点唯一地确定一个圆,故而,只需在此基础上判断其他的点是否位于此包围圆内,不停地更新pk。当最内层循环完成时,退出循环,转而更新pj;当次内层循环结束时,退出循环,更新pi。当i=n时,表明对所有的顶点均已处理过 ,此时的Dn即表示覆盖了给定n个点的最小包围圆。
总结:
假设圆O是前i-1个点得最小覆盖圆,加入第i个点,如果在圆内或边上则什么也不做。否,新得到的最小覆盖圆肯定经过第i个点。然后以第i个点为基础(半径为0),重复以上过程依次加入第j个点,若第j个点在圆外,则最小覆盖圆必经过第j个点。重复以上步骤(因为最多需要三个点来确定这个最小覆盖圆,所以重复三次)。遍历完所有点之后,所得到的圆就是覆盖所有点得最小圆。证明可以考虑这么做:最小圆必定是可以通过不断放大半径,直到所有以任意点为圆心,半径为半径的圆存在交点,此时的半径就是最小圆。所以上述定理可以通过这个思想得到。这个做法复杂度是O(n)的,当加入圆的顺序随机时,因为三点定一圆,所以不在圆内概率是3/i,求出期望可得是O(n)。
方法二:模拟退火法:对于每个枚举的点找到改点到所给点的最远点的距离,然后保证这个距离最小,即为所求圆的半径;
程序:
方法一:随机增量法:
#include"string.h" #include"stdio.h" #include"queue" #include"stack" #include"vector" #include"algorithm" #include"iostream" #include"math.h" #include"stdlib.h" #define M 522 #define inf 100000000 #define eps 1e-8 #define PI acos(-1.0) using namespace std; double X,Y; struct Point { double x,y; }p[M]; struct Triangle { Point v[3]; }; struct Circle { Point center; double r; }; double pow(double x) { return x*x; } double Len(Point a,Point b) { return sqrt(pow(a.x-b.x)+pow(a.y-b.y)); } double TriangleArea(Triangle a)//求三角形的面积 { double px1=a.v[1].x-a.v[0].x; double py1=a.v[1].y-a.v[0].y; double px2=a.v[2].x-a.v[0].x; double py2=a.v[2].y-a.v[0].y; return fabs(px1*py2-px2*py1)/2; } Circle CircleOfTriangle(Triangle t)//就三角形外接圆 { Circle tmp; double a=Len(t.v[0],t.v[1]); double b=Len(t.v[0],t.v[2]); double c=Len(t.v[1],t.v[2]); tmp.r=a*b*c/4/TriangleArea(t); double a1=t.v[1].x-t.v[0].x; double b1=t.v[1].y-t.v[0].y; double c1=(a1*a1+b1*b1)/2; double a2=t.v[2].x-t.v[0].x; double b2=t.v[2].y-t.v[0].y; double c2=(a2*a2+b2*b2)/2; double d=a1*b2-a2*b1; tmp.center.x=t.v[0].x+(c1*b2-c2*b1)/d; tmp.center.y=t.v[0].y+(a1*c2-a2*c1)/d; return tmp; } void Run(int n) { random_shuffle(p+1,p+n+1);//随机排序取点 int i,j,k; Circle tep; tep.center=p[1]; tep.r=0; for(i=2;i<=n;i++) { if(Len(p[i],tep.center)>tep.r+eps) { tep.center=p[i]; tep.r=0; for(j=1;j<i;j++) { if(Len(p[j],tep.center)>tep.r+eps) { tep.center.x=(p[i].x+p[j].x)/2; tep.center.y=(p[i].y+p[j].y)/2; tep.r=Len(p[i],p[j])/2; for(k=1;k<j;k++) { if(Len(p[k],tep.center)>tep.r+eps) { Triangle t; t.v[0]=p[i]; t.v[1]=p[j]; t.v[2]=p[k]; tep=CircleOfTriangle(t); } } } } } } printf("%.2lf %.2lf %.2lf\n",tep.center.x,tep.center.y,tep.r); } int main() { int n,i; while(scanf("%d",&n),n) { for(i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); Run(n); } }
方法二:模拟退火法:(hdu3932)
#include"string.h" #include"stdio.h" #include"queue" #include"stack" #include"vector" #include"algorithm" #include"iostream" #include"math.h" #include"stdlib.h" #define M 1009 #define inf 100000000 #define eps 1e-8 #define PI acos(-1.0) using namespace std; double X,Y; int n; struct Point { double x,y,dis; Point(){} Point(double xx,double yy){x=xx;y=yy;} bool check(){ if(x>0&&y>0&&x<X&&y<Y)return true; return false; } }p[M],q[50]; double pow(double x) { return x*x; } double Len(Point a,Point b) { return sqrt(pow(a.x-b.x)+pow(a.y-b.y)); } double fun(Point a) { double maxi=0; for(int i=1;i<=n;i++) { double L=Len(a,p[i]); if(maxi<L) maxi=L; } return maxi; } int main() { int i; while(scanf("%lf%lf%d",&X,&Y,&n)!=EOF) { for(i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); int po=15,est=15; for(int i=1;i<=po;i++) { q[i].x=(rand()%1000+1)/1000.0*X; q[i].y=(rand()%1000+1)/1000.0*Y; q[i].dis=fun(q[i]); } double temp=max(X,Y); while(temp>0.001) { for(int i=1;i<=po;i++) { for(int j=1;j<=est;j++) { double rad=(rand()%1000+1)/1000.0*PI*2; Point cur; cur.x=q[i].x+temp*cos(rad); cur.y=q[i].y+temp*sin(rad); if(!cur.check())continue; cur.dis=fun(cur); if(cur.dis<q[i].dis) q[i]=cur; } } temp*=0.8; } int id=1; for(int i=1;i<=po;i++) if(q[id].dis>q[i].dis) id=i; printf("(%.1lf,%.1lf).\n%.1lf\n",q[id].x,q[id].y,q[id].dis); } return 0; }