題目連結:http://poj.org/problem?id=3335
這個題目意思就是給你一個多邊形,讓你判斷是否在多邊形內部存在一個點(或者一個地區)使得只要在這個地區內部就能觀察到這個多邊形內部的所有地方
多邊形的邊是不透明的;
對於這個題目一個很好理解的演算法就是不斷切割多邊形,維護一個可能是核心地區的一個多邊形點集,最後判斷點的個數>0就滿足條件,否則就不滿足條件
如果當前點滿足在測試直線的一側,那麼把這個點加入當前點的集合,如果不在,那麼如果他的前一個或者後一個點在,那麼前後兩個直線和當前測試直線
一定有兩個交點,那麼這兩個交點一定是在當前的合格集合裡面的!注意前面的先進去!
具體解法看下面代碼
補充一下:後來我瞭解到這種方法原來叫半平面求交,囧啊,開始竟然不知道就直接用了,開始知道這個方法名字還不知道是怎麼回事,為什麼會叫這個名字
突然明白了,就是這個圖形有若干條邊,每一條邊的直線會將這個平面分成兩份,現在要你求的分出的N*2份半平面中值為正的那N個平面的公用部分,而這個
公用部分正好就是多邊形的核心,哎,開始還沒反應過來!
另有大神部落格:http://www.cnblogs.com/ka200812/archive/2012/01/20/2328316.html
#include <iostream>#include <stdio.h>#include <string.h>#include <cmath>#include <algorithm>#define maxn 105struct node{ double x; double y;}point[maxn],p[maxn],q[maxn];int n,m;double a,b,c;void getline(node x,node y) //擷取直線ax+by+c==0{ a=y.y-x.y; b=x.x-y.x; c=y.x*x.y-x.x*y.y;}node intersect(node x,node y) //擷取直線ax+by+c==0 和點x和y所連直線的交點{ double u=fabs(a*x.x+b*x.y+c); double v=fabs(a*y.x+b*y.y+c); node ans; ans.x=(x.x*v+y.x*u)/(u+v); ans.y=(x.y*v+y.y*u)/(u+v); return ans;}int cut_line(){ int i,j,k; int num=1; for(i=1;i<=m;i++) { if(a*p[i].x+b*p[i].y+c >=0) { q[num++]=p[i]; } else { if(a*p[i-1].x+b*p[i-1].y+c>0) q[num++]=intersect(p[i-1],p[i]); if(a*p[i+1].x+b*p[i+1].y+c>0) q[num++]=intersect(p[i],p[i+1]); } } for(i=1;i<num;i++) p[i]=q[i]; p[0]=p[num-1]; p[num]=p[1]; m=num-1; return 0;}int main(){ int t,i,j,k; scanf("%d",&t); while(t--) { scanf("%d",&n); for(i=1;i<=n;i++) scanf("%lf%lf",&point[i].x,&point[i].y); m=n; point[0]=point[n]; point[n+1]=point[1]; for(i=0;i<=m;i++) p[i]=point[i]; p[m+1]=p[1]; for(i=1;i<=n;i++) { getline(point[i],point[i+1]); cut_line(); } if(m==0) printf("NO\n"); else printf("YES\n"); } return 0;}
POJ 3130:http://poj.org/problem?id=3130
這個題目和上面的一樣,但是給的點是逆時針的,所以把上面的代碼的大於符號改成小於符號,或者直接轉換成順時針的就直接可以利用上面代碼了!
#include <iostream>#include <stdio.h>#include <string.h>#include <cmath>#include <algorithm>#define maxn 105struct node{ double x; double y;}point[maxn],p[maxn],q[maxn];int n,m;double a,b,c;void getline(node x,node y) //擷取直線ax+by+c==0{ a=y.y-x.y; b=x.x-y.x; c=y.x*x.y-x.x*y.y;}node intersect(node x,node y) //擷取直線ax+by+c==0 和點x和y所連直線的交點{ double u=fabs(a*x.x+b*x.y+c); double v=fabs(a*y.x+b*y.y+c); node ans; ans.x=(x.x*v+y.x*u)/(u+v); ans.y=(x.y*v+y.y*u)/(u+v); return ans;}int cut_line(){ int i,j,k; int num=1; for(i=1;i<=m;i++) { if(a*p[i].x+b*p[i].y+c <=0) { q[num++]=p[i]; } else { if(a*p[i-1].x+b*p[i-1].y+c<0) q[num++]=intersect(p[i-1],p[i]); if(a*p[i+1].x+b*p[i+1].y+c<0) q[num++]=intersect(p[i],p[i+1]); } } for(i=1;i<num;i++) p[i]=q[i]; p[0]=p[num-1]; p[num]=p[1]; m=num-1; return 0;}int main(){ int t,i,j,k=0; //scanf("%d",&t); while(scanf("%d",&n),n) { for(i=1;i<=n;i++) scanf("%lf%lf",&point[i].x,&point[i].y); m=n; point[0]=point[n]; point[n+1]=point[1]; for(i=0;i<=m;i++) p[i]=point[i]; p[m+1]=p[1]; for(i=1;i<=n;i++) { getline(point[i],point[i+1]); cut_line(); } if(m==0) printf("0\n"); else printf("1\n"); } return 0;}
下面介紹轉自:http://www.cnblogs.com/ka200812/archive/2012/01/20/2328316.html
關於求多邊形核心的演算法
什麼是多邊形的核心?
它是平面簡單多邊形的核是該多邊形內部的一個點集,該點集中任意一點與多邊形邊界上一點的連線都處於這個多邊形內部。就是一個在一個房子裡面放一個攝像 頭,能將所有的地方監視到的放網路攝影機的地點的集合即為多邊形的核。
如,第一個圖是有核心的,比如那個黑點,而第二個圖就不存在核心了,無論點在哪裡,總有地區是看不到的。
那麼,如何求得這個核心區間呢?通常的演算法是用兩點的直線去不斷切割多邊形,切割到最後剩下的,就是核心區間了。
我們都知道一條直線可以將平面切割成兩個地區,假設直線方程為
ax+by+c==0,那麼,兩個平面可分別表示成ax+by+c>=0 和 ax+by+c<0
具體如何用程式實現直線對多邊形的切割呢?
流程是這樣的:
1、 用一個順時針或者逆時針的順序,將最初的多邊形的點集儲存起來。
2、 按順序取連續的兩個點組成一條直線,用這條直線來切割原先的多邊形
我首先假設點是順時針儲存的,
此時,多邊形的點集是{1,2,3,4,5,6,7,8,9,10}
取點1,和點2組成直線ax+by+c==0,這時候,將點集中的點一次帶入方程ax+by+c,得到的值都將會是大於等於0的,說明所有的點都在該直線的同一側,繼續保持點集不變
取點2和點3組成直線,同樣,將點集中的點依次帶入方程ax+by+c中,此時,4和5兩個點的結果是小於0的,而其他的點的值依舊是大於等於0,這時候說明4和5兩個點被切割出了該多邊形,於是現在點集只剩下{1,2,3,6,7,8,9,10,X},(X是直線23和直線56的交點)
依次類推,一直執行到點10和點1,那麼核心的集合就得到了。
值得說明的是,這個例子的圖形比較特殊,全是直角,如果圖形比較隨意,那麼當某一個點被斷定在多邊形區間之外的時候,我們還應該考慮它和它相鄰的兩個點各自組成的直線和ax+by+c有沒有交點,有交點的話,更新的點集中還應該加上這些交點,比如例子中執行完點2和點3組成的直線後,點集是{1,2,3,6,7,8,X},其中3和X就是這樣的結果
還有,為什麼將所有的點依次執行一遍,然後取剩下的某一邊的點構成新的點集就夠了呢?答案是,點是順時針或者逆時針給出的~~~