hdu 3007 Buried memory (最小圆覆盖)

源自于

1.问题:

最小圆覆盖指平面上有n个点,给定n个点的坐标,试找一个半径最小的圆,将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个点的最小包围圆。
 
算法  MINIDISC(P)
输入:由平面上n个点组成的一个集合P
输出:P的最小包围圆
1.    令D2为对应于{ p1,p2}的最小包围圆
2.    for  i ← 3 to n
3.      do if  pi ∈ Di-1
4.          then  Di ← Di-1
5.          else  Di ← MINIDISCWITHPOINT{{ p1 ,p2 ,p3 ……pi-1},pi}
6.    return  Dn
算法  MINIDISCWITHPOINT(P,q)
输入:由平面上n个点构成的一个集合P,以及另外一个点q
输出:在满足“边界穿过q”的前提下,P的最小包围圆
1.    令D1为对应于{ p1,q}的最小包围圆
2.    for  j ← 2 to n
3.      do if  pj ∈ Dj-1
4.          then  Dj ← Dj-1
5.          else  Dj ← MINIDISCWITH2POINT{{ p1 ,p2 ,p3 ……pj-1},pj,pi}
6.    return  Dn
算法  MINIDISCWITH2POINT(P,q1,q2)
输入:由平面上n个点构成的一个集合P,以及另外两个点q1,q2
输出:在满足“边界穿过q1,q2”的前提下,P的最小包围圆
1.    令D0为对应于{ q1,q2}的最小包围圆
2.    for  k ← 1 to n
3.      do if  pk∈ Dk-1
4.          then  Dk ← Dk-1
5.          else  Dk ← q1,q2和pk确定的圆
6.    return  Dn
 
时间复杂度:此算法对于任意给定的n个点,可以再O(n)的期望运行时间内计算出来。
算法MINIDISCWITH2POINT中的每一轮迭代循环只需要常数时间,因此其运行时间为O(n);另外,它只需要线性的空间。至于算法MINIDISCWITHPOINT和MINIDISC,他们也同样只需要线性的存储空间。对于它们的期望运行时间分析如下:对于算法MINIDISCWITHPOINT,只要不计入其调用MINIDISCWITH2POINT的时间,其余计算所需要的时间为O(n)。依据概率论分析可以求其期望运行时间的上界O(n)+。

3.与点增量算法对比

点增量算法:从平面上的任意三点出发,求其最小包围圆,;再依次判断之外的点,看其相对位置是位于圆内(包括在圆上)还是圆外。若在圆内,则最小包围圆不变,再判断下个点;否则,求包围此四点的最小包围圆。逐个迭代,直至完全遍历。显然,该算法的时间复杂度比较大,达到O(n4),在实际应用中是不可取的。

4.以杭电3007 Buried memory为例:

Buried memory

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2422    Accepted Submission(s): 1312


Problem Description
Each person had do something foolish along with his or her growth.But,when he or she did this that time,they could not predict that this thing is a mistake and they will want this thing would rather not happened.
The world king Sconbin is not the exception.One day,Sconbin was sleeping,then swakened by one nightmare.It turned out that his love letters to Dufein were made public in his dream.These foolish letters might ruin his throne.Sconbin decided to destroy the letters by the military exercises's opportunity.The missile is the best weapon.Considered the execution of the missile,Sconbin chose to use one missile with the minimum destruction.
Sconbin had writen N letters to Dufein, she buried these letters on different places.Sconbin got the places by difficult,he wants to know where is the best place launch the missile,and the smallest radius of the burst area. Let's help Sconbin to get the award.
 

Input
There are many test cases.Each case consists of a positive integer N(N<500,^V^,our great king might be a considerate lover) on a line followed by N lines giving the coordinates of N letters.Each coordinates have two numbers,x coordinate and y coordinate.N=0 is the end of the input file.
 

Output
For each case,there should be a single line in the output,containing three numbers,the first and second are x and y coordinates of the missile to launch,the third is the smallest radius the missile need to destroy all N letters.All output numbers are rounded to the second digit after the decimal point.
 

Sample Input
  
  
3 1.00 1.00 2.00 2.00 3.00 3.00 0
 

Sample Output
  
  
2.00 2.00 1.41
 

Source
 

Recommend
gaojie
 


代码:

#include <cstdio>
#include <cstring>
#include <cmath>

struct Point{
    double x, y;
} p[501], o; //定义点和圆心

int n;
double r;//圆半径
double eps = 1e-6;

double dist(Point a, Point b){
    return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}
/*
//求线段交点
Point intersection(Point u1, Point u2, Point v1, Point v2){
    Point ans = u1;
    double t = ((u1.x - v1.x)*(v1.y - v2.y) - (u1.y - v1.y)*(v1.x - v2.x))/
               ((u1.x - u2.x)*(v1.y - v2.y) - (u1.y - u2.y)*(v1.x - v2.x));
    ans.x += (u2.x - u1.x)*t;
    ans.y += (u2.y - u1.y)*t;
    return ans;
}

//计算三角形外接圆圆心
Point circumcenter(Point a, Point b, Point c){
    Point ua, ub, va, vb;
    ua.x = (a.x + b.x)/2;
    ua.y = (a.y + b.y)/2;
    ub.x = ua.x - a.y + b.y;			//根据 垂直判断,两线段点积为0
    ub.y = ua.y + a.x - b.x;
    va.x = (a.x + c.x)/2;
    va.y = (a.y + c.y)/2;
    vb.x = va.x - a.y + c.y;
    vb.y = va.y + a.x - c.x;
    return intersection(ua, ub, va, vb);
}
*/

//计算三角形外接圆圆心
Point circumcenter(Point a, Point b, Point c){
    Point center;
    center.x = ((a.x-b.x)*(a.x+b.x)/(a.y-b.y)/2 - (c.x-b.x)*(c.x+b.x)/(c.y-b.y)/2 + (a.y+b.y)/2 - (c.y+b.y))/
					((a.x-b.x)/(a.y-b.y) - (c.x-b.x)/(c.y-b.y));
	center.y = ((a.y-b.y)*(a.y+b.y)/(a.x-b.x)/2 - (c.y-b.y)*(c.y+b.y)/(c.x-b.x)/2 + (a.x+b.x)/2 - (c.x+b.x))/
					((a.y-b.y)/(a.x-b.x) - (c.y-b.y)/(c.x-b.x));
	return center;
}

void min_center(){
    o = p[0];
    r = 0;
    for(int i=1;i<n;i++){					//准备加入的点
        if(dist(p[i], o)-r>eps){	//如果第i点在 i-1前最小圆外面
            o = p[i];					//另定圆心
            r = 0;						//另定半径
            for(int j=0;j<i;j++){	//循环再确定半径
                if(dist(p[j], o)-r>eps){
                    o.x = (p[i].x + p[j].x)/2.0;
                    o.y = (p[i].y + p[j].y)/2.0;

                    r = dist(o, p[j]);

                    for(int k=0;k<j;k++){
                        if(dist(o,p[k])-r>eps){		//如果j前面有点不符和 i与j确定的圆,则更新
                            o = circumcenter(p[i], p[j], p[k]);
                            r = dist(o, p[k]);
                        }
                    }//循环不超过3层,因为一个圆最多3个点可以确定
                }
            }
        }
    }
}

int main(){
    int i, j, k;
    while(scanf("%d", &n), n){
        for( i = 0 ; i < n ; i++)
            scanf("%lf%lf", &p[i].x, &p[i].y);
        min_center();
        printf("%.2lf %.2lf %.2lf\n", o.x, o.y, r);
    }
    return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值