2020 Multi-University Training Contest 1---- HDU--6762、Mow(半平面交、思维)

题目链接

题面:
在这里插入图片描述

题意:
给定一个凸多边形的草坪来割草,有两种方式:

人工割草,单位代价为A,可以随意在任何位置割草
半径为 r 的圆形割草机割草,单位代价为B,要求是割草机不能超过草坪的边界
求割草的最小花费。

题目保证 r 不等于给定凸包的内切圆半径。

题解:
如果人工割草更优,直接人工,面积是多边形面积;
否则的话,很显然只有多边形夹角那里割草机割不到的地方用人工,其他地方都用割草机。

如下图所示,我们将原多边形的每一条边向内部平移 r,构成一个半平面交。
红色:半平面交,也是割草机圆心的移动范围,面积为 半平面交面积。
蓝色:每条边两端向原多边形作垂线,构成多个四边形,这里割草机显然可以覆盖到,面积为 半平面交周长 * r 。
黄色:那么现在还剩原多边形夹角位置的扇形面积,可以观察到他们恰好构成一个半径为r的圆,面积为 半径为 r 的圆的面积。
绿色:只能用人工割草。
割草机面积:蓝色+红色+黄色
人工割草面积:绿色

因为题目保证 割草机半径 r 不等于给定凸包的内切圆半径,所以如果半平面交面积为0,我们可以认为不能用割草机(不会出现刚好只有一个点可以放下割草机的情况。)

在这里插入图片描述

代码:
HDU又交不上题去了,删了好多代码才交上去的,也不知道为啥。
就是 新增了一个 直线的平移函数★。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<set>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
#define lc (cnt<<1)
#define rc (cnt<<1|1)
#define tmid ((l+r)>>1)
using namespace std;

const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=998244353;
const double eps=1e-8;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=210;
const int maxp=210;
const int maxm=100100;
const int up=100000;

//浮点型数值是否为0
int sgn(double x)
{
    if(abs(x)<eps) return 0;
    if(x<0) return -1;
    return 1;
}


//返回x的平方
double sqr(double x)
{
    return x*x;
}

//二维点
struct Point
{
    double  x,y;
    Point(){}
    Point(double xx,double yy)
    {
        x=xx,y=yy;
    }
    void input(void)
    {
        scanf("%lf%lf",&x,&y);
    }

    //重载比较运算符
    bool operator == (const Point &b) const
    {
        return sgn(x-b.x)==0&&sgn(y-b.y)==0;
    }

    bool operator < (const Point &b) const
    {
        if(sgn(x-b.x)!=0) return x<b.x;
        else return sgn(y-b.y)<0;
    }

    //重载加减乘除
    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 &k) const
    {
        return Point(x*k,y*k);
    }

    Point operator / (const double &k) const
    {
        return Point(x/k,y/k);
    }

    //叉乘,叉积
    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;
    }

    //长度
    double len(void)
    {
        return hypot(x,y);
    }

    //长度的平方
    double len2(void)
    {
        return x*x+y*y;
    }

    //两点距离
    double dis(const Point &b) const
    {
        return hypot(x-b.x,y-b.y);
    }

    //计算pa,pb的夹角,就是这个点看a,b形成的角度
    double rad(const Point &a,const Point &b)const
    {
        Point p=*this;
        return fabs(atan2(fabs((a-p)^(b-p)),(a-p)*(b-p)));
    }

    //化向量长度为r
    Point turn_to_r(double r)
    {
        double l=len();
        if(!sgn(l)) return *this;
        r/=l;
        return Point(x*r,y*r);
    }

    //逆时针转90
    Point turn_left(void)
    {
        return Point(-y,x);
    }

    //顺时针转90
    Point turn_right(void)
    {
        return Point(y,-x);
    }

    //绕p点逆时针转angle
    Point turn_p_angle(const Point &p,double angle)
    {
        Point v=(*this)-p;
        double c=cos(angle),s=sin(angle);
        return Point(p.x+v.x*c-v.y*s,p.y+v.x*s+v.y*c);
    }

};


struct Line
{
    Point s,e;
    Line(){}

    Line(const Point ss,const Point ee)
    {
        s=ss,e=ee;
    }

    double len(void)
    {
        return s.dis(e);
    }

    double angle(void)
    {
        double k=atan2(e.y-s.y,e.x-s.x);
        if(sgn(k)<0) k+=pi;
        if(sgn(k-pi)==0) k-=pi;
        return k;
    }

    /*********************新加函数***********************/
    //假设现在直线s->e
    //向s->e左侧(逆时针侧)法线方向平移len
    void translation(double len)
    {
        Point f=(e-s).turn_to_r(1.0).turn_left();
        s=s+f*len,e=e+f*len;
    }

    int relation(const Point &p)
    {
        int c=sgn((p-s)^(e-s));
        if(c<0) return 1;
        else if(c>0) return 2;
        else return 3;
    }

    bool parallel(const Line &v)
    {
        return sgn((e-s)^(v.e-v.s))==0;
    }

    int seg_cross_seg(const Line &v)
    {
        int d1=sgn((e-s)^(v.s-s));
        int d2=sgn((e-s)^(v.e-s));
        int d3=sgn((v.e-v.s)^(s-v.s));
        int d4=sgn((v.e-v.s)^(e-v.s));
        if((d1^d2)==-2&&(d3^d4)==-2) return 2;
        d1=(d1==0&&sgn((v.s-s)*(v.s-e))<=0);
        d2=(d2==0&&sgn((v.e-s)*(v.e-e))<=0);
        d3=(d3==0&&sgn((s-v.s)*(s-v.e))<=0);
        d4=(d4==0&&sgn((e-v.s)*(e-v.e))<=0);
        return d1||d2||d3||d4;
    }

    int line_cross_seg(const Line &v)
    {
        int d1=sgn((e-s)^(v.s-s));
        int d2=sgn((e-s)^(v.e-s));
        if(d1^d2==-2) return 2;
        return (d1==0||d2==0);
    }

    int line_cross_line(Line &v)
    {
        if((*this).parallel(v))
            return v.relation(s)==3;
        return 2;
    }

    Point cross_point(Line v)
    {
        double a1=(v.e-v.s)^(s-v.s);
        double a2=(v.e-v.s)^(e-v.s);
        return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
    }


};

struct polygon
{
    int n;
    Point p[maxp];

    void input(int nn)
    {
        n=nn;
        for(int i=0;i<n;i++)
            p[i].input();
    }
    struct cmp
    {
        Point p;
        cmp(const Point &p0) { p=p0;}
        bool operator()(const Point &aa,const Point &bb)
        {
            Point a=aa,b=bb;
            int d=sgn((a-p)^(b-p));
            if(d==0)
                return sgn(a.dis(p)-b.dis(p))<0;
            return d>0;
        }
    };

    void norm(void)
    {
        Point mi=p[0];
        for(int i=1;i<n;i++) mi=min(mi,p[i]);
        sort(p,p+n,cmp(mi));
    }

    double get_cir(void)
    {
        double sum=0;
        for(int i=0;i<n;i++)
            sum+=p[i].dis(p[(i+1)%n]);
        return sum;
    }

    double get_area(void)
    {
        double sum=0;
        for(int i=0;i<n;i++)
            sum+=(p[i]^p[(i+1)%n]);
        return abs(sum)/2;
    }

};


struct halfplane:public Line
{
    double angle;
    halfplane(){}
    //表示向量 s->e 逆时针 (左侧) 的半平面
    halfplane(Point ss,Point ee)
    {
        s=ss,e=ee;
    }
    halfplane(Line v)
    {
        s=v.s;
        e=v.e;
    }
    void calc_angle(void)
    {
        angle=atan2(e.y-s.y,e.x-s.x);
    }
    bool operator <(const halfplane &b) const
    {
        return angle<b.angle;
    }
};

struct halfplanes
{
    int n;
    halfplane hp[maxp];
    Point p[maxp];
    int que[maxp];
    int st,ed;

    halfplanes()
    {
        n=0;
    }

    void clear(void)
    {
        n=0;
    }
    void push(halfplane tmp)
    {
        hp[n++]=tmp;
    }

    //去重
    void _unique(void)
    {
        int m=1;
        for(int i=1;i<n;i++)
        {
            if(sgn(hp[i].angle-hp[i-1].angle)!=0)
                hp[m++]=hp[i];
            else if(sgn((hp[m-1].e-hp[m-1].s)^(hp[i].s-hp[m-1].s))>0)
                hp[m-1]=hp[i];
        }
        n=m;
    }

    bool halfplane_insert(void)
    {
        for(int i=0;i<n;i++) hp[i].calc_angle();
        sort(hp,hp+n);
        _unique();

        que[st=0]=0;
        que[ed=1]=1;
        p[1]=hp[0].cross_point(hp[1]);
        for(int i=2;i<n;i++)
        {
            while(st<ed&&sgn((hp[i].e-hp[i].s)^(p[ed]-hp[i].s))<0) ed--;
            while(st<ed&&sgn((hp[i].e-hp[i].s)^(p[st+1]-hp[i].s))<0) st++;
            que[++ed]=i;
            if(hp[i].parallel(hp[que[ed-1]])) return false;
            p[ed]=hp[i].cross_point(hp[que[ed-1]]);
        }
        while(st<ed&&sgn((hp[que[st]].e-hp[que[st]].s)^(p[ed]-hp[que[st]].s))<0) ed--;
        while(st<ed&&sgn((hp[que[ed]].e-hp[que[ed]].s)^(p[st+1]-hp[que[ed]].s))<0) st++;

        if(st+1>=ed) return false;
        return true;
    }

    //得到最后半平面交得到的凸多边形
    //需要先调用 halfplaneinsert() 且返回 true
    void get_convex(polygon &con)
    {
        p[st]=hp[que[st]].cross_point(hp[que[ed]]);
        con.n=ed-st+1;
        for(int j=st,i=0;j<=ed;i++,j++)
            con.p[i]=p[j];
    }

};




polygon p,g;
halfplanes h;


int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,r,a,b;
        scanf("%d%d%d%d",&n,&r,&a,&b);
        p.input(n);
        p.norm();
        double polygonarea=p.get_area();
        //如果人工便宜
        if(a<=b)
        {
            printf("%.12f\n",polygonarea*a);
            continue;
        }

        h.clear();
        for(int i=0;i<n;i++)
        {
            halfplane hf=halfplane(p.p[i],p.p[(i+1)%n]);
            hf.translation(r);
            h.push(hf);
        }
        //如果不构成半平面交
        if(!h.halfplane_insert())
        {
            printf("%.12f\n",polygonarea*a);
            continue;
        }

        //如果半平面交的面积为0
        h.get_convex(g);
        double halfarea=g.get_area();
        if(sgn(halfarea)==0)
        {
            printf("%.12f\n",polygonarea*a);
            continue;
        }

        double area=g.get_cir()*r+pi*r*r;
        printf("%.12f\n",(halfarea+area)*b+(polygonarea-halfarea-area)*a);
    }
    return 0;

}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值