题目:给你平面上的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;
}