题目链接:http://poj.org/problem?id=2826
题意很简单:就是两根木块组成一个槽,问槽里能装多少雨水,注意雨水垂直落下
思路也很简单,就是分类讨论,但是感觉讨论过程还是比较复杂的,纠结了一天:
1.如果两条线段不相交或者平行,则装0;
2.有一条平行x轴,装0;
3.若上面覆盖下面的,装0;
4.其它,叉积求面积。
#include <iostream> #include <cstdio> #include <cmath> using namespace std; const double eps = 1e-8; struct Point { double x, y; }; struct Line { Point a, b; } l1, l2; double Max(double a, double b) { return a > b ? a : b; } double Min(double a, double b) { return a > b ? b : a; } int dblcmp(double d) { if (fabs(d) < eps) return 0; return d > 0 ? 1 : -1; } //求叉积 double multi(Point p0, Point p1, Point p2) { return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x); } //判断两条线段是否相交,且如何相交 int cross(Line l1, Line l2) { int d1, d2, d3, d4, d5, d6, d7, d8; d1 = dblcmp(Max(l1.a.x, l1.b.x) - Min(l2.a.x, l2.b.x)); d2 = dblcmp(Max(l2.a.x, l2.b.x) - Min(l1.a.x, l1.b.x)); d3 = dblcmp(Max(l1.a.y, l1.b.y) - Min(l2.a.y, l2.b.y)); d4 = dblcmp(Max(l2.a.y, l2.b.y) - Min(l1.a.y, l1.b.y)); d5 = dblcmp(multi(l2.a, l1.a, l2.b)); d6 = dblcmp(multi(l2.a, l1.b, l2.b)); d7 = dblcmp(multi(l1.a, l2.a, l1.b)); d8 = dblcmp(multi(l1.a, l2.b, l1.b)); if (d1 >= 0 && d2 >= 0 && d3 >= 0 && d4 >= 0) { if (!d5 && !d6) return 1; //共线相交 if (d5 * d6 > 0 || d7 * d8 > 0) return 0; //不相交,与下句代码不能交换顺序 if (!d5 || !d6 || !d7 || !d8) return 2; //交点为端点 return 3; //规范相交 } return 0; } //求斜率,若平行y轴,返回false;否则,返回true,k返回斜率值 bool getSlope(Line l, double &k) { double t = l.a.x-l.b.x; if (!dblcmp(t)) return false; k = (l.a.y - l.b.y) / t; return true; } void getIntersect(Line l1, Line l2, Point& p) { //求线段交点 double A1 = l1.b.y - l1.a.y; double B1 = l1.a.x - l1.b.x; double C1 = (l1.b.x - l1.a.x) * l1.a.y - (l1.b.y - l1.a.y) * l1.a.x; double A2 = l2.b.y - l2.a.y; double B2 = l2.a.x - l2.b.x; double C2 = (l2.b.x - l2.a.x) * l2.a.y - (l2.b.y - l2.a.y) * l2.a.x; p.x = (C2 * B1 - C1 * B2) / (A1 * B2 - A2 * B1); p.y = (C1 * A2 - C2 * A1) / (A1 * B2 - A2 * B1); } void getBiggerY(Point a, Point b, Point& p) { //求a,b两点中y坐标更大的点 if (dblcmp(a.y-b.y) > 0) { p.x = a.x; p.y = a.y; } else { p.x = b.x; p.y = b.y; } } double getArea(Point p0, Point p1, Point p2) { if (!dblcmp(p0.x-p1.x) && !dblcmp(p0.y-p1.y) || !dblcmp(p0.x-p2.x) && !dblcmp(p0.y-p2.y)) //处理情况4,情况5的面积为0的情况,即p0与p1或p2为同一点时 return 0; Point p; if (dblcmp(p1.y-p2.y) >= 0) { p.y = p2.y; p.x = p0.x + (p1.x - p0.x) * (p2.y - p0.y) / (p1.y - p0.y); //求另一点的坐标 return fabs(multi(p0, p2, p)) / 2; //叉积求面积 } else { p.y = p1.y; p.x = p0.x + (p2.x - p0.x) * (p1.y - p0.y) / (p2.y - p0.y); return fabs(multi(p0, p1, p)) / 2; } } int main() { int t; double k1, k2, ans; Point p, p1, p2; scanf ("%d", &t); while (t--) { scanf ("%lf%lf%lf%lf%lf%lf%lf%lf", &l1.a.x, &l1.a.y, &l1.b.x, &l1.b.y, &l2.a.x, &l2.a.y, &l2.b.x, &l2.b.y); int ok = cross(l1, l2); if (ok <= 1) ans = 0; //情况1:两条线段共线相交或者不相交 else { getBiggerY(l1.a, l1.b, p1); getBiggerY(l2.a, l2.b, p2); getIntersect(l1, l2, p); bool f1, f2; f1 = getSlope(l1, k1); f2 = getSlope(l2, k2); if (f1 && f2) { //如果两条线都不与y轴平行 if (!dblcmp(k1) || !dblcmp(k2)) ans = 0; //当有一条线段平行x轴时 else if (dblcmp(k1*k2) > 0) { //当两条线段的斜率符号相同时, int d1 = dblcmp(k1-k2); int d2 = dblcmp(k2); //情况2:上面的线段将下面的线段覆盖 if (d1 > 0 && d2 > 0 && dblcmp(p2.x-p1.x)*dblcmp(p2.x-p.x) <= 0 || d1 < 0 && d2 > 0 && dblcmp(p1.x-p2.x)*dblcmp(p1.x-p.x) <= 0 || d1 > 0 && d2 < 0 && dblcmp(p1.x-p2.x)*dblcmp(p1.x-p.x) <= 0 || d1 < 0 && d2 < 0 && dblcmp(p2.x-p1.x)*dblcmp(p2.x-p.x) <= 0) ans = 0; else ans = getArea(p, p1, p2); //情况3:若未覆盖,求面积 } else ans = getArea(p, p1, p2); //情况4:当两条线段斜率符号不同时,直接求面积,此时面积也可能是0,getArea()函数中有特殊处理 } else ans = getArea(p, p1, p2); //情况5:当有一条线段平行于y轴时,直接求面积,此时面积也可能是0,getArea()函数中有特殊处理 } printf ("%.2lf\n", ans); } return 0; }