解法大致如下:
注意一下加点的顺序即可,特别起点终点不要漏了。还有就是叉乘真的玄学…用了原先模板里的叉乘疯狂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;
}