poj 3521 Geometric Map

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

你可能感兴趣的:(poj 3521 Geometric Map)