kuangbin专题二十三计算几何半平面交总结

本专题的内容主要就是学会如何用模板了。整理了一下午的半平面交模板(参考kuangbin聚聚的代码),感觉还是挺不错的。
注:该模板存点都是以逆时针存的,也就是半平面交为所有边的左边的相交的平面。
F - Feng Shui
把所有的向量往内部平移r,再枚举端点求最远距离的两个点即可。

//计算几何半平面交逆时针模板
//读点的时候要逆时针
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const double eps=1e-8;
const double PI=acos(-1.0);
const int maxn=105;
int sgn(double x)
{
    if(fabs(x)<eps)return 0;
    if(x<0)return -1;
    return 1;
}
struct Point
{
    double x,y;
    Point() {}
    Point(double sx,double sy)
    {
        x=sx;
        y=sy;
    }
    Point operator-(const Point &b)const
    {
        return Point(x-b.x,y-b.y);
    }
    double operator^(const Point &b)const//叉积
    {
        return x*b.y-y*b.x;
    }
    double operator *(const Point &b)const
    {
        return x*b.x+y*b.y;
    }
};
struct Line
{
    Point s,e;
    double k;
    Line() {}
    Line(Point _s,Point _e)
    {
        s=_s;
        e=_e;
        k=atan2(e.y-s.y,e.x-s.x);
    }
    Point operator&(const Line &b)const
    {
        Point res=s;
        double t=((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
        res.x+=(e.x-s.x)*t;
        res.y+=(e.y-s.y)*t;
        return res;
    }
};
//
bool HPIcmp(Line a,Line b)
{
    if(fabs(a.k-b.k)>eps)return a.k<b.k;
    return ((a.s-b.s)^(b.e-b.s))<0;
}
Line Q[110];
void HPI(Line line[],int n,Point res[],int &resn)
{
    int tot=n;
    sort(line,line+n,HPIcmp);
    tot=1;
    for(int i=1; i<n; i++)
        if(fabs(line[i].k-line[i-1].k)>eps)
            line[tot++]=line[i];
    int head=0,tail=1;
    Q[0]=line[0];
    Q[1]=line[1];
    resn=0;
    for(int i=2; i<tot; i++)
    {
        if(fabs((Q[tail].e-Q[tail].s)^(Q[tail-1].e-Q[tail-1].s))<eps||
                fabs((Q[head].e-Q[head].s)^(Q[head+1].e-Q[head+1].s))<eps)
            return;
        while(head<tail&&(((Q[tail]&Q[tail-1])-line[i].s)^(line[i].e-line[i].s))>eps)
            tail--;
        while(head<tail&&(((Q[head]&Q[head+1])-line[i].s)^(line[i].e-line[i].s))>eps)
            head++;
        Q[++tail]=line[i];
    }
    while(head<tail&&(((Q[tail]&Q[tail-1])-Q[head].s)^(Q[head].e-Q[head].s))>eps)
        tail--;
    while(head<tail&&(((Q[head]&Q[head+1])-Q[tail].s)^(Q[tail].e-Q[tail].s))>eps)
        head++;
    if(tail<=head+1)return;
    for(int i=head; i<tail; i++)
        res[resn++]=Q[i]&Q[i+1];
    if(head<tail-1)
        res[resn++]=Q[head]&Q[tail];
}
Line lines[maxn];
Point p[maxn];
Point res[maxn];
int resn;
//
double dist(Point a,Point b)
{
    return sqrt((a-b)*(a-b));
}
void change(Point a,Point b,Point &c,Point &d,double p)//向向量的左侧平移
{
    double len=dist(a,b);
    double dx=(a.y-b.y)*p/len;
    double dy=(b.x-a.x)*p/len;
    c.x=a.x+dx;c.y=a.y+dy;
    d.x=b.x+dx;d.y=b.y+dy;
}
int main()
{
    int n,r;
    scanf("%d %d",&n,&r);
    for(int i=n-1;i>=0;i--)
        scanf("%lf %lf",&p[i].x,&p[i].y);
    Point c,d;
    for(int i=0;i<n;i++)
    {
        change(p[i],p[(i+1)%n],c,d,r);
        lines[i]=Line(c,d);
    }
    HPI(lines,n,res,resn);
    //cout<<resn<<endl;
    double dd=-1;
    int idx1,idx2;
    for(int i=0;i<resn;i++)
        for(int j=i;j<resn;j++)
        {
            double tmp=dist(res[i],res[j]);
            if(dd<tmp)
            {
                dd=tmp;
                idx1=i;
                idx2=j;
            }
        }
    printf("%.4f %.4f %.4f %.4f\n",res[idx1].x,res[idx1].y,res[idx2].x,res[idx2].y);
    return 0;
}

G - Triathlon
一开始简单的认为随便判断一下就行。。不出意料的wa了。
正解就是半平面交,对于一个人能否得第一建不等式组,用半平面交解出解集的平面。
如何建立不等式组呢?
设一个人的三个值为V1,V2,V3,另一个人的值为U1,U2,U3,如果第一个人想得第一,那么必须满足 (1/U11/V1)x+(1/U21/V2)y+(1/U31/V3)z>0 ( 1 / U 1 − 1 / V 1 ) ∗ x + ( 1 / U 2 − 1 / V 2 ) ∗ y + ( 1 / U 3 − 1 / V 3 ) ∗ z > 0 ,由于z>0,那么我们可以除以z,将3维变成2维计算。
在建立向量的时候注意方向。方向如图所示。
这里写图片描述

//计算几何半平面交逆时针模板
//读点的时候要逆时针
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const double eps=1e-16;
const double PI=acos(-1.0);
const double inf=1e9;
const int maxn=105;
int sgn(double x)
{
    if(fabs(x)<eps)return 0;
    if(x<0)return -1;
    return 1;
}
struct Point
{
    double x,y;
    Point() {}
    Point(double sx,double sy)
    {
        x=sx;
        y=sy;
    }
    Point operator-(const Point &b)const
    {
        return Point(x-b.x,y-b.y);
    }
    double operator^(const Point &b)const//叉积
    {
        return x*b.y-y*b.x;
    }
    double operator *(const Point &b)const
    {
        return x*b.x+y*b.y;
    }
};
struct Line
{
    Point s,e;
    double k;
    Line() {}
    Line(Point _s,Point _e)
    {
        s=_s;
        e=_e;
        k=atan2(e.y-s.y,e.x-s.x);
    }
    Point operator&(const Line &b)const
    {
        Point res=s;
        double t=((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
        res.x+=(e.x-s.x)*t;
        res.y+=(e.y-s.y)*t;
        return res;
    }
};
//
bool HPIcmp(Line a,Line b)
{
    if(fabs(a.k-b.k)>eps)return a.k<b.k;
    return ((a.s-b.s)^(b.e-b.s))<0;
}
Line Q[110];
void HPI(Line line[],int n,Point res[],int &resn)
{
    int tot=n;
    sort(line,line+n,HPIcmp);
    tot=1;
    for(int i=1; i<n; i++)
        if(fabs(line[i].k-line[i-1].k)>eps)
            line[tot++]=line[i];
    int head=0,tail=1;
    Q[0]=line[0];
    Q[1]=line[1];
    resn=0;
    for(int i=2; i<tot; i++)
    {
        if(fabs((Q[tail].e-Q[tail].s)^(Q[tail-1].e-Q[tail-1].s))<eps||
                fabs((Q[head].e-Q[head].s)^(Q[head+1].e-Q[head+1].s))<eps)
            return;
        while(head<tail&&(((Q[tail]&Q[tail-1])-line[i].s)^(line[i].e-line[i].s))>eps)
            tail--;
        while(head<tail&&(((Q[head]&Q[head+1])-line[i].s)^(line[i].e-line[i].s))>eps)
            head++;
        Q[++tail]=line[i];
    }
    while(head<tail&&(((Q[tail]&Q[tail-1])-Q[head].s)^(Q[head].e-Q[head].s))>eps)
        tail--;
    while(head<tail&&(((Q[head]&Q[head+1])-Q[tail].s)^(Q[tail].e-Q[tail].s))>eps)
        head++;
    if(tail<=head+1)return;
    for(int i=head; i<tail; i++)
        res[resn++]=Q[i]&Q[i+1];
    if(head<tail-1)
        res[resn++]=Q[head]&Q[tail];

}
Line lines[maxn];
Point p[maxn];
Point res[maxn];
int resn;
//
double dist(Point a,Point b)
{
    return sqrt((a-b)*(a-b));
}
void change(Point a,Point b,Point &c,Point &d,double p)//向平面内平移
{
    double len=dist(a,b);
    double dx=(a.y-b.y)*p/len;
    double dy=(b.x-a.x)*p/len;
    c.x=a.x+dx;
    c.y=a.y+dy;
    d.x=b.x+dx;
    d.y=b.y+dy;
}
int V[105],U[105],W[105];
int n;
bool check(int idx)
{
    resn=0;
    int num=0;
    lines[num++]=Line(Point(eps,eps),Point(inf,eps));
    lines[num++]=Line(Point(inf,eps),Point(inf,inf));
    lines[num++]=Line(Point(inf,inf),Point(eps,inf));
    lines[num++]=Line(Point(eps,inf),Point(eps,eps));
    double x1,y1,x2,y2;
    for(int i=1; i<=n; i++)if(i!=idx)
        {
            if(V[i]>=V[idx]&&U[i]>=U[idx]&&W[i]>=W[idx])return 0;
            double a=-1.0/V[idx]+1.0/V[i];
            double b=-1.0/U[idx]+1.0/U[i];
            double c=-1.0/W[idx]+1.0/W[i];
            int d1=sgn(a);
            int d2=sgn(b);
            int d3=sgn(c);
            if(!d1)
            {
                if(!d2)continue;
                x1=0;
                x2=d2;
                y1=y2=-c/b;
            }
            else
            {
                if(!d2)
                {
                    x1=x2=-c/a;
                    y1=0;
                    y2=-d1;
                }
                else
                {
                    x1=0;y1=-c/b;
                    x2=d2;
                    y2=-(c+a*x2)/b;
                }
            }
            lines[num++]=Line(Point(x1,y1),Point(x2,y2));
        }
        HPI(lines,num,res,resn);
        if(resn==0)return 0;
        return 1;
}
int main()
{
    scanf("%d",&n);
    {
        for(int i=1; i<=n; i++)
        {
            scanf("%d %d %d",&V[i],&U[i],&W[i]);
        }
        for(int i=1; i<=n; i++)
            if(check(i))
                puts("Yes");
            else
                puts("No");
    }
    return 0;
}

J - Nice Milk
dfs状压枚举一下选择的边,求半平面交的平面的最小值即可。
注意小细节,k有可能大于n。

//计算几何半平面交逆时针模板
//读点的时候要逆时针
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const double eps=1e-7;
const double PI=acos(-1.0);
const double inf=1e9;
const int maxn=30;
const int maxs=1100000;
int sgn(double x)
{
    if(fabs(x)<eps)return 0;
    if(x<0)return -1;
    return 1;
}
struct Point
{
    double x,y;
    Point() {}
    Point(double sx,double sy)
    {
        x=sx;
        y=sy;
    }
    Point operator-(const Point &b)const
    {
        return Point(x-b.x,y-b.y);
    }
    Point operator+(const Point &b)const
    {
        return Point(x+b.x,y+b.y);
    }
    Point operator/(const double b)const
    {
        return Point(x/b,y/b);
    }
    double operator^(const Point &b)const//叉积
    {
        return x*b.y-y*b.x;
    }
    double operator *(const Point &b)const
    {
        return x*b.x+y*b.y;
    }
};
struct Line
{
    Point s,e;
    double k;
    Line() {}
    Line(Point _s,Point _e)
    {
        s=_s;
        e=_e;
        k=atan2(e.y-s.y,e.x-s.x);
    }
    Point operator&(const Line &b)const
    {
        Point res=s;
        double t=((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
        res.x+=(e.x-s.x)*t;
        res.y+=(e.y-s.y)*t;
        return res;
    }
    void out()
    {
        printf("%lf %lf %lf %lf\n",s.x,s.y,e.x,e.y);
    }
};
//
bool HPIcmp(Line a,Line b)
{
    if(fabs(a.k-b.k)>eps)return a.k<b.k;
    return ((a.s-b.s)^(b.e-b.s))<0;
}
Line Q[maxn];
void HPI(Line line[],int n,Point res[],int &resn)
{
    //for(int i=0;i<n;i++)line[i].out();
    int tot=n;
    sort(line,line+n,HPIcmp);
    //for(int i=0;i<n;i++)line[i].out();
    tot=1;
    for(int i=1; i<n; i++)
        if(fabs(line[i].k-line[i-1].k)>eps)
            line[tot++]=line[i];
    int head=0,tail=1;
    Q[0]=line[0];
    Q[1]=line[1];
    resn=0;
    for(int i=2; i<tot; i++)
    {
        if(fabs((Q[tail].e-Q[tail].s)^(Q[tail-1].e-Q[tail-1].s))<eps||
                fabs((Q[head].e-Q[head].s)^(Q[head+1].e-Q[head+1].s))<eps)
            return;
        while(head<tail&&(((Q[tail]&Q[tail-1])-line[i].s)^(line[i].e-line[i].s))>eps)
            tail--;
        while(head<tail&&(((Q[head]&Q[head+1])-line[i].s)^(line[i].e-line[i].s))>eps)
            head++;
        Q[++tail]=line[i];
    }
    while(head<tail&&(((Q[tail]&Q[tail-1])-Q[head].s)^(Q[head].e-Q[head].s))>eps)
        tail--;
    while(head<tail&&(((Q[head]&Q[head+1])-Q[tail].s)^(Q[tail].e-Q[tail].s))>eps)
        head++;
    if(tail<=head+1)return;
    for(int i=head; i<tail; i++)
        res[resn++]=Q[i]&Q[i+1];
    if(head<tail-1)
        res[resn++]=Q[head]&Q[tail];

}
Line lines[maxn];
Point res[maxn];
int resn;
//
double dist(Point a,Point b)
{
    return sqrt((a-b)*(a-b));
}
void change(Point a,Point b,Point &c,Point &d,double p)//向向量ab的左侧移动
{
    double len=dist(a,b);
    double dx=(a.y-b.y)*p/len;
    double dy=(b.x-a.x)*p/len;
    c.x=a.x+dx;
    c.y=a.y+dy;
    d.x=b.x+dx;
    d.y=b.y+dy;
}
double Area()
{
    double S=0;
    for(int i=1;i<resn-1;i++)
        S+=(((res[i]-res[0])^(res[i+1]-res[0]))/2.0);
    return S;
}
double dp[maxs];
Line L1[maxn],L2[maxn];
Point P[maxn];
int n,k,h;
double Cal(int sta)
{
    int num=0;
    for(int i=0;i<n;i++)
    {
        if((sta>>i)&1)
            lines[num++]=L2[i];
        else
            lines[num++]=L1[i];
    }
    HPI(lines,num,res,resn);
    return Area();
}

double dfs(int sta,int idx,int d)
{
    if(d==k)
        return Cal(sta);
    if(n-idx<k-d)return inf;
    if(dp[sta]!=inf)return dp[sta];
    double res=inf;
    for(int i=idx;i<n;i++)
        res=min(res,dfs(sta+(1<<i),i+1,d+1));
    return dp[sta]=res;
}

int main()
{
    while(~scanf("%d %d %d",&n,&k,&h)&&n)
    {
        k=min(k,n);
        for(int i=0;i<maxs;i++)dp[i]=inf;
        for(int i=0;i<n;i++)
            scanf("%lf %lf",&P[i].x,&P[i].y);
        Point c,d;
        for(int i=0;i<n;i++)
        {
            L1[i]=Line(P[i],P[(i+1)%n]);
            change(P[i],P[(i+1)%n],c,d,h);
            L2[i]=Line(c,d);
        }
        for(int i=0;i<n;i++)
            lines[i]=L1[i];
        HPI(lines,n,res,resn);
        double S=Area();
        printf("%.2f\n",S-dfs(0,0,0));
    }
}

K - Joining with Friend
这题让我想起了高中的时候。怀念高中的时光,终究回不去了。。
用横坐标表示一个人的时间,纵坐标表示另一个人的时间,用他们时间的范和一条y-x<=w,一条x-y<=w来建立半平面交,再用该面积除以总面积就是概率了。

//计算几何半平面交逆时针模板
//读点的时候要逆时针
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const double eps=1e-16;
const double PI=acos(-1.0);
const double inf=1e9;
const int maxn=20015;
int sgn(double x)
{
    if(fabs(x)<eps)return 0;
    if(x<0)return -1;
    return 1;
}
struct Point
{
    double x,y;
    Point() {}
    Point(double sx,double sy)
    {
        x=sx;
        y=sy;
    }
    Point operator-(const Point &b)const
    {
        return Point(x-b.x,y-b.y);
    }
    Point operator+(const Point &b)const
    {
        return Point(x+b.x,y+b.y);
    }
    Point operator/(const double b)const
    {
        return Point(x/b,y/b);
    }
    double operator^(const Point &b)const//叉积
    {
        return x*b.y-y*b.x;
    }
    double operator *(const Point &b)const
    {
        return x*b.x+y*b.y;
    }
};
struct Line
{
    Point s,e;
    double k;
    Line() {}
    Line(Point _s,Point _e)
    {
        s=_s;
        e=_e;
        k=atan2(e.y-s.y,e.x-s.x);
    }
    Point operator&(const Line &b)const
    {
        Point res=s;
        double t=((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
        res.x+=(e.x-s.x)*t;
        res.y+=(e.y-s.y)*t;
        return res;
    }
    void out()
    {
        printf("%lf %lf %lf %lf\n",s.x,s.y,e.x,e.y);
    }
};
//
bool HPIcmp(Line a,Line b)
{
    if(fabs(a.k-b.k)>eps)return a.k<b.k;
    return ((a.s-b.s)^(b.e-b.s))<0;
}
Line Q[maxn];
void HPI(Line line[],int n,Point res[],int &resn)
{
    //for(int i=0;i<n;i++)line[i].out();
    int tot=n;
    sort(line,line+n,HPIcmp);
    //for(int i=0;i<n;i++)line[i].out();
    tot=1;
    for(int i=1; i<n; i++)
        if(fabs(line[i].k-line[i-1].k)>eps)
            line[tot++]=line[i];
    int head=0,tail=1;
    Q[0]=line[0];
    Q[1]=line[1];
    resn=0;
    for(int i=2; i<tot; i++)
    {
        if(fabs((Q[tail].e-Q[tail].s)^(Q[tail-1].e-Q[tail-1].s))<eps||
                fabs((Q[head].e-Q[head].s)^(Q[head+1].e-Q[head+1].s))<eps)
            return;
        while(head<tail&&(((Q[tail]&Q[tail-1])-line[i].s)^(line[i].e-line[i].s))>eps)
            tail--;
        while(head<tail&&(((Q[head]&Q[head+1])-line[i].s)^(line[i].e-line[i].s))>eps)
            head++;
        Q[++tail]=line[i];
    }
    while(head<tail&&(((Q[tail]&Q[tail-1])-Q[head].s)^(Q[head].e-Q[head].s))>eps)
        tail--;
    while(head<tail&&(((Q[head]&Q[head+1])-Q[tail].s)^(Q[tail].e-Q[tail].s))>eps)
        head++;
    if(tail<=head+1)return;
    for(int i=head; i<tail; i++)
        res[resn++]=Q[i]&Q[i+1];
    if(head<tail-1)
        res[resn++]=Q[head]&Q[tail];

}
Line lines[maxn];
Point p[maxn];
Point res[maxn];
int resn;
//
double dist(Point a,Point b)
{
    return sqrt((a-b)*(a-b));
}
void change(Point a,Point b,Point &c,Point &d,double p)//向平面内平移
{
    double len=dist(a,b);
    double dx=(a.y-b.y)*p/len;
    double dy=(b.x-a.x)*p/len;
    c.x=a.x+dx;
    c.y=a.y+dy;
    d.x=b.x+dx;
    d.y=b.y+dy;
}
Line pbline(Line l)
{
    Line ret;ret.s=(l.s+l.e)/2;
    double a=l.e.x-l.s.x,b=l.e.y-l.s.y;
    double c=(l.s.y-l.e.y)*ret.s.y+(l.s.x-l.e.x)*ret.s.x;
    if(sgn(a))
    {
        ret.e.y=0;ret.e.x=-c/a;
        if(!sgn(dist(ret.s,ret.e)))
            ret.e.y=1e10;ret.e.x=-(c-b*ret.e.y)/a;
    }
    else
    {
        ret.e.x=0.0;ret.e.y=-c/b;
    }
    if(!sgn(dist(ret.s,ret.e)))
    {
        ret.e.x=1e10;ret.e.y=-(c-a*ret.e.x)/b;
    }
    return ret;
}
double Area()
{
    double S=0;
    for(int i=1;i<resn-1;i++)
        S+=(((res[i]-res[0])^(res[i+1]-res[0]))/2.0);
    return S;
}
int main()
{
    int T;
    scanf("%d",&T);
    int cas=1;
    while(T--)
    {
        int t1,t2,s1,s2,w;
        scanf("%d %d %d %d %d",&t1,&t2,&s1,&s2,&w);
        int num=0;
        Point a(t1,s1),b(t2,s1),c(t2,s2),d(t1,s2);
        lines[num++]=Line(a,b);
        lines[num++]=Line(b,c);
        lines[num++]=Line(c,d);
        lines[num++]=Line(d,a);
        Point tmp1(0,w),tmp2(-w,0);
        lines[num++]=Line(tmp1,tmp2);
        tmp1=Point(0,-w),tmp2=Point(w,0);
        lines[num++]=Line(tmp1,tmp2);
        HPI(lines,num,res,resn);
        double S=(t2-t1)*(s2-s1);
        printf("Case #%d: %.6f\n",cas++,Area()/S);
    }
}

这个专题还算比较简单吧,主要是整理出自己习惯的模板。
kuangbin专题终于完结了,但专题二十一概率dp的总结还没写,主要是概率dp可能还没有入门,等入门之后会补上来的。
完结撒花~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值