2
4 2 7 8 9
7 3 4.5 6 7
The first line contains the number of interior walls. Then there is a line for each such wall, containing five real numbers. The first number is the x coordinate of the wall (0 < x < 10), and the remaining four are the y coordinates of the ends of the doorways in that wall. The x coordinates of the walls are in increasing order, and within each line the y coordinates are in increasing order. The input file will contain at least one such set of data. The end of the data comes when the number of walls is -1.
1 5 4 6 7 8 2 4 2 7 8 9 7 3 4.5 6 7 -1Sample Output
10.00 10.06
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
typedef long long ll;
using namespace std;
const int maxn=100;
const double inf=1e9;
struct P {
double x,y;
P() {}
P(double _x,double _y) {
x=_x,y=_y;
}
P operator-(const P &b)const {
return P(x-b.x,y-b.y);
}
double operator *(const P &b)const {
return x*b.x + y*b.y;
}
double cp(P b)const {
return x*b.y-b.x*y;
}
} p[maxn];
struct L {
P s,e;
bool cross(L c) {//判断是否相交
P a1=s,a2=e,b1=c.s,b2=c.e;
if(!(min(a1.x,a2.x)<=max(b1.x,b2.x)&&//快速排斥实验
min(b1.x,b2.x)<=max(a1.x,a2.x)&&
min(a1.y,a2.y)<=max(b1.y,b2.y)&&
min(b1.y,b2.y)<=max(a1.y,a2.y)))
return false;
if((b2-b1).cp(a2-b1)*(b2-b1).cp(a1-b1)>=0.0||//跨立实验
(a2-a1).cp(b1-a1)*(a2-a1).cp(b2-a1)>=0.0)
return false;
return true;
}
};
int n;
double cal(P a,P b)//判断是否能直接到达,可以的话,计算距离
{
P up,down;
for(int i=0; i<n; i++) {
up.x=down.x=p[4*i+1].x;
up.y=10;
down.y=0;
L l1,l2;
l1.s=a,l1.e=b,l2.s=down,l2.e=p[4*i+1];
if(l1.cross(l2))return inf;
l2.s=p[4*i+2],l2.e=p[4*i+3];
if(l1.cross(l2))return inf;
l2.s=p[4*i+4],l2.e=up;
if(l1.cross(l2))return inf;
}
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double dis[maxn][maxn],vis[maxn];
double d[maxn];
void dijstra()
{
memset(vis,0,sizeof(vis));
for(int i=1; i<=99; i++) d[i]=1e9;
d[0]=0;
for(int i=0; i<=4*n+1; i++) {
double md=inf;
int k;
for(int j=0; j<=4*n+1; j++) {
if(!vis[j]&&d[j]<md) {
md=d[j];
k=j;
}
}
vis[k]=1;
for(int j=0; j<=4*n+1; j++) {
if(!vis[j]&&d[j]>d[k]+dis[k][j]) {
d[j]=d[k]+dis[k][j];
}
}
}
}
int main()
{
while(scanf("%d",&n)&&n!=-1) {
p[0].x=0,p[0].y=5;
for(int i=0; i<n; i++) {
double x,y1,y2,y3,y4;
scanf("%lf %lf %lf %lf %lf",&x,&y1,&y2,&y3,&y4);
p[i*4+1].x=x,p[i*4+1].y=y1;
p[i*4+2].x=x,p[i*4+2].y=y2;
p[i*4+3].x=x,p[i*4+3].y=y3;
p[i*4+4].x=x,p[i*4+4].y=y4;
}
p[n*4+1].x=10,p[n*4+1].y=5;
for(int i=0; i<=4*n+1; i++) {
dis[i][i]=0.0;
for(int j=i+1; j<=4*n+1; j++) {
dis[i][j]=dis[j][i]=cal(p[i],p[j]);
}
}
dijstra();
printf("%.2f\n",d[4*n+1]);
}
return 0;
}
知识点:
信息学竞赛中,当我们知道两线段4个点的坐标,后可以利用计算几何来判断用他们是否相交
基本思路
① 快速排斥实验(不涉及计算几何):如果两条边所在矩形不相交则一定不相交,即满足
min(x1,x2)<=max(x3,x4) &&
min(x3,x4)<=max(x1,x2) &&
min(y1,y2)<=max(y3,y4) &&
min(y3,y4)<=max(y1,y2) 才可能相交
②跨立实验:即以其中一条线段为直线,判断另一线段的两端点是否在它两边,
但是可能出现以下情况:
所以要判断两次,即两条线段都要为直线,判断另一线段的两端点是否在它两边
若是则两线段相交
跨立实验可用叉积来解决:
设这四个点为x1,y1,x2,y2,x3,y3,x4,y4
L1的坐标为t1=x1-x2,w1=y1-y2,
1端点到3,4的线段向量分别为t2=x1-x3,w2=y1-y3,t3=x1-x4,w3=y1-y4;
则3,4在L1两端即为(t1*w2-t2*w1)*(t1*w3-t3*w1)<=0
如果等于0则有一点在直线L1上,属于非规范相交
判断L1的两点在L2两侧同理。
如果仅仅满足跨立实验,是不行的,因为有如下情况:
我们发现,在左图中只满足了一个“跨立”条件,即Q1和Q2在直线P1P2的两侧,于是我们应该做两次跨立实验才能保证它们是相交的;而右图则是边界条件的特殊情况,不方便用跨立实验来判断,不过通过观察两张图后,我们发现:当两线段不相交时,以这两条线段为对角线的格点矩形没有交集!于是我们可以借助这一点来增加判交条件以保证算法的正确性,我们称之为快速排斥实验,如下图:
——————————————————————————————————————————————————————
即两线段共直线。
所以要同时满足快速排斥实验和跨立实验才能证明两线段相交。
补:
正如jurary所说,应该是线段,直线的话直接判断斜率就行了。