杭電 ACM 1007

先說下題意,很簡單,給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):

  1. /*分治算法求最小點對*/  
  2. /*AC代碼:828ms*/  
  3. #include <iostream>  
  4. #include <cmath>  
  5. #include <algorithm>  
  6. #define MAXN 100005  
  7. //#define min(a,b) (a<b?a:b)//爲什麼用這個就超時!!!  
  8. using namespace std;  
  9. struct Point  
  10. {  
  11.     double x,y;  
  12. };  
  13. struct Point point[MAXN],*px[MAXN],*py[MAXN];  
  14. double get_dis(Point *p1,Point *p2)  
  15. {  
  16.     return sqrt((p1->x-p2->x)*(p1->x-p2->x)+(p1->y-p2->y)*(p1->y-p2->y));  
  17. }  
  18. bool cmpx(Point *p1,Point *p2) {return p1->x<p2->x;}  
  19. bool cmpy(Point *p1,Point *p2) {return p1->y<p2->y;}  
  20. double min(double a,double b){return a<b?a:b;}  
  21. //-------核心代碼------------//  
  22. double closest(int s,int e)  
  23. {  
  24.     if(s+1==e)  
  25.         return get_dis(px[s],px[e]);  
  26.     if(s+2==e)  
  27.         return min(get_dis(px[s],px[s+1]),min(get_dis(px[s+1],px[e]),get_dis(px[s],px[e])));  
  28.     int mid=(s+e)>>1;  
  29.     double ans=min(closest(s,mid),closest(mid+1,e));//遞歸求解  
  30.     int i,j,cnt=0;  
  31.     for(i=s;i<=e;i++)//把x座標在px[mid].x-ans~px[mid].x+ans範圍內的點取出來  
  32.     {  
  33.         if(px[i]->x>=px[mid]->x-ans&&px[i]->x<=px[mid]->x+ans)  
  34.             py[cnt++]=px[i];  
  35.     }  
  36.     sort(py,py+cnt,cmpy);//按y座標排序  
  37.     for(i=0;i<cnt;i++)  
  38.     {  
  39.         for(j=i+1;j<cnt;j++)//py數組中的點是按照y座標升序的  
  40.         {  
  41.             if(py[j]->y-py[i]->y>=ans)  
  42.                 break;  
  43.             ans=min(ans,get_dis(py[i],py[j]));  
  44.         }  
  45.     }  
  46.     return ans;  
  47. }  
  48. int main()  
  49. {  
  50.     int i,n;  
  51.     while(scanf("%d",&n)!=EOF)  
  52.     {  
  53.         if(n==0)  
  54.             break;  
  55.         for(i=0;i<n;i++)  
  56.         {  
  57.             scanf("%lf%lf",&point[i].x,&point[i].y);  
  58.             px[i]=&point[i];  
  59.         }  
  60.         sort(px,px+n,cmpx);  
  61.         //for(i=0;i<n;i++)  
  62.         // printf("(%.2lf,%.2lf)--",px[i].x,px[i].y);  
  63.         double distance=closest(0,n-1);  
  64.         printf("%.2lf\n",distance/2);  
  65.     }  
  66.     return 0;  
  67. }  

 

 


 

參考網上思路,由於這題數據比較弱,(是網上說的,不過確實很弱)。可以以一種投機取巧的枚舉水過。。。

具體方法:對點進行x軸排序,這樣,可以知道最短的距離最有可能出現的是在兩個相鄰的點之間(但不一定),所以我枚舉了離當前點距離(只看x軸)最近的50個點,這樣,最短距離一般就在裏面了。但是更讓人喫驚的是,這題數據弱到,只要對x軸排序,枚舉相鄰3個點就可以A掉。。。。囧。。。。。

http://hi.baidu.com/yzy%D1%EE%D7%D3%D1%DC/blog/item/4b4da1ece0365a4379f05571.html

 

 

 

 

源代碼(2):

  1. /*最近點對(枚舉)*/  
  2. /*AC代碼:609ms*/  
  3. #include <iostream>  
  4. #include <cmath>  
  5. #define min(a,b) (a<b?a:b)  
  6. #define MAXN 100005  
  7. #define INF 99999999  
  8. #define step 2  
  9. struct point  
  10. {  
  11.     double x,y;  
  12. };  
  13. struct point ps[MAXN];  
  14. double ansr;  
  15. int N;  
  16. int cmp1(const void *p1,const void *p2)  
  17. {  
  18.     if(((struct point *)p1)->x!=((struct point *)p2)->x)  
  19.         return ((struct point *)p1)->x>((struct point *)p2)->x?1:-1;  
  20.     return ((struct point *)p1)->y>((struct point *)p2)->y?1:-1;  
  21. }  
  22. int cmp2(const void *p1,const void *p2)  
  23. {  
  24.     if(((struct point *)p1)->y!=((struct point *)p2)->y)  
  25.         return ((struct point *)p1)->y>((struct point *)p2)->y?1:-1;  
  26.     return ((struct point *)p1)->x>((struct point *)p2)->x?1:-1;  
  27. }  
  28. double get_dis(point p1,point p2)  
  29. {  
  30.     return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));  
  31. }  
  32. void work()  
  33. {  
  34.     int i,j;  
  35.     for(i=1;i<=N;i++)  
  36.         for(j=i+1;j<=N&&j<=i+step;j++)  
  37.             ansr=min(ansr,get_dis(ps[i],ps[j]));  
  38. }  
  39. int main()  
  40. {  
  41.     int i;  
  42.     while(scanf("%d",&N)!=EOF)  
  43.     {  
  44.         if(N==0)  
  45.             break;  
  46.         for(i=1;i<=N;i++)  
  47.             scanf("%lf%lf",&ps[i].x,&ps[i].y);  
  48.         ansr=INF;  
  49.         qsort(ps+1,N,sizeof(ps[0]),cmp1);  
  50.         work();  
  51.         //qsort(ps+1,N,sizeof(ps[0]),cmp2);  
  52.         //work();  
  53.         printf("%.2lf\n",ansr/2);  
  54.     }  
  55.     return 0;  
  56. }  


 

 

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章