[BZOJ 2618][CQOI 2006]凸多边形(半平面交+三角剖分求多边形面积)

142 篇文章 0 订阅
98 篇文章 0 订阅

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=2618

思路

实际上就是很多条直线求半平面交的面积,这是很显然的。
所以直接用半平面交+三角剖分模板就能水过。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <cmath>

#define MAXN 10000
#define EPS 1e-6

using namespace std;

int dcmp(double a)
{
    if(fabs(a)<EPS) return 0;
    if(a>EPS) return 1;
    return -1;
}

struct Point
{
    double x,y;
    Point(){}
    Point(double _x,double _y):x(_x),y(_y){}
}points[MAXN],polygon[MAXN];

int n=0,sizeOfPoly=0;

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 b)
{
    return Point(a.x*b,a.y*b);
}

Point operator/(Point a,double b)
{
    return Point(a.x/b,a.y/b);
}

double operator*(Point a,Point b)
{
    return a.x*b.x+a.y*b.y;
}

double cross(Point a,Point b)
{
    return a.x*b.y-a.y*b.x;
}

struct Line
{
    Point st,ed;
    double ang;
    Line(){}
    Line(Point _st,Point _ed):st(_st),ed(_ed)
    {
        ang=atan2((st-ed).y,(st-ed).x);
    }
}lines[MAXN],q[MAXN];

int top=0,h=1,t=0;

Point getLineIntersec(Line a,Line b)
{
    double x=cross(a.ed-a.st,b.ed-b.st); //!!!!!
    double y=cross(b.st-a.st,a.ed-a.st);
    return b.st+(b.ed-b.st)*y/x;
}

bool cmp(Line a,Line b) //按照极角升序排序
{
    if(!dcmp(a.ang-b.ang)) return dcmp(cross(a.ed-a.st,b.ed-a.st))>0;
    return dcmp(a.ang-b.ang)<0;
}

bool onLeft(Line a,Point b)
{
    return dcmp(cross(b-a.st,a.ed-a.st))<=0;
}

void HalfPanelIntersec() //半平面交
{
    int tot=0;
    sort(lines+1,lines+n+1,cmp);
    for(int i=1;i<=n;i++) //去掉极角相同的直线
    {
        if(dcmp(lines[i].ang-lines[i-1].ang)) tot++;
        lines[tot]=lines[i];
    }
    q[++t]=lines[1],q[++t]=lines[2];
    for(int i=1;i<=tot;i++)
    {
        while(h<t&&!onLeft(lines[i],getLineIntersec(q[t-1],q[t]))) t--;
        if(h<t&&!dcmp(fabs(cross(q[t].ed-q[t].st,q[t-1].ed-q[t-1].st))))
        {
            if(onLeft(q[t],lines[i].st)) //i与队尾直线平行且方向相同,则需要保留靠近半平面交内部的那条
                q[t]=lines[i];
        }
        else q[++t]=lines[i];
    }
    while(t-h>=2&&!onLeft(q[t],getLineIntersec(q[h],q[h+1]))) h++;
    while(t-h>=2&&!onLeft(q[h],getLineIntersec(q[t],q[t-1]))) t--;
    for(int i=h;i<t;i++)
        polygon[++sizeOfPoly]=getLineIntersec(q[i],q[i+1]);
    polygon[++sizeOfPoly]=getLineIntersec(q[t],q[h]);
}

double getSqrOfPolygon()
{
    double sqr=0;
    if(sizeOfPoly<=2) return 0;
    polygon[++sizeOfPoly]=polygon[1];
    for(int i=1;i<=sizeOfPoly;i++)
        sqr+=cross(polygon[i],polygon[i+1]);
    return fabs(sqr)/2;
}

int main()
{
    int m;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        int cnt=0;
        scanf("%d",&cnt);
        Point p1,p2,tmp;
        scanf("%lf%lf",&p1.x,&p1.y);
        tmp=p1;
        for(int j=2;j<=cnt;j++)
        {
            scanf("%lf%lf",&p2.x,&p2.y);
            lines[++n]=Line(p1,p2);
            p1=p2;
        }
        lines[++n]=Line(p1,tmp);
    }
    HalfPanelIntersec();
    printf("%.3lf\n",getSqrOfPolygon());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值