POJ1556——The Doors 计算几何,最短路

xiaoxiao2021-02-28  2

The Doors Time Limit: 1000MS Memory Limit: 10000KTotal Submissions: 9012 Accepted: 3458


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. 


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. 


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

题意:从(0,5)到(10.5) 中间有一些障碍,求最短路。


#include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<vector> #include<algorithm> #include <iostream> #include <string> #include <set> #include <map> #include <queue> using namespace std; const int MAXN = 500000+10; const int INF=1e9+7; const double eps=1e-8; const double pi=acos(-1.0); //计算几何误差修正 //输入为一个double类型的数,返回-1表示负数,1表示正数,0表示x为0 int cmp(double x){ if(fabs(x)<eps) return 0; if(x>0) return 1; return -1; } //计算几何点类 inline double sqr(double x){ return x*x; } struct point{ double x,y; point(){} point(double a,double b):x(a),y(b){} void input(){ scanf("%lf%lf",&x,&y); } //加法 friend point operator + (const point &a,const point &b){ return point(a.x+b.x,a.y+b.y); } //减法 friend point operator - (const point &a,const point &b){ return point(a.x-b.x,a.y-b.y); } //判断相等 friend bool operator == (const point &a,const point &b){ return cmp(a.x-b.x)==0&&cmp(a.y-b.y)==0; } //倍增 friend point operator * (const point &a,const double &b){ return point(a.x*b,a.y*b); } //倍增 friend point operator * (const double &b,const point &a){ return point(a.x*b,a.y*b); } //除法 friend point operator / (const point &a,const double b){ return point(a.x/b,a.y/b); } //模长 double norm(){ return sqrt(sqr(x)+sqr(y)); } }; //叉积,a×b>0代表a在b的顺时针方向,<0代表a在b的逆时针方向,等于0代表a和b向量共线,但不确定方向是否相同 double det(const point &a,const point &b){ return a.x*b.y-a.y*b.x; } //点积 double dot(const point &a,const point &b){ return a.x*b.x+a.y*b.y; } //距离 double dist(const point &a,const point &b){ return (a-b).norm(); } //op向量绕原点逆时针旋转A(弧度) point rotate_point(const point &p,double A){ double tx=p.x,ty=p.y; return point(tx*cos(A)-ty*sin(A),tx*sin(A)+ty*cos(A)); } //计算几何线段类 struct line{ point a,b; line(){} line(point x,point y):a(x),b(y){} void input(){ a.input(); b.input(); } }; //用两个点a,b生成的一个线段或者直线 line point_make_line(point a,point b){ return line(a,b); } //求点p到线段st的距离 double dis_point_segment(point p,point s,point t){ if(cmp(dot(p-s,t-s))<0) return (p-s).norm(); if(cmp(dot(p-s,s-t))<0) return (p-t).norm(); return fabs(det(s-p,t-p)/dist(s,t)); } //求点p到线段st的垂足,保存在cp中 void PointProjLine(point p,point s,point t,point &cp){ double r=dot((t-s),(p-s))/dot(t-s,t-s); cp=s+(t-s)*r; } //判断p点是否在线段st上 bool PointOnSegment(point p,point s,point t){ return cmp(det(p-s,t-s))==0&&cmp(dot(p-s,p-t))<=0; } //判断a和b是否平行 bool parallel(line a,line b){ return !cmp(det(a.a-a.b,b.a-b.b)); } //判断a和b是否共线 bool contribution(line a,line b){ if(!parallel(a, b)) return false; if(!cmp(det(a.b-a.a,b.a-a.b))) return true; return false; } //判断a和b是否相交,若相交则返回true且交点保存在res中 bool line_make_point(line a,line b,point &res){ if(parallel(a, b)) return false; double s1=det(a.a-b.a,b.b-b.a); double s2=det(a.b-b.a,b.b-b.a); res=(s1*a.b-s2*a.a)/(s1-s2); return true; } //判断线段是否相交 bool segment_make_point(line a,line b,point &res){ if(!line_make_point(a,b,res)) return false; if(PointOnSegment(res, a.a, a.b)&&PointOnSegment(res, b.a, b.b)) return true; return false; } //判断线段和直线是否相交,a是直线,b是线段 bool line_across_segment(line a,line b){ if(cmp(det(a.b-a.a,b.a-a.a)*det(a.b-a.a,b.b-a.a))==1){ return false; } return true; } //将直线a沿法向量方向平移距离len得到的直线 line move_d(line a,const double &len){ point d=a.b-a.a; d=d/d.norm(); d=rotate_point(d, pi/2); return line(a.a+d*len,a.b+d*len); } point p[MAXN]; line l[MAXN]; struct edge{ int v,next; double w; }e[MAXN]; int head[MAXN]; int tot=0; double d[MAXN]; int vis[MAXN]; //点的个数和边的个数 int s1=0,s2=0; queue<int> que; void add(int u,int v,double w){ e[tot].v=v; e[tot].next=head[u]; e[tot].w=w; head[u]=tot++; } void spfa(int u){ fill(d,d+MAXN,INF); d[u]=0; memset(vis,0,sizeof vis); que.push(u); vis[u]=1; while(!que.empty()){ int s=que.front(); que.pop(); vis[s]=0; for(int k=head[s];k!=-1;k=e[k].next){ int v=e[k].v; double w=e[k].w; if(d[v]>d[s]+w){ d[v]=d[s]+w; if(!vis[v]){ que.push(v); vis[v]=1; } } } } printf("%.2f\n",d[s1-1]); } void add_edge(int u,int v,int num){ point a=p[u],b=p[v]; line temp=point_make_line(a, b); int ok=1; for(int i=0;i<num;i++){ point x; if(segment_make_point(temp, l[i],x)&&!PointOnSegment(l[i].a, a, b)&&!PointOnSegment(l[i].b, a, b)){ ok=0; break; } } if(ok){ add(u,v,dist(a,b)); //cout<<dist(a, b)<<endl; } } int main(){ int n; while(scanf("%d",&n)&&n!=-1){ s1=0,s2=0; p[s1++]=point(0,5); tot=0; memset(head,-1,sizeof head); for(int i=0;i<n;i++){ double y,x1,x2,x3,x4; scanf("%lf%lf%lf%lf%lf",&y,&x1,&x2,&x3,&x4); point p1(y,0); point p2(y,10); int x=s1; p[s1++]=point(y,x1); p[s1++]=point(y,x2); p[s1++]=point(y,x3); p[s1++]=point(y,x4); for(int i=1;i<=4;i++){ for(int j=0;j<x;j++){ add_edge(j,s1-i,s2); } } l[s2++]=point_make_line(p1, p[s1-4]); l[s2++]=point_make_line(p[s1-3], p[s1-2]); l[s2++]=point_make_line(p[s1-1], p2); } p[s1++]=point(10,5); for(int i=0;i<s1-1;i++){ add_edge(i,s1-1,s2); } spfa(0); } }

转载请注明原文地址: https://www.6miu.com/read-1250368.html