1. 程式人生 > >poj 2826 An Easy Problem?! (變態卡資料題)

poj 2826 An Easy Problem?! (變態卡資料題)

題目連結:poj 2826

題意:給出兩條直線,水從天上落下,問:這兩條直線組成的平面容器能裝多少水?

題解:很gou的題,超級變態卡資料,艹,足足搞了我幾天,總算搞出來了。

參考連結:https://www.cnblogs.com/kuangbin/p/3192511.html kuangbin神犇的,不過它程式碼少了一句話,已經加上去了

 

這種情況是覆蓋的,

 

注意答案要加eps,這是一個好習慣,浮點數運算可能得到-0.00,而測評機不接受負數,要加eps變為0.00。

習慣參考於:https://blog.csdn.net/wl16wzl/article/details/82107592

至於為什麼會這樣,目前還不是太清楚,以後再補充,反正我程式碼答案要不加eps的話,不能AC,kuangbin大佬的不加能AC,可以評測機也勢力......

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;

typedef long long LL;

struct point{
    double x,y;
    point(){}
    point(double _x,double _y)
    {
        x=_x;y=_y;
    }
};

struct LINE{
    point a,b;
    LINE(){}
    LINE(point _a,point _b){
        a=_a;b=_b;
    }
};

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 p) { return point(A.x * p, A.y * p); }
point operator / (point A, double p) { return point(A.x/p, A.y/p); }

bool operator < (const point& a, const point& b) {
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}

const double eps = 1e-8;
int dcmp(double x) {
    if(fabs(x) < eps) return 0;
    else return x < 0 ? -1 : 1;
}

bool operator == (const point& a, const point& b) {
    return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0;
}

double Dot(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; } ///叉積

point Getlinenode(point P, point v, point Q, point w) { ///兩直線交點(點,向量,點,向量)
//    printf("w.x=%f,w.y=%f\n",w.x,w.y);
    point u = P - Q;
    double t = Cross(w, u) / Cross(v, w);
    return P + v * t;
}

bool seg_seg_Cross(point s1,point e1,point s2,point e2)///判斷兩線段是否相交(不包括端點)
{
    ///第一步,快速排斥實驗
    if(!(min(s1.x,e1.x)<=max(s2.x,e2.x)&&min(s2.x,e2.x)<=max(s1.x,e1.x)&&
       min(s1.y,e1.y)<=max(s2.y,e2.y)&&min(s2.y,e2.y)<=max(s1.y,e1.y))) return false;

    double c1=Cross(s2-s1,e1-s1),c2=Cross(e1-s1,e2-s1);
    double c3=Cross(s1-s2,e2-s2),c4=Cross(e2-s2,e1-s2);

    if(dcmp(c1*c2)>=0&&dcmp(c3*c4)>=0) return 1;
    return 0;
}

bool line_seg_Cross(point s1,point e1,point s2,point e2) ///判斷直線與線段相交,s1e1表示直線,s2e2表示線段
{
    double c1=Cross(s2-s1,e1-s1),c2=Cross(e1-s1,e2-s1);

     ///==0表示,相交於端點也認定為相交
    if(dcmp(c1*c2)>=0) return true;

    return false;
}






int main()
{
        int ncase;
        double x1,y1,x2,y2,x3,y3,x4,y4;
        scanf("%d",&ncase);

        LINE l1,l2;
        while(ncase--)
        {
            scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4);

            l1=LINE(point(x1,y1),point(x2,y2));
            l2=LINE(point(x3,y3),point(x4,y4));

            if(dcmp(y1-y2)==0||dcmp(y3-y4)==0){ ///某一個木板與地面平行
                printf("0.00\n");continue;
            }

            if(dcmp(y1-y2)<0) swap(l1.a,l1.b);
            if(dcmp(y3-y4)<0) swap(l2.a,l2.b);

            if(seg_seg_Cross(l1.a,l1.b,l2.a,l2.b)==0){ ///兩木板沒有交點
                printf("0.00\n");continue;
            }
             if(dcmp(Cross(l1.a-l1.b,l2.a-l2.b))==0){ ///兩木板平行或重合
                printf("0.00\n");continue;
             }

            ///口被l2封掉
            if(seg_seg_Cross(l1.a,point(l1.a.x,100000),l2.a,l2.b)){
                printf("0.00\n");continue;
            }
            ///口被l1封掉
            if(seg_seg_Cross(l2.a,point(l2.a.x,100000),l1.a,l1.b)){
                printf("0.00\n");continue;
            }
            
            ///求面積了
            point p=Getlinenode(l1.a,l1.a-l1.b,l2.a,l2.a-l2.b); ///求兩木板的交點
            
            ///求過l1.a點並與地面平行的直線和直線l2的交點
            point p1=Getlinenode(point(100000,l1.a.y),l1.a-point(100000,l1.a.y),l2.a,l2.b-l2.a);
        
            double ans1=fabs(Cross(p1-p,l1.a-p))/2.0;

            p1=Getlinenode(point(100000,l2.a.y),l2.a-point(100000,l2.a.y),l1.a,l1.b-l1.a);
            double ans2=fabs(Cross(p1-p,l2.a-p))/2.0;

            if(dcmp(ans1-ans2)<0) printf("%.2f\n",ans1+eps); ///這裡加個eps
            else printf("%.2f\n",ans2+eps);

        }
        return 0;
}

/*
10
0 5 3 0
1 1 3 0

3 0 6 3
3 0 5 1

3 0 6 3
3 0 7 3

0 1 1 0
1 0 2 1

0 1 2 1
1 0 1 2

0 5 3 0
2 5 3 0
*/














再貼下神犇的程式碼:

#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <string.h>
#include <math.h>
using namespace std;

const double eps = 1e-8;
int sgn(double x)
{
    if(fabs(x) < eps)return 0;
    if(x < 0)return -1;
    else return 1;
}
struct Point
{
    double x,y;
    Point(){}
    Point(double _x,double _y)
    {
        x = _x;y = _y;
    }
    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;
    Line(){}
    Line(Point _s,Point _e)
    {
        s = _s;e = _e;
    }
    //兩直線相交求交點
    //第一個值為0表示直線重合,為1表示平行,為0表示相交,為2是相交
    //只有第一個值為2時,交點才有意義
    pair<int,Point> operator &(const Line &b)const
    {
        Point res = s;
        if(sgn((s-e)^(b.s-b.e)) == 0)
        {
            if(sgn((s-b.e)^(b.s-b.e)) == 0)
                return make_pair(0,res);//重合
            else return make_pair(1,res);//平行
        }
        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 make_pair(2,res);
    }
};

//*判斷線段相交
bool inter(Line l1,Line l2)
{
    return
    max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) &&
    max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) &&
    max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) &&
    max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) &&
    sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e)) <= 0 &&
    sgn((l1.s-l2.e)^(l2.s-l2.e))*sgn((l1.e-l2.e)^(l2.s-l2.e)) <= 0;
}

int main()
{
    int x1,y1,x2,y2,x3,y3,x4,y4;
    int T;
    Line l1,l2;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d%d%d%d%d%d",&x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4);
        l1 = Line(Point(x1,y1),Point(x2,y2));
        l2 = Line(Point(x3,y3),Point(x4,y4));
        if(sgn(l1.s.y-l1.e.y)==0 || sgn(l2.s.y-l2.e.y) == 0)
        {
            printf("0.00\n");
            continue;
        }
        if(sgn(l1.s.y-l1.e.y) < 0)swap(l1.s,l1.e);
        if(sgn(l2.s.y-l2.e.y) < 0)swap(l2.s,l2.e);
        if(inter(l1,l2) == false)
        {
            printf("0.00\n");
            continue;
        }
        //口被封掉的情況
        if(inter(Line(l1.s,Point(l1.s.x,100000)),l2) )
        {
            printf("0.00\n");
            continue;
        }
        //口被封掉
        if(inter(Line(l2.s,Point(l2.s.x,100000)),l1) )
        {
            printf("0.00\n");
            continue;
        }
        pair<int,Point>pr;
        pr = l1 & l2;

        if(pr.first!=2) { ///原始碼少了這句話,判斷兩木板是否平行或重合
            printf("0.00\n");continue;
        }
        Point p = pr.second;
        double ans1;
        pr = l1 & Line(Point(100000,l2.s.y),l2.s);
        Point p1 = pr.second;
        ans1 = fabs( (l2.s-p)^(p1-p) )/2;
        double ans2;
        pr = l2 & Line(Point(100000,l1.s.y),l1.s);
        Point p2 = pr.second;
        ans2 = fabs( (l1.s-p)^(p2-p) )/2;
        printf("%.2lf\n",min(ans1,ans2));
    }
    return 0;
}