半平面交,最近点对,旋转卡壳 --- 计算几何模板

计算几何模板

全是纯干货,方法理解可查阅算法入门到进阶一书 !

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const double INF = 1e20;
const double pi = acos(-1.0);//高精度圆周率
const double eps = 1e-8;//偏差值
const int maxn = 1010;//点的数量
//判断是否等于零,返回0为等于零,返回-1为小于,1为大于
int sgn(double x) {
    if (fabs(x) < eps)return 0;
    else return x < 0 ? -1 : 1;
}

struct Point {
    double x, y;
    Point() {}
    Point(double x, double y) : x(x), y(y) {}
    Point operator+(Point B) { return Point(x + B.x, y + B.y); }
    Point operator-(Point B) { return Point(x - B.x, y - B.y); }
    Point operator*(double k) { return Point(x * k, y * k); }//放大k倍
    Point operator/(double k) { return Point(x / k, y / k); }//缩小k倍
    bool operator==(Point B) { return sgn(x - B.x) == 0 && sgn(y - B.y) == 0; }
    bool operator<(Point B) {return sgn(x - B.x) < 0 || (sgn(x - B.x) == 0 && sgn(y - B.y) < 0); }//用于对顶点的排序
    friend bool operator < (Point a,Point b){
        return sgn(a.x - b.x) < 0 || (sgn(a.x - b.x) == 0 && sgn(a.y - b.y) < 0);
    }
};

bool cmpxy(Point A,Point B){ //先对x排序再对y排序
	return sgn(A.x - B.x) < 0 || (sgn(A.x - B.x) == 0 && sgn(A.y - B.y) < 0);
}
bool cmpy(Point A, Point B) { return sgn(A.y - B.y) < 0; } //只对y坐标排序
typedef Point Vector;

//向量叉积;大于0,B在A逆时针方向;等于0,A、B重合
double Cross(Vector A, Vector B) { return A.x * B.y - A.y * B.x;}
//两点距离
double Distance(Point A, Point B) { return hypot(A.x - B.x, A.y - B.y);}
// 半平面交
struct Line{
	Point p;    //直线上一个点
	Vector v;	//方向向量,左边是半平面
	double ang; //极角,从x正半轴旋转到v的角度
	Line(){};
	Line(Point p,Vector v):p(p),v(v){ang = atan2(v.y,v.x);}
	friend bool operator < (Line a,Line b){return a.ang < b.ang;}//用于排序
};
// p在线L的左边
bool OnLeft(Line L,Point p){return sgn(Cross(L.v,p - L.p )) > 0;}

Point Cross_point(Line a,Line b){ //两直线的交点
    Vector u = a.p - b.p;
    double t = Cross(b.v, u) / Cross(a.v, b.v);
    return a.p + a.v * t;
}

vector<Point> HPI(vector<Line> L){
	int n=L.size();
	sort(L.begin(),L.end());//将所有半平面按照极角排序。
	int first,last;
	vector<Point> p(n);
	vector<Line> q(n);
	vector<Point> ans;
	q[first=last=0]=L[0];
	for(int i=1;i<n;i++){
		while(first<last&&!OnLeft(L[i],p[last-1]))last--;//删除顶部的半平面
		while(first<last&&!OnLeft(L[i],p[first]))first++;//删除底部的半平面
		q[++last]=L[i];//将当前的半平面假如双端队列顶部。
		if(fabs(Cross(q[last].v,q[last-1].v))<eps){//对于极角相同的,选择性保留一个。
			last--;
			if(OnLeft(q[last],L[i].p))q[last]=L[i];
		}
		if(first<last)p[last-1]=Cross_point(q[last-1],q[last]);//计算队列顶部半平面交点。
	}
	while(first<last&&!OnLeft(q[first],p[last-1]))last--;//删除队列顶部的无用半平面。
	if(last-first<=1)return ans;//半平面退化
	p[last]=Cross_point(q[last],q[first]);//计算队列顶部与首部的交点。
	for(int i=first;i<=last;i++)ans.push_back(p[i]);//将队列中的点复制。
	return ans;
}

//最近点对
Point p[maxn], tmp_p[maxn];
double Closest_Pair(int left,int right){
	double dis = INF;
	if(left == right) return dis; //只剩一个点
	if(left + 1 == right) //只剩两个点
		return Distance(p[left], p[right]);
	int mid = (left + right) / 2; //分治
	double d1 = Closest_Pair(left, mid); //求S1内的最近点对
	double d2 = Closest_Pair(mid + 1, right); //求S2内的最近点对
	dis = min(d1, d2);
	int k = 0;
	for (int i = left; i <= right;i ++){ //在s1和s2中间附近找可能的最小点对
		if(fabs(p[mid].x - p[i].x) <= dis) //按x坐标来找
			tmp_p[k++] = p[i];
	}
	sort(tmp_p, tmp_p + k, cmpy); //按照y坐标进行排序
	for (int i = 0; i < k;i ++){
		for (int j = i + 1; j < k;j ++){
			if(tmp_p[j].y - tmp_p[i].y >= dis) break;
			dis = min(dis, Distance(tmp_p[i], tmp_p[j]));
		}
	}
	return dis;
}

//
int main() {
    //ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    
    return 0;
}

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值