最小圓覆蓋 hdu 3007


今天學習了一下最小圓覆蓋, 看了一下午都沒看懂, 晚上慢慢的摸索這代碼,接合着別人的講解, 畫着圖跟着代碼一步一步的走着,竟然有些理解了.

最小圓覆蓋: 給定n個點, 求出半徑最小的圓可以把這些點全部包圍, 可以在圓的邊界上

下面是我的個人理解. 如果不對, 還請路過大牛指出

  先找一個點, 讓圓心等於這個點的坐標, 半徑等於0, 顯然這個對的, 接着找下一個點, 如果只有兩個點的話, 那么最小的圓一定是以他們為直徑做圓, 接着找第三個點, 如果第三個點在園內或者在邊界上, 那么不用更新當前的最小圓, 如果不在的話, 就要更新當前的最小圓了,使它包括這三個點, 那么更新的辦法就是從他開始做圓, 依次判斷它前面的點是否滿足在最小圓內, 如果不在的話, 就需要根據兩個點或者三個點來確定圓了, 它的外接圓最多三個點就確定了,剛開始一直不理解這個為什么三個點,后來畫畫圖,走走就出來了. 我這可能說的比較籠統. 表達能力太差. 還是把大牛的原話拷過來把.

最小覆蓋圓, 增量法
假設圓O是前i-1個點得最小覆蓋圓,加入第i個點,如果在圓內或邊上則什么也不做。否,新得到的最小覆蓋圓肯定經過第i個點。
然后以第i個點為基礎(半徑為0),重復以上過程依次加入第j個點,若第j個點在圓外,則最小覆蓋圓必經過第j個點。
重復以上步驟(因為最多需要三個點來確定這個最小覆蓋圓,所以重復三次)。遍歷完所有點之后,所得到的圓就是覆蓋所有點得最小圓。

證明可以考慮這么做:
最小圓必定是可以通過不斷放大半徑,直到所有以任意點為圓心,半徑為半徑的圓存在交點,此時的半徑就是最小圓。所以上述定理可以通過這個思想得到。這個做法復雜度是O(n)的,當加入圓的順序隨機時,因為三點定一圓,所以不在圓內概率是3/i,求出期望可得是O(n)。

下面是代碼(模板)

/*************************************************************************
    > File Name:            hdu_3007.cpp
    > Author:               Howe_Young
    > Mail:                 1013410795@qq.com
    > Created Time:         2015年05月04日 星期一 18時42分33秒
 ************************************************************************/
/*最小圓覆蓋*/
/*給定n個點, 讓求半徑最小的圓將n個點全部包圍,可以在圓上*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define EPS 1e-8
using namespace std;
const int maxn = 550;
struct point{
    double x, y;
};
int sgn(double x)
{
    if (fabs(x) < EPS)
        return 0;
    return x < 0 ? -1 : 1;
}
double get_distance(const point a, const point b)//兩點之間的距離
{ 
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); 
}
point get_circle_center(const point a, const point b, const point c)//得到三角形外接圓的圓心
{
    point center;
    double a1 = b.x - a.x;
    double b1 = b.y - a.y;
    double c1 = (a1 * a1 + b1 * b1) / 2.0;
    double a2 = c.x - a.x;
    double b2 = c.y - a.y;
    double c2 = (a2 * a2 + b2 * b2) / 2.0;
    double d = a1 * b2 - a2 * b1;
    center.x = a.x + (c1 * b2 - c2 * b1) / d;
    center.y = a.y + (a1 * c2 - a2 * c1) / d;
    return center;
}
//p表示定點, n表示頂點的個數, c代表最小覆蓋圓圓心, r是半徑
void min_cover_circle(point *p, int n, point &c, double &r)//找最小覆蓋圓(這里沒有用全局變量p[], 因為是為了封裝一個函數便於調用)
{
    random_shuffle(p, p + n);//隨機函數,使用了之后使程序更快點,也可以不用
    c = p[0];
    r = 0;
    for (int i = 1; i < n; i++)
    {
        if (sgn(get_distance(p[i], c) - r) > 0)//如果p[i]在當前圓的外面, 那么以當前點為圓心開始找
        {
            c = p[i];//圓心為當前點
            r = 0;//這時候這個圓只包括他自己.所以半徑為0
            for (int j = 0; j < i; j++)//找它之前的所有點
            {
                if (sgn(get_distance(p[j], c) - r) > 0)//如果之前的點有不滿足的, 那么就是以這兩點為直徑的圓
                {
                    c.x = (p[i].x + p[j].x) / 2.0;
                    c.y = (p[i].y + p[j].y) / 2.0;
                    r = get_distance(p[j], c);
                    for (int k = 0; k < j; k++)
                    {
                        if (sgn(get_distance(p[k], c) - r) > 0)//找新作出來的圓之前的點是否還有不滿足的, 如果不滿足一定就是三個點都在圓上了
                        {
                            c = get_circle_center(p[i], p[j], p[k]);
                            r = get_distance(p[i], c);
                        }
                    }
                }
            }
        }
    }
}
int main()
{
    int n;
    point p[maxn];
    point c; double r;
    while (~scanf("%d", &n) && n)
    {
        for (int i = 0; i < n; i++)
            scanf("%lf %lf", &p[i].x, &p[i].y);
        min_cover_circle(p, n, c, r);
        printf("%.2lf %.2lf %.2lf\n", c.x, c.y, r);
    }
    return 0;
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM