HDU

来源:互联网 发布:钢笔 知乎 编辑:程序博客网 时间:2024/05/20 07:16

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1154

题目大意:一条直线穿过一个多边形,求出在多边形内的线段的总长

解题思路:把直线和多边形的交点求出来,然后交点的连线如果在多边形内部,那么累加长度,与判断线段是否在多边形的算法类似,注意的是当直线与边重合时,记录边的两个端点

AC代码:

#include<cstdio>#include<cmath>#include<iostream>#include<algorithm>#include<iomanip>using namespace std;const int MAXN = 20000 + 5;const double PI = 3.1415926;const double EPS = 1e-8;double R;//反演圆半径int judgeZero(double a)//a>0为1,<0为-1,=0为0{    return (a > EPS) - (a < -EPS);}struct Point{    double _x, _y;    Point(double x = 0.0, double y = 0.0) :_x(x), _y(y) {}    Point(const Point& p) { _x = p._x, _y = p._y; }    bool operator<(const Point& p)const    {        if (judgeZero(_x - p._x) != 0) return _x < p._x;        return _y < p._y;    }    bool operator==(const Point& p)const    {        return judgeZero(_x - p._x) == 0 && judgeZero(_y - p._y) == 0;    }    void toMove(Point a, double rad, double d)    {        _x = a._x + cos(rad)*d;        _y = a._y + sin(rad)*d;    }    Point operator+(Point a) { return Point(_x + a._x, _y + a._y); }    Point operator-(Point a) { return Point(_x - a._x, _y - a._y); }    friend Point operator*(double a, Point p) { return Point(a*p._x, a*p._y); }    friend istream& operator >> (istream& in, Point& point)    {        in >> point._x >> point._y;        return in;    }    friend ostream& operator<<(ostream& out, const Point& point)    {        out << fixed << setprecision(8) << point._x << ' ' << point._y;        return out;    }}polygon[MAXN], crossp[MAXN];double getDis(Point a, Point b){    return sqrt((a._x - b._x)*(a._x - b._x) + (a._y - b._y)*(a._y - b._y));}struct Circle{    Point _o;    double _r;    Circle(double x = 0.0, double y = 0.0, double r = 0.0) :_o(x, y), _r(r) {}    Circle(const Point& o, double r) :_o(o), _r(r) {}    Circle getAnti(const Point& point)    {        Circle antic;        double dis = getDis(point, _o);        double tmp = R*R / (dis*dis - _r*_r);        antic._r = tmp*_r;        antic._o._x = point._x + tmp*(_o._x - point._x);        antic._o._y = point._y + tmp*(_o._y - point._y);        return antic;    }    friend istream& operator >> (istream& in, Circle& circle)    {        in >> circle._o >> circle._r;        return in;    }    friend ostream& operator<<(ostream& out, const Circle& circle)    {        out << circle._o << ' ' << circle._r;        return out;    }};double getCross(Point p1, Point p2, Point p){    return (p1._x - p._x)*(p2._y - p._y) - (p2._x - p._x)*(p1._y - p._y);}bool onSegment(Point p, Point a, Point b)//点p与线段ab{    if (judgeZero(getCross(a, b, p)) != 0)        return false;    if (p._x<min(a._x, b._x) || p._x>max(a._x, b._x))//trick:垂直或平行坐标轴;        return false;    if (p._y<min(a._y, b._y) || p._y>max(a._y, b._y))        return false;    return true;}bool segmentIntersect(Point a, Point b, Point c, Point d)//线段ab与线段cd{    if (onSegment(c, a, b) || onSegment(d, a, b) || onSegment(a, c, d) || onSegment(b, c, d))        return true;    if (judgeZero(getCross(a, b, c)*getCross(a, b, d)) < 0 && judgeZero(getCross(c, d, a)*getCross(c, d, b)) < 0)//==0的特殊情况已经在前面排除        return true;    return false;}bool onLine(Point p, Point a, Point b)//点p与直线ab{    return judgeZero(getCross(a, b, p)) == 0;}bool lineIntersect(Point a, Point b, Point c, Point d)//直线ab与直线cd{    if (judgeZero((a._x - b._x)*(c._y - d._y) - (c._x - d._x)*(a._y - b._y)) != 0)        return true;    if (onLine(a, c, d))//两直线重合        return true;    return false;}bool segment_intersectLine(Point a, Point b, Point c, Point d)//线段ab与直线cd{    if (judgeZero(getCross(c, d, a)*getCross(c, d, b)) <= 0)        return true;    return false;}Point getPoint(Point p1, Point p2, Point p3, Point p4)//求出交点,直线(线段)p1p2与直线(线段)p3p4{//t=lamta/(lamta+1),必须用t取代lamta,不然算lamta可能分母为0    double x1 = p1._x, y1 = p1._y;    double x2 = p2._x, y2 = p2._y;    double x3 = p3._x, y3 = p3._y;    double x4 = p4._x, y4 = p4._y;    double t = ((x2 - x1)*(y3 - y1) - (x3 - x1)*(y2 - y1)) / ((x2 - x1)*(y3 - y4) - (x3 - x4)*(y2 - y1));    return Point(x3 + t*(x4 - x3), y3 + t*(y4 - y3));}bool inPolygon(Point a, int n)//点是否含于多边形{    Point b(-1e15 + a._x, a._y);//向左无穷远的线段    int count = 0;    for (int i = 0;i < n;++i)    {        Point c = polygon[i], d = polygon[(i + 1) % n];        if (onSegment(a, c, d)) //如果点在线段上,那么一定在多边形内            return true;        if (judgeZero((a._x - b._x)*(c._y - d._y) - (c._x - d._x)*(a._y - b._y)) == 0)            continue;//如果边与射线平行,trick1        if (!segmentIntersect(a, b, c, d))            continue;//如果边与射线没有交点        Point lower;        if (c._y < d._y) lower = c;        else lower = d;        if (onSegment(lower, a, b)) //如果纵坐标小的点与射线有交点,trick2            continue;        count++;    }    return count % 2 == 1;}bool segment_inPolygon(Point a, Point b, int n)//线段是否含于多边形{    if (!inPolygon(a, n) || !inPolygon(b, n))//两个端点都不在多边形内        return false;    int tot = 0;    for (int i = 0;i < n;++i)    {        Point c = polygon[i], d = polygon[(i + 1) % n];        if (onSegment(a, c, d))//以下只用记录一个交点,多的要么重复,要么一定在边上            crossp[tot++] = a;        else if (onSegment(b, c, d))            crossp[tot++] = b;        else if (onSegment(c, a, b))            crossp[tot++] = c;        else if (onSegment(d, a, b))            crossp[tot++] = d;        else if (segmentIntersect(a, b, c, d))//端点没有在线段上且相交            return false;    }    sort(crossp, crossp + tot);//按x,y排序    tot = unique(crossp, crossp + tot) - crossp;    for (int i = 0;i < tot - 1;++i)    {        Point tmp = 0.5*(crossp[i] + crossp[i + 1]);        if (!inPolygon(tmp, n))//中点不在多边形内            return false;    }    return true;}double toSlove(Point a, Point b, int n){    int tot = 0;    for (int i = 0;i < n;++i)    {        Point c = polygon[i], d = polygon[(i + 1) % n];        if (onLine(c,a,b)&&onLine(d,a,b))        {            crossp[tot++] = c;            crossp[tot++] = d;        }        else if (segment_intersectLine(c, d, a, b))            crossp[tot++] = getPoint(a, b, c, d);    }    sort(crossp, crossp + tot);    tot = unique(crossp, crossp + tot) - crossp;    double ans = 0.0;    for (int i = 0;i < tot - 1;++i)    {        Point tmp = 0.5*(crossp[i] + crossp[i + 1]);        if (inPolygon(tmp, n))            ans += getDis(crossp[i], crossp[i + 1]);    }    return ans;}int main(){    for (int n, m;scanf("%d%d", &n, &m) == 2 && (n || m);)    {        for (int i = 0;i < n;++i)            cin >> polygon[i];        for (int i = 1;i <= m;++i)        {            Point a, b;            cin >> a >> b;            printf("%.3f\n", toSlove(a, b, n));        }    }    return 0;}