Timus Online Judge 1966 Cycling Roads

再一次对timus感到无力,这道题目是放在Graph Theory Problems里,却感觉和Graph Theory没有毛关系。明明是个计算几何嘛。

题目大意:Vova在深圳骑自行车逛公园,公园里有N处景点,给出N个景点的坐标,然后再给出M条为线段的边。若两条线段有公共点,那么这两条线段的四个端点中的任意一个都能到达其余三个。问Vova是否能从一个点出发,到达其余所有的景点。N和M的最大值都是200,每个景点的坐标的x和y的绝对值是小于等于30000的。

当看到这道题目的时候,也许会有很多人会想到用某种生成树的算法来解决,确实是这样的,假如所有点都在一棵生成树上,那么答案就是“YES”。所以,我们可以直接用并查集来维护,当所有的点都在一个集合内的时候我们就输出“YES”,否则答案就“NO”。我们可以将每条边的两个端点所在的集合先给合并,然后判断两条线段是否相交,若相交,就把两条线段所在的集合合并成一个集合。还有就是得考虑出现单点的情况,若某一个点在某一条线段上,我们就要合并这个点所在集合和这条线段所在的集合。最后判断一下总共有多少个集合就可以了。

其中, 判断线段相交用叉积,我想这个没什么好说的吧,需要注意的是坐标范围是-30000~30000,那么计算叉积的时候中间爆long long的情况,注意处理一下就可以了,反正我是在这里WA两次。

这段代码我调试了很久,原因是把main写成了mian,编译一直不能通过,报了一个看不懂的error,感到莫名其妙的,还以为是自己的编译器哪里文件被删掉了,还找别人帮我编译,实在是羞愧,还好吃饭的时候在手机上看代码看到了这个“mian”!!!

Show me the code!

#include <iostream>
#include <vector>
#define ll long long
#define pll pair<ll, ll>
#define pii pair<int, int>
#define x first
#define y second
using namespace std;
vector<pii> vec(201), edge;
vector<int> fa(201);
ll turn(ll a) {//因为叉积的结果只需要正负,所以,为了避免报long long,这里需要转换一下
    if (a == 0) return 0;
    return a > 0 ? 1 : -1;
}
ll CrossProduct(pll a, pll b, pll c) {//判断点C在线段ab的左边还是右边
    pll ab(b.x - a.x, b.y - a.y), ac(c.x - a.x, c.y - a.y);
    return turn(ab.x * ac.y - ab.y * ac.x);//abc三点共线是结果为0,c在ab左边的时候结果小于0,否则大于0
}
bool is_intersection(pll a, pll b, pll c, pll d) {//判断线段ab和cd是否有公共点
    ll res_ab = CrossProduct(a, b, c) * CrossProduct(a, b, d);
    ll res_cd = CrossProduct(c, d, a) * CrossProduct(c, d, b);
    return res_ab < 0 && res_cd < 0;
}
int find(int x) {
    return fa[x] = fa[x] == x ? x : find(fa[x]);
}
void unite(int x, int y) {
    int fx = find(x), fy = find(y);
    fa[fy] = fx;
}
bool solve(int N) {
    for (int i = 1; i <= N; ++i) fa[i] = i;
    for (int i = 0; i < edge.size(); ++i) {
        unite(edge[i].x, edge[i].y);//合并同一线段的两个端点所在的集合
        for (int j = i + 1; j < edge.size(); ++j) {
            unite(edge[j].x, edge[j].y);//合并同一线段的两个端点所在的集合
            if (is_intersection(vec[edge[i].x], vec[edge[i].y],
                vec[edge[j].x], vec[edge[j].y])) {//若两条线段有公共点,那么就合并两条线段所在的集合
                unite(edge[i].x, edge[j].x);
            }
        }
    }
    for (int i = 0; i < edge.size(); ++i) {
        for (int j = 1; j <= N; ++j) {
            pll a = vec[edge[i].x], b = vec[edge[i].y], c = vec[j];
            int minx = min(a.x, b.x), maxx = max(a.x, b.x),
                miny = min(a.y, b.y), maxy = max(a.y, b.y);
            if (CrossProduct(a, b, c) == 0 &&
                minx <= c.x && c.x <= maxx &&
                miny <= c.y && c.y <= maxy) {//判断单点是否在这条线段上,若是,则合并单点所在的结合和这条线段所在集合
                unite(edge[i].x, j);
            }
        }
    }
    int f1 = find(1);
    for (int i = 2; i <= N; ++i) {//计算集合的个数
        if (find(i) != f1) return false;
    }
    return true;
}
int main() {
    int N, M;
    cin >> N >> M;
    for (int i = 1; i <= N; ++i) cin >> vec[i].x >> vec[i].y;
    for (int i = 0; i < M; ++i) {
        int u, v;
        cin >> u >> v;
        edge.push_back(pll(u, v));
    }
    cout << (solve(N) ? "YES" : "NO") << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值