POJ 1556(The Doors )计算几何(线段相交)+最短路

题意:
要求的是在一个空间内有许多墙,每个墙上有两扇门你要从最左边中点去最右边中点的最短路。
思路:
把每扇门看作是两个点然后利用计算几何的知识来判断一下两个点所形成的线段与图中的墙(也就是线段)有没有相交,规范相交与重合才算相交,对于不相交的两个点之间建立边,然后跑最短路就可以了。
细节请见代码注释。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const double eps = 1e-8;
const double pi = acos(-1.0);
const double inf = 1e20;
const int maxn = 1010;
int sgn(double x){
    if(fabs(x)<0)return 0;
    if(x<0)return -1;
    else return 1;
}
struct Point {
    double x,y;
    Point(){};
    Point(double _x,double _y){
        x=_x;y=_y;
    }
    Point operator - (const Point &b)const{
        return Point (x-b.x,y-b.y);
    }
    //叉乘
    double operator ^ (const Point &b)const{
        return x*b.y-y*b.x;
    }
    double operator * (const Point &b)const{
        return x*b.x+y*b.y;
    }
    double distance (Point b){
        return hypot(x-b.x,y-b.y);
    }
};
//两点距离
double dist(Point a,Point b)
{
    return sqrt((b-a)*(b-a));
}
struct Line{
    Point s,e;
    Line(){};
    Line(Point _s,Point _e){
        s=_s;e=_e;
    }
    Line(double x1,double y1,double x2,double y2){
        s.x=x1;s.y=y1;e.x=x2;e.y=y2;
    }
    //线段与线段相交判断
    //2 规范相交
    //1 非规范相交
    //0 不相交
    int seg_cross_seg(Line v){
        int d1 = sgn((e-s)^(v.s-s));
        int d2 = sgn((e-s)^(v.e-s));
        int d3 = sgn((v.e-v.s)^(s-v.s));
        int d4 = sgn((v.e-v.s)^(e-v.s));
        if((d1^d2)==-2&&(d3^d4)==-2) return 2;
        return (d1==0 &&sgn((v.s-s)*(v.s-e))<=0)||
               (d2==0 &&sgn((v.e-s)*(v.e-e))<=0)||
               (d3==0 &&sgn((s-v.s)*(s-v.e))<=0)||
               (d4==0 &&sgn((e-v.s)*(e-v.e))<=0);
    }
    int relation(Point p){
        int c=sgn((p-s)^(p-e));
        if(c<0)return 1;
        else if(c>0) return 2;
        else return 3;
    }
    bool parallel(Line v){
        return sgn((e-s)^(v.e-v.s))==0;
    }
    //直线与直线相交判断
    //0 平行
    //1 重合
    //2 相交
    int line_cross_line(Line v){
        if((*this).parallel(v))
        return v.relation(s)==3;
        return 2;
    }
};
struct edge{
    int ss,ee;
    double w;
    int next;
};
struct qnode{
    double dist;
    int num;
    bool operator < (const qnode &b)const{
        return dist>b.dist;
    }
};
edge e[maxn*maxn];
double dis[maxn];
int vis[maxn];
Point p[maxn];
Line s[maxn];
int head[maxn];
int cnt;
void add_edge(int ss,int ee,double ww){
    e[++cnt].ss=ss;e[cnt].ee=ee;e[cnt].w=ww;e[cnt].next=head[ss];head[ss]=cnt;
}
double dijkstra(int ss,int ee){
    priority_queue<qnode>q;
    qnode now,temp;
    dis[ss]=0;
    vis[ss]=1;
    now.dist=dis[ss];
    now.num=ss;
    q.push(now);
    while(!q.empty()){
        now=q.top();
        q.pop();
        vis[now.num]=0;
        for(int i=head[now.num];i!=-1;i=e[i].next){
            if(dis[e[i].ee]>now.dist+e[i].w){
                dis[e[i].ee]=now.dist+e[i].w;
                if(!vis[e[i].ee]){
                    temp.dist=dis[e[i].ee];
                    temp.num=e[i].ee;
                    q.push(temp);
                    vis[e[i].ee]=1;
                }
            }
        }
    }
    return dis[ee];
}
int main(){
    int n;
    while (~scanf("%d",&n)&&n!=-1){
        cnt=0;
        fill(dis,dis+maxn,inf);
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        int p_cnt=0,s_cnt=0;
        for(int i=0;i<n;i++){
            double x,y[4];
            scanf("%lf%lf%lf%lf%lf",&x,&y[0],&y[1],&y[2],&y[3]);
            for(int j=0;j<4;j++){
                p[p_cnt++]=Point(x,y[j]);
            }
            s[s_cnt++]=Line(x,0,x,y[0]);
            s[s_cnt++]=Line(x,y[1],x,y[2]);
            s[s_cnt++]=Line(x,y[3],x,10.0);
        }
        p[p_cnt++]=Point (10.0,5.0);
        p[p_cnt++]=Point (0.0,5.0);
        for(int i=0;i<p_cnt;i++){
            for(int j=0;j<p_cnt;j++){
                Line temp=Line(p[i],p[j]);
                int flag=0;
                for(int k=0;k<s_cnt;k++){
                    if(temp.seg_cross_seg(s[k])==2){// 规范相交
                        flag=1;
                        break;
                    }
                    if(temp.seg_cross_seg(s[k])==1){ //非规范相交要判断是否重合
                        if(temp.line_cross_line(s[k])==1){//如成立 说明重合
                            flag=1;
                            break;
                        }
                    }
                }
                //cout<<i<<" "<<j<<" "<<flag<<" "<<g<<endl;
                if(!flag){
                    add_edge(i,j,dist(p[i],p[j]));
                    //cout<<e[cnt].ss<<" "<<e[cnt].ee<<" "<<e[cnt].w<<endl;
                    add_edge(j,i,dist(p[i],p[j]));
                    //cout<<e[cnt].ss<<" "<<e[cnt].ee<<" "<<e[cnt].w<<endl;
                }
            }
        }
        double ans = dijkstra(p_cnt-2,p_cnt-1);
        printf("%.2f\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值