1) 上一篇求最大连续子数组之和了解到分治策略之后,做本题时先是找到中间界线,依然分成三部分,左边集合、右边集合、两个点分别在左边和右边的集合的情况。前两种情况不断递归,直到该集合内只有两个点,则求两点之间距离并返回,第三种情况如果常规来做,则是左边集合中的每一个点都要对应求到右边集合中每个点的距离,比较出其中最小的,此处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)
Quoit Design
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 43109 Accepted Submission(s): 11195
In the field of Cyberground, the position of each toy is fixed, and the ring is carefully designed so it can only encircle one toy at a time. On the other hand, to make the game look more attractive, the ring is designed to have the largest radius. Given a configuration of the field, you are supposed to find the radius of such a ring.
Assume that all the toys are points on a plane. A point is encircled by the ring if the distance between the point and the center of the ring is strictly less than the radius of the ring. If two toys are placed at the same point, the radius of the ring is considered to be 0.