计算机几何,极角序,旋转卡壳的板子

最近春招笔试遇到的很眼熟的算法,复习一下~

https://ac.nowcoder.com/acm/contest/66585/D

https://ac.nowcoder.com/acm/contest/66585/E

https://ac.nowcoder.com/acm/contest/66585/G

#include<bits/stdc++.h>
using namespace std;
const int N=2200;
typedef long long db; //全整形直接在这里改 
typedef long long ll;
const db EPS=0;
inline int sign(db a){return a<-EPS?-1:a>EPS;}  //判断a的符号
inline int cmp(db a,db b){return sign(a-b);}
struct P{
	db x,y;
	P(){}
	P(db _x,db _y):x(_x),y(_y){}
	P operator+(P p){return{x+p.x,y+p.y};}
	P operator-(P p){return{x-p.x,y-p.y};}
	P operator*(db d){return{x*d,y*d};}
	P operator/(db d){return{x/d,y/d};}
	
	bool operator<(P p) const{
		int c=cmp(x,p.x);
		if(c) return c==-1;
		return cmp(y,p.y)==-1;
    }
	
	bool operator==(P o) const{
		return cmp(x,o.x)==0&&cmp(y,o.y)==0;
	}
	
	db dot(P p) {return x*p.x+y*p.y;} 
	//对应相乘的点积 |a|*|b|*cosC
	db det(P p) {return x*p.y-y*p.x;}  
	//交差相乘再相减的叉积 |a|*|b|*sinC,有方向以第一个向量为准 ==平行四边形的面积 
	db distTo(P p){return (*this-p).abs();} //|x1-x2|+|y1-y2|
	db alpha(){return atan2(y,x);}  //极角与x轴正半轴的夹角 弧度制 
	db abs(){return sqrt(abs2());}
	db abs2(){ return x*x+y*y;}  //到原点的距离平方 
	P rot90(){return P(-y,x);}  //旋转90 
	P unit(){return *this/abs();} //除于模长
	int quad() const{return sign(y)==1||(sign(y)==0&&sign(x)>=0);} //判断是不是位于上半
};
#define cross(p1,p2,p3) ((p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y)) 
//算(^^p1p2)X(^^p1p3) 
#define crossOp(p1,p2,p3) sign(cross(p1,p2,p3))
// crossOp 精度有一点问题 点的范围1e9 eps<=10^(-9) 
//算一个上面的符号 
bool chkLL(P p1, P p2, P q1,P q2){
	db a1=cross(q1,q2,p1),a2=-cross(q1,q2,p2);
	return sign(a1+a2)!=0;
}
// 两条直线是不是相交关系
// p1p2,q1q2 是不是恰有一个交点
P isLL(P p1,P p2,P q1,P q2){
	db a1=cross(q1,q2,p1),a2=-cross(q1,q2,p2);
	return (p1*a2+p2*a1)/(a1*a2);
} 
//通过叉积求出面积,然后等比分线段,求p1,p2的交点 
bool intersect(db l1,db r1,db l2,db r2){
	if(l1>r1) swap(l1,r1);if(l2>r2) swap(l2,r2);
	return !( cmp(r1,l2)==-1 || cmp(r2,l1)==-1 );
}
// 判断l1,l2 是否相交 ,可以重,一个点 
bool isSS(P p1,P p2,P q1,P q2){
	return intersect(p1.x,p2.x,q1.x,q2.x)&&intersect(p1.y,p2.y,q1.y,q2.y)&&
	crossOp(p1,p2,q1)*crossOp(p1,p2,q2)<=0&&crossOp(q1,q2,p1)*
	crossOp(q1,q2,p2)<0;
}
//判断是不是严格相交有且仅有一个交点 
bool isSS_strict(P p1, P p2, P q1, P q2){
	return crossOp(p1,p2,q1)*crossOp(p1,p2,q2)<0&&crossOp(q1,q2,p1)*
	crossOp(q1,q2,p2)<0;
}
bool isMiddle(db a,db m,db b){
/*	
	if(a>b) swap(a,b);
	return cmp(a,m)<=0&&cmp(m,b)<=0;
*/
	return sign(a-m)==0|| sign(b-m)==0|| (a<m!=b<m);
}
// 判断一个点是否在两个点之间 
bool isMiddle(P a,P m,P b){
	return isMiddle(a.x,m.x,b.x) && isMiddle(a.y,m.y,b.y);
}
//点p是不是在线段p1 p2 上
bool onSeg(P p1,P p2,P q){
	return crossOp(p1,p2,q)==0&&isMiddle(p1,q,p2);
}
// 点P严格在p1 p2上m先判断三点共线再判断方向 
bool onSeg_strict(P p1,P p2, P q){
	return crossOp(p1,p2,q)==0&&sign((q-p1).dot(p1-p2))*sign((q-p2).dot(p1-p2))<0;
}
// q到直线p1,p2的投影 (垂足) !!!p1==p2的情况 
P proj(P p1,P p2,P q){
	P dir=p2-p1;
	return p1+dir*(dir.dot(q-p1)/dir.abs2());
} 
// q到直线p1,p2的反射 (垂足) !!!p1==p2的情况
P reflect(P p1,P p2, P q){
	return proj(p1,p2,q)*2-q;
}

// 求q到线段p1p2的最小距离
// 先求垂足,判断在不在,不再返回端点 
db nearest(P p1,P p2, P q){
	if(p1==p2) return p1.distTo(q);
	P h=proj(p1,p2,q);
	if(isMiddle(p1,h,p2)) return q.distTo(h);
	return min(p1.distTo(q),p2.distTo(q));
}
// 求两个线段 p1p2线段q1q2的距离 
// 找两个点之间的最小值 
// 直接找两个端点四个距离求个min 
db disSS(P p1,P p2,P q1,P q2){
	if(isSS(p1,p2,q1,q2)) return 0.0;  //相交返回0 
	return min(min(nearest(p1,p2,q1),nearest(p1,p2,q2)),min(nearest(q1,q2,p1),nearest(q1,q2,p2)));
}
// 极角排序 
// 给一些点找这些点按极角大小排序(原点的角度[-PI,PI])
// atan2() 求度数但是有10-15次方里的误差而且很慢
// 可以用det叉积判断方向 ,一个点在另一个点的方向 这样也不行判段方向会绕一圈
// 先判一个极角是正/负[-PI,0),[0,PI]; 
//	在同一边再用det判断方向 
/*
sort(p,p+n,[&](P &a,P &b){
	int qa=a.quad(),qb=b.quad();
	if(qa!=qb) return qa<qb;
	else  return sign(a.det(b))>0;
})
*/  
// 算凸多边形的面积 
db area(vector<P> ps){
	db ret=0;
	int sz=ps.size();
	for(int i=0;i<sz;i++) ret+=ps[i].det(ps[(i+1)%sz]);
	return ret/2.0; 
}
// 点是不是在多边形上,2表示在里面,1表示在边界,表示在外面 
int contiain(vector<P> ps,P p){
	int n=ps.size(),ret=0;
	for(int i=0;i<n;i++){
		P u=ps[i],v=ps[(i+1)%n];
		if(onSeg(u,v,p)) return 1;
		if(cmp(u.y,v.y)<=0) swap(u,v);
		if(cmp(p.y,u.y)>0||cmp(p.y,v.y)<=0) continue;
		ret^=crossOp(p,u,v)>0;
	}
	return ret*2;
}
// 考虑点在边上的情况 
//算凸包的上下凸壳 ,严格凸包 
vector<P> convexHull(vector<P> ps){
	int n=ps.size();
	if(n<=1) return ps;
	sort(ps.begin(),ps.end()); //正常的按x,y排序 
	vector<P> qs(n*2); //栈 ,共线是不算的 
	int k=0; //栈顶 
	for(int i=0;i<n;qs[k++]=ps[i++])
		while(k>1&&crossOp(qs[k-2],qs[k-1],ps[i])<=0) --k; //判断是不是顺时针方向 ,是顺时针或着共线就弹掉 
	for(int i=n-2,t=k;i>=0;qs[k++]=ps[i--])
		while(k>t&&crossOp(qs[k-2],qs[k-1],ps[i])<=0) --k;
	qs.resize(k-1);//保留前k-1个最后一个重的不要 
	return qs; 
}
// 不严格的凸包,共线也算上 
vector<P> convexHullNonStrict(vector<P> ps){
	//用这个的时候要把点去重一下不然队列里的会有一模一样的点 
	//共线和下一个的乘积一定是0,要去重 
	int n=ps.size();
	if(n<=1) return ps;
	sort(ps.begin(),ps.end()); 
	vector<P> qs(n*2); 
	int k=0; 
	for(int i=0;i<n;qs[k++]=ps[i++])
		while(k>1&&crossOp(qs[k-2],qs[k-1],ps[i])<0) --k;  
	for(int i=n-2,t=k;i>=0;qs[k++]=ps[i--])
		while(k>t&&crossOp(qs[k-2],qs[k-1],ps[i])<0) --k;
	qs.resize(k-1);
	return qs; 	
}
// 一条线把凸多边型分开了 
vector<P> convexCut(const vector<P> &ps,P q1, P q2){
	vector<P> qs;
	int n=ps.size();
	for(int i=0;i<n;i++){
		P p1=ps[i],p2=ps[(i+1)%n];
		int d1=crossOp(q1,q2,p1),d2=crossOp(q1,q2,p2);
		if(d1>=0) qs.push_back(p1);
		if(d1*d2<0) qs.push_back(isLL(p1,p2,q1,q2));
		// 如果确定严格相交就直接把端点放进去 
	}
	return qs;
} 
// 旋转卡壳 (双指针)
// 有一个点集合,希望找到最远(欧几里得距离)的两个点
// 这两个点一定在凸包上 
// 象想有两个平行线在凸多边变行的两端,现在要不停的旋转这个凸多变形
// 我们的全局最远点在某个地方一定会被卡住
// 模拟这个过程即可,双指针卡住 
// 考虑到每个平行的位置 对于两个指针的移动看i和j向量的关系 谁在谁的逆时针方向 
db convexDiameter(vector<P> ps){
	int n=ps.size();
	if(n<=1) return 0;
	int is=0,js=0;
	for(int k=1;k<n;k++) is=ps[k]<ps[is]?k:is,js=ps[js]<ps[k]?k:js; //取最大和最小的 
	int i=is,j=js;
	db ret=(ps[i]-ps[j]).abs2();
	do{
		if((ps[(i+1)%n]-ps[i]).det(ps[(j+1)%n]-ps[j])>=0) (++j)%=n;
		else (++i)%=n;
		ret=max(ret,(ps[i]-ps[j]).abs2());
	}while(i!=is||j!=js);
	return ret;
} 
int main(){
    int n;
    scanf("%d",&n);
    vector<P> a,b;
    for(int i=1;i<=n;i++){
        ll x,y;
        scanf("%lld%lld",&x,&y);
        a.push_back((P){x,y});
    }
    b=convexHull(a);
    long long  t=convexDiameter(b);
    printf("%lld\n",t);
	return 0;
}

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务