题目大意
给出如上图所示的封闭区域,要求从坐标
(
0
,
5
)
(0,5)
(0,5)走到坐标
(
10
,
5
)
(10,5)
(10,5),但是中间可能会有墙,现在要求两点间的最短路径。
解题思路
一个不难证明的结论是:始终走的是墙而不会沿着墙绕路
刚开始我的想法是先判断每道竖直的墙是否挡住了
y
=
5
y=5
y=5这条线,如果挡住就先走到墙的最近的边缘,依次贪心选择每道竖直墙之间的最短距离直到走到终点,但是我忘记了如果能走下面红线时,我的贪心策略就不对了。
显然是最短路的问题嘛,但是本题是隐式图,需要转换成普通的节点式的图。因为输入的数据量不大,那么我们直接枚举任意两个节点,使用Floyd算法即可。需要注意的是起点和终点也要加入。而且这里判断是否相交是非严格的相交,即可以和端点相交,也需要注意
代码:
#include <iostream>
#include <math.h>
#include <cstring>
using namespace std;
#define Vector Point
#define Seg Line
const double INF=1e300;
const double eps=1e-8;
int dcmp(double d){
if(fabs(d)<eps) return 0;
if(d<0) return -1;
return 1;
}
struct Point{
double x,y;
Point(double a=0,double b=0){
x=a,y=b;
}
Point operator + (Point p){ return Point(x+p.x,y+p.y); }
Point operator - (Point p){ return Point(x-p.x,y-p.y); }
Point operator * (double d){ return Point(x*d,y*d); }
double operator * (Point p){ return x*p.x+y*p.y; }
Point operator / (double d){ return Point(x/d,y/d); }
double operator ^ (Point p){ return x*p.y-y*p.x; }
bool operator == (const Point &p) const { return x==p.x&&y==p.y; }
void print(){ printf("(%.2lf,%.2lf)\n",x,y); }
};
struct Line{
Point p,q;
Vector v;
Line(){}
Line(Point a,Point b){
p=a,q=b,v=b-a;
}
Point point(double t){
return p+v*t;
}
};
double dis(Point a,Point b){
return sqrt((b-a)*(b-a));
}
bool isSegInter(Seg a,Seg b){
double c1=a.v^b.p-a.p,c2=a.v^b.q-a.p;
double c3=b.v^a.p-b.p,c4=b.v^a.q-b.p;
return (dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0); //必须是<
}
bool vis[105][105]; //判断两点之间是否能直接相连
Point P[105]; //保存所有点
double G[105][105]; //存两点间的距离,初始根据vis数组初始化为两点间距离
Line L[50]; //保存所有线段
int main()
{
int n;
double x,a,b,c,d;
while(~scanf("%d",&n)){
if(n==-1) break;
memset(vis,0,sizeof vis);
for(int i=1,j=1;i<=4*n;i+=4,j+=3){ //n个竖直墙有4*n个点,3*n个线段
scanf("%lf",&x);
scanf("%lf %lf %lf %lf",&a,&b,&c,&d);
P[i]=Point(x,a),P[i+1]=Point(x,b);
P[i+2]=Point(x,c),P[i+3]=Point(x,d);
L[j]=Line(Point(x,0),Point(x,a));
L[j+1]=Line(Point(x,b),Point(x,c));
L[j+2]=Line(Point(x,d),Point(x,10));
}
P[0]=Point(0,5),P[4*n+1]=Point(10,5); //起点终点
//首先一次二维循环判断两点之间是否能直接用距离公式求距离
for(int i=0;i<=4*n+1;i++){
for(int j=0;j<=4*n+1;j++){
int flag=1;
if(i==j){ G[i][j]=0; continue; } //这里初始化到本身距离为0
for(int k=1;k<=3*n;k++){
if(isSegInter(Line(P[i],P[j]),L[k])){
flag=0;
break;
}
}
if(flag) vis[i][j]=1;
}
}
//对于能相连的点调用距离公式,否则为INF
for(int i=0;i<=4*n+1;i++){
for(int j=0;j<=4*n+1;j++){
if(vis[i][j]) G[i][j]=dis(P[i],P[j]);
else G[i][j]=INF;
//printf("%.1lf ",G[i][j]);
//printf("%d",vis[i][j]);
}
//printf("\n");
}
//Floyd主体
for(int k=0;k<=4*n+1;k++){
for(int i=0;i<=4*n+1;i++){
for(int j=0;j<=4*n+1;j++){
if(G[i][k]+G[k][j]<G[i][j])
G[i][j]=G[i][k]+G[k][j];
}
}
}
printf("%.2lf\n",G[0][4*n+1]);
}
return 0;
}