【POJ - 1556】The Doors (计算几何,线段相交)

版权声明:欢迎学习我的博客,希望ACM的发展越来越好~ https://blog.csdn.net/qq_41289920/article/details/83447182

题干:

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. 


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

题目大意:

房间里有n堵墙,每面墙上有两扇门,求从房间最左端中点到最右端中点的最短路径 

解题报告:

把起点,终点,每个墙上的4个点分别看成图的顶点,这样一共就是4*n+2个点,我们把起点和终点当做第4*n+1和4*n+2这两个点,其余的每四个点为一组读数,读完顺便加边。

然后就是把这个点和之前所有读过的点连成一条边(假设共能连cnt条边),然后每条边依次连好的边(也就是墙)判断是否相交,如果和所有的墙都不相交,那么就加边(到dis数组中),然后跑floyd最短路就可以了。

写代码能力还是挫啊、、搞了一小时、、

AC代码:

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstdio>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define fi first
#define se second
using namespace std;
const double eps = 1e-8;
int sgn(double x) {
	if(fabs(x) < eps)return 0;
	if(x < 0) return -1;
	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;}
} p[1005];
struct Line {
	Point s,e;
	Line() {}
	Line(Point s,Point e):s(s),e(e) {}
	pair<Point,int> operator &(const Line &b)const {
		Point res = s;
		if(sgn((s-e)^(b.s-b.e)) == 0) {
			if(sgn((b.s-s)^(b.e-s)) == 0)
				return make_pair(res,0);//两直线重合
			else return make_pair(res,1);//两直线平行
		}
		double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
		res.x += (e.x - s.x)*t;
		res.y += (e.y - s.y)*t;
		return make_pair(res,2);//有交点,并返回交点
	}
} line[1005];
inline double xmult(Point p0,Point p1,Point p2) { return (p1-p0)^(p2-p0);}//p0p1 X p0p2
bool Seg_inter_line(Line l1,Line l2) { return sgn(xmult(l2.s,l1.s,l1.e))*sgn(xmult(l2.e,l1.s,l1.e)) <= 0;}//判断直线l1和线段l2是否相交
double dist(Point a,Point b) {return sqrt((b - a)*(b - a));}
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;
}
//两线段相交判断
//2 规范相交
//1 非规范相交
//0 不相交
int segcrossseg(Line l1,Line l2) {
	int d1 = sgn((l1.e-l1.s)^(l2.s-l1.s));
	int d2 = sgn((l1.e-l1.s)^(l2.e-l1.s));
	int d3 = sgn((l2.e-l2.s)^(l1.s-l2.s));
	int d4 = sgn((l2.e-l2.s)^(l1.e-l2.s));
	if( (d1^d2)==-2 && (d3^d4)==-2 )return 2;
	return (d1==0 && sgn((l2.s-l1.s)*(l2.s-l1.e))<=0) ||(d2==0 && sgn((l2.e-l1.s)*(l2.e-l1.e))<=0) ||
	       (d3==0 && sgn((l1.s-l2.s)*(l1.s-l2.e))<=0) ||(d4==0 && sgn((l1.e-l2.s)*(l1.e-l2.e))<=0);
}
//直线和线段相交判断
//-*this line -v seg
//2 规范相交
//1 非规范相交
//0 不相交
int linecrossseg(Line l1,Line l2) {//l1直线,l2线段 
	int d1 = sgn((l1.e-l1.s)^(l2.s-l1.s));
	int d2 = sgn((l1.e-l1.s)^(l2.e-l1.s));
	if((d1^d2)==-2) return 2;
	return (d1==0||d2==0);
}
int totP,totL;
int n;
double dis[1005][1005];
void add(int num) {
	int up = num%4==0 ?  num-4 : num-(num%4);//最多跑到哪个点 
	for(int i = 1; i<=up; i++) {
		int flag = 1;
		for(int j = 1; j<=totL; j++) {
			if(segcrossseg(Line(p[num],p[i]),line[j]) == 2) {
				flag=0;break;
			}
		}
		if(flag) {
			dis[i][num] = dis[num][i] = dist(p[i],p[num]);
		}
	}
	//与起点 
	int flag = 1;
	for(int j = 1; j<=totL; j++) {
		if(segcrossseg(Line(p[num],p[4*n+1]),line[j]) == 2) {
			flag=0;break;
		}
	}
	if(flag) {
		dis[4*n+1][num] = dis[num][4*n+1] = dist(p[4*n+1],p[num]);
	}
}
int main()
{
	double y1,y2,y3,y4,x;
	while(~scanf("%d",&n)) {
		if(n == -1) break;
		//共4*n+1个顶点
		totP = 0;//因为起点 站了一个了 (还是起点单独算吧、、、) 
		totL = 0;
		p[4*n+1] = Point(0,5);
		p[4*n+2] = Point(10,5);
		for(int i = 1; i<=4*n+10; i++) {
			for(int j = 1; j<=4*n+10; j++) {
				dis[i][j] = 1000000000;
			}
		}
		for(int i = 1; i<=n; i++) {
			scanf("%lf%lf%lf%lf%lf",&x,&y1,&y2,&y3,&y4);
			p[++totP] = Point(x,y1);
			line[++totL] = Line(Point(x,0),p[totP]);
			p[++totP] = Point(x,y2);
			p[++totP] = Point(x,y3);
			line[++totL] = Line(p[totP-1],p[totP]);
			p[++totP] = Point(x,y4);
			line[++totL] = Line(p[totP],Point(x,10));
			add(totP);add(totP-1);add(totP-2);add(totP-3);
		}
		add(4*n+2);
//		dis[totP][4*n+2] = dis[4*n+2][totP] = dist(Point(totP),Point(p[4*n+2]));
//		dis[totP-1][4*n+2] = dis[4*n+2][totP] = dist(Point(totP),Point(p[4*n+2]));
//		dis[totP][4*n+2] = dis[4*n+2][totP] = dist(Point(totP),Point(p[4*n+2]));
//		dis[totP][4*n+2] = dis[4*n+2][totP] = dist(Point(totP),Point(p[4*n+2]));
		for(int k = 1; k<=totP+2; k++) {
			for(int i = 1; i<=totP+2; i++) {
				for(int j = 1; j<=totP+2; j++) {
					if(dis[i][k] + dis[k][j] < dis[i][j]) {
						dis[i][j] = dis[i][k] + dis[k][j];
					}
				}
			}
		}
		printf("%.2f\n",dis[4*n+1][4*n+2]);
	}	
}
/*
1
7 3 4.5 6 7

*/ 

总结:

  说一下选定思路后开始敲代码的历程,首先模板没的说,然后开始想读入点的时候,用构造函数构造点,构造边,是都需要记录下来呢还是可以只构造点,然后遍历的时候再   每两个点连成一条边    来进行判断,后来发现这样不太好,也不太适合。因为你这样会把  -每条墙的边和矩形的边框的边的交点-  那个点,也算在判断的范围中了,但是我们是不需要这个点的,也就是说不需要判断这个点和其他点之间的关系,所以我们考虑也把每一条可以用到的边都记录下来,然后一边读入一边存点存边并且构图(最短路的图),刚开始想把起点存成id=1,终点存成id=4*n+2,但是发现这样不太好,因为这样就打乱了四条边一组的这一不错的关系,因为用这一关系(因为可以取模了啊 毕竟可以分组),我们可以用一个处理就得出了我们需要判断的up的值,(如果不是这种存id的方法的话,那就是2,3,4,5是一组,6,7,8,9是一组,这样就得写一堆if else、、)然后写add函数,刚开始直接up=num-(num%4),后来发现第一个样例都跑不出来,找bug,找到,改。这也是一个小trick啊以后别再犯了、、、找到这个bug的同时发现我没有让他和起点去连线(因为他的坐标是4*n+1啊所以肯定没有被比较过)所以我们需要单独让他俩连线跑一次。

   然后第二个样例没过去,检查后发现是最短路中应该是totP+2,我忘了+2了。。

   然后 dis数组初始化,本来想memset,,一想不对啊这是double类型,不能memset了,,然后最后写了四行想让终点和最后一个墙中的点都建图,但是一想这样写不好,因为可能他和其他前面的墙也可以建图呢。。所以直接add一下得了。。于是乎代码出来了。。。然后一发AC、、

坑点

猜你喜欢

转载自blog.csdn.net/qq_41289920/article/details/83447182