POJ1556

解法大致如下:
这里写图片描述
注意一下加点的顺序即可,特别起点终点不要漏了。还有就是叉乘真的玄学…用了原先模板里的叉乘疯狂wa,换了一个就AC了QAQ…
似乎判断直线与线段相交的问题都要用这种叉乘…单存算两个向量的叉乘好像有点问题,问题暂时不明。

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include<iostream>
#include<map>
#include<queue>
using namespace std;
const int MAXN = 100000;
const double EPS = 1e-10;

// 带误差比较
inline bool dcmp(double x, double y = 0)
{
    return fabs(x - y) <= EPS;
}
typedef struct Vec
{
    double x, y;

    Vec(double x = 0, double y = 0) : x(x), y(y) {}

    // 相加
    Vec operator+(const Vec &v) const
    {
        return Vec(x + v.x, y + v.y);
    }

    // 相减
    Vec operator-(const Vec &v) const
    {
        return Vec(x - v.x, y - v.y);
    }

    // 数乘(伸长、缩短)
    Vec operator*(double d) const
    {
        return Vec(x * d, y * d);
    }

    Vec operator/(const double d) const
    {
        return Vec(x / d, y / d);
    }
    bool operator<(const Vec&v) const {
        if(v.x==x)return y<v.y;
        return x<v.x;
    }
    // 范数,用来比较长度,等于长度的平方
    double norm() const
    {
        return x * x + y * y;
    }
} Pt;

// 点乘
double dot(const Vec &a, const Vec &b)
{
    return a.x * b.x + a.y * b.y;
}

// 叉乘
double cross(const Vec &a, const Vec &b)
{
    return a.x * b.y - a.y * b.x;
}

// 线段(Segment),用两个点表示
struct Seg
{
    Pt a, b;

    Seg(const Pt &a, const Pt &b) : a(a), b(b) {}

    // 线段包含点(点在线段上)
    bool include(const Pt &p)
    {
        // PA × PB = 0:PA 与 PB 共线,即点在线段所在的直线上
        // PA · PB = 0:PA 与 PB 方向不同(A 和 B 分别在 P 的两边),如果 PA · PB = 0 则 P = A 或 P = B
        return dcmp(cross(a - p, b - p)) && dot(a - p, b - p) <= 0;
    }
};

// 直线,用两个点表示
struct Line
{
    Pt a, b;

    Line() {} // 提供一个不需要参数的构造函数
    Line(const Pt &a, const Pt &b) : a(a), b(b) {}

    bool include(const Pt &p) const
    {
        return dcmp(cross(a - p, b - p));
    }

    // 两直线关系(交点个数)
    // 0 表示平行(无交点)
    // 1 表示相交(一个交点)
    // -1 表示重合(无数个交点)
    static int relation(const Line &a, const Line &b)
    {
        if (a.include(b.a) && a.include(b.b)) return -1;
        else if (dcmp(cross(a.b - a.a, b.b - b.a))) return 0;
        else return 1;
    }

    // 求两直线交点(需要保证两直线有交点)
    static Pt intersect(const Line &a, const Line &b)
    {
        double s1 = cross(b.a - a.a, b.b - a.a), s2 = cross(b.b - a.b, b.a - a.b);
        return a.a + (a.b - a.a) * s1 / (s1 + s2);
    }
};
struct edge{
    int to;double cost;
};
double cross(Pt a,Pt b,Pt c)//直线叉积,前面两个点是直线上的点,最后那个点是线段上的点,本题一定要用这个叉乘
{
    return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
int main()
{
    int n;
    while(cin>>n&&n!=-1) {
        map<Pt,int>ys;
        vector<Pt> pt;
        int i, j, k;
        double x[20];
        vector<Seg> seg;vector<edge>G[100];
        int cnt=0;pt.push_back(Pt(0,5));ys[Pt(0,5)]=0;//先把起点丢进去
        for (i = 1; i <= n; i++) {
            double a, b, c, d;
            scanf("%lf%lf%lf%lf%lf", &x[i], &a, &b, &c, &d);//把4个点丢进去
            pt.push_back(Pt(x[i], a));ys[Pt(x[i],a)]=++cnt;
            pt.push_back(Pt(x[i], b));ys[Pt(x[i],b)]=++cnt;
            pt.push_back(Pt(x[i], c));ys[Pt(x[i],c)]=++cnt;
            pt.push_back(Pt(x[i], d));ys[Pt(x[i],d)]=++cnt;
            Pt p1, p2;
            p1.x = p2.x = x[i];
            p1.y = 10;
            p2.y = d;
            seg.push_back(Seg(p1, p2));//把线段丢进去
            p1.y = c;
            p2.y = b;
            seg.push_back(Seg(p1, p2));
            p1.y = a;
            p2.y = 0;
            seg.push_back(Seg(p1, p2));
        }
        pt.push_back(Pt(10,5));ys[Pt(10,5)]=100;
        for (i = 0; i < pt.size(); i++) {
            for (j = i + 1; j < pt.size(); j++) {
                if (pt[j].x == pt[i].x)continue;
                double a, b;
                a = pt[i].x;
                b = pt[j].x;
                Vec t = pt[i] - pt[j];
                bool iscon = true;
                for (k = 0; k < seg.size(); k++) {//求两个点之间的连线是否与线段相交
                    if (seg[k].a.x <= a || seg[k].a.x >= b)continue;
                    if (cross(pt[i],pt[j], seg[k].a) * cross(pt[i],pt[j], seg[k].b) < 0) {
                        iscon = false;
                        break;
                    }
                }
                if(iscon){
                    //cout<<i<<' '<<j<<endl;
                    int c=ys[pt[i]],d=ys[pt[j]];
                    G[c].push_back(edge{d,sqrt(t.norm())});
                }
            }
        }
        double dis[105];for(i=0;i<105;i++)dis[i]=1e10;dis[0]=0;
        queue<int>que;bool inque[105]={false};que.push(0);//spfa
        while(!que.empty()){
            int now=que.front();inque[now]=false;que.pop();if(now==100)continue;
            for(i=0;i<G[now].size();i++){
                edge e=G[now][i];
                if(dis[e.to]>dis[now]+e.cost){
                    dis[e.to]=dis[now]+e.cost;
                    if(!inque[e.to]){
                        que.push(e.to);inque[e.to]=true;
                    }
                }
            }
        }
        printf("%.2f\n",dis[100]);
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/humveea6/article/details/80294463