参考链接:https://blog.csdn.net/zuzhiang/article/details/78395670
https://blog.csdn.net/qq_40861916/article/details/83541403
先给道题目:poj 3335
题意:给出顺时针的点,有一群人做到边界上,要你从中找出一个点来放记分牌,使得这个点能被所有人看到,问:能不能找到?
题解:很显然这就是让你求多边形的核,而求多边形的核用半平面交就行了。
多边形的核
平面简单多边形的核是该多边形内部的一个点集,该点集中任意一点与多边形边界上一点的连线都处于这个多边形内部。
算法简述:
多边形的核可以直接通过求多边形的边所在的直线表示的半平面的交求得。
最后一张图片参考于:https://blog.csdn.net/acm_zl/article/details/11153475
第一个图是有内核的,比如那个黑点,而第二个图就不存在内核了,无论点在哪里,总有地区是看不到的。
模板如下所示:
其中对于给出点的顺时针和逆时针顺序不同,模板需要更改 cmp函数和Onright函数中的 < 或 >。
为什么呢?给个图你瞧瞧,我就是因为这里卡了我一天啊,艹,以前理解得还是不够透彻。
看 ,同样Q点在向量P的右边,但是因为顺时针和逆时针的关系,产生的结果有着千差万里的区别。
再回顾下 P*Q>0,表示向量P在向Q的顺时针方向上;
P*Q<0,表示向量P在向量Q的逆时针方向上;
P*Q=0,表示共线。
///可以当做半平面交模板,不过有时需要在cmp函数和判断交点是否在直线右边时要改变下符号
///其它就没什么了
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=111;
struct point
{
double x,y;
point(){}
point(double _x,double _y){
x=_x;y=_y;
}
}poly[maxn];///存储半平面交后的交点
point operator + (point a,point b) { return point(a.x+b.x,a.y+b.y);}
point operator - (point a,point b) { return point(a.x-b.x,a.y-b.y);}
point operator * (point a,double p) { return point(a.x*p,a.y*p);}
point operator / (point a,double p) { return point(a.x/p,a.y/p);}
bool operator < (const point &a,const point &b){
return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
const double esp=1e-8;
int dcmp(double x){
if(fabs(x)<esp) return 0;
else return x<0?-1:1;
}
bool operator == (const point &a,const point &b){
return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}
double Cross(point P,point Q)
{
return P.x*Q.y-P.y*Q.x;
}
struct LINE
{
point s,t;
double angle;
LINE(){}
LINE(point _s,point _t){
s=_s;t=_t;
angle=atan2(t.y-s.y,t.x-s.x);
}
}L[maxn],dq[maxn];///存储向量,半平面交时用到的双端队列dq
point Getlinenode(LINE a,LINE b)
{
point P=a.s,v=a.t-a.s; ///点,向量
point Q=b.s,w=b.t-b.s;
point u=P-Q;
double t=Cross(w,u)/Cross(v,w);
return P+v*t;
}
bool cmp(LINE P,LINE Q) ///两向量方向相同,靠下的优先
{
if(fabs(P.angle-Q.angle)<esp)
return Cross(P.s-P.t,Q.s-P.t)>esp;
return P.angle<Q.angle;
}
bool Onright(LINE L,point p){ ///判断点p在直线L的右边,
return Cross(L.s-L.t,p-L.t)>esp;
}
void Half(int n,int &m)
{
sort(L,L+n,cmp);
int l=0,r=1;
m=1;
for(int i=1;i<n;++i)///筛掉同斜率,保留靠下的向量,其余不要
if(fabs(L[i].angle-L[i-1].angle)>esp)L[m++]=L[i];
n=m;
m=0;
dq[0]=L[0],dq[1]=L[1];
for(int i=2;i<n;++i)
{
if(dcmp(Cross(dq[r].s-dq[r].t,dq[r-1].s-dq[r-1].t))==0||dcmp(Cross(dq[l].s-dq[l].t,dq[l+1].s-dq[l+1].t))==0) return;
///交点在直线的右边,舍去
while(l<r&&Onright(L[i],Getlinenode(dq[r],dq[r-1]))>0)--r;
while(l<r&&Onright(L[i],Getlinenode(dq[l],dq[l+1]))>0)++l;
dq[++r]=L[i];
}
///判断下头尾向量
while(l<r&&Onright(dq[l],Getlinenode(dq[r],dq[r-1]))>0)--r;
while(l<r&&Onright(dq[r],Getlinenode(dq[l],dq[l+1]))>0)++l;
dq[++r]=dq[l];///将头向量加到尾部
for(int i=l;i<r;++i)
poly[m++]=Getlinenode(dq[i],dq[i+1]);///存储m+1个(范围在[0,m])半平面交点
}
int main()
{
int n,m,t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0;i<n;++i)
scanf("%lf%lf",&poly[i].x,&poly[i].y);
poly[n]=poly[0];
for(int i=0;i<n;++i)
{
L[i]=LINE(poly[i+1],poly[i]);
}
Half(n,m);
puts(m>2?"YES":"NO");
}
return 0;
}
再来一道:题目链接:poj 3130
题意:在多边形内找出一点,使得这点与任意顶点连接成的线段在凸边形内,问:能否找出?
题解:很显然也是让你求多边形核,直接半平面交就好了,这里和上面有区别的是,这里输入的边是按逆时针的,我们只需在上份代码改下cmp函数,Onright函数,以及存储那里改下就好了。
代码如下:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=100;
struct point{
double x,y;
point(){}
point(double _x,double _y){
x=_x;y=_y;
}
}poly[maxn];
point operator + (point a,point b) { return point(a.x+b.x,a.y+b.y);}
point operator - (point a,point b) { return point(a.x-b.x,a.y-b.y);}
point operator * (point a,double p) { return point(a.x*p,a.y*p);}
point operator / (point a,double p) { return point(a.x/p,a.y/p);}
bool operator < (const point &a,const point &b){
return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
const double esp=1e-8;
int dcmp(double x){
if(fabs(x)<esp) return 0;
else return x<0?-1:1;
}
bool operator == (const point &a,const point &b){
return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}
double Cross(point P,point Q)
{
return P.x*Q.y-P.y*Q.x;
}
struct LINE{
point s,t;
double angle;
LINE(){}
LINE(point _s,point _t){
s=_s;t=_t;
angle=atan2(t.y-s.y,t.x-s.x);
}
}L[maxn],dq[maxn];
point Getlinenode(LINE a,LINE b)
{
point P=a.s,v=a.t-a.s;
point Q=b.s,w=b.t-b.s;
point u=P-Q;
double t=Cross(w,u)/Cross(v,w);
return P+v*t;
}
bool cmp(LINE P,LINE Q){
if(fabs(P.angle-Q.angle)<esp)
return Cross(P.t-P.s,Q.s-P.s)<0;
return P.angle<Q.angle;
}
bool Onright(LINE a,point p){
return Cross(a.t-a.s,p-a.s)<0;
}
void Half(int n,int &m)
{
sort(L,L+n,cmp);
int l=0,r=1;
m=1;
for(int i=1;i<n;i++)
if(fabs(L[i].angle-L[i-1].angle)>esp) L[m++]=L[i];
n=m;
m=0;
dq[0]=L[0],dq[1]=L[1];
for(int i=2;i<n;i++)
{
if(dcmp(Cross(dq[r].s-dq[r].t,dq[r-1].s-dq[r-1].t))==0||dcmp(Cross(dq[l].s-dq[l].t,dq[l+1].s-dq[l+1].t))==0) return;
while(l<r&&Onright(L[i],Getlinenode(dq[r],dq[r-1]))>0) r--;
while(l<r&&Onright(L[i],Getlinenode(dq[l],dq[l+1]))>0) l++;
dq[++r]=L[i];
}
while(l<r&&Onright(dq[l],Getlinenode(dq[r],dq[r-1]))>0) r--;
while(l<r&&Onright(dq[r],Getlinenode(dq[l],dq[l+1]))>0) l++;
dq[++r]=dq[l];
for(int i=l;i<r;i++)
poly[m++]=Getlinenode(dq[i],dq[i+1]);
}
int main()
{
int n,m,t;
while(scanf("%d",&n)&&n)
{
for(int i=0;i<n;i++)
scanf("%lf%lf",&poly[i].x,&poly[i].y);
poly[n]=poly[0];
for(int i=0;i<n;i++)
L[i]=LINE(poly[i],poly[i+1]);
Half(n,m);
puts(m>2?"1":"0");
}
return 0;
}
我的标签:做个有情怀的程序员。