更好的閱讀體驗
顧名思義,就是平面的一半。一條直線會把平面分成兩部分,就是兩個半平面。對於半平面,我們可以用直線方程式如:\(ax + by >= c\) 表示,更常用的是用直線表示。
顧名思義,就是多個半平面求交集。其結果可能是乙個凸多邊形、無窮平面、直線、線段、點等。
如果多邊形中存在乙個區域使得在區域中可以看到多邊形中任意位置(反之亦然),則這個區域就是多邊形的核。可以用半平面交來求解。
點 \((x,y)\) 與原點的連線與 \(x\) 軸的夾角,其範圍為 [0,360].為了方便求解我們假設所有的直線的左側為我們所需要的半平面。一般來說求解半平面交有兩種方法
① 分治法 \(o(n\log_2 n)\)
② 增量法 \(o(n\log_2 n)\)
但是在這裡由於分治法常數較大,**實現較第二種複雜,所以我們著重介紹第二種方法。
① 將所有直線極角排序,角度相同的保留下需要的乙個
② 用乙個雙端佇列儲存當前半平面交,每次通過判斷隊首與隊尾第乙個交點是否滿足當前直線來更新
③ 先用隊尾判定隊首交點是否合法,再用隊首判斷隊尾交點是否合法
④ 最後求出來的半平面交是乙個凸多邊形
如下圖排序後
\((a,b),(c,d),(e,f),(g,h),(i,j)\)
所以我們可以更方便的逆時針依次構造,半平面交
由於我們規定了所有的直線的左側為我們所需要的半平面
所以極角相同的直線,我們保留最靠左的。
我們可以依照以下流程來構造乙個半平面交,並且構造完成的半平面交有多種情況
① 直線、線段、點不合法
② 凸多邊形,無窮平面(可以增加4個用於限制的半平面,使得平面變得有限)我們維護兩個雙端佇列
乙個儲存當前有用的直線(半平面),乙個儲存半平面交的點。
我們依次加入每一條直線(半平面),在加入之前先將之前儲存了的點,但不是最終半平面交中的點彈出佇列
如下圖我們首先讓 直線\(ab\) 進入佇列,再加入直線\(fg\),並且求出 \(ab\) 與 \(fg\) 的交點 \(h\),並且把它加入第二個佇列裡
那麼加入 直線\(cd\) 時我們會發現, \(h\) 這個點在半平面之外,那麼就將它彈出佇列,同時將這條邊也彈出佇列。
所以只要乙個點在加入的這條直線的右邊我們就將它彈出。
為什麼我們要使用雙端佇列?
可以明顯的發現,雙端佇列的中的點是逆時針排列的
且滿足一定的單調性(這個需要自己畫圖思考,或者配合以下思考)
當前加入一條直線,存放點的佇列中相鄰的兩個元素,所對應的直線的極角滿足 \(\angle a<=\angle b\)(\(a\) 對應點的在佇列中的位置位置小於 \(b\))但是因為構造半平面交是環形的構造,如下圖那麼也就是說,這條直線要麼使隊首的點處於半平面外,要麼使隊尾的點處於半平面外
我們順次處理 \(ij\) ,\(ab\) ,\(cd\) ,\(ef\),\(gh\) 所以當處理到 \(gh\) 的時候會發現,\(k\) 點是需要彈出的點但是,\(k\) 因為在處理 \(ab\) 時就已經加入了佇列而且處在佇列的首部,所以我們如果要彈出 \(k\) 就必須要維護乙個雙端佇列來支援我們的彈出隊首的操作。
最後隊首和隊尾都會剩下一些點,它們在半平面交之外
如下圖,由於半平面交構造時實際是乙個上凸殼和下凸殼,然後是環形的,所以說,會發現某些情況下,有些點多餘了
我們需要用隊首的直線,判斷一下多餘的隊尾隊首的點。
poj2451模板題,求半平面交面積
#include#include#include#include#include#includeusing namespace std;
const double eps=1e-6;
const int maxn=2e5+10;
const double pi=acos(-1.00);
inline int dcmp(double x)
struct vector
bool operator == (const vector &b)const
double angle() };
typedef vector point;
vector operator + (vector a,vector b)
vector operator - (vector a,vector b)
vector operator * (vector a,double b)
vector operator / (vector a,double b)
struct line
};typedef line segment;
double dot(vector a,vector b)
double cross(vector a,vector b)
bool is_parallel(line a,line b)//判斷a,b直線是否平行
point intersection(line a,line b)//求出a,b的交點
double area(point *p,int n)//求出多邊形的面積
bool operator < (const line &a,const line &b)//極角排序,如果極角相同則,選擇最靠左的直線
bool onright(line a,point b)//檢查b是否在a直線的右邊
bool si(line *l,int n,point *s,int &m)//增量法求半平面交
while(head
while(head
if(tail-head<=1)return false;//只有乙個點或零個點,沒有半平面交
que2[tail]=intersection(que[head],que[tail]);//加入最後一條邊,和第一條邊的交點
m=0;
for(int i=head;i<=tail;i++)s[++m]=que2[i];
return true;
}const double lim=10000;
int n,m;
point p[maxn];
line l[maxn];
double solve()
int main()
printf("%.1f\n",solve());
}
半平面交模板
妹的,一直沒有想清楚無解的情況到底是如何判斷的。偷來乙個模板。半平面交的結果 1.凸多邊形 後面會講解到 2.無界,因為有可能若干半平面沒有形成封閉3.直線,線段,點,空 屬於特殊情況吧 演算法 1 根據上圖可以知道,運用給出的多邊形每相鄰兩點形成一條直線來切割原有多邊形,如果多邊形上的點i在有向直...
模板 半平面交
考慮用射線 乙個點和乙個向量 表示它左側的半平面 那麼我們可以先按與x軸正半軸夾角 可用atan2 y,x 實現 排序,然後再用雙端佇列維護當前在交中的射線即可 之所以要用雙端佇列,是因為新插入乙個半平面時隊首和隊尾都有可能被彈出,而且要注意的是,要先彈隊尾再彈隊首 在最後,還要再用隊首的彈一些隊尾...
半平面交 板子
poj 2451 敲了個板子,比帶花樹稍微好一些,但是還是很麻煩.寫了點注釋 include include include define point vector const int n 2e4 10 struct vector vector double x,double y double an...