题目:http://poj.org/problem?id=1556
题意:
给出一个(0,0)(0,10)(10,0)(10,10)的正方形房子,里面有一些墙,每堵墙上有两扇门;
求从(0,5)到(10,5)的最短距离;
题解:
本题结合了计算几何和最短路,是一道综合性的题目。
首先建一个有向图,把(0,5)作为起始点,(10,5)作为目标点,其他所有墙上的门的两个端点也加入到这个有向图中;
尝试枚举连接任意两个点,只有当这两个点之间没有墙阻隔,这连个点才可能连接起来;
所有能连接起来的两个点都作为一条有向边加入到有向图中,并且靠左的作为u,靠右的作为v,而两点之间的距离作为w;
最后,我们只要求出起始点和目标点之间的最短路即可。
题目特点:
每一列的点是有顺序的,利用这个特点,可以确定哪些是墙,哪些是窗子。
代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<queue>
#define MAXN 4*20
#define INF 0x3f3f3f3f
using namespace std;
//--------------------------------------计算几何模板 - st--------------------------------------
const double eps = 1e-6;
struct Point{
double x,y;
Point(double tx=0,double ty=0):x(tx),y(ty){}
};
typedef Point Vctor;
//向量的加减乘除
Vctor operator + (Vctor A,Vctor B){return Vctor(A.x+B.x,A.y+B.y);}
Vctor operator - (Point A,Point B){return Vctor(A.x-B.x,A.y-B.y);}
Vctor operator * (Vctor A,double p){return Vctor(A.x*p,A.y*p);}
Vctor operator / (Vctor A,double p){return Vctor(A.x/p,A.y/p);}
int dcmp(double x)
{
if(fabs(x)<eps) return 0;
else return (x<0)?(-1):(1);
}
bool operator == (Point A,Point B){return dcmp(A.x-B.x)==0 && dcmp(A.y-B.y)==0;}
//向量的点积,长度,夹角
double Dot(Vctor A,Vctor B){return A.x*B.x+A.y*B.y;}
double Length(Vctor A){return sqrt(Dot(A,A));}
double Angle(Vctor A,Vctor B){return acos(Dot(A,B)/Length(A)/Length(B));}
//叉积,三角形面积
double Cross(Vctor A,Vctor B){return A.x*B.y-A.y*B.x;}
double TriangleArea(Point A,Point B,Point C){return Cross(B-A,C-A);}
//判断线段是否规范相交
bool SegmentProperIntersection(Point a1,Point a2,Point b1,Point b2)
{
double c1 = Cross(a2 - a1,b1 - a1), c2 = Cross(a2 - a1,b2 - a1),
c3 = Cross(b2 - b1,a1 - b1), c4 = Cross(b2 - b1,a2 - b1);
return dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0;
}
//--------------------------------------计算几何模板 - ed--------------------------------------
//--------------------------------------spfa算法 - st--------------------------------------
double d[MAXN];
double mp[MAXN][MAXN];
bool vis[MAXN];
void init(int n){
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
mp[i][j]=0;
}
void addedge(int u,int v,double w)
{
mp[u][v]=w;
}
//spfa求最短路
void spfa(int st,int n)
{
//初始化,第一个点距离0,其他点距离正无穷
for(int i=0;i<n;i++)
{
i==st ? d[i]=0 : d[i]=INF;
vis[i]=0;
}
queue<int> q;
q.push(st);
vis[st]=1;
while(!q.empty())
{
int u=q.front();q.pop();vis[u]=0;
for(int v=0;v<n;v++)
{
if(u==v || mp[u][v]==0) continue;
double tmp=d[v];
if(d[v]>d[u]+mp[u][v]) d[v]=d[u]+mp[u][v];
if(d[v]<tmp && !vis[v])
{
q.push(v);
vis[v]=1;
}
}
}
}
//--------------------------------------spfa算法 - ed--------------------------------------
int n;
vector<Point> p;
int main()
{
while(scanf("%d",&n) && n!=-1)
{
p.clear();
p.push_back(Point(0,5));
for(int i=1;i<=n;i++)
{
double x,y1,y2,y3,y4;
scanf("%lf%lf%lf%lf%lf",&x,&y1,&y2,&y3,&y4);
p.push_back(Point(x,y1));
p.push_back(Point(x,y2));
p.push_back(Point(x,y3));
p.push_back(Point(x,y4));
}
p.push_back(Point(10,5));
int _size=p.size();
init(_size);
for(int i=0;i<_size;i++)
{
for(int j=i+1;j<_size;j++)
{
if(p[i].x==p[j].x) continue;
bool ok=1;
for(int k=i+1;k<j;k++)
{
if(k%4==1)
{
if(SegmentProperIntersection(p[i],p[j],p[k],Point(p[k].x,0)))
{
ok=0;
break;
}
}
else if(k%4==2)
{
if(SegmentProperIntersection(p[i],p[j],p[k],p[k+1]))
{
ok=0;
break;
}
}
else if(k%4==3)
{
if(SegmentProperIntersection(p[i],p[j],p[k],p[k-1]))
{
ok=0;
break;
}
}
else if(k%4==0)
{
if(SegmentProperIntersection(p[i],p[j],p[k],Point(p[k].x,10)))
{
ok=0;
break;
}
}
}
if(ok) addedge(i,j,Length(p[i]-p[j]));
}
}
spfa(0,_size);
printf("%.2f\n",d[_size-1]);
}
return 0;
}