HDU 1007 Quoit Design 分治法

来源:互联网 发布:中国网络诗歌网新歌词 编辑:程序博客网 时间:2024/06/06 09:49

Quoit Design

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 28566    Accepted Submission(s): 7475


Problem Description
Have you ever played quoit in a playground? Quoit is a game in which flat rings are pitched at some toys, with all the toys encircled awarded.
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.
 

Input
The input consists of several test cases. For each case, the first line contains an integer N (2 <= N <= 100,000), the total number of toys in the field. Then N lines follow, each contains a pair of (x, y) which are the coordinates of a toy. The input is terminated by N = 0.
 

Output
For each test case, print in one line the radius of the ring required by the Cyberground manager, accurate up to 2 decimal places. 
 

Sample Input
20 01 121 11 13-1.5 00 00 1.50
 

Sample Output
0.710.000.75
 

Author
CHEN, Yue
 

Source
ZJCPC2004
 

第一次接触分治法,用分治法求两点间最短距离。
重点是循环递归。

在每一层递归上都有三个步骤:

  1. 分解:将原问题分解为若干个规模较小,相对独立,与原问题形式相同的子问题。
  2. 解决:若子问题规模较小且易于解决时,则直接解。否则,递归地解决各子问题。
  3. 合并:将各子问题的解合并为原问题的解。
将所有的点按x坐标分为两块left,right,分别在这两块中求解,而在left与right中求解的方法与依然是将left,right分成两块求解,这样就将一个问题分成若干个子问题然后递归求解。这样求解在两边分别都能求出一个最短距离dl,dr,但是这两个最短距离的两个点都是在left或者都是在right的,如果是一个在left一个在right就无法求解。这时需要求一个在right一个在left的特殊情况,令d=min(dl,dr),则假如存在这样两个点,则距离一定小于d,且两个的横纵坐标之差都必小于d,否则不成立,因为我们之前按x排过序了,就可以筛掉一部分x不满足要求的点,将这些点的编号记录,再按y排一次序,这样再进行选择,筛掉的那部分点就可以不用遍历了,提高了效率,边筛边计算距离,保留最小的距离,最后返回两点最小的距离。

AC代码:
#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>bool cmpx(const struct point a,const struct point b);bool cmpy(const int& a,const int& b);double mi(double a,double b);double dis(const point a,const point b);double dc(int left,int right);#define N 100005#define INF 0x3f3f3f3fusing namespace std;struct point{    double x;    double y;}p[N];int n;int f[N];int main(){    int i;    while(~scanf("%d",&n),n)    {        for(i=0;i<n;i++)            scanf("%lf%lf",&p[i].x,&p[i].y);        double ans;        sort(p,p+n,cmpx);        ans=dc(0,n-1);//这里传的是点的标号        printf("%.2lf\n",ans/2);    }    return 0;}bool cmpx(const point a,const point b){    return a.x<b.x;}bool cmpy(const int& a,const int& b){    return p[a].y<p[b].y;}double mi(double a,double b){    return a>b?b:a;}double dis(const point a,const point b){    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}double dc(int left,int right){    if(left+1==right)//如果只有两个点,则最短距离为两点间距离        return dis(p[left],p[right]);    if(left==right)        return 0;    int mid;    mid=(left+right)/2;//分两段    double dl;    dl=dc(left,mid);    double dr;    dr=dc(mid,right);    double d;    d=mi(dl,dr);    int cnt=0;    for(int i=left;i<=right;i++)        if(fabs(p[mid].x-p[i].x)<=d)        {             f[cnt]=i;//f记录筛完后的点,这样在下面那个循环就可以提高效率,只遍历有效的点             cnt++;        }    sort(f,f+cnt,cmpy);    for(int i=0;i<cnt;i++)    {        for(int j=i+1;j<cnt&&p[f[j]].y-p[f[i]].y<d;j++)//y的差也要小于d            {                double d3=dis(p[f[i]],p[f[j]]);                d=mi(d3,d);            }    }    return d;}


0 0
原创粉丝点击