poj 2932 計算幾何入門題 平面掃描
阿新 • • 發佈:2019-01-30
連結:http://poj.org/problem?id=2932
題意:
有n個圓,給定每個圓的圓心和半徑,求所有最外層的,即不包含在其他圓內部的圓。
(任意兩圓都沒有公共點)
思路:
由於兩兩沒有公共點,所以如果圓不在其他圓內部,那麼這個圓的圓心就不可以在任何一個圓的內部。很直觀的就
平面掃描:
將掃描線在平面上按照給定的軌跡進行移動,同時不斷根據掃描線掃過部分更新資訊,從而得到整體所要求的結果。
做法:
考慮兩個臨界的條件,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;
}