[POJ2826]An Easy Problem?!(计算几何)

=== ===

这里放传送门

=== ===

题解

这题特判的情况比较多。。首先应该判断这两个线段是不是有且仅有一个焦点。不相交或者重合的情况都是应该出0的。然后就是分情况讨论了,可以发现如果这两个线段的斜率一个大于0一个小于0,它们一定是能接到雨水的,否则要判断从上面竖直落下来的雨水能否打到下面的线段上。一开始用的方法是从上面那条线段的一个端点引了一条竖直向下的直线看有没有交点,但是如果这两个东西的端点是恰好对齐的,实际上也是接不到雨水的但是它会在端点处有一个交点。实际上直接判横坐标大小关系就可以了。
最后就是对于能接到雨水的情况算能接到多少,就是从两条线段的上端点引平行于x轴的直线看是否与另一条相交,求出交点以后算三角形面积就可以了。
这个题精度非常恶心,有可能出现-0.00这样的东西,输出的时候要加个eps。然后就是如果G++不行的话拿C++交。。。

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const double eps=1e-8;
const double inf=1e7;
int T;
struct Vector{
    double x,y;
    Vector(double X=0,double Y=0){x=X;y=Y;}
    Vector operator + (const Vector &a){return Vector(x+a.x,y+a.y);}
    Vector operator - (const Vector &a){return Vector(x-a.x,y-a.y);}
    double operator * (const Vector &a){return x*a.y-y*a.x;}
    Vector mul(double t){return Vector(x*t,y*t);}
    void read(){scanf("%lf%lf",&x,&y);} 
};
struct Line{
    Vector P,v,p[2];
    double k;
    Line(){P=Vector();v=Vector();p[0]=p[1]=Vector();}
    void get(){
        p[0].read();p[1].read();
        if (p[1].y-p[0].y>eps) swap(p[0],p[1]);
        P=p[0];v=p[1]-p[0];
        if (fabs(v.x)<eps) k=inf;
        else k=v.y/v.x;
    }//P存储靠上的那个端点 
    void rev(){
        p[0].x=-p[0].x;p[1].x=-p[1].x;
        P=p[0];v=p[1]-p[0];
        if (fabs(v.x)<eps) k=inf;
        else k=v.y/v.x;
    }
}l1,l2;
bool Ins(Line l1,Line l2){
    Vector a1,a2,b1,b2,t1,t2;
    if (fabs(l1.v*l2.v)<eps) return false;//特判平行的情况 
    a1=l1.P;a2=a1+l1.v;b1=l2.P;b2=b1+l2.v;
    if (min(a1.x,a2.x)>max(b1.x,b2.x)||min(b1.x,b2.x)>max(a1.x,a2.x)) return false;
    if (min(a1.y,a2.y)>max(b1.y,b2.y)||min(b1.y,b2.y)>max(a1.y,a2.y)) return false;
    t1=a1-b1;t2=a2-b1;
    if ((t1*l2.v)*(t2*l2.v)>eps) return false;
    t1=b1-a1;t2=b2-a1;
    if ((t1*l1.v)*(t2*l1.v)>eps) return false;
    return true;
}
Vector GLI(Line a,Line b){
    Vector u=a.P-b.P;
    double t=(b.v*u)/(a.v*b.v);
    return a.P+a.v.mul(t);
}
bool check(){return l2.P.x-l1.P.x>eps;}
double Calc(){ 
    Line tmp;
    Vector i1,i2;
    tmp.P=l1.P;tmp.v=Vector(inf,0);//引出平行于x轴的直线
    if (Ins(tmp,l2)){
        i1=GLI(tmp,l2);i2=GLI(l1,l2);
        return fabs((l1.P-i1)*(i2-i1)/2);
    }
    tmp.P=l2.P;tmp.v=Vector(-inf,0);
    if (Ins(tmp,l1)){
        i1=GLI(tmp,l1);i2=GLI(l1,l2);
        return fabs((l2.P-i1)*(i2-i1)/2);
    }
    return 0;
}
int main()
{
    scanf("%d",&T);
    for (int wer=1;wer<=T;wer++){
        l1.get();l2.get();
        if (!Ins(l1,l2)){printf("0.00\n");continue;}
        if (l1.k*l2.k<-eps){
            if (l1.k>l2.k) swap(l1,l2);
            printf("%.2lf\n",Calc());
            continue;
        }
        if (l1.k<-eps){l1.rev();l2.rev();}
        if (l1.k<l2.k) swap(l1,l2);
        if (!check()) printf("0.00\n");
        else printf("%.2lf\n",Calc());
    }
    return 0;
}

偏偏在最后出现的补充说明

在此郑重感谢cloverhxy和zyf2000用无数的WA挖出了这道题的所有坑点(手动滑稽
这里写图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值