今天學習了一下最小圓覆蓋, 看了一下午都沒看懂, 晚上慢慢的摸索這代碼,接合着別人的講解, 畫着圖跟着代碼一步一步的走着,竟然有些理解了.
最小圓覆蓋: 給定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; }