1. 程式人生 > >[ZOJ 1450] Minimal Circle(最小圓覆蓋問題)

[ZOJ 1450] Minimal Circle(最小圓覆蓋問題)

題目描述:

給出一組點的座標,求出能夠覆蓋掉他們的最小圓的座標及半徑.

演算法分析:

解決這個問題有一種演算法,隨機化增量演算法,在隨機資料下可以O(N)的解決這個問題.

具體實現方法:

1.先將所有的點隨機化處理

2.按順序把點一個一個的加入(一步一步的求前i個點的最小覆蓋圓),每加入一個點就進入步驟3

3.判斷當前點是否在當前的最下覆蓋圓內,如果不在進入4,在進入2

4.將圓心設為當前點 i 半徑為 0,然後把前 i-1個點加入進這個最小覆蓋圓中,每加入一個點進入5

5.如果發現當前j號點在當前的最小圓的外面,那麼說明點j也一定在前j個點(包括i)的最小覆蓋圓邊界上,我們轉到6來再進一步確定這個圓,否則前j個點(包括i)的最小覆蓋圓與前i-1個點(包括i)的最小覆蓋圓是一樣的,則不需要更新,返回

6.此時已經確認點i,j一定在前j個點(包括i)的最小覆蓋圓的邊界上了,那麼我們可以把當前圓的圓心設為第i個點與第j的點連線的中點,半徑為到這兩個點的距離(就是找一個覆蓋這兩個點的最小圓),然後重新把前j-1個點加入這個圓中(還是類似上面的步驟,只不過這次我們提前確定了兩個點在圓上,目的是求出包含點i,j的前k個點的最小覆蓋圓),每加入一個點就進入7

7.如果發現當前k號點在當前的最小圓的外面,那麼說明點k也一定在前k個點(包括i,j)的最小覆蓋圓邊界上,我們不需要再進一步確定這個圓了(因為三個點能確定一個圓!),直接求出這三點共圓,否則前k個點(包括i,j)的最小覆蓋圓與前k-1個點(包括i,j)的最小覆蓋圓是一樣的,則不需要更新。

題目連結:

傳送門

程式碼如下:

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <cstdlib>
const int maxm=1005;
const double eps=1e-9;
struct Point{
    double x,y;
    int tim;
};
int n;
Point a[maxm],o;
double r;
double distance(Point p1,Point p2)
{
    return sqrt((p1.x
-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); } void makecir(Point p1,Point p2,Point p3)//三點確定一個圓 { double x1=2*(p2.x-p1.x),y1=2*(p2.y-p1.y); double c1=p2.x*p2.x+p2.y*p2.y-p1.x*p1.x-p1.y*p1.y; double x2=2*(p3.x-p1.x),y2=2*(p3.y-p1.y); double c2=p3.x*p3.x+p3.y*p3.y-p1.x*p1.x-p1.y*p1.y; o.x=(y1*c2-y2*c1)/(y1*x2-y2*x1); o.y=(x2*c1-x1*c2)/(y1*x2-y2*x1); r=distance(o,p1); } int main() { while(scanf("%d",&n)!=EOF) { if(!n) break; for(int i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y); std::random_shuffle(a+1,a+n+1); o=a[1],r=0; for(int i=2;i<=n;i++) if(distance(o,a[i])>r+eps) { o=a[i],r=0; for(int j=1;j<=i-1;j++) if(distance(o,a[j])>r+eps) { o.x=(a[i].x+a[j].x)/2; o.y=(a[i].y+a[j].y)/2; r=distance(o,a[j]); for(int k=1;k<=j-1;k++) if(distance(o,a[k])>r+eps) makecir(a[i],a[j],a[k]); } } printf("%.2lf %.2lf %.2lf\n",o.x,o.y,r); } return 0; }