题目描述
有一个nnn个点mmm条边的图画在了平面上,你想知道有多少对边之间对应的线段相交。
特别地,对于图中的一对边,如果有公共点且只在对应的端点相交,那么我们不认为这对边相交。
输入描述
第一行两个整数n,m(1≤n≤1000,1≤m≤2000)n, m(1\leq n\leq 1000, 1\leq m\leq 2000)n,m(1≤n≤1000,1≤m≤2000),表示点数和边数。
接下来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(0≤xi,yi≤109)表示图中第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
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);
}