POJ 1556 The Doors(线段相交)

本文介绍了一次接触计算几何的经历,通过解决一个关于寻找房间内最短路径的编程问题,从无到有地构建了解题思路。重点讨论了如何在存在障碍墙的情况下,利用最短路模型计算路径长度,特别提到了对线段相交问题的理解和解决方法。
摘要由CSDN通过智能技术生成

最近刚刚开始接触计算几何。一口气还是刷了些题,都没来得及写blog。这十多题大部分都是巨水的题目,也算是入门了。

这道题是第一道,没有使用任何模板,写法也显得十分菜鸟,后面几题的思路和代码风格就渐渐成形了。先说题目


题目大意:

有一个包含障碍墙的房间,你要去寻到一条穿过房间的最短路径。这个房间始终位于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


解题思路:
这道题只要能建立一个最短路模型,就能算出答案。建立最短路时需要判断线段相交的问题。当时对线段相交的问题脑袋里面一团浆糊,也不知道什么是规范相交和非规范相交。如果有人做这道题的时候有同样的问题,那就去看看黑书吧,上面对线段相交花了好几页来讲,看完之后豁然开朗。

#include <cstdio>
#include <cstring>
#include <cmath>

struct point {
    double x, y;
};

struct line{
    point a, b;
};

int n;
line l[300];
point p[600];
int nump, numl;
double y[40];

double dis[1000][1000];

int cmp( double a){
    if(fabs(a) < 10e-8) return 0;
    if(a > 0) return 1;
    else return -1;
}
double min(double a, double b){
    if( cmp(a - b) <= 0) return a;
    return b;
}

double max(double a, double b){
    if( cmp(a - b) >= 0) return a;
    return b;
}

point operator + (const point &a, const point &b){
    point p;
    p.x = a.x + b.x;
    p.y = a.y + b.y;
    return p;
}

point operator - (const point &a, const point &b){
    point p;
    p.x = a.x - b.x;
    p.y = a.y - b.y;
    return p;
}

bool operator == (const point &a, const point &b){
    return cmp(a.x - b.x) == 0 && cmp(a.y - b.y) == 0;
}

point makePoint(double a, double b){
    point p;
    p.x = a;
    p.y = b;
    return p;
}

line makeLine(point a, point b){
    line l;
    l.a = a;
    l.b = b;
    return l;
}

line makeLine(double x1, double y1, double x2, double y2){
    point a, b;
    line l;
    a.x = x1;
    a.y = y1;
    b.x = x2;
    b.y = y2;
    l.a = a;
    l.b = b;
    return l;
}

double dist(point a, point b){
    return sqrt( (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}


double det( point a, point b){
    return (a.x * b.y - a.y * b.x);
}

int OK(int i, int j){
    point a = p[i], b = p[j];
    if(p[i] == p[j]) return 1;
    for(int i = 0; i < numl; i++){
            point c = l[i].a, d = l[i].b;
            if(min(a.x, b.x) <= max(c.x, d.x) && min(c.x, d.x) <= max(a.x, b.x)
                && min(a.y, b.y) <= max(c.y, d.y) && min(c.y, d.y) <= max(a.y, b.y)
                && det(c - a, b - a) * det(b - a, d - a) > 0
                && det(a - c, d - c) * det(d - c, b - c) > 0)  return 0;
    }
    return 1;
}


void init(){
    memset(p, 0, sizeof(p));
    memset(l, 0, sizeof(l));
    for(int i = 0; i < 90; i++){
        for(int j = 0; j < 90; j++) dis[i][j]=123456789.0;
    }
    numl = 0;
    nump = 0;
}

double x;

int main(){
    while (scanf("%d", &n) && n != -1){
        init();
        p[nump++] = makePoint(0.0, 5.0);
        p[nump++] = makePoint(10.0, 5.0);
        for(int i = 0; i < n; i++){
            scanf("%lf%lf%lf%lf%lf", &x ,&y[0], &y[1], &y[2], &y[3]);
            for(int j = 0; j < 4; j++) p[nump++] = makePoint(x, y[j]);
            l[numl++] = makeLine( x, 0.0, x, y[0]);
            l[numl++] = makeLine( x, y[1], x, y[2]);
            l[numl++] = makeLine( x, y[3], x, 10.0);
        }

        for(int i = 0;i < nump; i++){
            for(int j = i + 1; j < nump; j++) if (OK(i, j)){
               // printf("%lf %lf %lf %lf\n",p[i].x , p[i].y, p[j].x, p[j].y);
                dis[i][j] = dis[j][i] = dist(p[i], p[j]);
            }
        }


        for(int k = 0; k < nump ;k++){
            for(int i = 0; i < nump; i++){
                for(int j = 0; j < nump; j++)if( i != j && i != k && j != k){
                    dis[i][j]=min(dis[i][j], dis[i][k] + dis[k][j]);
                }
            }
        }
        printf("%.2f\n", dis[0][1]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值