poj2932 Coneology【扫描线+set】

本文介绍了一种使用扫描线算法解决平面内多个不相交圆的最外层圆识别问题的方法。通过维护一个数据结构来跟踪扫描过程中与扫描线相交的圆,可以有效地判断这些圆是否被其他圆包含。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

平面上N个两两没有公共点的圆,i号圆的半径为ri,圆心在(xi,yi),求所有在最外层,即不被包含在其他圆内部的圆 。

解题思路:

注意所给的圆都没有公共点。

维护一个set,里面保存着和扫描线相交的圆,再用扫描线从左往右扫。

如果扫到某圆的最左点,当它不被其他圆包含时,就把它加入到set中。

如果扫到是某圆的最右点,并且该圆在数据结构中,就把这个圆删掉。

怎么检查它有没有被其他圆包含呢?set里的圆(注意是不被其他圆覆盖的)互不相交,也就是说他们在当前的扫描线上占的区间也互不相交, 我们可以只记录圆心在扫描线的投影, 把低的投影称为前,把高的投影低称为后,那么可能覆盖当前圆的只可能是它的前一个圆或后一个圆。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<queue>
#include<vector>
#include<set>
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=40005;
const double eps=1e-8;
struct cone
{
    double r,x,y;
    inline friend bool operator < (const cone &a,const cone &b)
    {
        if(a.x==b.x&&a.y==b.y)return a.r<b.r;
        if(a.y==b.y)return a.x<b.x;
        return a.y<b.y;
    }
}a[N];
struct point
{
    double p;
    int id,f;
    inline friend bool operator < (const point &a,const point &b)
    {
        if(a.p==b.p)return a.f>b.f;
        return a.p<b.p;
    }
}b[N<<1];
int n,m,cnt;
bool exist[N];
set<cone>S;
set<cone>::iterator it;

bool in(cone u,cone v)
{
    return (u.x-v.x)*(u.x-v.x)+(u.y-v.y)*(u.y-v.y)<=v.r*v.r;
}

int main()
{
    //freopen("lx.in","r",stdin);
    //freopen("lx.out","w",stdout);
    n=getint();
    for(int i=1;i<=n;i++)
    {
        scanf("%lf%lf%lf",&a[i].r,&a[i].x,&a[i].y);
        b[++m].p=a[i].x-a[i].r,b[m].id=i,b[m].f=1;
        b[++m].p=a[i].x+a[i].r,b[m].id=i,b[m].f=0;
    }
    sort(b+1,b+m+1);
    for(int i=1;i<=m;i++)
    {
        int x=b[i].id;
        if(b[i].f==1)
        {
            S.insert(a[x]);
            it=S.find(a[x]);
            if(it!=S.begin())
            {
                cone t=*--it;++it;
                if(in(a[x],t))
                {
                    S.erase(it);
                    continue;
                }
            }
            if(++it!=S.end())
            {
                cone t=*it;--it;
                if(in(a[x],t))
                {
                    S.erase(it);
                    continue;
                }
            }
            exist[x]=true,cnt++;
        }
        else if(exist[x]) 
        {
            it=S.find(a[x]);
            S.erase(it);
        }
    }
    cout<<cnt<<'\n';
    for(int i=1;i<=n;i++)
        if(exist[i])cout<<i<<" ";
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值