题目:给你平面上的n条线段,这些线段分成两类:
1.街道边,两个端点都与其他线段相连,是可以行走的路径;
2.标记边,只有一个端点和其他线段相连,标记路径的方向(角度大于90方向的可以通行)。
计算起点到终点的最短距离。输出路径上的点,用0结束;如果不存在输出-1。
分析:计算几何、最短路。预处理有点麻烦的题目。由于分割街道的都是线段的端点,所以也并不是很纠结。
首先,把边分成两种:街道边和标记边;
然后,利用街道把街道截成不同的小街道,利用标记边判断小街道的方向;构造抽象的图、;
最后,在抽象的图上计算最短路,输出结果即可。
注意:库函数sort在<algorithm>。
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> using namespace std; //点结构 typedef struct pnode { int x,y; pnode( int a, int b ){x = a;y = b;} pnode(){} }point; point s,g,O,SAVE[205]; //线段结构 typedef struct snode { point s,t; snode( point a, point b ){s = a;t = b;} snode(){} }segment; segment S[205],L[205],A[205]; segment T[1205]; //两点间距离 double dist( point a, point b ) { return sqrt(0.0+(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } //点乘判断角度 int angle( point a, point b, point c, point d ) { return ((b.x-a.x)*(d.x-c.x)+(b.y-a.y)*(d.y-c.y)); } //点在线段上 bool p_on_s( point p, segment l ) { if ( (l.t.x-l.s.x)*(p.y-l.s.y)-(l.t.y-l.s.y)*(p.x-l.s.x) == 0 ) if ( (p.x-l.s.x)*(p.x-l.t.x) <= 0 && (p.y-l.s.y)*(p.y-l.t.y) <= 0 ) return true; return false; } int MAP[1005][1005];//存储坐标点的编号 point place[405];//储存对应编号点的坐标 //临接表 typedef struct enode { int point; double length; enode* next; }edge; edge *H[405],E[1205]; int E_count; //初始化 void initial( int n ) { for ( int i = 1 ; i <= n ; ++ i ) H[i] = NULL; E_count =0; } //添加边 void addedge( int a, int b, double d ) { E[E_count].point = b; E[E_count].length = d; E[E_count].next = H[a]; H[a] = &E[E_count ++]; } //临接表 end //spfa int stack[400]; int visit[400]; int father[400]; double path[400]; //递归,逆向输出路径上的点 void output( int s, int e ) { if ( s != e ) output( s, father[e] ); printf("%d %d\n",place[e].x,place[e].y); } //spfa计算最短路,存储父节点 void spfa( int s, int e, int n ) { for ( int i = 1 ; i <= n ; ++ i ) { visit[i] = false; path[i] = 1e20; } path[s] = 0; stack[0] = s; visit[s] = true; int top = 1; while ( top ) { int now = stack[-- top]; for ( edge* p = H[now] ; p ; p = p->next ) if ( path[p->point] > path[now] + p->length ) { path[p->point] = path[now] + p->length; father[p->point] = now; if ( !visit[p->point] ) { visit[p->point] = true; stack[top ++] = p->point; } } visit[now] = false; } if ( path[e] != 1e20 ) { output( s, e ); printf("0\n"); }else printf("-1\n"); } //spfa end //距离判断 bool cmp( point a, point b ) { return dist( a, O ) < dist( b, O ); } int main() { int n; while( ~scanf("%d",&n) && n ) { scanf("%d%d%d%d",&s.x,&s.y,&g.x,&g.y); for ( int i = 0 ; i < n ; ++ i ) scanf("%d%d%d%d",&S[i].s.x,&S[i].s.y,&S[i].t.x,&S[i].t.y); //计算结点的编号和坐对应标 memset( MAP, 0, sizeof(MAP) ); int number = 1; for ( int i = 0 ; i < n ; ++ i ) { if ( !MAP[S[i].s.x][S[i].s.y] ) { place[number] = S[i].s; MAP[S[i].s.x][S[i].s.y] = number ++; } if ( !MAP[S[i].t.x][S[i].t.y] ) { place[number] = S[i].t; MAP[S[i].t.x][S[i].t.y] = number ++; } } //把街道边和标记边分开 int L_count = 0,A_count = 0; for ( int i = 0 ; i < n ; ++ i ) { bool flag1 = false,flag2 = false; for ( int j = 0 ; j < n ; ++ j ) { if ( i == j ) continue; if ( p_on_s( S[i].s, S[j] ) ) flag1 = true; if ( p_on_s( S[i].t, S[j] ) ) flag2 = true; } if ( flag1 && flag2 ) L[L_count ++] = S[i]; else A[A_count ++] = S[i]; } //计算地图,把街道划分 int T_count = 0; for ( int i = 0 ; i < L_count ; ++ i ) { int count = 0; SAVE[count ++] = L[i].s; SAVE[count ++] = L[i].t; for ( int j = 0 ; j < L_count ; ++ j ) { if ( i == j ) continue; if ( p_on_s( L[j].s, L[i] ) ) SAVE[count ++] = L[j].s; if ( p_on_s( L[j].t, L[i] ) ) SAVE[count ++] = L[j].t; } //去掉重点 O = S[i].s; sort( SAVE, SAVE+count, cmp ); int Count = 0; for ( int j = 1 ; j < count ; ++ j ) if ( SAVE[Count].x != SAVE[j].x || SAVE[Count].y != SAVE[j].y ) SAVE[++ Count] = SAVE[j]; for ( int j = 0 ; j < Count ; ++ j ) T[T_count ++] = segment( SAVE[j], SAVE[j+1] ); } //构造抽象图,邻接矩阵 initial( number ); for ( int i = 0 ; i < T_count ; ++ i ) { bool l_flag = true,r_flag = true; int id1 = MAP[T[i].s.x][T[i].s.y]; int id2 = MAP[T[i].t.x][T[i].t.y]; double dis = dist( T[i].s, T[i].t ); for ( int j = 0 ; j < A_count ; ++ j ) { if ( p_on_s( A[j].s, T[i] ) ) { int ang = angle( T[i].s, T[i].t, A[j].s, A[j].t ); if ( ang >= 0 ) l_flag = false; if ( ang <= 0 ) r_flag = false; } if ( p_on_s( A[j].t, T[i] ) ) { int ang = angle( T[i].s, T[i].t, A[j].t, A[j].s ); if ( ang >= 0 ) l_flag = false; if ( ang <= 0 ) r_flag = false; } } //对应方向可以同行,添加对应的边 if ( l_flag ) addedge( id1, id2, dis ); if ( r_flag ) addedge( id2, id1, dis ); } //计算最短路 spfa( MAP[s.x][s.y], MAP[g.x][g.y], number ); } return 0; }