1. 程式人生 > >hdu1007最近點對問題(分冶java)

hdu1007最近點對問題(分冶java)

題目連結
題意就是給若干點,求最近點對問題。

  • 首先這題是我很久前看到的,我那時候用了o(n^2)因為資料量太大,計算太多超時。當時看了別人的分析就說分冶當時看程式碼太長也就沒靜下心看。前天翻了資料結構看到分冶演算法的最近點問題恍然大悟,一下子就懂了。理解了其中的奧祕。
  • 對於分冶的問題,就是一個問題可以拆成若干個子問題,若干個子問題之間沒有聯絡,並且這個問題的處理方法同樣適用於子問題。
    首先上圖
    在這裡插入圖片描述
  • 如果用最暴力的方法,那麼我們就是要將每兩個點計算一遍,比較大小。如果有n個點,那麼就要計算n^2次。我們下面進行初步優化:
  • 取中間點,先算出所有左側點的距離最小值,再算出右側距離最小值,這兩個中更小的那個為min。這個距離不一定是最短的那個點,因為有可能最短的那個點再中間跨越兩邊。我們思考一下,這個中間兩點一個在左,一個在右,兩個點的橫座標之差一定小於 min不然他的投影都大於最短,所以我只需要處理中間左右區間為min裡面的點進行處理,對於這些點,左右距離是小於min的,如果上下距離大於min,就跳過不在考慮。
  • 考慮一下計算量,左側(n^2) /4 右側 也是,加起來是n*n/2,再加上中間的部分資料。這是一次優化的,另外,對於海量資料,我們分析其左側,我們也可以把左側拆成一半,先算左右再算中間,右側也是如此。並且問題可以一直拆分直到不能在拆為止。這個複雜度的差距就出來了。
  • 但是對於這個題,如果取x排序分冶事件耗費也比較大,這個可能就是點都大部分聚集在中間,上下間距較大的緣故,我們採用y排序劃分分冶。效率還是很高的。
    附上程式碼
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.StreamTokenizer; import java.util.Arrays; import java.util.Comparator; public class hdu1007 { static int n; public static void main(String[] args) throws IOException { StreamTokenizer in=new StreamTokenizer(new BufferedReader
(new InputStreamReader(System.in))); PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); while(in.nextToken()!=StreamTokenizer.TT_EOF) { n=(int)in.nval;if(n==0) {break;} node no[]=new node[n]; for(int i=0;i<n;i++) { in.nextToken();double x=in.nval; in.nextToken();double y=in.nval; no[i]=new node(x,y); } Arrays.sort(no, com); double min= search(no,0,n-1); out.println(String.format("%.2f", Math.sqrt(min)/2));out.flush(); } } private static double search(node[] no, int left,int right) { int mid=(right+left)/2;//中間 double minleng=0; if(left==right) {return Double.MAX_VALUE;}//一個點沒有距離之說,返回最大不影響 //相鄰直接返回兩點距離 else if(left+1==right) {minleng= (no[left].x-no[right].x)*(no[left].x-no[right].x)+(no[left].y-no[right].y)*(no[left].y-no[right].y);} else minleng= min(search(no,left,mid),search(no,mid,right));//左右側小的那個 int ll=mid;int rr=mid+1; while(no[mid].y-no[ll].y<=Math.sqrt(minleng)/2&&ll-1>=left) {ll--;}//從中間向左找到不在距離內的那個點 while(no[rr].y-no[mid].y<=Math.sqrt(minleng)/2&&rr+1<=right) {rr++;}//從中間向右找到不在距離內的那個帶 for(int i=ll;i<rr;i++) { for(int j=i+1;j<rr+1;j++) { double team=0; if(Math.abs((no[i].x-no[j].x)*(no[i].x-no[j].x))>minleng) {continue;} else { team=(no[i].x-no[j].x)*(no[i].x-no[j].x)+(no[i].y-no[j].y)*(no[i].y-no[j].y); if(team<minleng)minleng=team; } } } return minleng; } private static double min(double a, double b) { return a<b?a:b; } static Comparator<node>com=new Comparator<node>() { public int compare(node a1, node a2) { return a1.y-a2.y>0?1:-1; }}; static class node { double x; double y; public node(double x,double y) { this.x=x; this.y=y; } } }