题意:给出一个(0,0)(0,10)(10,0)(10,10)的正方形房子,里面有n个墙,每堵墙上有两扇门,求从(0,5)到(10,5)的最短距离。
题解:线段与线段相交+dijkstra
先将所有点存起来,再将墙(线段)存起来。
暴力枚举两点,看是否与已存线段相交,若不相交,相连,然后跑最短路。
注意数据范围。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#define ll long long
using namespace std;
const double eps = 1e-8;
const double inf = 1e20;
const double pi = acos(-1.0);
const int maxp = 1010;
//Compares a double to zero
int sgn(double x) {
if (fabs(x) < eps) return 0;
if (x < 0) return -1;
else return 1;
}
//POINT
struct Point {
double x, y;
Point() {}
Point(double _x, double _y) {
x = _x;
y = _y;
}
Point operator -(const Point& b)const {
return Point(x - b.x, y - b.y);
}
//叉积
double operator ^(const Point& b)const {
return x * b.y - y * b.x;
}
//点积
double operator *(const Point& b)const {
return x * b.x + y * b.y;
}
//返回两点的距离
double distance(Point p) {
return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
}
};
//LINE
struct Line {
Point s, e;
Line() {}
Line(Point _s, Point _e) {
s = _s;
e = _e;
}
//两线段相交判断
//2 规范相交
//1 非规范相交
//0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e - s) ^ (v.s - s));
int d2 = sgn((e - s) ^ (v.e - s));
int d3 = sgn((v.e - v.s) ^ (s - v.s));
int d4 = sgn((v.e - v.s) ^ (e - v.s));
if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2)return 2;
return (d1 == 0 && sgn((v.s - s) * (v.s - e)) <= 0) ||
(d2 == 0 && sgn((v.e - s) * (v.e - e)) <= 0) ||
(d3 == 0 && sgn((s - v.s) * (s - v.e)) <= 0) ||
(d4 == 0 && sgn((e - v.s) * (e - v.e)) <= 0);
}
};
//dijkstra
const int manx = 1e2 + 5; //与n相对,对应顶点的个数
const int mamx = 5e6 + 5; //与m相对,对应边的个数
priority_queue< pair<double, int> >q;
struct node {
int next, v;
double w;
}edge[mamx]; //边去mamx,其余取manx
bool vis[manx]; //这里的标记数组与spfa的vis数组含义不同,这里标记是否入过队列
int head[manx];
double d[manx];
int k = 0;
int nn, s, e; //s作为起点,e作为终点
void add(int u, int v, double w) { //链式前向星存图
edge[++k].next = head[u];
edge[k].v = v;
edge[k].w = w;
head[u] = k;
}
void dijkstra() {
for (int i = 1; i <= 100; i++) {
d[i] = 0x3f3f3f3f;
}
memset(vis, false, sizeof(vis));
d[s] = 0; //s作为起点
q.push(make_pair(0, s));
while (q.size()) {
int x = q.top().second; //取出队头
q.pop();
if (vis[x]) continue; //如果点x访问过,跳过,访问下一个队头
vis[x] = 1; //访问x做标记
for (int i = head[x]; i; i = edge[i].next) {
int v = edge[i].v;
double w = edge[i].w;
if (d[v] > d[x] + w) { //松弛操作,更新距离
d[v] = d[x] + w;
q.push(make_pair(-d[v], v)); //把更新的距离和点入队,这里距离取负变成小根堆
}
}
}
}
int n;
double x, ya, yb, yc, yd;
Point p[100];
Line l[100];
int main() {
while (~scanf("%d", &n) && n != -1) {
k = 0;
memset(head, 0, sizeof(head));
int pcnt = 0, lcnt = 0;
p[++pcnt] = Point(0, 5);
p[++pcnt] = Point(10, 5);
for (int i = 1; i <= n; i++) {
scanf("%lf%lf%lf%lf%lf", &x, &ya, &yb, &yc, &yd);
p[++pcnt] = Point(x, ya);
p[++pcnt] = Point(x, yb);
p[++pcnt] = Point(x, yc);
p[++pcnt] = Point(x, yd);
l[++lcnt] = Line(Point(x, 0), p[pcnt - 3]);
l[++lcnt] = Line(p[pcnt - 2], p[pcnt - 1]);
l[++lcnt] = Line(p[pcnt], Point(x, 10));
}
for (int i = 1; i <= pcnt; i++) {
for (int j = i + 1; j <= pcnt; j++) {
Line temp = Line(p[i], p[j]);
int flag = 1;
for (int k = 1; k <= lcnt; k++) {
int re = temp.segcrossseg(l[k]);
if (re == 2) {
flag = 0;
break;
}
}
if (flag) {
add(i, j, p[i].distance(p[j]));
add(j, i, p[i].distance(p[j]));
}
}
}
s = 1;
dijkstra();
printf("%.2f\n", d[2]);
}
return 0;
}