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

The Doors

Description

You are to find the length of the shortest path through a chamber containing obstructing walls. The chamber will always have sides at x = 0, x = 10, y = 0, and y = 10. The initial and final points of the path are always (0, 5) and (10, 5). There will also be from 0 to 18 vertical walls inside the chamber, each with two doorways. The figure below illustrates such a chamber and also shows the path of minimal length.

Input

The input data for the illustrated chamber would appear as follows.

2
4 2 7 8 9
7 3 4.5 6 7

The first line contains the number of interior walls. Then there is a line for each such wall, containing five real numbers. The first number is the x coordinate of the wall (0 < x < 10), and the remaining four are the y coordinates of the ends of the doorways in that wall. The x coordinates of the walls are in increasing order, and within each line the y coordinates are in increasing order. The input file will contain at least one such set of data. The end of the data comes when the number of walls is -1.

Output

The output should contain one line of output for each chamber. The line should contain the minimal path length rounded to two decimal places past the decimal point, and always showing the two decimal places past the decimal point. The line should contain no blanks.

Sample Input

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

Sample Output

10.00
10.06

【思路分析】

   这个题目有意思之处在于将计算几何和图论的问题巧妙地结合起来。题意大概是在一个图中给你4 * n + 1个点,其中有一个起点,一个终点以及每一列的四个点,每一列的四个点又构成了三个线段,即隔墙,要求起点到终点的最短路径(当然该路径不能“穿墙”)。该题最关键的就是建图,通过判断两点之间构成的路径是否和隔墙相交来在邻接矩阵中键入权值(即两点之间的路径),要注意的是这里的线段相交要忽视两线段有一个公共端点的情况,即考虑“擦墙而过”,所以两线段不相交时在邻接矩阵中相应位置键入两点之间的距离,若两线段相交则表示此路不通,键入一个无穷大值。当建图好之后就是找最短路了。


代码如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define eps 1e-8
#define inf 1e20
#define maxn 150
double maps[maxn][maxn];
double dis[maxn],visited[maxn];
int n;
struct Point
{
    double x,y;
    Point()
    {

    }
    Point(double x1,double y1)
    {
        x = x1;
        y = y1;
    }
    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;
    }
}points[maxn];
struct Line
{
    Point s,e;
    Line()
    {

    }
    Line(Point s1,Point e1)
    {
        s = s1;
        e = e1;
    }
}lines[maxn];
int sgn(double x)
{
    if(fabs(x) < eps)
        return 0;
    if(x < 0)
        return -1;
    return 1;
}
double distances(Point p1,Point p2)
{
    return sqrt((p2 - p1) * (p2 - p1));
}
bool intersection(Line l1,Line l2)
{
    return
    max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) &&
    max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) &&
    max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) &&
    max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) &&
    sgn((l2.s - l1.e) ^ (l1.s - l1.e)) * sgn((l2.e - l1.e) ^ (l1.s - l1.e)) < 0 &&
    sgn((l1.s - l2.e) ^ (l2.s - l2.e)) * sgn((l1.e - l2.e) ^ (l2.s - l2.e)) < 0;
    //注意此处是小于0,即忽视两个线段有一个公共端点的相交情况!
}
void init()
{
    points[0] = Point(0,5);
    for(int i = 1;i <= n;i++)
    {
        double x;
        scanf("%lf %lf %lf %lf %lf",&x,&points[i].y,&points[i + n].y,&points[i + n * 2].y,&points[i + n * 3].y);
        points[i].x = points[i + n].x = points[i + n * 2].x = points[i + n * 3].x = x;
        lines[i] = Line(Point(x,0),points[i]);
        lines[i + n] = Line(points[i + n],points[i + n * 2]);
        lines[i + n * 2] = Line(points[i + n * 3],Point(x,10));
    }
    points[n * 4 + 1] = Point(10,5);
    bool flag = true;
    for(int i = 0;i <= n * 4 + 1;i++)//起点、终点加上每一列的4个点
    {
        for(int j = 0;j <= n * 4 + 1;j++)
        {
            flag = true;
            for(int k = 1;k <= 3 * n;k++)//每列三条线段
            {
                if(intersection(Line(points[i],points[j]),lines[k]))
                {
                    flag = false;
                    break;
                }
            }
            if(flag == true)//两线段不相交
            {
                maps[i][j] = distances(points[i],points[j]);
            }
            else
            {
                maps[i][j] = inf;
            }

        }
    }
}
double dijkstra(int s,int t)
{
    for(int i = 0;i <= n * 4 + 1;i++)
    {
        dis[i] = maps[s][i];
        visited[i] = 0;
    }
    dis[s] = 0;
    visited[s] = 1;
    int temp,k = s;
    for(int i = 1;i <= n * 4 + 1;i++)
    {
        temp = inf;
        for(int j = 0;j <= n * 4 + 1;j++)
        {
            if(visited[j] == 0 && temp > dis[j])
            {
                k = j;
                temp = dis[j];
            }
        }
        if(temp == inf)
            break;
        visited[k] = 1;
        for(int j = 0;j <= n * 4 + 1;j++)
        {
            if(dis[j] > dis[k] + maps[k][j])
                dis[j] = dis[k] + maps[k][j];
        }
    }
    return dis[t];
}
int main()
{
    while(scanf("%d",&n) != EOF && n != -1)
    {
        init();
        printf("%.2lf\n",dijkstra(0,n * 4 + 1));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值