POJ 1556 The Doors(判断线段相交+最短路)

题目链接


题目大意

在这里插入图片描述
给出如上图所示的封闭区域,要求从坐标 ( 0 , 5 ) (0,5) (0,5)走到坐标 ( 10 , 5 ) (10,5) (10,5),但是中间可能会有墙,现在要求两点间的最短路径。

解题思路

一个不难证明的结论是:始终走的是墙而不会沿着墙绕路

刚开始我的想法是先判断每道竖直的墙是否挡住了 y = 5 y=5 y=5这条线,如果挡住就先走到墙的最近的边缘,依次贪心选择每道竖直墙之间的最短距离直到走到终点,但是我忘记了如果能走下面红线时,我的贪心策略就不对了。
在这里插入图片描述
显然是最短路的问题嘛,但是本题是隐式图,需要转换成普通的节点式的图。因为输入的数据量不大,那么我们直接枚举任意两个节点,使用Floyd算法即可。需要注意的是起点和终点也要加入。而且这里判断是否相交是非严格的相交,即可以和端点相交,也需要注意

代码:

#include <iostream>
#include <math.h>
#include <cstring>
using namespace std;
#define Vector Point
#define Seg Line
const double INF=1e300;
const double eps=1e-8;

int dcmp(double d){
    if(fabs(d)<eps) return 0;
    if(d<0) return -1;
    return 1;
}

struct Point{
    double x,y;
    Point(double a=0,double b=0){
        x=a,y=b;
    }
    Point operator + (Point p){ return Point(x+p.x,y+p.y); }
    Point operator - (Point p){ return Point(x-p.x,y-p.y); }
    Point operator * (double d){ return Point(x*d,y*d); }
    double operator * (Point p){ return x*p.x+y*p.y; }
    Point operator / (double d){ return Point(x/d,y/d); }
    double operator ^ (Point p){ return x*p.y-y*p.x; }
    bool operator == (const Point &p) const { return x==p.x&&y==p.y; }
    void print(){ printf("(%.2lf,%.2lf)\n",x,y); }
};

struct Line{
    Point p,q;
    Vector v;
    Line(){}
    Line(Point a,Point b){
        p=a,q=b,v=b-a;
    }
    Point point(double t){
        return p+v*t;
    }
};

double dis(Point a,Point b){
    return sqrt((b-a)*(b-a));
}

bool isSegInter(Seg a,Seg b){
    double c1=a.v^b.p-a.p,c2=a.v^b.q-a.p;
    double c3=b.v^a.p-b.p,c4=b.v^a.q-b.p;
    return (dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0);  //必须是<
}


bool vis[105][105];   //判断两点之间是否能直接相连
Point P[105];        //保存所有点
double G[105][105];  //存两点间的距离,初始根据vis数组初始化为两点间距离
Line L[50];         //保存所有线段

int main()
{
    int n;
    double x,a,b,c,d;
    while(~scanf("%d",&n)){
        if(n==-1) break;
        memset(vis,0,sizeof vis);
        for(int i=1,j=1;i<=4*n;i+=4,j+=3){ //n个竖直墙有4*n个点,3*n个线段
            scanf("%lf",&x);
            scanf("%lf %lf %lf %lf",&a,&b,&c,&d);
            P[i]=Point(x,a),P[i+1]=Point(x,b);
            P[i+2]=Point(x,c),P[i+3]=Point(x,d);
            L[j]=Line(Point(x,0),Point(x,a));
            L[j+1]=Line(Point(x,b),Point(x,c));
            L[j+2]=Line(Point(x,d),Point(x,10));
        }
        P[0]=Point(0,5),P[4*n+1]=Point(10,5);  //起点终点
        //首先一次二维循环判断两点之间是否能直接用距离公式求距离
        for(int i=0;i<=4*n+1;i++){
            for(int j=0;j<=4*n+1;j++){
                int flag=1;
                if(i==j){ G[i][j]=0; continue; } //这里初始化到本身距离为0
                for(int k=1;k<=3*n;k++){
                    if(isSegInter(Line(P[i],P[j]),L[k])){
                        flag=0;
                        break;
                    }
                }
                if(flag) vis[i][j]=1;
            }
        }
        //对于能相连的点调用距离公式,否则为INF
        for(int i=0;i<=4*n+1;i++){
            for(int j=0;j<=4*n+1;j++){
                if(vis[i][j]) G[i][j]=dis(P[i],P[j]);
                else G[i][j]=INF;
                //printf("%.1lf ",G[i][j]);
                //printf("%d",vis[i][j]);
            }
            //printf("\n");
        }
        //Floyd主体
         for(int k=0;k<=4*n+1;k++){
            for(int i=0;i<=4*n+1;i++){
                for(int j=0;j<=4*n+1;j++){
                    if(G[i][k]+G[k][j]<G[i][j])
                        G[i][j]=G[i][k]+G[k][j];
                }
            }
         }
        printf("%.2lf\n",G[0][4*n+1]);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值