Description
房间里有n堵墙,每面墙上有两扇门,求从房间最左端中点到最右端中点的最短路径
Input
多组用例,每组用例第一行为墙数n,之后n行每行五个浮点数x,y1,y2,y3,y4分别表示墙的横坐标,两扇门上下端点的纵坐标,以n=-1结束输入
Output
对于每组用例,输出从房间左端中点到右端中点的最短路径
Sample Input
1
5 4 6 7 8
2
4 2 7 8 9
7 3 4.5 6 7
-1
Sample Output
10.00
10.06
Solution
总共4*n+2个点,对于任意两点,判断它们是否与门相交,相交就连一条线,最后用最短路算法计算左端中点到右端中点的最短路径
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const double eps = 1e-8;
#define maxn 111
#define INF 1<<29
int sgn(double x)
{
if(fabs(x)<eps) return 0;
if(x<0) return -1;
else return 1;
}
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;
}
};
struct Line
{
Point s,e;
Line(){}
Line(Point _s,Point _e)
{
s=_s;e=_e;
}
};
//判断线段相交
bool inter(Line l1,Line l2)
{
return
max(l1.s.x,l1.e.x)>=min(l2.s.x,l2.e.x)&&
max(l2.s.x,l2.e.x)>=min(l1.s.x,l1.e.x)&&
max(l1.s.y,l1.e.y)>=min(l2.s.y,l2.e.y)&&
max(l2.s.y,l2.e.y)>=min(l1.s.y,l1.e.y)&&
sgn((l2.s-l1.s)^(l1.e-l1.s))*sgn((l2.e-l1.s)^(l1.e-l1.s))<=0&&
sgn((l1.s-l2.s)^(l2.e-l2.s))*sgn((l1.e-l2.s)^(l2.e-l2.s))<=0;
}
double dist(Point a,Point b)
{
return sqrt((b-a)*(b-a));
}
Line line[maxn];
double dis[maxn][maxn];
int main()
{
int n;
double x,y1,y2,y3,y4;
while(scanf("%d",&n)&&n!=-1)
{
for(int i=1;i<=n;i++)
{
scanf("%lf%lf%lf%lf%lf",&x,&y1,&y2,&y3,&y4);
line[2*i-1]=Line(Point(x,y1),Point(x,y2));//将门看作线段
line[2*i]=Line(Point(x,y3),Point(x,y4));
}
for(int i=0;i<=4*n+1;i++)//初始化
for(int j=0;j<=4*n+1;j++)
{
if(i==j)
dis[i][j]=0;
else
dis[i][j]=INF;
}
for(int i=1;i<=4*n;i++)//判断中间4*n个点与左右端中点是否能连线
{
int col=(i+3)/4;//该点所在墙的编号
bool flag=true;
Point temp;
if(i&1) temp=line[(i+1)/2].s;//判断该点是门的上端点还是下端点
else temp=line[(i+1)/2].e;
for(int j=1;j<col;j++)//如果该点与它和左端中点间某堵墙的两个门都不相交则不能连线
if(inter(line[2*j-1],Line(Point(0,5),temp))==false&&inter(line[2*j],Line(Point(0,5),temp))==false)
flag=false;
if(flag) dis[i][0]=dis[0][i]=dist(Point(0,5),temp);//可以连线
flag=true;
for(int j=col+1;j<=n;j++)//如果该点与它和右端中点间某堵墙的两个门都不相交则不能连线
if(inter(line[2*j-1],Line(Point(10,5),temp))==false&&inter(line[2*j],Line(Point(10,5),temp))==false)
flag=false;
if(flag) dis[i][4*n+1]=dis[4*n+1][i]=dist(Point(10,5),temp);//可以连线
}
for(int i=1;i<=4*n;i++)//判断中间4*n个点是否能相互连线
for(int j=i+1;j<=4*n;j++)
{
int col1=(i+3)/4;//第i个点所在墙的编号
int col2=(j+3)/4;//第j个点所在墙的编号
bool flag=true;
Point t1,t2;
if(i&1) t1=line[(i+1)/2].s;//判断该点是门的上端点还是下端点
else t1=line[(i+1)/2].e;
if(j&1) t2=line[(j+1)/2].s;//判断该点是门的上端点还是下端点
else t2=line[(j+1)/2].e;
for(int k=col1+1;k<col2;k++)//如果两点与其之间某堵墙的两个门都不相交则不能连线
if(inter(line[2*k-1],Line(t1,t2))==false&&inter(line[2*k],Line(t1,t2))==false)
flag=false;
if(flag) dis[i][j]=dis[j][i]=dist(t1,t2);//可以连线
}
bool flag=true;
//判断左右两端中点是否能直接连线
for(int i=1;i<=n;i++)//如果两中点与其之间的某堵墙的两个门都不相交则不能连线
if(inter(line[2*i-1],Line(Point(0,5),Point(10,5)))==false&&inter(line[2*i],Line(Point(0,5),Point(10,5)))==false)
flag=false;
if(flag) dis[0][4*n+1]=dis[4*n+1][0]=10;//可以连线
for(int k=0;k<=4*n+1;k++)//Floyd求最短路
for(int i=0;i<=4*n+1;i++)
for(int j=0;j<=4*n+1;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
printf("%.2lf\n",dis[0][4*n+1]);//输出左右两端中点的最短路径
}
return 0;
}