CCPC-Wannafly Winter Camp Day5 (Div2, onsite) J Special Judge 计算几何

本文详细解析了计算几何中线段相交的判断方法,包括常规相交(X形)和非常规相交(T形)。通过使用计算几何模板,介绍了如何判断线段是否相交,并特别关注于判断一个线段的端点是否位于另一个线段上,但排除了两端点的情况。文章提供了完整的AC代码,展示了如何在实际编程中应用这些理论。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解

题意的交点可以理解为两种 1常规相交如X形 2交点不是都是两个线段的端点如T形和…__…下划线表示重叠位置
直接套计算几何模版(以下代码使用kuangbin大大的计算几何模版直接复制粘贴无删减篇幅过长233)
判断交点是否常规相交 非常规相交判断一个线段的端点在另一个线段上要求这个端点不能等于另一个线段的两个端点

AC代码

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int MAXN = 2e3 + 10;
const double pi = acos(-1.0);

int sgn(double x){
	if (fabs(x) < 1e-11)return 0;
	if (x < 0)return -1;
	else return 1;
}
struct Point{
	double x, y;
	Point(){}
	Point(double _x, double _y){
		x = _x;
		y = _y;
	}
	void input(){
		scanf("%lf%lf", &x, &y);
	}
	void output(){
		printf("%.2f␣%.2f\n", x, y);
	}
	bool operator == (Point b)const{
		return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
	}
	bool operator < (Point b)const{
		return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
	}
	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 len(){
		return hypot(x, y);//库函数
	}
	//返回长度的平方
	double len2(){
		return x*x + y*y;
	}
	//返回两点的距离
	double distance(Point p){
		return hypot(x - p.x, y - p.y);
	}
	Point operator +(const Point &b)const{
		return Point(x + b.x, y + b.y);
	}
	Point operator *(const double &k)const{
		return Point(x*k, y*k);
	}
	Point operator /(const double &k)const{
		return Point(x / k, y / k);
	}
	//计算 pa 和 pb 的夹角
	//就是求这个点看 a,b 所成的夹角
	//测试 LightOJ1203
	double rad(Point a, Point b){
		Point p = *this;
		return fabs(atan2(fabs((a - p) ^ (b - p)), (a - p)*(b - p)));
	}
	//化为长度为 r 的向量
	Point trunc(double r){
		double l = len();
		if (!sgn(l))return *this;
		r /= l;
		return Point(x*r, y*r);
	}
	//逆时针旋转 90 度
	Point rotleft(){
		return Point(- y, x);
	}
	//顺时针旋转 90 度
	Point rotright(){
		return Point(y, - x);
	}
	//绕着 p 点逆时针旋转 angle
	Point rotate(Point p, double angle){
		Point v = (*this) - p;
		double c = cos(angle), s = sin(angle);
		return Point(p.x + v.x*c - v.y*s, p.y + v.x*s + v.y*c);
	}
};
struct Line{
	Point s, e;
	Line(){}
	Line(Point _s, Point _e){
		s = _s;
		e = _e;
	}
	bool operator ==(Line v){
		return (s == v.s) && (e == v.e);
	}
	//根据一个点和倾斜角 angle 确定直线,0<=angle<pi
	Line(Point p, double angle){
		s = p;
		if (sgn(angle - pi / 2) == 0){
			e = (s + Point(0, 1));
		}
		else{
			e = (s + Point(1, tan(angle)));
		}
	}
	//ax+by+c=0
	Line(double a, double b, double c){
		if (sgn(a) == 0){
			s = Point(0, - c / b);
			e = Point(1, - c / b);
		}
		else if (sgn(b) == 0){
			s = Point(- c / a, 0);
			e = Point(- c / a, 1);
		}
		else{
			s = Point(0, - c / b);
			e = Point(1, (- c - a) / b);
		}
	}
	void input(){
		s.input();
		e.input();
	}
	void adjust(){
		if (e < s)swap(s, e);
	}
	//求线段长度
	double length(){
		return s.distance(e);
	}
	//返回直线倾斜角 0<=angle<pi
	double angle(){
		double k = atan2(e.y - s.y, e.x - s.x);
		if (sgn(k) < 0)k += pi;
		if (sgn(k - pi) == 0)k -= pi;
		return k;
	}
	//点和直线关系
	//1 在左侧
	//2 在右侧
	//3 在直线上
	int relation(Point p){
		int c = sgn((p - s) ^ (e - s));
		if (c < 0)return 1;
		else if (c > 0)return 2;
		else return 3;
	}
	// 点在线段上的判断
	bool pointonseg(Point p){
		return sgn((p - s) ^ (e - s)) == 0 && sgn((p - s)*(p - e)) <= 0;
	}
	//两向量平行 (对应直线平行或重合)
	bool parallel(Line v){
		return sgn((e - s) ^ (v.e - v.s)) == 0;
	}
	//两线段相交判断
	//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);
	}
	//直线和线段相交判断
	//-*this line -v seg
	//2 规范相交
	//1 非规范相交
	//0 不相交
	int linecrossseg(Line v){
		int d1 = sgn((e - s) ^ (v.s - s));
		int d2 = sgn((e - s) ^ (v.e - s));
		if ((d1^d2) == - 2) return 2;
		return (d1 == 0 || d2 == 0);
	}
	//两直线关系
	//0 平行
	//1 重合
	//2 相交
	int linecrossline(Line v){
		if ((*this).parallel(v))
			return v.relation(s) == 3;
		return 2;
	}
	//求两直线的交点
	//要保证两直线不平行或重合
	Point crosspoint(Line v){
		double a1 = (v.e - v.s) ^ (s - v.s);
		double a2 = (v.e - v.s) ^ (e - v.s);
		return Point((s.x*a2 - e.x*a1) / (a2 - a1), (s.y*a2 - e.y*a1) / (a2 - a1));
	}
	//点到直线的距离
	double dispointtoline(Point p){
		return fabs((p - s) ^ (e - s)) / length();
	}
	//点到线段的距离
	double dispointtoseg(Point p){
		if (sgn((p - s)*(e - s)) < 0 || sgn((p - e)*(s - e)) < 0)
			return min(p.distance(s), p.distance(e));
		return dispointtoline(p);
	}
	//返回线段到线段的距离
	//前提是两线段不相交,相交距离就是 0 了
	double dissegtoseg(Line v){
		return min(min(dispointtoseg(v.s), dispointtoseg(v.e)), min(v
			.dispointtoseg(s), v.dispointtoseg(e)));
	}
	//返回点 p 在直线上的投影
	Point lineprog(Point p){
		return s + (((e - s)*((e - s)*(p - s))) / ((e - s).len2()));
	}
	//返回点 p 关于直线的对称点
	Point symmetrypoint(Point p){
		Point q = lineprog(p);
		return Point(2 * q.x - p.x, 2 * q.y - p.y);
	}
};

Point p[MAXN];
Line l[MAXN];
int g[MAXN][MAXN];
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	{
		int u, v;
		scanf("%d%d", &u, &v);
		g[u][v] = 1;
	}
	for (int i = 1; i <= n; i++)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		p[i] = Point(x, y);
	}
	int pos = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			if (g[i][j])
				l[++pos] = Line(p[i], p[j]);
	int ans = 0;
	for (int i = 1; i <= m; i++)
		for (int j = i + 1; j <= m; j++)
			if (l[i].segcrossseg(l[j]) == 2) //X形
				ans++;
			else if (l[i].pointonseg(l[j].s) && !(l[i].s == l[j].s) && !(l[i].e == l[j].s) || //一个边的点在另一个边上有交点且不是端点
					l[i].pointonseg(l[j].e) && !(l[i].s == l[j].e) && !(l[i].e == l[j].e) ||
					l[j].pointonseg(l[i].s) && !(l[j].s == l[i].s) && !(l[j].e == l[i].s) ||
					l[j].pointonseg(l[i].e) && !(l[j].s == l[i].e) && !(l[j].e == l[i].e))
					ans++;
	cout << ans << endl;

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值