题目链接:
http://poj.org/problem?id=2932
解题思路:
题目大意:
平面上有n个两两没有公共点的圆,i号圆的圆心在(x[i],y[i]),半径为r[i]。求所有最外层的,即不包含于其他圆内部的圆。
算法思想:
因为题目内的圆不存在相交的情况, 所以直接储存每个圆的左端点和右端点的x坐标,然后从左扫到右。我们在从左向右平移与y轴
平行的直线的同时,维护与扫面线相交的最外层的圆的集合。从左到右移动中,只有扫面线移动到圆的左右两端时,圆与扫描线的
相交关系才会发生变化,因此我们先将所有这样的x坐标枚举出来并排好序。如果满足是最外面的圆,就储存在set里面。如何判断
满足,就是对每一个x,如果是左端点的x坐标就来判断其对应的圆,是否是在set中储存的圆内,如果不是 ,就存到set中。如果是
右端点的x坐标,就pop出该圆。
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
const int N = 40005;
int n;
double x[N],y[N],r[N];
bool inside(int i, int j){
double dx = x[i]-x[j];
double dy = y[i]-y[j];
return dx*dx+dy*dy <= r[j]*r[j];
}
void solve(){
vector<pair<double,int> > events;//圆的左右两端的x坐标
for(int i = 0; i < n; i++){
events.push_back(make_pair(x[i]-r[i],i));//圆的左端
events.push_back(make_pair(x[i]+r[i],i+n));//圆的右端
}
sort(events.begin(), events.end());
//平面扫描
set<pair<double, int> > outers;//与扫面线相交的最外层的圆的集合
vector<int>ans;//最外层圆的列表
for(int i = 0; i < events.size(); i++){
int id = events[i].second % n;
if(events[i].second < n){//扫描到左端
set<pair<double, int> >::iterator it = outers.lower_bound(make_pair(y[id],id));
if(it != outers.end() && inside(id, it->second)) continue;
if(it != outers.begin() && inside(id, (--it)->second)) continue;
ans.push_back(id);
outers.insert(make_pair(y[id], id));
}
else{//扫描到右端
outers.erase(make_pair(y[id], id));
}
}
sort(ans.begin(), ans.end());
int len = ans.size();
printf("%d\n", len);
for(int i = 0; i < len; i++){
printf("%d%c",ans[i]+1,i+1 == len?'\n' : ' ');
}
}
int main(){
while(~scanf("%d",&n)){
for(int i = 0; i < n; i++)
scanf("%lf%lf%lf", &r[i],&x[i],&y[i]);
solve();
}
return 0;
}