1)AC总是让人感到兴奋。
上一篇求最大连续子数组之和了解到分治策略之后,做本题时先是找到中间界线,依然分成三部分,左边集合、右边集合、两个点分别在左边和右边的集合的情况。前两种情况不断递归,直到该集合内只有两个点,则求两点之间距离并返回,第三种情况如果常规来做,则是左边集合中的每一个点都要对应求到右边集合中每个点的距离,比较出其中最小的,此处O((n/2)^2),时间复杂度依然是n^2层次,于是继续优化,先筛选出左边集合中到中间界线的x的距离已经大于当前最小距离的点,同理,筛选并去掉右边集合x的距离已经大于当前最小距离的点,然后提交,TLE(Time Limit Exceeded),再将当前集合,左边集合中到中间界线的y的距离已经大于当前最小距离的点,去掉,(在结构体中另开一个Int做一下标记即可)同理右边集合也是如此,再提交,AC
我自己的思路只是做到分治,但是停留在n^2的层次,看了其他人的思路之后,才知道可以进行后两次优化,而且没想到这样做会成功,成功的原因是,在上面所述第三种情况中,因为前提已经限定一个点在左边,另一个点在右边,则由此划出了一个区域,即在每个点的x值上,应该是在中界线往左minn距离,和中界线往右minn距离的范围内,(minn距离是另外两种情况中的最小对的距离,第三种情况若对则存在的最小点对距离必定小于minn,若不存在则返回minn),但这并不能保证,两个点x方向距离很近,但y反向很远的问题,我们似乎应该将y方向上大于minn的点也给去掉,然后进行两层fou循环,不断求两点间距离并更新最小值,这样的做法,看起来似乎还是让这一步的算法处于O(n^2)的层次,但实际上,在中界线上确定某个点,该点往左、右、上、下所划定的范围内,点的个数是有限的,是不大于8个的(因为必须保证一点在左、一点在右,则自然保证了同一边的点互相之间是必须保持一定距离的,否则就不会属于第三种情况。换句话说,小于minn距离的点对有限而绝不会无限多),于是在两层for循环中,如果两个点y的距离大于minn,就可以break了,相当于使内层循环控制在了O(1)的层次上,于是这一步的时间复杂度到了O(n),整个算法的时间复杂度,降为O(nlogn)。(参见《导论》33-4)
#include <iostream> #include <string.h>//memser() #include <algorithm>//sort() #include <math.h> //#include <bits/stdc++.h> using namespace std; struct Num{ double x; double y; }Number[100010]; bool cmp(const Num &a,const Num &b) { return a.x<b.x; } bool cmpy(const Num &a,const Num &b) { return a.y<b.y; } double juli(double x1,double y1,double x2,double y2) { return sqrt(pow(x2-x1,2)+pow(y2-y1,2)); } double zhongjian(int low,int high,double minn) { int mid=(low+high)/2; int ran_left=low,ran_right=high; for(int i=mid;i>=low;i--){ if((Number[mid].x-Number[i].x)>minn){ ran_left=i; break; } } for(int i=mid;i<=high;i++){ if((Number[i].x-Number[mid].x)>minn){ ran_right=i; break; } } struct Num Number2[100010]; int cur=0; for(int i=ran_left;i<=ran_right;i++) { Number2[cur].x=Number[i].x; Number2[cur].y=Number[i].y; cur++; } sort(Number2,Number2+cur,cmpy); double temp; double mid_minn=minn; for(int i=0;i<cur-1;i++){ for(int j=i+1;j<cur;j++){ if((Number2[j].y-Number2[i].y)>minn) break; temp=juli(Number2[i].x,Number2[i].y,Number2[j].x,Number2[j].y); if(temp<mid_minn) mid_minn=temp; } } return mid_minn; } double Erfen(int low,int high) { if(low==high-1){ return juli(Number[low].x,Number[low].y,Number[high].x,Number[high].y); } else{ int mid=(low+high)/2; double left=Erfen(low,mid);//cout<<"left:"<<left<<endl; double right=Erfen(mid,high);//cout<<"right:"<<right<<endl; double minn=min(left,right);//cout<<"minn:"<<minn<<endl; double middle=zhongjian(low,high,minn); if(left<=right&&left<=middle){ return left; } else if(right<=left&&right<=middle){ return right; } else{ return middle; } } } int main() { int n; while(cin>>n){ if(n==0) break; memset(Number,0,sizeof(Number)); for(int i=1;i<=n;i++){ cin>>Number[i].x>>Number[i].y; } sort(Number+1,Number+n+1,cmp); //double low_x=Number[0].x,high_x=Number[n-1].x; double r=Erfen(1,n); printf("%.2f\n",r/2); } }
2 0 0 1 1 2 1 1 1 1 3 -1.5 0 0 0 0 1.5 0
0.71 0.00 0.75