傳說中的半平面交……
看的是朱澤園 06 年的論文,他自創的排序增量法,n*logn 的複雜度,相當給力
PS:朱澤園為了他的論文,專門在POJ上加了一道題,就是 POJ2451 Uyuw's Concert ,裸模板題,看完論文可以先去試試
而對於 POJ1279/ZOJ1369 Art Gallery ,來說,其實也不複雜,只是點的順序是按順時針給出的,處理時注意下就好了
再說說我的個人體會,做這個 Art Gallery 時,天真的套了模板,一直 WA ,就開始胡思亂想,是不是點需要排序啊,是不是有可能按逆時針給出啊,然後加各種判斷啊,各種處理啊,把代碼搞到 250 多行,最後還是 WA ,萬般無奈之下,加了個初始化,就 AC 了,一測試,探索資料是很仁慈的,他遠沒有我想象的那麼不厚道,只是我自已又一次沙茶了,當然某人的“提醒”也有很大作用。教訓啊,血的教訓,一定要初始化,要不自己會瞎想,一瞎想,然後再加上誤導,就會沙茶……
POJ2451 那個就不貼代碼了,就是抄的,全都是抄的,甚至連基礎的精度判斷,我都被征服了,果斷放棄了原來的,改成人家的了,好吧,我淪陷了,在那 “傾國傾城” 的 code 面前……
下面是 Art Gallery 的代碼,雖然抄襲還是很嚴重,但是好歹我也 沙茶 的 debug 了半天的……
#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>#include<cmath>using namespace std;const int N = 1501;const double eps = 1e-8;const double maxl = 70000;const double pi = acos(-1.0);struct cpoint {//C++建構函式,預設預設值為(0,0) double x, y; cpoint(double xx = 0, double yy = 0): x(xx), y(yy) {};};int dcmp(double x) {//判斷參數的符號,負數返回-1,0返回0,正數返回1 if (x < -eps) return -1; else return x > eps;}double xmult(cpoint p0, cpoint p1, cpoint p2) { // p0p1 與 p0p2 叉積 return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);}bool EqualPoint(cpoint a, cpoint b) {//兩點相等 return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0;}struct cvector {//向量 cpoint s, e; double ang, d;};//atan (double x)弧度表示的反正切//atan2(double x,double y)弧度表示的反正切,相當於atan(x/y)void setline(double x1, double y1, double x2, double y2, cvector &v) { v.s.x = x1; v.s.y = y1; v.e.x = x2; v.e.y = y2; v.ang = atan2(y2 - y1, x2 - x1);//這裡的 d 代表向量(直線)和座標軸的截距,正數表示 原點 在該向量的左邊//(這道題要求左半平面交),負號則表示 原點 在右邊 if (dcmp(x1 - x2)) // x1 > x2v.d = (x1 * y2 - x2 * y1) / fabs(x1 - x2); else v.d = (x1 * y2 - x2 * y1) / fabs(y1 - y2);}//判向量平行bool parallel(const cvector &a, const cvector &b) { double u = (a.e.x - a.s.x) * (b.e.y - b.s.y) - (a.e.y - a.s.y) * (b.e.x - b.s.x); return dcmp(u) == 0;}//求兩向量(直線)交點 (兩向量不能平行或重合)cpoint CrossPoint(const cvector &a, const cvector &b) { cpoint res; double u = xmult(a.s, a.e, b.s), v = xmult(a.e, a.s, b.e); res.x = (b.s.x * v + b.e.x * u) / (u + v); res.y = (b.s.y * v + b.e.y * u) / (u + v); return res;}//半平面交排序函數[優先順序: 1.極角 2.前面的直線在後面的左邊]static bool VecCmp(const cvector &l, const cvector &r) { if (dcmp(l.ang - r.ang)) return l.ang < r.ang; return l.d < r.d;}cvector deq[N]; //用於計算的雙端隊列void HalfPanelCross(cvector vec[],int n,cpoint cp[],int &m){int i,tn;sort(vec,vec+n,VecCmp);for(tn=i=1;i<n;i++){if(dcmp(vec[i].ang-vec[i-1].ang) != 0)vec[tn++]=vec[i];}n=tn;int bot=0, top=1;deq[0]=vec[0], deq[1]=vec[1];for(i=2;i<tn;i++){if(parallel(deq[top],deq[top-1]) || parallel(deq[bot],deq[bot+1]))return ;while(bot<top && dcmp(xmult(vec[i].s,vec[i].e,CrossPoint(deq[top],deq[top-1])))<0)top--;while(bot<top && dcmp(xmult(vec[i].s,vec[i].e,CrossPoint(deq[bot],deq[bot+1])))<0)bot++;deq[++top]=vec[i];}while(bot<top && dcmp(xmult(deq[bot].s,deq[bot].e,CrossPoint(deq[top],deq[top-1])))<0)top--;while(bot<top && dcmp(xmult(deq[top].s,deq[top].e,CrossPoint(deq[bot],deq[bot+1])))<0)bot++;if(top <= bot+1)return ;for(m=0,i=bot;i<top;i++)cp[m++]=CrossPoint(deq[i],deq[i+1]);if(bot<top+1)cp[m++]=CrossPoint(deq[bot],deq[top]);m=unique(cp,cp+m,EqualPoint)-cp;}double PolygonArea(cpoint p[], int n) { if (n < 3) return 0; double s = p[0].y * (p[n - 1].x - p[1].x); for (int i = 1; i < n; ++i) s += p[i].y * (p[i - 1].x - p[(i + 1) % n].x); return fabs(s / 2); // 順時針方向s為負}int n,m;cvector v[N];cpoint cp[N];void solve(){int i,j;setline(-maxl, -maxl, maxl, -maxl, v[0]); setline(maxl, -maxl, maxl, maxl, v[1]); setline(maxl, maxl, -maxl, maxl, v[2]); setline(-maxl, maxl, -maxl, -maxl, v[3]);cpoint p[N];for(i=0;i<n;i++)scanf("%lf%lf",&p[i].x,&p[i].y);p[n]=p[0];for(j=4,i=n;i>0;i--)setline(p[i].x,p[i].y,p[i-1].x,p[i-1].y,v[j++]);n=j;HalfPanelCross(v,n,cp,m);//向量(直線)集合,長度;點集,長度printf("%.2lf\n",PolygonArea(cp,m));}int main(){int t;scanf("%d",&t);while(t--){scanf("%d",&n);memset(cp,0,sizeof(cp));memset(v,0,sizeof(v));solve();}return 0;}