圆的扫描线合集

POJ - 2932 - Coneology

题目链接http://poj.org/problem?id=2932


题意

给出n个不相交的圆,问有那些圆不被包含


题解

题目不存在圆相交,可以扫描线解。把每个圆拆成两个点,左端点和右端点,然后从左到右扫描。
扫到左端点,判断这个点代表的圆是否会被其他圆包含,如果不被包含就加入set内。判断只需在set内查找在该圆上下的两个圆进行判断即可。
扫到右端点就把set内的圆删掉。


#include<iostream>
#include<set>
#include<stdio.h>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N=4e4+7;
int n;
const db inf=1e30;
const db eps=1e-8;
int sign(db k){if(k>eps)return 1;if(k<-eps) return -1;return 0;}
int dcmp(db k1,db k2){return sign(k1-k2);}
struct Point{
    db x,y;
    Point operator - (const Point k)const{return(Point){x-k.x,y-k.y};}
    Point operator + (const Point k)const{return(Point){x+k.x,y+k.y};}
    db abs2(){return x*x+y*y;}
    db abs(){return sqrt(abs2());}
    db dis(Point k){return ((*this)-k).abs();}
    void input(){scanf("%lf%lf",&x,&y);}
};
struct Circle{
    Point o;db r;
    bool inCircle(Circle k){
        return dcmp(o.dis(k.o)+k.r,r)<=0;
    }
    bool operator <(const Circle k)const{return o.y<k.o.y;}
    void input(){scanf("%lf",&r);o.input();}
}c[N];
struct Node{
    db pos;
    int id,val;
    bool operator<(const Node k)const{return pos<k.pos;}
}a[N*2];
int tot;
set<Circle>st;
set<Circle>::iterator it;
bool in[N];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        c[i].input();
        a[++tot]=(Node){c[i].o.x-c[i].r,i,1};
        a[++tot]=(Node){c[i].o.x+c[i].r,i,-1};
    }
    sort(a+1,a+1+tot);
    int ans=0;
    for(int i=1;i<=tot;i++){
        int id=a[i].id;
        if(a[i].val==1){
            if(st.empty()){
                in[id]=true;
                st.insert(c[id]);
                ans++;
                continue;
            }
            it=st.lower_bound(c[id]);
            Circle tmp=*it;
            if(tmp.inCircle(c[id])) continue;
            if(it!=st.begin()){
                it--;
                tmp=*it;
                if(tmp.inCircle(c[id])) continue;
            }
            in[id]=true;
            st.insert(c[id]);
            ans++;
        }
        else{
            if(in[id]) st.erase(st.find(c[id]));
        }
    }
    printf("%d\n",ans);
    for(int i=1;i<=n;i++) if(in[i]) printf("%d ",i);
}


HDU - 3511 - Prison Break

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=3511


题意

给出n个不相交的圆,求出被包含的最大层数。


题解

很秀的写法。
一样的扫描线,遇到左端点,把圆拆成上下两个半圆插入set内,set内按每个半圆与扫描线相交的点排序,这样就要设一个全局变量GX表示扫描线的x坐标,然后set内的比较函数是以GX位标准的。而且set内的大小关系随着GX右移是不变的。

然后分类讨论一下插入的圆,扫描线相交的上下两点的关系即可算出答案。


#include<iostream>
#include<set>
#include<stdio.h>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N=2e5+7;
int n,tot;
int ans[N];
db GX;
const db inf=1e30;
const db eps=1e-8;
int sign(db k){if(k>eps)return 1;if(k<-eps) return -1;return 0;}
int dcmp(db k1,db k2){return sign(k1-k2);}
struct Point{
    db x,y;
    Point operator - (const Point k)const{return(Point){x-k.x,y-k.y};}
    Point operator + (const Point k)const{return(Point){x+k.x,y+k.y};}
    db abs2(){return x*x+y*y;}
    db abs(){return sqrt(abs2());}
    db dis(Point k){return ((*this)-k).abs();}
    void input(){scanf("%lf%lf",&x,&y);}
};
struct Circle{
    Point o;db r;
    db gety(const int flag)const{return o.y+flag*sqrt(r*r-(GX-o.x)*(GX-o.x));}
    void input(){o.input();scanf("%lf",&r);}
}c[N];
struct Node{
    db pos;
    int id,flag;
    bool operator<(const Node k)const{return pos<k.pos;}
}a[N*2];
struct Nd{
    int id,flag;
    bool operator<(const Nd k)const{
        if(id==k.id) return flag<k.flag;
        return c[id].gety(flag)<c[k.id].gety(k.flag);
    }
};
set<Nd>st;
set<Nd>::iterator it,jt;

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF){
        tot=0;st.clear();
        for(int i=1;i<=n;i++){
            c[i].input();
            a[++tot]=(Node){c[i].o.x-c[i].r,i,1};
            a[++tot]=(Node){c[i].o.x+c[i].r,i,-1};
        }
        sort(a+1,a+1+tot);
        int res=1;
        for(int i=1;i<=tot;i++){
            int id=a[i].id;
            GX=a[i].pos;
            if(a[i].flag==1){
                it=st.insert((Nd){id,1}).first;
                jt=st.insert((Nd){id,-1}).first;
                it++;
                if(it==st.end()||jt==st.begin()){
                    ans[id]=1;
                }
                else{
                    jt--;
                    if(it->id==jt->id) ans[id]=ans[it->id]+1;
                    else ans[id]=max(ans[it->id],ans[jt->id]);
                }
                res=max(res,ans[id]);
            }
            else{
                st.erase((Nd){id,1});
                st.erase((Nd){id,-1});
            }
        }
        printf("%d\n",res);
    }

}

【最近圆对 / 判断圆是否相交】HDU - 3124 - Moonmist

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=3124


题意

给出若干个圆,问任意两圆之间的距离最小是多少。


题解

二分答案,然后就是判断圆是否产生交。比较奇怪的做法,不敢保证正确性。
两根扫描线,一根从左到右扫左端点,一根从左到右扫右端点,左端点在set里加入圆,右端点在set里删除圆。set按照圆心的y轴排序。
每次插入判断插入的圆和两边是否产生交集;每次删除判断删除前是否会与两边的圆产生交集,删除后两边的圆是否会互相产生交集。反正多判断几次,好像一些乱七八糟的样例就能过去了…

希望有大佬给出正确解法


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N=5e4+7;
const db eps=1e-8;
const db inf=2e5;
int sign(db k){if(k>eps)return 1;if(k<-eps) return -1;return 0;}
int dcmp(db k1,db k2){return sign(k1-k2);}
int t,n;
struct Point{
    db x,y;
    Point operator -(const Point k)const{return (Point){x-k.x,y-k.y};}
    db abs2()const{return x*x+y*y;}
    db abs()const{return sqrt(abs2());}
    db dis(const Point k)const{return ((*this)-k).abs();}
    void input(){scanf("%lf%lf",&x,&y);}
    void output(){printf("(%f,%f)\n",x,y);}
};
struct Circle{
    Point o;db r;
    bool operator<(const Circle k)const{return o.y<k.o.y;}
    void input(){o.input();scanf("%lf",&r);}
}c[N];
struct Node{
    db pos;
    int id;
    bool operator <(const Node k)const{return pos<k.pos;}
}a[N],b[N];
set<int>st;
set<int>::iterator it,jt;
bool jg(int id,db mid,set<int>::iterator it){
    it++;
    Circle tmp;
    if(it!=st.end()){
        tmp=c[*it];
        if(dcmp(tmp.o.dis(c[id].o),mid*2+c[id].r+tmp.r)<=0) return true;
    }
    it--;
    if(it!=st.begin()){
        it--;
        tmp=c[*it];
        if(dcmp(tmp.o.dis(c[id].o),mid*2+c[id].r+tmp.r)<=0) return true;
    }
    return false;
}
bool ins(int id,db mid){
    it=st.insert(id).first;
    return jg(id,mid,it);
}
bool ers(int id,db mid){
    jt=it=st.find(id);
    if(jg(id,mid,it)) return true;
    it++;
    if(it!=st.end()&&jt!=st.begin()){
        jt--;
        Circle c1=c[*it],c2=c[*jt];
        if(dcmp(c1.o.dis(c2.o),mid*2+c1.r+c2.r)<=0) return true;
    }
    st.erase(id);
    return false;
}
bool ck(db mid){
    st.clear();
    int i=1,j=1;
    while(i<=n&&j<=n){
        if(a[i].pos-mid<=b[j].pos+mid){
            if(ins(a[i++].id,mid)) return true;
        }
        else{
            if(ers(b[j++].id,mid)) return true;
        }
    }
    while(i<=n){
        if(ins(a[i++].id,mid)) return true;
    }
    while(j<=n){
        if(ers(b[j++].id,mid)) return true;
    }
    return false;
}
int main()
{
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            c[i].input();
        }
        sort(c+1,c+1+n);
        for(int i=1;i<=n;i++){
            a[i]=(Node){c[i].o.x-c[i].r,i};
            b[i]=(Node){c[i].o.x+c[i].r,i};
        }
        sort(a+1,a+1+n);
        sort(b+1,b+1+n);
        db lo=0,hi=c[1].o.dis(c[2].o)-c[1].r-c[2].r;
        while(hi-lo>eps){
            db mid=(lo+hi)/2;
            if(ck(mid)) hi=mid;
            else lo=mid;
        }
        printf("%.6f\n",lo*2);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值