半平面交

具体步骤看训练指南

( 一 )求解半平面交

Uyuw's Concert

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=40010;
const double EPS=1e-10;
struct Point
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
Vector operator -(Vector A,Vector B)
{
    return Vector(A.x-B.x,A.y-B.y);
}
Vector operator+(Vector A,Vector B)
{
    return Vector(A.x+B.x,A.y+B.y);
}
double cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}
Vector operator*(Vector A,double val)
{
    return Vector(A.x*val,A.y*val);
}
struct Line
{
    Point p;//直线上的任意一点
    Vector v;//方向向量,它的左边是对应的半平面
    double ang;//极角
    Line(){}
    Line(Point p,Vector v):p(p),v(v){  ang=atan2(v.y,v.x); }
    bool operator<(const Line &l) const
    {
        return ang<l.ang;
    }
};
bool onLeft(Line L,Point p)//点p在有向直线的左边(在线上不算)
{
    return cross(L.v,p-L.p)>0;
}
Point getIntersection(Line a,Line b)//两直线交点,假定交点唯一存在
{
    Vector u=a.p-b.p;
    double t=cross(b.v,u)/cross(a.v,b.v);
    return a.p+a.v*t;
}
Point p[MAXN];
Line q[MAXN];//双端队列
Line line[MAXN];
Point out[MAXN];
//半平面交的过程
int halfInter(Line *L,int n,Point *poly)
{
    sort(L,L+n);
    int first,last;
    q[first=last=0]=L[0];
    for(int i=1;i<n;i++)
    {
        while(first<last&&!onLeft(L[i],p[last-1])) last--;
        while(first<last&&!onLeft(L[i],p[first])) first++;
        q[++last]=L[i];
        if(fabs(cross(q[last].v,q[last-1].v))<EPS)
        {//两直线平行,取内侧的一个
            last--;
            if(onLeft(q[last],L[i].p))
            {
                q[last]=L[i];
            }
        }
        if(first<last) p[last-1]=getIntersection(q[last],q[last-1]);
    }
    while(first<last&&!onLeft(q[first],p[last-1])) last--;//删除无用平面
    if(last-first<=1) return 0;//空集
    p[last]=getIntersection(q[last],q[first]);//计算首尾两个半平面的交点
    int m=0;
    for(int i=first;i<=last;i++)
    {
        poly[m++]=p[i];
    }
    return m;
}
double polyArea(Point *p,int n)
{
    double area=0.0;
    for(int i=1;i<n-1;i++)
    {
        area+=cross(p[i]-p[0],p[i+1]-p[0]);
    }
    return area/2;
}
int main()
{
    int n,m=0;
    scanf("%d",&n);
    double x1,y1,x2,y2;
    for(int i=0;i<n;i++)
    {
        scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
        line[m++]=Line(Point(x1,y1),Vector(x2-x1,y2-y1));
    }
    for(int i=0;i<4;i++)
    {
        line[m++]=Line(Point(0,0),Vector(1,0));
        line[m++]=Line(Point(10000,0),Vector(0,1));
        line[m++]=Line(Point(10000,10000),Vector(-1,0));
        line[m++]=Line(Point(0,10000),Vector(0,-1));
    }
    int res=halfInter(line,m,out);
    double area=polyArea(out,res);
    printf("%.1f\n",area);
    return 0;
}

类似的题目:
hdu 1632 Polygons

( 二 )求解多边形的核

什么是多边形的内核?
它是平面简单多边形的核是该多边形内部的一个点集,该点集中任意一点与多边形边界上一点的连线都处于这个多边形
内部。就是一个在一个房子里面放一个摄像 头,能将所有的地方监视到的放摄像头的地点的集合即为多边形的核。

如上图,第一个图是有内核的,比如那个黑点,而第二个图就不存在内核了,无论点在哪里,总有地区是看不到的。

求解步骤:
只需要把多边形的边当做半平面来求即可
但是,判断点是否在直线的左边的时候,点在直线上的情况也是可以的
同时,在判断点在直线的位置时,还用注意下精度
Rotating Scoreboard

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1010;
const double EPS=1e-10;
struct Point
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
Vector operator-(Vector A,Vector B)
{
    return Vector(A.x-B.x,A.y-B.y);
}
Vector operator+(Vector A,Vector B)
{
    return Vector(A.x+B.x,A.y+B.y);
}
Vector operator *(Vector A,double val)
{
    return Vector(A.x*val,A.y*val);
}
double cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}
struct Line
{
    Point p;
    Vector v;
    double ang;
    Line(){}
    Line(Point p,Vector v):p(p),v(v){ ang=atan2(v.y,v.x); }
    bool operator<(const Line &l) const
    {
        return ang<l.ang;
    }
};
int dcmp(double val)
{
    if(abs(val)<EPS) return 0;
    return val<0?-1:1;
}
bool onLeft(Line line,Point p)//点在直线上也可以,同时考虑下误差
{
    return dcmp(cross(line.v,p-line.p))>=0;
}
Point getIntersection(Line a,Line b)
{
    Vector u=a.p-b.p;
    double t=cross(b.v,u)/cross(a.v,b.v);
    return a.p+a.v*t;
}
Line line[MAXN];
Line que[MAXN];
Point p[MAXN];
Point ans[MAXN];
Point point[MAXN];
int halfIntersection(Line *L,int n,Point *poly)
{
    sort(L,L+n);
    int first,last;
    que[first=last=0]=L[0];
    for(int i=1;i<n;i++)
    {
        while(first<last&&!onLeft(L[i],p[last-1])) last--;
        while(first<last&&!onLeft(L[i],p[first])) first++;
        que[++last]=L[i];
        if(dcmp(cross(que[last].v,que[last-1].v))==0)
        {
            last--;
            if(onLeft(que[last],L[i].p)) que[last]=L[i];
        }
        if(first<last) p[last-1]=getIntersection(que[last],que[last-1]);
    }
    while(first<last&&!onLeft(que[first],p[last-1])) last--;
    if(last-first<=1) return 0;
    p[last]=getIntersection(que[first],que[last]);
    int m=0;
    for(int i=first;i<=last;i++)
    {
        poly[m++]=p[i];
    }
    return m;
}
int main()
{
    int n,t,res;
    double x,y;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&point[i].x,&point[i].y);
        }
        point[n]=point[0];
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            line[cnt++]=Line(point[i-1],point[i-1]-point[i]);
        }
        res=halfIntersection(line,cnt,ans);
        if(res) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

( 三 )求解凸多边形的最大内切圆

凸多边形的最大内切圆半径?
等价于从凸多边形内部中找一个点,使得这个点到凸多边形边界的距离的最小值最大。
如何求?
二分半径加半平面交,将凸多边形的每条边向内部(垂直方向)收缩半径r,看每条边的半平面是否还会交出凸多边形。如果推进后的半平面交面积小于等于0,则说明距离太大,否则太小。
Most Distant Point from the Sea

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1510;
const double EPS=1e-10;
struct Point
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
Vector operator -(Vector A,Vector B)
{
    return Vector(A.x-B.x,A.y-B.y);
}
Vector operator +(Vector A,Vector B)
{
    return Vector(A.x+B.x,A.y+B.y);
}
Vector operator*(Vector A,double val)
{
    return Vector(A.x*val,A.y*val);
}
double cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}
struct Line
{
    Point p;
    Vector v;
    double ang;
    Line(){}
    Line(Point p,Vector v):p(p),v(v){ ang=atan2(v.y,v.x); }
    bool operator <(const Line &l)const
    {
        return ang<l.ang;
    }
};
int dcmp(double val)
{
    if(abs(val)<EPS) return 0;
    return val>0?1:-1;
}
bool onLeft(Line line,Point p)
{
    return dcmp(cross(line.v,p-line.p))>=0;
}
Point getIntersection(Line a,Line b)
{
    Vector u=a.p-b.p;
    double t=cross(b.v,u)/cross(a.v,b.v);
    return a.p+a.v*t;
}
Point p[MAXN];
Point point[MAXN];
Point change[MAXN];
Line que[MAXN];
Line line[MAXN];
Vector normal[MAXN];
bool halfIntersection(Line *L,int n)
{
    sort(L,L+n);
    int first,last;
    que[first=last=0]=L[0];
    for(int i=1;i<n;i++)
    {
        while(first<last&&!onLeft(L[i],p[last-1])) last--;
        while(first<last&&!onLeft(L[i],p[first])) first++;
        que[++last]=L[i];
        if(dcmp(cross(que[last].v,que[last-1].v))==0)
        {
            last--;
            if(onLeft(que[last],L[i].p)) que[last]=L[i];
        }
        if(first<last) p[last-1]=getIntersection(que[last],que[last-1]);
    }
    while(first<last&&!onLeft(que[first],p[last-1])) last--;
    if(last-first<=1) return false;
    else return true;
}
double polyArea(Point *p,int n)
{
    double area=0;
    for(int i=1;i<n-1;i++)
    {
        area+=cross(p[i]-p[0],p[i+1]-p[0]);
    }
    return area/2;
}
double length(Vector A)
{
    return sqrt(A.x*A.x+A.y*A.y);
}
Vector Normal(Vector A)//单位法向量
{
    double len=length(A);
    return Vector(-A.y/len,A.x/len);
}
int main()
{
    int n;
    double x,y,lef,rig,mid;
    while(scanf("%d",&n)!=EOF,n)
    {
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&point[i].x,&point[i].y);
        }
        point[n]=point[0];
        for(int i=1;i<=n;i++)
        {
            normal[i-1]=Normal(point[i]-point[i-1]);
        }
        lef=0.0;rig=10000.0;
        while(lef+EPS<rig)
        {
            mid=(lef+rig)/2.0;
            for(int i=1;i<=n;i++)
            {
                line[i-1]=Line(point[i-1]+normal[i-1]*mid,point[i]-point[i-1]);//直线向垂直方向平移
            }
            if(halfIntersection(line,n)) lef=mid;
            else rig=mid;
        }
        printf("%.6f\n",mid);
    }
    return 0;
}

Feng Shui
题意:
给定多边形和圆的半径r,在多边形内安排两个圆,使得两个圆覆盖的区域尽可能大(重合的部分只算一次),求两个圆的圆心坐标。
题解:
首先,我们应该先确定两个圆心的活动范围,怎么确定呢?
很简单,要使得圆在多边形内部,我们可以把多边形沿着边的垂直方向往内缩r个长度,然后形成的区域就是圆心的活动范围。这个可以用半平面交来求。
怎么才能使得圆覆盖的区域尽可能大?
根据常识,两个圆越远他们的重合区域越少,即覆盖面积越大,也就是求区域最远的两点,所以就变成了凸包的直径问题。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1510;
const double EPS=1e-10;
int dcmp(double val)
{
    if(abs(val)<EPS) return 0;
    return val>0?1:-1;
}
struct Point
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
Vector operator -(Vector A,Vector B)
{
    return Vector(A.x-B.x,A.y-B.y);
}
Vector operator +(Vector A,Vector B)
{
    return Vector(A.x+B.x,A.y+B.y);
}
Vector operator*(Vector A,double val)
{
    return Vector(A.x*val,A.y*val);
}
double cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}
struct Line
{
    Point p;
    Vector v;
    double ang;
    Line(){}
    Line(Point p,Vector v):p(p),v(v){ ang=atan2(v.y,v.x); }
    bool operator <(const Line &l)const
    {
        return ang<l.ang;
    }
};
bool onLeft(Line line,Point p)
{
    return dcmp(cross(line.v,p-line.p))>=0;
}
Point getIntersection(Line a,Line b)
{
    Vector u=a.p-b.p;
    double t=cross(b.v,u)/cross(a.v,b.v);
    return a.p+a.v*t;
}
Point p1,p2;
Point p[MAXN];
Point point[MAXN];
Point ans[MAXN];
Line que[MAXN];
Line line[MAXN];
int halfIntersection(Line *L,int n,Point *poly)
{
    sort(L,L+n);
    int first,last;
    que[first=last=0]=L[0];
    for(int i=1;i<n;i++)
    {
        while(first<last&&!onLeft(L[i],p[last-1])) last--;
        while(first<last&&!onLeft(L[i],p[first])) first++;
        que[++last]=L[i];
        if(dcmp(cross(que[last].v,que[last-1].v))==0)
        {
            last--;
            if(onLeft(que[last],L[i].p)) que[last]=L[i];
        }
        if(first<last) p[last-1]=getIntersection(que[last],que[last-1]);
    }
    while(first<last&&!onLeft(que[first],p[last-1])) last--;
    if(last-first<=1) return 0;
    p[last]=getIntersection(que[first],que[last]);
    int m=0;
    for(int i=first;i<=last;i++)
    {
        poly[m++]=p[i];
    }
    return m;
}
double length(Vector A)
{
    return sqrt(A.x*A.x+A.y*A.y);
}
Vector normal(Vector A)
{
    double len=length(A);
    return Vector(-A.y/len,A.x/len);
}
void rotateCalipers(Point *ch,int n)
{
    double res=-1,area,len;
    int up=1;
    ch[n]=ch[0];
    for(int i=1;i<=n;i++)
    {
        while(cross(ch[i]-ch[i-1],ch[up+1]-ch[i-1])>cross(ch[i]-ch[i-1],ch[up]-ch[i-1])) up=(up+1)%n;
        len=length(ch[up+1]-ch[i]);
        if(dcmp(len-res)>=0)
        {
            p1=ch[up+1];
            p2=ch[i];
            res=len;
        }
        len=length(ch[up]-ch[i-1]);
        if(dcmp(len-res)>=0)
        {
            p1=ch[i-1];
            p2=ch[up];
            res=len;
        }
    }
}
int main()
{
    int n,mid;
    double x,y,r,maxr,d;
    while(scanf("%d%lf",&n,&r)!=EOF)
    {
        maxr=0.0;
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&point[i].x,&point[i].y);
        }
        point[n]=point[0];
        for(int i=1;i<=n;i++)
        {
            line[i-1]=Line(point[i-1]+normal(point[i-1]-point[i])*r,point[i-1]-point[i]);
        }
        int res=halfIntersection(line,n,ans);
        rotateCalipers(ans,res);
        printf("%lf %lf %lf %lf\n",p1.x,p1.y,p2.x,p2.y);
    }
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容