先說下題意,很簡單,給n個點的座標,求距離最近的一對點之間距離的一半。
第一行是一個數n表示有n個點,接下來n行是n個點的x座標和y座標。實數。
這個題目其實就是求最近點對的距離。《算法導論》上有詳細講解,王曉東的書上也有代碼。主要思想就是分治。先把n個點按x座標排序,然後求左邊n/2個和右邊n/2個的最近距離,最後合併。
合併要重點說一下,比較麻煩。
首先,假設點是n個,編號爲1到n。我們要分治求,則找一箇中間的編號m,先求出1到m點的最近距離設爲d1,還有m+1到n的最近距離設爲d2。這裏的點需要按x座標的順序排好,並且假設這些點中,沒有2點在同一個位置。(若有,則直接最小距離爲0了)。
然後,令d爲d1, d2中較小的那個點。如果說最近點對中的兩點都在1-m集合中,或者m+1到n集合中,則d就是最小距離了。但是還有可能的是最近點對中的兩點分屬這兩個集合,所以我們必須先檢測一下這種情況是否會存在,若存在,則把這個最近點對的距離記錄下來,去更新d。這樣我們就可以得道最小的距離d了。
關鍵是要去檢測最近點對,理論上每個點都要和對面集合的點匹配一次,那效率還是不能滿足我們的要求。所以這裏要優化。怎麼優化呢?考慮一下,假如以我們所選的分割點m爲界,如果某一點的橫座標到點m的橫座標的絕對值超過d1並且超過d2,那麼這個點到m點的距離必然超過d1和d2中的小者,所以這個點到對方集合的任意點的距離必然不是所有點中最小的。
所以我們先把在m爲界左右一個範圍內的點全部篩選出來,放到一個集合裏。
篩選好以後,當然可以把這些點兩兩求距離去更新d了,不過這樣還是很慢,萬一滿足條件的點很多呢。這裏還得繼續優化。首先把這些點按y座標排序。假設排序好以後有p個點,編號爲1到p。那麼我們用1號去和2到p號的點求一下距離,然後2號和3到p號的點求一下距離。。。還沒完,因爲這樣比,求的次數還是O(p^2), 所以其實和沒優化沒區別。
假設有一個點q,座標是xq, yq。可以證明在以q爲底邊中點,長爲2d,寬爲d的矩形區域內不會有超過6個點。具體證明過程可以參看算法導論。
利用這個結論我們就可以來繼續優化比較的過程了。剛剛我們是用用1號點去和2到p號的點求一下距離,我們知道以1號點構造圖中矩形內,不會有超過6個點存在。但是我們又不能直接從1號求到6號,因爲這裏的p個點是按y座標排序而不是按距離排序的,有可能在y座標上,前10個點距離1號點都很近,但是前6個點的x座標很遠,而第10個點的x座標和1號點的x座標很進,這樣第10個點到1號點的距離反而更近。
那麼我們怎麼利用這個結論呢?應該這樣,假設1號點和2到p號點比較,由於y座標排序的緣故,假設第p個點的y座標距離1號點的y座標大於當前能求出的最小值,那麼這點以及這點後的所有點距離1號點的距離必然大於當前已經獲得的最小值,所以直接不用比較後面的了。
又因爲滿足比較條件的點很少,不會超過6個,所以這裏可以看成O(1)的效率。那麼整個算法的效率大概是在O(nlogn),非常快!
源代碼(1):
- /*分治算法求最小點對*/
- /*AC代碼:828ms*/
- #include <iostream>
- #include <cmath>
- #include <algorithm>
- #define MAXN 100005
- //#define min(a,b) (a<b?a:b)//爲什麼用這個就超時!!!
- using namespace std;
- struct Point
- {
- double x,y;
- };
- struct Point point[MAXN],*px[MAXN],*py[MAXN];
- double get_dis(Point *p1,Point *p2)
- {
- return sqrt((p1->x-p2->x)*(p1->x-p2->x)+(p1->y-p2->y)*(p1->y-p2->y));
- }
- bool cmpx(Point *p1,Point *p2) {return p1->x<p2->x;}
- bool cmpy(Point *p1,Point *p2) {return p1->y<p2->y;}
- double min(double a,double b){return a<b?a:b;}
- //-------核心代碼------------//
- double closest(int s,int e)
- {
- if(s+1==e)
- return get_dis(px[s],px[e]);
- if(s+2==e)
- return min(get_dis(px[s],px[s+1]),min(get_dis(px[s+1],px[e]),get_dis(px[s],px[e])));
- int mid=(s+e)>>1;
- double ans=min(closest(s,mid),closest(mid+1,e));//遞歸求解
- int i,j,cnt=0;
- for(i=s;i<=e;i++)//把x座標在px[mid].x-ans~px[mid].x+ans範圍內的點取出來
- {
- if(px[i]->x>=px[mid]->x-ans&&px[i]->x<=px[mid]->x+ans)
- py[cnt++]=px[i];
- }
- sort(py,py+cnt,cmpy);//按y座標排序
- for(i=0;i<cnt;i++)
- {
- for(j=i+1;j<cnt;j++)//py數組中的點是按照y座標升序的
- {
- if(py[j]->y-py[i]->y>=ans)
- break;
- ans=min(ans,get_dis(py[i],py[j]));
- }
- }
- return ans;
- }
- int main()
- {
- int i,n;
- while(scanf("%d",&n)!=EOF)
- {
- if(n==0)
- break;
- for(i=0;i<n;i++)
- {
- scanf("%lf%lf",&point[i].x,&point[i].y);
- px[i]=&point[i];
- }
- sort(px,px+n,cmpx);
- //for(i=0;i<n;i++)
- // printf("(%.2lf,%.2lf)--",px[i].x,px[i].y);
- double distance=closest(0,n-1);
- printf("%.2lf\n",distance/2);
- }
- return 0;
- }
參考網上思路,由於這題數據比較弱,(是網上說的,不過確實很弱)。可以以一種投機取巧的枚舉水過。。。
具體方法:對點進行x軸排序,這樣,可以知道最短的距離最有可能出現的是在兩個相鄰的點之間(但不一定),所以我枚舉了離當前點距離(只看x軸)最近的50個點,這樣,最短距離一般就在裏面了。但是更讓人喫驚的是,這題數據弱到,只要對x軸排序,枚舉相鄰3個點就可以A掉。。。。囧。。。。。
http://hi.baidu.com/yzy%D1%EE%D7%D3%D1%DC/blog/item/4b4da1ece0365a4379f05571.html
源代碼(2):
- /*最近點對(枚舉)*/
- /*AC代碼:609ms*/
- #include <iostream>
- #include <cmath>
- #define min(a,b) (a<b?a:b)
- #define MAXN 100005
- #define INF 99999999
- #define step 2
- struct point
- {
- double x,y;
- };
- struct point ps[MAXN];
- double ansr;
- int N;
- int cmp1(const void *p1,const void *p2)
- {
- if(((struct point *)p1)->x!=((struct point *)p2)->x)
- return ((struct point *)p1)->x>((struct point *)p2)->x?1:-1;
- return ((struct point *)p1)->y>((struct point *)p2)->y?1:-1;
- }
- int cmp2(const void *p1,const void *p2)
- {
- if(((struct point *)p1)->y!=((struct point *)p2)->y)
- return ((struct point *)p1)->y>((struct point *)p2)->y?1:-1;
- return ((struct point *)p1)->x>((struct point *)p2)->x?1:-1;
- }
- double get_dis(point p1,point p2)
- {
- return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
- }
- void work()
- {
- int i,j;
- for(i=1;i<=N;i++)
- for(j=i+1;j<=N&&j<=i+step;j++)
- ansr=min(ansr,get_dis(ps[i],ps[j]));
- }
- int main()
- {
- int i;
- while(scanf("%d",&N)!=EOF)
- {
- if(N==0)
- break;
- for(i=1;i<=N;i++)
- scanf("%lf%lf",&ps[i].x,&ps[i].y);
- ansr=INF;
- qsort(ps+1,N,sizeof(ps[0]),cmp1);
- work();
- //qsort(ps+1,N,sizeof(ps[0]),cmp2);
- //work();
- printf("%.2lf\n",ansr/2);
- }
- return 0;
- }
P.S:這裏還有一個非常令人費解的問題,CODE(1)裏爲什麼我用,#define min(a,b) (a<b?a:b)會超時,而改成
double min(double a,double b){return a<b?a:b;}就AC
原文地址:http://blog.csdn.net/allenjy123/article/details/6627751