【POJ2826】An Easy Problem?!(线段相交+分情况讨论+精度)

题目地址:http://poj.org/problem?id=2826

题目:


给出两条线段(木板的侧切面,木板宽度默认为1),求能接住雨水的水量,保留两位小数

解题思路:


这道题有很多种情况需要考虑。

(1)首先判断两条线段是否相交且只有一个交点。

(2)若满足条件(1),取交点上面的点(这些点才有用),最好的情况是线段L1的a点在交点上方,线段L2的b点在交点上方,那么剩下的只需要求a、交点、b组成的形状最终能够接住多少水量。a、交点、b组成的形状又可细分为下面几种:

上述三类情况计算面积的方法都是相同的:设y=较低点.y   与 两条射线交点的横坐标的差值为X

s = 0.5 *(较低点.y-交点.y)* X,故可归于一大类。

注意上述两类情况有些情况是接不到水的,已在图中标明,要特判。这两类情况计算面积的方法是一样的:设y=较低点.y  与 两条射线交点分别为aa,bb:

s = 0.5 * sin(夹角) * Length(aa-c) * Length(bb-c)(计算方法不限这一种),故可归于一大类。

较低点要么是a,要么是b,判断一下即可,本题中,一些距离和差值的计算可以通过三角形形似得出公式,在判断和比较的过程中为了避免精度,要用dcmp。

wa到怀疑人生的点:最终结果+eps再输出?

ac代码:


#include <iostream>
#include <vector>
#include <math.h>
using namespace std;
const double eps = 1e-10;
const double pi = acos(-1.0);
int dcmp(double x)//精度三态函数(>0,<0,=0)
{
    if (fabs(x) < eps)return 0; //等于
    else return x < 0 ? -1 : 1;//小于,大于
}
struct Point{
    double x, y;
    Point(double x = 0, double y = 0):x(x),y(y){}
    bool operator ==(const Point& rhs) const {
        return dcmp(x-rhs.x)==0 && dcmp(y-rhs.y)==0;
    }
};
typedef Point Vector;
Vector operator - (Vector a, Vector b)//向量减法
{
    return Vector(a.x - b.x, a.y - b.y);
}
Vector operator * (Vector a, double p)//向量数乘
{
    return Vector(a.x * p, a.y * p);
}
Vector operator + (Vector a, Vector b)//向量加法
{
    return Vector(a.x + b.x, a.y + b.y);
}

double Dot(Vector a, Vector b)//点积
{
    return a.x * b.x + a.y * b.y;
}
double Length(Vector a)//模
{
    return sqrt(Dot(a, a));
}
double Cross(Vector a, Vector b)//外积
{
    return a.x * b.y - a.y * b.x;
}
double Angle(Vector a, Vector b)//夹角,弧度制
{
    return acos(Dot(a, b) / Length(a) / Length(b));
}
double Atan(Point a)//极角
{
    return atan2(a.y, a.x);
}

bool JudgeSegmentInter(Point A, Point B, Point C, Point D) //线段相交(包括端点)
{
    return
            dcmp(max(A.x, B.x) - min(C.x, D.x)) >= 0
            && dcmp(max(C.x, D.x) - min(A.x, B.x)) >= 0
            && dcmp(max(A.y, B.y) - min(C.y, D.y)) >= 0
            && dcmp(max(C.y, D.y) - min(A.y, B.y)) >= 0
            && dcmp(Cross(C - A, D - A)*Cross(C - B, D - B)) <= 0
            && dcmp(Cross(A - C, B - C)*Cross(A - D, B - D)) <= 0;
}

Point SegmentIntersection(Point a,Point b,Point c,Point d)
{
    Point s;
    double  a1=a.y-b.y,b1=b.x-a.x,c1=a.x*b.y-b.x*a.y;
    double  a2=c.y-d.y,b2=d.x-c.x,c2=c.x*d.y-d.x*c.y;
    s.x=(c1*b2-c2*b1)/(a2*b1-a1*b2);
    s.y=(a2*c1-a1*c2)/(a1*b2-a2*b1);
    return s;
}
double alow(Point a, Point b, double L2,  double y)
{
    return L2*(a.y-y)/(b.y-y);
}
double blow(Point a,Point b, double L1, double y)
{
    return  L1*(b.y-y)/(a.y-y);
}
double getL(Point a, Point b, double L1, double L2, double y)
{
    if(dcmp(a.y-b.y) <= 0)
        return alow(a, b, L2, y)*L1;
    else return blow(a, b, L1, y)*L2;
}

double solve(Point A1, Point B1, Point A2, Point B2)
{
    double ans = 0;
    vector<Point> a, b;
    if(JudgeSegmentInter(A1, B1, A2, B2) && dcmp(Cross(A1-B1, A2-B2))!=0) //保证线段有唯一交点
    {
        Point c = SegmentIntersection(A1, B1, A2, B2);
        //只保留交点上方的
        if(dcmp(A1.y-c.y) > 0) a.push_back(A1);
        if(dcmp(B1.y-c.y) > 0) a.push_back(B1);
        if(dcmp(A2.y-c.y) > 0) b.push_back(A2);
        if(dcmp(B2.y-c.y) > 0) b.push_back(B2);
        for(int i = 0; i < a.size(); i++)
        {
            for(int j = 0; j < b.size(); j++)
            {
                if(dcmp(dcmp(a[i].x-c.x) * dcmp(b[j].x-c.x)) <= 0)//两侧或中间
                {
                    if(dcmp(a[i].y - b[j].y) >= 0)//a在上
                    {
                        double x = fabs(a[i].x-c.x)*(b[j].y-c.y)/(a[i].y-c.y);
                        ans = 0.5 * (b[j].y-c.y) * (fabs(b[j].x-c.x)+x);
                    }
                    else//b在上
                    {
                        double x = fabs(b[j].x-c.x)*(a[i].y-c.y)/(b[j].y-c.y);
                        ans = 0.5 * (a[i].y-c.y) * (fabs(a[i].x-c.x)+x);
                    }
                }
                else//同侧
                {
                    double L1 = Length(a[i]-c), L2 = Length(b[j]-c);
                    double ang = Angle(a[i]-c, b[j]-c);
                    double L = getL(a[i], b[j], L1, L2, c.y);
                    double ka = Atan(a[i]-c), kb = Atan(b[j]-c);

                    if(dcmp(a[i].x-c.x) < 0 && dcmp(b[j].x-c.x) < 0)//同在左侧
                    {
                        if(dcmp(ka-kb) < 0 && dcmp(a[i].x-b[j].x) > 0)//a在上
                            ans = 0.5 * sin(ang) * L;
                        if(dcmp(kb-ka) < 0 && dcmp(b[j].x-a[i].x) > 0)//b在上
                            ans = 0.5 * sin(ang) *  L;
                    }
                    else //同在右侧
                    {
                        if(dcmp(ka-kb) > 0 && dcmp(a[i].x -b[j].x) < 0)//a在上
                            ans = 0.5 * sin(ang) * L;
                        if(dcmp(kb-ka) > 0 && dcmp(b[j].x-a[i].x) < 0)//b在上
                            ans = 0.5 * sin(ang) * L;
                    }

                }
            }
        }
        return ans;
    }
    else return 0;
}
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    int t;
    Point A1, B1, A2, B2;
    scanf("%d",  &t);
    while(t--)
    {
        scanf("%lf %lf %lf %lf %lf %lf %lf %lf", &A1.x, &A1.y, &B1.x, &B1.y, &A2.x, &A2.y, &B2.x, &B2.y);
        double ans = solve(A1, B1, A2, B2);
        printf("%.2lf\n", ans+eps);
    }
    return 0;
}
发布了299 篇原创文章 · 获赞 81 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/Cassie_zkq/article/details/100717551