POJ 1556 The Doors (线段与线段相交+dijkstra)

题意:给出一个(0,0)(0,10)(10,0)(10,10)的正方形房子,里面有n个墙,每堵墙上有两扇门,求从(0,5)到(10,5)的最短距离。
在这里插入图片描述

题解:线段与线段相交+dijkstra
先将所有点存起来,再将墙(线段)存起来。

暴力枚举两点,看是否与已存线段相交,若不相交,相连,然后跑最短路。

注意数据范围。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#define ll long long
using namespace std;
const double eps = 1e-8;
const double inf = 1e20;
const double pi = acos(-1.0);
const int maxp = 1010;
//Compares a double to zero
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    else return 1;
}

//POINT
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 p) {
        return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
    }
};

//LINE
struct Line {
    Point s, e;
    Line() {}
    Line(Point _s, Point _e) {
        s = _s;
        e = _e;
    }
    //两线段相交判断
    //2 规范相交
    //1 非规范相交
    //0 不相交
    int segcrossseg(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);
    }
};
//dijkstra
const int manx = 1e2 + 5; //与n相对,对应顶点的个数
const int mamx = 5e6 + 5; //与m相对,对应边的个数
priority_queue< pair<double, int> >q;
struct node {
    int next, v;
    double w;
}edge[mamx];  //边去mamx,其余取manx
bool vis[manx];  //这里的标记数组与spfa的vis数组含义不同,这里标记是否入过队列
int head[manx];
double d[manx];
int k = 0;
int nn, s, e; //s作为起点,e作为终点
void add(int u, int v, double w) { //链式前向星存图
    edge[++k].next = head[u];
    edge[k].v = v;
    edge[k].w = w;
    head[u] = k;
}
void dijkstra() {
    for (int i = 1; i <= 100; i++) {
        d[i] = 0x3f3f3f3f;
    }
    memset(vis, false, sizeof(vis));
    d[s] = 0; //s作为起点
    q.push(make_pair(0, s));
    while (q.size()) {
        int x = q.top().second; //取出队头
        q.pop();
        if (vis[x]) continue; //如果点x访问过,跳过,访问下一个队头
        vis[x] = 1; //访问x做标记
        for (int i = head[x]; i; i = edge[i].next) {
            int v = edge[i].v;
            double w = edge[i].w;
            if (d[v] > d[x] + w) { //松弛操作,更新距离
                d[v] = d[x] + w;
                q.push(make_pair(-d[v], v)); //把更新的距离和点入队,这里距离取负变成小根堆
            }
        }
    }
}
int n;
double x, ya, yb, yc, yd;
Point p[100];
Line l[100];
int main() {
    while (~scanf("%d", &n) && n != -1) {
        k = 0;
        memset(head, 0, sizeof(head));
        int pcnt = 0, lcnt = 0;
        p[++pcnt] = Point(0, 5);
        p[++pcnt] = Point(10, 5);
        for (int i = 1; i <= n; i++) {
            scanf("%lf%lf%lf%lf%lf", &x, &ya, &yb, &yc, &yd);
            p[++pcnt] = Point(x, ya);
            p[++pcnt] = Point(x, yb);
            p[++pcnt] = Point(x, yc);
            p[++pcnt] = Point(x, yd);
            l[++lcnt] = Line(Point(x, 0), p[pcnt - 3]);
            l[++lcnt] = Line(p[pcnt - 2], p[pcnt - 1]);
            l[++lcnt] = Line(p[pcnt], Point(x, 10));
        }
        for (int i = 1; i <= pcnt; i++) {
            for (int j = i + 1; j <= pcnt; j++) {
                Line temp = Line(p[i], p[j]);
                int flag = 1;
                for (int k = 1; k <= lcnt; k++) {
                    int re = temp.segcrossseg(l[k]);
                    if (re == 2) {
                        flag = 0;
                        break;
                    }
                }
                if (flag) {
                    add(i, j, p[i].distance(p[j]));
                    add(j, i, p[i].distance(p[j]));
                }
            }
        }
        s = 1;
        dijkstra();
        printf("%.2f\n", d[2]);
    }
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值