POJ The Doors (构图+最短路)

  • The Doors
  • 思路
    图1
    这个题最难的就是建图了,建图之后求个最短路也就出来了,说下我的建图方法,首先我用pos[ ] 数组将起点和终点以及每个墙线段的端点作为图的顶点(去除墙根的两个点,因为没有意义)。然后用 seg[][]数组表示每个墙的各个线段的信息,seg[i][] 表示第i个墙的信息,这个数组的目的主要是为了:遍历pos[]数组的每两个点判断是否能建边,如果这两个点形成的线段被之间的墙挡住了(与该墙中某一个线段相交),就无法建边,否则建边。
    这里我再说一下判断线段a-b和c-d是否相交如何判断?

    如果两条线段相交,有 c a ⃗ × c b ⃗ \vec{ca}\times\vec{cb} ca ×cb d a ⃗ × d b ⃗ \vec{da}\times\vec{db} da ×db 异号并且 d a ⃗ × c a ⃗ \vec{da}\times\vec{ca} da ×ca d b ⃗ × c b ⃗ \vec{db}\times\vec{cb} db ×cb 也是异号,这两个条件同时满足才是相交,我只写了满足一个条件就可以了,但这并不是判断线段相交的充分条件,这是因为本题题意数据给出的特殊,希望大家知道就好。
  • 代码
#pragma GCC optimize(2)
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring> 
 
using namespace std;

typedef long long ll;
typedef unsigned long ul;
typedef unsigned long long ull;
#define pi acos(-1.0)
#define e exp(1.0)
#define pb push_back
#define mk make_pair
#define fir first
#define sec second
#define scf scanf
#define prf printf
typedef pair<double,int> pa;
const double INF=0x3f3f3f3f;
const double eps=1e-8;
const int MAX_I=1000;//墙的数量 
const int MAX_N=MAX_I*7;//点的数量 
const int MAX_M=MAX_N<<1;//边的数量 
int head[MAX_N],e_cnt,N,cnt=0;; 
double d[MAX_N];
struct node{
    
    
	double x,y; 
}seg[MAX_I][6];//记录线段 
struct node_p{
    
    
	double x,y;
	int id;//对应一个顶点编号,便于后面建图 
	int bel;//属于哪一个墙 
}pos[MAX_N];//记录点的信息 
double dis_(node_p a,node_p b){
    
    
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
struct Edge{
    
    
	int to,next;
	double val;
}edge[MAX_M];
void add_edge(int u,int v,double dis){
    
    
	edge[e_cnt].to=v;
	edge[e_cnt].val=dis;
	edge[e_cnt].next=head[u];
	head[u]=e_cnt++;
	return ;
}
double fuck(node_p a,node c,node_p b){
    
    //计算叉乘 
	double x1,y1,x2,y2;
	x1=a.x-c.x;
	y1=a.y-c.y;
	x2=b.x-c.x;
	y2=b.y-c.y;
	return x1*y2-x2*y1;
}
bool is_inter(node_p a,node_p b,node c,node d){
    
    //叉乘判断线段a-b和c-d是否相交,如果相交,返回1 
	if(fuck(a,c,b)*fuck(a,d,b)<eps)//相乘结果小于0,说明是相交的 
	return 1;
	return 0;
}
//求最短路 
priority_queue<pa,vector<pa>,greater<pa> >Q;
void Dijkstra(int s){
    
    
	int i,j,k;
	for(i=0;i<cnt;i++)
	d[i]=INF;
	d[s]=0;
	while(!Q.empty())
	Q.pop();
	Q.push({
    
    0,s});
	while(!Q.empty()){
    
    
		pa jie=Q.top();
		Q.pop();
		int v=jie.sec;
		if(d[v]<jie.fir)
		continue;
		for(i=head[v];~i;i=edge[i].next){
    
    
			if(d[edge[i].to]>d[v]+edge[i].val){
    
    
				d[edge[i].to]=d[v]+edge[i].val;
				Q.push({
    
    d[edge[i].to],edge[i].to});
			}
		}
	}
	return ;
}
int main()
{
    
    
//  freopen(".../.txt","w",stdout);
//  freopen(".../.txt","r",stdin);
//	ios::sync_with_stdio(false);
	while(~scf("%d",&N)&&N!=-1){
    
    
		int i,j,k,g;
		double x,y;
		cnt=0;
		//将起点存入 
		pos[cnt].x=0;
		pos[cnt].y=5;
		pos[cnt].bel=0;
		pos[cnt].id=cnt++;
		for(i=1;i<=N;i++){
    
    
			scf("%lf",&x);
			seg[i][0].x=x;
			seg[i][0].y=0;
			for(j=1;j<=4;j++){
    
    
				scf("%lf",&y);
				seg[i][j].x=x;
				seg[i][j].y=y;
				pos[cnt].x=x;
				pos[cnt].y=y;
				pos[cnt].bel=i;
				pos[cnt].id=cnt++;
			}
			seg[i][5].x=x;
			seg[i][5].y=10;
		}
		//将终点存入 
		pos[cnt].x=10;
		pos[cnt].y=5;
		pos[cnt].bel=N+1;
		pos[cnt].id=cnt++;
		//建图
		memset(head,-1,sizeof(head));
		e_cnt=0;
		for(i=0;i<cnt-1;i++){
    
    
			for(j=i+1;j<cnt;j++){
    
    
				if(pos[i].bel==pos[j].bel)//在同一个墙上的两个点
				continue;
				double dis=dis_(pos[i],pos[j]);
				if(pos[j].bel-pos[i].bel==1){
    
    //说明是相邻的墙,可以直接连成一条边 
					add_edge(pos[i].id,pos[j].id,dis);
					add_edge(pos[j].id,pos[i].id,dis);
					continue;
				}
				//判断这两个点有没有被线段隔开 
				bool flag=0;
				for(k=pos[i].bel+1;k<pos[j].bel;k++)//看看中间的墙能不能挡住他们
					for(g=0;g<=4;g+=2){
    
    // 每个墙的三条线段
						if(is_inter(pos[i],pos[j],seg[k][g],seg[k][g+1])){
    
    
							flag=1;
							break;
						}
					}
					if(flag)
					break;
				}
				if(!flag){
    
    
					add_edge(pos[i].id,pos[j].id,dis);
					add_edge(pos[j].id,pos[i].id,dis);				
				}
			}
		}
		//求最短路
		Dijkstra(0);
		prf("%.2f\n",d[cnt-1]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43311695/article/details/108843442