分治算法求最近点对
?????http://acm.hdu.edu.cn/showproblem.php?pid=1007
???????? 先說下題意,很簡單,給n個(gè)點(diǎn)的坐標(biāo),求距離最近的一對點(diǎn)之間距離的一半。第一行是一個(gè)數(shù)n表示有n個(gè)點(diǎn),接下來n行是n個(gè)點(diǎn)的x坐標(biāo)和y坐標(biāo),實(shí)數(shù)。
????? 這個(gè)題目其實(shí)就是求最近點(diǎn)對的距離。《算法導(dǎo)論》上有詳細(xì)講解,王曉東的書上也有代碼。主要思想就是分治。先把n個(gè)點(diǎn)按x坐標(biāo)排序,然后求左邊n/2個(gè)和右邊n/2個(gè)的最近距離,最后合并。合并要重點(diǎn)說一下,比較麻煩。
????? 首先,假設(shè)點(diǎn)是n個(gè),編號為1到n。我們要分治求,則找一個(gè)中間的編號mid,先求出1到mid點(diǎn)的最近距離設(shè)為d1,還有mid+1到n的最近距離設(shè)為d2。這里的點(diǎn)需要按x坐標(biāo)的順序排好,并且假設(shè)這些點(diǎn)中,沒有2點(diǎn)在同一個(gè)位置。(若有,則直接最小距離為0了)。
????? 然后,令d為d1, d2中較小的那個(gè)點(diǎn)。如果說最近點(diǎn)對中的兩點(diǎn)都在1-mid集合中,或者mid+1到n集合中,則d就是最小距離了。但是還有可能的是最近點(diǎn)對中的兩點(diǎn)分屬這兩個(gè)集合,所以我們必須先檢測一下這種情況是否會(huì)存在,若存在,則把這個(gè)最近點(diǎn)對的距離記錄下來,去更新d。這樣我們就可以得道最小的距離d了。
????? 關(guān)鍵是要去檢測最近點(diǎn)對,理論上每個(gè)點(diǎn)都要和對面集合的點(diǎn)匹配一次,那效率還是不能滿足我們的要求。所以這里要優(yōu)化。怎么優(yōu)化呢?考慮一下,假如以我們所選的分割點(diǎn)mid為界,如果某一點(diǎn)的橫坐標(biāo)到點(diǎn)mid的橫坐標(biāo)的絕對值超過d1并且超過d2,那么這個(gè)點(diǎn)到mid點(diǎn)的距離必然超過d1和d2中的小者,所以這個(gè)點(diǎn)到對方集合的任意點(diǎn)的距離必然不是所有點(diǎn)中最小的。
????? 所以我們先把在mid為界左右一個(gè)范圍內(nèi)的點(diǎn)全部篩選出來,放到一個(gè)集合里。篩選好以后,當(dāng)然可以把這些點(diǎn)兩兩求距離去更新d了,不過這樣還是很慢,萬一滿足條件的點(diǎn)很多呢。這里還得繼續(xù)優(yōu)化。首先把這些點(diǎn)按y坐標(biāo)排序。假設(shè)排序好以后有cnt個(gè)點(diǎn),編號為0到cnt-1。那么我們用0號去和1到cnt-1號的點(diǎn)求一下距離,然后1號和2到cnt-1號的點(diǎn)求一下距離。。。如果某兩個(gè)點(diǎn)y軸距離已經(jīng)超過了d,這次循環(huán)就可以直接break了,開始從下一個(gè)點(diǎn)查找了。
按照y值進(jìn)行升序排列后,還可以進(jìn)一步進(jìn)行優(yōu)化的,就是每次選取7個(gè)點(diǎn)就OK了,具體原因編程之美上面有介紹的。
for(i = 0 ; i < cnt ; ++i){int k = (i+7) > cnt ? cnt :(i+7); //只要選取出7個(gè)點(diǎn)(證明過程沒看懂) for(j = i+1 ; j < k ; ++j){if(p[a[j]].y - p[a[i]].y >= ans) //注意下標(biāo)索引break;ans = min(ans , dis(p[a[i]] , p[a[j]]));}}
總結(jié)
- 上一篇: N*N匹马,N个赛道,求出最快N匹马的解
- 下一篇: 随机数范围扩展方法总结