题意:
求一个圆的半径,半径为在所给坐标点中距离最近两点距离的一半
思路:
一开始用暴力枚举求解其最近点,然而超时了。
wa了很久,最后从网上学习了,分治思想求解最短距离
更为详细的题解
主要思想:
- 先给坐标点用x的方式排序,后给其设计编号
- 从中间对半,分成两区域,然后分别求其最小值d1,d2在进行比较得到d最小值
- 但是会出现一种情况就是在两个区域边界点才是最小值的情况
- 因此我们可以给中间点mid添加范围来求最小值
- mid为x坐标值,则范围就是mid-d<=x<=mid+d
- 然后得到答案与d进行比较就得出答案
代码:
//看着别人代码打一遍0.0
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
struct point
{
double x , y;
}p[100005];
int a[100005]; //保存筛选的坐标点的索引
int cmpx(const point &a , const point &b)
{
return a.x < b.x;
}
int cmpy(int a , int b) //这里用的是下标索引
{
return p[a].y < p[b].y;
}
inline double dis(point a , point b)
{
return sqrt( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
inline double min(double a , double b)
{
return a < b ? a : b;
}
double closest(int low , int high)
{
if(low + 1 == high)
return dis(p[low] , p[high]);
if(low + 2 == high)
return min(dis(p[low] , p[high]) , min( dis(p[low] , p[low+1]) , dis(p[low+1] , p[high]) ));
int mid = (low + high)>>1;
double ans = min( closest(low , mid) , closest(mid + 1 , high) ); //分治法进行递归求解
int i , j , cnt = 0;
for(i = low ; i <= high ; ++i) //把x坐标在p[mid].x-ans~p[mid].x+ans范围内的点取出来
{
if(p[i].x >= p[mid].x - ans && p[i].x <= p[mid].x + ans)
a[cnt++] = i; //保存的是下标索引
}
sort(a , a + cnt , cmpy); //按y坐标进行升序排序
for(i = 0 ; i < cnt ; ++i)
{
for(j = i+1 ; j < cnt ; ++j)
{
if(p[a[j]].y - p[a[i]].y >= ans) //注意下标索引
break;
ans = min(ans , dis(p[a[i]] , p[a[j]]));
}
}
return ans;
}
int main(void)
{
int i,n;
while(scanf("%d",&n) != EOF)
{
if(!n)
break;
for(i = 0 ; i < n ; ++i)
scanf("%lf %lf",&p[i].x,&p[i].y);
sort(p , p + n , cmpx);
printf("%.2lf
",closest(0 , n - 1)/2);
}
return 0;
}
体会
见识到了sort的另外一种用法,利用递归函数进行分治。。等等一系列新写法