计算几何 点和线

题目描述

有一个nnn个点mmm条边的图画在了平面上,你想知道有多少对边之间对应的线段相交。

特别地,对于图中的一对边,如果有公共点且只在对应的端点相交,那么我们不认为这对边相交。

输入描述

第一行两个整数n,m(1≤n≤1000,1≤m≤2000)n, m(1\leq n\leq 1000, 1\leq m\leq 2000)n,m(1n1000,1m2000),表示点数和边数。

接下来mmm行,每行两个整数(u,v)(u,v)(u,v)表示一条uuu与vvv之间的无向边,保证图中没有重边和自环。

接下来nnn行,每行两个整数xi,yi(0≤xi,yi≤109)x_i, y_i (0\leq x_i, y_i\leq 10^9)xi,yi(0xi,yi109)表示图中第iii个顶点的坐标,保证所有的坐标两两不同。

输出描述

输出一个整数,表示答案。

样例输入 1

4 6
1 2
1 3
1 4
2 3
2 4
3 4
0 0
0 1
1 1
1 0

样例输出 1

扫描二维码关注公众号,回复: 5068830 查看本文章
1




第一次自己写计算几何,拿来当个板子吧。

虽然题目理解复杂了,也没写对。

对于这类题,写好Point类和Edge类后确实好写很多。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2000 + 1005;
const double eps = 1e-8;
typedef double Ld;

struct Point
{
    Ld x, y;
    Point(Ld xx = 0, Ld yy = 0) : x(xx), y(yy) {}
    Point operator - (const Point &a) { return Point(x-a.x, y-a.y); }
};
struct Edge
{
    int lx, ly, rx, ry;
    Point lnode, rnode;
    Edge() {}
    Edge(Point a, Point b) : lnode(a), rnode(b) {}
};

Point Nd[maxn];
int X[maxn], Y[maxn];
Edge e[maxn];
vector<Edge> S, T;

bool Eq(Ld x, Ld y) { return fabs(x-y) < eps; }
Ld cross(Point a, Point b) { return a.x*b.y - a.y*b.x; }
bool coinci(Point a, Point b) { return (Eq(a.x, b.x) && Eq(a.y, b.y)); }
bool quick_exclude(Point a1, Point a2, Point b1, Point b2)
{
    if (max(a1.x, a2.x) < min(b1.x, b2.x)) return false;
    if (max(b1.x, b2.x) < min(a1.x, a2.x)) return false;
    if (max(a1.y, a2.y) < min(b1.y, b2.y)) return false;
    if (max(b1.y, b2.y) < min(a1.y, a2.y)) return false;
    return true;
}

bool walk_across(Point a1, Point a2, Point b1, Point b2)
{
    if (!quick_exclude(a1, a2, b1, b2)) return false;
    if (coinci(a1, b1) || coinci(a1, b2) || coinci(a2, b1) || coinci(a2, b2)) return false;
    if (cross(a1-b1, b2-b1) * cross(a2-b1, b2-b1) > 0) return false;
    if (cross(b1-a1, a2-a1) * cross(b2-a1, a2-a1) > 0) return false;
    return true;
}

bool check(Edge &cat, Edge a, Edge b)
{
    //判断斜率相等 或者 都无斜率
    Ld k1, k2;
    Point Va = a.lnode - a.rnode, Vb = b.lnode - b.rnode;
    if (Va.x) k1 = Va.y / Va.x;
    if (Vb.x) k2 = Vb.y / Vb.x;
    if (Eq(Va.x, 0) + Eq(Vb.x, 0) == 1) return false;
    if (!Eq(k1, k2)) return false;

    //有公共点
    if (coinci(a.lnode, b.lnode)) cat = Edge(a.rnode, b.rnode);
    else if (coinci(a.lnode, b.rnode)) cat = Edge(a.rnode, b.lnode);
    else if (coinci(a.rnode, b.lnode)) cat = Edge(a.lnode, b.rnode);
    else if (coinci(a.rnode, b.rnode)) cat = Edge(a.lnode, b.lnode);
    else return false;

    return true;
}

int main()
{
    freopen("in.txt", "r", stdin);
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) scanf("%d%d", &X[i], &Y[i]);
    for (int i = 1; i <= n; i++) scanf("%lf%lf", &(Nd[i].x), &(Nd[i].y));
    int ans = 0;
    for (int i = 1; i <= m; i++)
    {
        e[i] = Edge(Nd[X[i]], Nd[Y[i]]);
        Edge cat;
        int sz = S.size();
        for (int j = 0; j < sz; j++)
        {
            if (walk_across(e[i].lnode, e[i].rnode, S[j].lnode, S[j].rnode)) ans++;
            while (check(cat, e[i], S[j]))
            {
                for (int k = 0; k < sz; k++)
                    if (k != j && walk_across(cat.lnode, cat.rnode, S[k].lnode, S[k].rnode)) ans++;
                S.push_back(cat);
            }
        }
        S.push_back(e[i]);
    }
    printf("%d\n", ans);
}
 

猜你喜欢

转载自www.cnblogs.com/ruthank/p/10321608.html