Cycling Roads URAL 1966 线段相交 + 并查集

题目:https://vjudge.net/problem/URAL-1966

题意:给出n个点的坐标,并标号为1~n,给出m条线段,线段端点即为标号为1~n的点。判断是否所有点连通,两线段只要有公共点即互相连通。

思路:用叉积判断线段相交,把所有直接连通的点所在集合合并,顺便统计集合数量,最终只剩一个集合就连通。
注意有种情况,把我坑的不轻。数据里有孤立点位于一条线段上的情况,我本以为只要把各种线段相交情况考虑到就行,但还需要单独扫一遍点在线段上的情况。

训练赛的时候用高中解析几何的知识做了两道几何题,另外一道AC了,这道考虑了各种情况也没AC,赛后想想不能一直用高中知识做几何,算坐标考虑斜率不存在的情况就能难受死,于是对着刘汝佳的算法竞赛入门经典训练指南的第四章几何部分学了一下二维几何基础,重新写了一遍然而依旧一直WA,最后发现卡在之前说的坑上。不过好歹几何稍微入了个门。

代码:c++

#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cstdlib>
using namespace std;

const double esp = 1e-10;
const int maxn = 300;

int dcmp(double a)
{
    if (fabs(a) < esp)
        return 0;
    else
        return a < 0 ? -1 : 1;
}

struct Point
{
    int id;
    double x, y;
    Point(double xx = 0, double yy = 0, int idx = 0) : x(xx), y(yy), id(idx) {}
    bool operator == (Point p)
    {
        return dcmp(x - p.x) == 0 && dcmp(y - p.y) == 0;
    }
};

typedef Point Vector;
Point points[maxn];

Vector operator - (Point A, Point B)
{
    return Vector(A.x - B.x, A.y - B.y);
}

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;
}

double Dot(Vector A, Vector B)
{
    return A.x * B.x + A.y * B.y;
}

struct Line
{
    Point v1, v2;
    Line(Point p1 = Point(), Point p2 = Point()) : v1(p1), v2(p2) {}
};

Line lines[maxn];

//判断点在线段上
bool OnLine(Point p, Line l)
{
    return dcmp(Cross(l.v1 - p, l.v2 - p)) == 0 && dcmp(Dot(l.v1 - p, l.v2 - p)) <= 0;
}

//判断两线段相交
bool LineIntersection(Line l1, Line l2)
{
    if (OnLine(l1.v1, l2) || OnLine(l1.v2, l2) || OnLine(l2.v1, l1) || OnLine(l2.v2, l1))
    {
        return true;
    }
    double c1 = Cross(l1.v1 - l2.v1, l1.v1 - l1.v2);
    double c2 = Cross(l1.v1 - l2.v2, l1.v1 - l1.v2);
    double c3 = Cross(l2.v1 - l1.v1, l2.v1 - l2.v2);
    double c4 = Cross(l2.v1 - l1.v2, l2.v1 - l2.v2);
    return dcmp(c1) * dcmp(c2) < 0 && dcmp(c3) * dcmp(c4) < 0;
}

int fa[maxn];
int n, m;
int setamount;

//并查集的各种函数
void init()
{
    setamount = n;
    for (int i = 1; i <= n; i++)
    {
        fa[i] = i;
    }
}

int setfind(int u)
{
    return fa[u] == u ? u : fa[u] = setfind(fa[u]);
}

bool check(int a, int b)
{
    return setfind(a) == setfind(b);
}

void setunion(int a, int b)
{
    int p1 = setfind(a);
    int p2 = setfind(b);
    if (p1 != p2)
    {
        fa[p1] = p2;
        setamount--;
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    init();
    for (int i = 1; i <= n; i++)
    {
        double x, y;
        scanf("%lf%lf", &x, &y);
        points[i] = Point(x, y, i);
    }
    for (int i = 1; i <= m; i++)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        lines[i] = Line(points[a], points[b]);
        if (!check(a, b))
        {
            setunion(a, b);
        }
        for (int j = 1; j <= n; j++)
        {
            if (OnLine(points[j], lines[i]))
            {
                setunion(j, a);
            }
        }
        for (int j = 1; j < i; j++)
        {
            if (LineIntersection(lines[i], lines[j]))
            {
                setunion(lines[i].v1.id, lines[j].v1.id);
                setunion(lines[i].v2.id, lines[j].v1.id);
                setunion(lines[i].v1.id, lines[j].v2.id);
                setunion(lines[i].v2.id, lines[j].v2.id);
            }
        }
    }
    printf("%s", setamount == 1 ? "YES" : "NO");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值