题目链接: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 105
struct 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 105
struct 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就是这样的结果
还有,为什么将所有的点依次执行一遍,然后取剩下的某一边的点构成新的点集就够了呢?答案是,点是顺时针或者逆时针给出的~~~