BZOJ 1845 [Cqoi2005] 三角形面积并 计算几何扫描线

题意:链接

方法:扫描线

解析:

扫描线裸题。

先搞出所有边。

之后求一下所有边的交点。

按照x坐标排序。

之后每两个不同的坐标间的面积其实就是中间截出来的所有梯形以及三角形的中位线的和乘以高度。

至于中位线,再用这个与y轴平行的线截一下所有的三角形即可。

统计长度。把所有的加起来即可。


卡精!


代码:

#include <cmath>
#include <cstdio>
#include <iomanip>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1010
#define M 90010 
#define eps 1e-8
using namespace std;
int n;
struct Point
{
    long double x,y;
    Point(){}
    Point(long double a,long double b):x(a),y(b){}
    friend istream& operator >> (istream &_,Point &a)
    {cin>>a.x>>a.y;return _;}
    Point operator + (const Point &a)
    {return Point(x+a.x,y+a.y);}
    Point operator - (const Point &a)
    {return Point(x-a.x,y-a.y);}
    Point operator * (long double rate)
    {return Point(x*rate,y*rate);}
    long double operator * (const Point &a)
    {return x*a.x+y*a.y;}
    long double operator ^ (const Point &a)
    {return x*a.y-y*a.x;} 
};
struct Line
{
    Point p,v;
    Line(){}
    Line(Point a,Point b):p(a),v(b-a){}
    friend bool inside(Line &a,Point &b)
    {
        long double x1=min(a.p.x,(a.p+a.v).x);
        long double x2=max(a.p.x,(a.p+a.v).x);
        return b.x>=x1&&b.x<=x2;
    }
}l[N];
int lines;
Line MakeLine(Point &a,Point &b)
{
    l[++lines]=Line(a,b);
    return l[lines];
}
struct Triangle
{
    Point a,b,c;
    Line l1,l2,l3;
    Triangle(){}
    Triangle(Point _a,Point _b,Point _c):a(_a),b(_b),c(_c)
    {
        l1=MakeLine(_a,_b);
        l2=MakeLine(_b,_c);
        l3=MakeLine(_c,_a);
    }
}tri[N];
struct Interval
{
    long double l,r;
    Interval(){}
    Interval(long double x,long double y):l(x),r(y){}
    friend bool operator < (Interval a,Interval b)
    {
        if(a.l==b.l)return a.r<b.r;
        return a.l<b.l;
    }
}interval[N];
int intervals;
Point Get_Intersection(Line &l1,Line &l2)
{
    Point u=l1.p-l2.p;
    long double tmp=(l2.v^u)/(l1.v^l2.v);
    return l1.p+l1.v*tmp;
}
long double X[M];
int Xs;
long double Triangle_Intersect(long double x,long double height)
{
    intervals=0;
    for(int i=1;i<=n;i++)
    {
        Point p1=tri[i].a,p2=tri[i].b,p3=tri[i].c;
        if(x<min(p1.x,min(p2.x,p3.x))+eps||x+eps>max(p1.x,max(p2.x,p3.x)))continue;
        Line tmp=Line(Point(x,0),Point(x,1));
        Point pp1=Get_Intersection(tri[i].l1,tmp);
        Point pp2=Get_Intersection(tri[i].l2,tmp);
        Point pp3=Get_Intersection(tri[i].l3,tmp);
        if(!inside(tri[i].l1,pp1))
        {
            interval[++intervals].l=min(pp2.y,pp3.y);
            interval[intervals].r=max(pp2.y,pp3.y);
        }else if(!inside(tri[i].l2,pp2))
        {
            interval[++intervals].l=min(pp1.y,pp3.y);
            interval[intervals].r=max(pp1.y,pp3.y);
        }else
        {
            interval[++intervals].l=min(pp1.y,pp2.y);
            interval[intervals].r=max(pp1.y,pp2.y); 
        }
    }
    sort(interval+1,interval+intervals+1);
    int j;
    long double length=0;
    for(int i=1;i<=intervals;i=j)
    {   
        for(j=i+1;j<=intervals;j++)
        {
            if(interval[j].l<interval[i].r-eps)
                interval[i].r=max(interval[i].r,interval[j].r);
            else break;
        }
        length+=interval[i].r-interval[i].l;
    }
    return length*height;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        Point a,b,c;
        cin>>a>>b>>c;
        tri[i]=Triangle(a,b,c);
    }
    for(int i=1;i<=3*n;i++)
    {
        for(int j=i+1;j<=3*n;j++)
        {
            if(fabs(l[i].v^l[j].v)>eps)
                X[++Xs]=Get_Intersection(l[i],l[j]).x;
        }
    }
    sort(X+1,X+Xs+1);
    long double ans=0;
    for(int i=2;i<=Xs;i++)
    {
        if(fabs(X[i]-X[i-1])>eps)
        {
            long double tmpx=(X[i]+X[i-1])/2.0;
            long double height=X[i]-X[i-1]; 
            ans+=Triangle_Intersect(tmpx,height);
        }
    }
    cout<<fixed<<setprecision(2)<<ans-eps<<endl;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值