The Doors

本文介绍了一个寻找二维房间中最短路径的问题,房间内含有多个带有两个门的障碍墙。通过建立图模型并使用最短路径算法求解,文章详细阐述了解决方案的实现细节。

The Doors

题目来源

HIT1252
POJ1556
UVA393
UVAlive5310
ZOJ1721

题面

描述

你可以在一个包含障碍墙的房间中找到最短的一条路径。房间的边界位于x=0,x=10,y=0,y=10。路径的起始点和终点始终为(0,5)和(10,5)。房间中将有0到18面障碍墙,每一面墙有两个门。下图给出了这样一个房间并标出了房间中的最短路径。

输入

描述房间的输入数据将以以下的形式给出
2
4 2 7 8 9
7 3 4.5 6 7
第一行给出障碍墙的数量。接下来每一行包括五个实数。第一个数是墙的x坐标(0\<\x\<\10),剩下的四个数是门的两端的y坐标。保证x坐标按照增序给出,每一行中的y坐标也按照增序给出。输入文件将包括至少一组数据。数据以障碍墙数量为-1作为结束标志。

输出

对每个房间输出一行。行中应包含最短路径的长度,四舍五入到小数点后两位。行中无空格。

样例

输入

1
5 4 6 7 8
2
4 2 7 8 9
7 3 4.5 6 7
-1

输出

10.00
10.06

???

从第i面墙到第j面墙的最短路径,一定受限于第i到第j面墙中某一面墙的门的端点。
因此最终的最短路径,一定是从起点出发后,每次直线距离到达某面墙的门的端点。
以所有门的端点为顶点建图,将从端点x出发可以直线距离到达的所有顶点y连边,边权即两点的最短距离。
从起点到终点跑最短路。

代码

#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN=100;
const int MAXM=10000;
const double eps=1e-10;
int s,t;
int n,m;
bool vis[MAXN];
double val[MAXM],dist[MAXN];
double x[20],y[20][10];
int frst[MAXN],nxt[MAXM],ed[MAXM];
struct Point
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
int dcmp(double x)
{
    if (fabs(x)<eps)
        return 0;
    return x>0 ? 1:-1;
}
Vector operator - (Vector A,Vector B)
{
    return Vector(A.x-B.x,A.y-B.y);
}
double Cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}
bool intersect(Point A,Point B,Point P,Point Q)
{
    return dcmp(Cross(Q-P,A-P))*dcmp(Cross(Q-P,B-P))<0;
}
double dis(double a,double b,double c,double d)
{
    return sqrt((a-c)*(a-c)+(b-d)*(b-d));
}
void addedge(int a,int b,double s)
{
    nxt[++m]=frst[a];
    frst[a]=m;
    ed[m]=b;
    val[m]=s;
}
bool check(int i,double a,int j,double b)
{
    Point X=Point(x[i],a),Y=Point(x[j],b);
    for (int k=i+1;k<j;k++)
        for (int aa=0;aa<=2;aa++)
        {
            Point A=Point(x[k],y[k][aa*2]),B=Point(x[k],y[k][aa*2+1]);
            if (intersect(A,B,X,Y))
                return false;
        }
    return true;
}
int main()
{
    while (scanf("%d",&n)==1)
    {
        m=-1;
        memset(frst,-1,sizeof(frst));
        if (n==-1)
            return 0;
        for (int i=1;i<=n;i++)
        {
            scanf("%lf",&x[i]);
            y[i][0]=0;
            y[i][5]=10;
            for (int j=1;j<=4;j++)
                scanf("%lf",&y[i][j]);
        }
        s=1;
        t=n*4+5;
        x[0]=0;
        x[n+1]=10;
        if (check(0,5,n+1,5))
            addedge(s,t,10);
        for (int i=1;i<=n;i++)
            for (int j=1;j<=4;j++)
            {
                if (check(0,5,i,y[i][j]))
                    addedge(s,i*4+j,dis(0,5,x[i],y[i][j]));
                if (check(i,y[i][j],n+1,5))
                    addedge(i*4+j,t,dis(10,5,x[i],y[i][j]));
            }
        for (int i=1;i<=n;i++)
            for (int j=i+1;j<=n;j++)
                for (int k1=1;k1<=4;k1++)
                    for (int k2=1;k2<=4;k2++)
                        if (check(i,y[i][k1],j,y[j][k2]))
                            addedge(i*4+k1,j*4+k2,dis(x[i],y[i][k1],x[j],y[j][k2]));
        queue<int>que;
        que.push(s);
        for (int i=s;i<=t;i++)
            dist[i]=1000000;
        dist[s]=0;
        vis[s]=true;
        while (!que.empty())
        {
            int x=que.front();
            que.pop();
            vis[x]=false;
            for (int r=frst[x];r!=-1;r=nxt[r])
            {
                int y=ed[r];
                if (dist[x]+val[r]<dist[y])
                {
                    dist[y]=dist[x]+val[r];
                    if (!vis[y])
                    {
                        vis[y]=true;
                        que.push(y);
                    }
                }
            }
        }
        printf("%.2lf\n",dist[t]);
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值