poj 2932 计算几何入门题 平面扫描

链接:http://poj.org/problem?id=2932
题意:
有n个圆,给定每个圆的圆心和半径,求所有最外层的,即不包含在其他圆内部的圆。
(任意两圆都没有公共点)
思路:
由于两两没有公共点,所以如果圆不在其他圆内部,那么这个圆的圆心就不可以在任何一个圆的内部。很直观的就 O(n2) 的算法。这肯定是过不了的,学习一个新的方法。平面扫描法。
平面扫描:
将扫描线在平面上按照给定的轨迹进行移动,同时不断根据扫描线扫过部分更新信息,从而得到整体所要求的结果。
做法:
考虑两个临界的条件,1)扫描到某个圆的左端,2)扫描到某个圆的右端的时候
1)扫到左端时。判断当前圆是否包含在其他圆中:从当前与扫描线相交的最外层的圆中,找到上下y坐标方向距离当前圆的圆心最近的两个圆。之所以这样是对的,是因为假设不对的话,就是包含或者是相交,这都与题意违背。
2)扫到右端时。将这个圆从与扫描线相交的最外层圆的集合中删除。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
using namespace std;
#define M 40009
#define EPS 1e-9
typedef pair<double,int> P;
int n;
struct node
{
    double x,y,r;
};
node nod[M];
bool inside(int i,int j) // 判断圆i 是否在 圆j 的内部
{
    double dx = nod[i].x - nod[j].x;
    double dy = nod[i].y - nod[j].y;
    if(dx * dx + dy * dy - nod[j].r * nod[j].r <= EPS) return true;
    return false;
}
void slove()
{
    vector<P> events;
    for(int i = 0;i < n;i++)
    {
        events.push_back(P(nod[i].x - nod[i].r,i)); //记录左端
        events.push_back(P(nod[i].x + nod[i].r,i+n)); //记录右端
    }
    sort(events.begin(),events.end());
    set<P> outer; //记录与扫描线相交的最外层的圆的集合
    vector<int> ans; //确定的最外层的圆
    for(int i = 0;i < events.size();i++)
    {
        int id = events[i].second % n;
        if(events[i].second < n) //扫描到左端
        {
            set<P>::iterator it = outer.lower_bound(P(nod[id].y,id));//二分查找 y坐标大于等于当前圆心y的

            if(it != outer.end() && inside(id,it->second)) continue;  //上方的
            if(it != outer.begin() && inside(id,(--it)->second)) continue; //下方的
            ans.push_back(id); //加入最外层的圆
            outer.insert(P(nod[id].y,id));
        }
        else outer.erase(P(nod[id].y,id)); //扫描到右端
    }
    sort(ans.begin(),ans.end());
    printf("%d\n",ans.size());
    for(int i = 0;i < ans.size();i++) printf("%d%c",ans[i]+1,i + 1 == ans.size() ? '\n' : ' ');
}
int main()
{
    while(scanf("%d",&n) == 1)
    {
        for(int i = 0;i < n;i++)
        {
            scanf("%lf %lf %lf",&nod[i].r,&nod[i].x,&nod[i].y);
        }
        slove();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值