1. 程式人生 > >poj 2932 計算幾何入門題 平面掃描

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; }