HDU 3124 Moonmist (平面最近圆对,二分+扫描线)

Moonmist

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1765    Accepted Submission(s): 585


Problem Description
An Unidentified Flying Object (Commonly abbreviated as UFO) is the popular term for any aerial phenomenon whose cause cannot be easily or immediately identified. We always believe UFO is the vehicle of aliens. But there is an interrogation about why UFO always likes a circular saucer? There must be a reason. Actually our scientists are developing a new traffic system “Moonmist”. It is distinguished from the traditional traffic. We use circular saucers in this new traffic system and the saucers moves extremely fast. When our scientists did their test, they found that traffic accident was too hard to be avoided because of the high speed of the advanced saucer. They need us to develop a system that can tell them the nearest saucer. The distance between two saucers is defined as the shortest distance between any two points in different saucers.
 

Input
The first line consists of an integer T, indicating the number of test cases.
The first line of each case consists of an integer N, indicating the number of saucers. Each saucer is represented on a single line, consisting of three integers X, Y, R, indicating the coordinate and the radius. You can assume that the distance between any two saucers will never be zero.
 

Output
For each test case, please output a floating number with six fractional numbers, indicating the shortest distance.
Constraints
0 < T <= 10
2 <= N <= 50000
0 <= X, Y, R <= 100000
 

Sample Input
1 2 0 0 1 10 10 1
 

Sample Output
12.142136
 
题意:给你N个圆的x,y,r,问这些圆最近的两个距离为多少。
思路:快市赛了,就找了些扫描线的题目练练,这题详细讲解:http://blog.sina.com.cn/s/blog_6e7b12310100qnex.html
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stdlib.h>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
using namespace std;
const int N = 100005;
const double eps = 1e-8;
const double PI = acos(-1.0);
int n;
double mid;
set<int>st;
int rk[N];
int sgn(double x)
{
    if(fabs(x) < eps)return 0;
    if(x < 0)return -1;
    else return 1;
}
struct Point
{
    int id;
    double x,y;
    double r;
    Point() {}
    Point(double _x,double _y)
    {
        x = _x;  y = _y;
    }
    Point operator -(const Point &b)const
    {
        return Point(x - b.x,y - b.y);
    }
    double operator ^(const Point &b)const//叉积
    {
        return x*b.y - y*b.x;
    }
    double operator *(const Point &b)const//点积
    {
        return x*b.x + y*b.y;
    }
} p[N];
struct line
{
    double po;
    int id;
} lef[N],rig[N];
double dist(Point a,Point b)//*两点间距离
{
    return sqrt((a-b)*(a-b));
}
bool cmp(line a,line b)
{
    return a.po<b.po;
}
bool cmpp(Point a,Point b)
{
    if(a.y==b.y) return a.x<b.x;
    return a.y<b.y;
}
int is_cross(int a,int b)
{
    double dd = dist(p[a],p[b]);
    if(sgn(dd-p[a].r-p[b].r-mid-mid)>0return 0;
    return 1;
}
int judge(int a)
{
    set<int>::iterator it=st.insert(a).first;//插入a后所在位置
    if(it!=st.begin())
    {
        if(is_cross(a,*--it))
        return 1;
        it++;
    }
    if(++it!=st.end())
    {
        if(is_cross(a,*it)) return 1;
    }
    return 0;
}
int cal()
{
    st.clear();
    int i = 1,j = 1;
    while(i<=n||j<=n)
    {
        if(j==n+1||(i!=n+1&&sgn(lef[i].po-mid-(rig[j].po+mid))<=0))//如果当且i圆的最左端小于j圆的最右端 将i插进来
        {
            if(judge(rk[lef[i].id]))
            return 1;
            i++;
        }
        else
        {
            st.erase(rk[rig[j].id]);
            j++;
        }
    }
    return 0;
}
double solve()
{
    double low = 0.0,high = dist(p[1],p[2])-p[1].r-p[2].r;
    while(low+eps<high)//二分距离
    {
        mid = (high+low)/2;
        if(cal())
        high = mid;
        else low = mid;
    }
    return high+low;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i = 1; i <=n ; i++)
        {
            scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].r);
            lef[i].po = p[i].x-p[i].r;//每个圆的最左端
            lef[i].id = i;
            rig[i].po = p[i].x+p[i].r;//每个圆的最右端
            rig[i].id = i;
            p[i].id = i;
        }
        sort(lef+1,lef+n+1,cmp);
        sort(rig+1,rig+n+1,cmp);
        sort(p+1,p+n+1,cmpp);
        for(int i = 1; i <= n; i++)
            rk[p[i].id] = i;//每个圆按y坐标排序后位于第几
        double ans = solve();
        printf("%.6f\n",ans);
    }
    return 0;
}
posted @ 2015-06-03 20:41  Doli  阅读(323)  评论(0)    收藏  举报