hdu1007最近點對問題(分冶java)
阿新 • • 發佈:2018-11-12
題目連結
題意就是給若干點,求最近點對問題。
- 首先這題是我很久前看到的,我那時候用了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;
}
}
}