The Doors POJ - 1556

You are to find the length of the shortest path through a chamber containing obstructing walls. The chamber will always have sides at x = 0, x = 10, y = 0, and y = 10. The initial and final points of the path are always (0, 5) and (10, 5). There will also be from 0 to 18 vertical walls inside the chamber, each with two doorways. The figure below illustrates such a chamber and also shows the path of minimal length.  
Input
The input data for the illustrated chamber would appear as follows.  

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.  
Output
The output should contain one line of output for each chamber. The line should contain the minimal path length rounded to two decimal places past the decimal point, and always showing the two decimal places past the decimal point. The line should contain no blanks.
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


#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所说,应该是线段,直线的话直接判断斜率就行了。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值