POJ - 3335 Rotating Scoreboard (半平面交模板)

鏈接:https://cn.vjudge.net/problem/POJ-3335

題意:判斷直線的半平面交是否有核。(也是判斷一個不規則多邊形內部是否存在一個區域,可以看到多邊形的全貌。)

思路:半平面交板子題,這裏存個模板,都是存的邊,最後求交點。個人傾向於第一個板子,更易理解些。

1.

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;
const double eps = 1e-5;
const int N = 2e4+10; 
const double maxx=1e4;
int sgn(double x)
{
	if(fabs(x)<eps) return 0;
	else if(x<0) return -1;
	else return 1;
}
struct Point
{
	double x,y;
	Point(){}
	Point(double x,double y):x(x),y(y){}
	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;
	}
};
struct Line
{
	Point s,e;
	double A;
	Line(){}
	Line(Point ss,Point ee)
	{
		s=ss,e=ee;
	}
	void getangle()
	{
		A=atan2(e.y-s.y,e.x-s.x);
	}
	pair<Point,int> operator &(const Line& b)const//兩直線相對關係 
	{
		Point res=s;
		if(sgn((s-e)^(b.s-b.e))==0)
		{
			if(sgn((b.s-s)^(b.e-s))==0)//兩直線重合 
				return make_pair(res,0);
			else return make_pair(res,1);//兩直線平行 
		}
		double t=((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
		res.x+=(e.x-s.x)*t;
		res.y+=(e.y-s.y)*t;
		return make_pair(res,2);//兩直線相交 
	}
};
Point ps[N];
Line ls[N],q[N];
double x;
int n;
double dis(Point a,Point b)
{
	return sqrt((a-b)*(a-b));
}
//注意,這樣並不能把點按照逆時針排序 
/*
bool cmp(const Point& a,const Point& b)
{
	x=(a-ps[0])^(b-ps[0]);
	if(sgn(x)==0)
		return dis(ps[0],a)<dis(ps[0],b);
	else if(x>0) return 1;
	else return 0;
}
void anticlock()
{
	int pos=0;
	for(int i=1;i<n;i++)
		if(ps[i].y<ps[pos].y||(ps[i].y==ps[pos].y&&ps[i].x<ps[pos].x))
			pos=i;
	swap(ps[0],ps[pos]);
	sort(ps+1,ps+n,cmp);		
}
*/
//按極角排序,相同時,靠左(或靠下)的在前面 
bool hpicmp(Line a,Line b)
{
	if(sgn(a.A-b.A)==0)	
		return sgn((a.s-b.s)^(b.e-b.s))<=0;		
	else return a.A<b.A;
}
//判斷l1與l2的交點是否在l3的右側 
bool onright(Line l1,Line l2,Line l3)
{
	Point p=(l1&l2).first;
	x=((l3.e-l3.s)^(p-l3.s));
	if(sgn(x)<0) return 1;
	else return 0;
}
//半平面交求核 
bool hpi()
{
	int he=0,ta=1,cnt=0;
    sort(ls,ls+n,hpicmp);
    //去重,只保留極角互不相同的直線 
	cnt=1;
    for(int i = 1;i < n;i++)
        if(sgn(ls[i].A-ls[i-1].A)>0)
        	ls[cnt++]=ls[i];
	//將前兩條直線放進隊列		 
    q[0]=ls[0];
    q[1]=ls[1];
	for(int i=2;i<cnt;i++)
	{ 
		//說是判斷共線,我覺得沒必要 
		/*
		if(he<ta&&sgn((q[ta].e-q[ta].s)^(q[ta-1].e-q[ta-1].s))==0
		||sgn((q[he].e-q[he].s)^(q[he+1].e-q[he+1].s))==0)
			return 0;
		*/
		while(he<ta&&onright(q[ta-1],q[ta],ls[i])) ta--;
		while(he<ta&&onright(q[he],q[he+1],ls[i])) he++;
		q[++ta]=ls[i];
	}
	while(he<ta&&onright(q[ta-1],q[ta],q[he])) ta--;
	while(he<ta&&onright(q[he],q[he+1],q[ta-1])) he++;
	if(ta-he+1<=2) return 0;
	else return 1;
}
bool anticlock() 
{
	double ans=0;
	for(int i=1;i<n-1;i++) 
		ans+=((ps[i]-ps[0])^(ps[i+1]-ps[0]));
	return ans<0;
}
int main(void)
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=0;i<n;i++)
			scanf("%lf%lf",&ps[i].x,&ps[i].y);
		//判斷是否爲逆時針	
	    if(anticlock()) 
		{
		    for(int i=0;i<n;i++) 
		    {
		        ls[i]=Line(ps[(i+1)%n],ps[i]);
		    	ls[i].getangle(); 
			} 
		} 
		else 
		{
		    for(int i=0;i<n;i++) 
		    {
		      	ls[i]=Line(ps[i],ps[(i+1)%n]);
		      	ls[i].getangle();
			}  
	    }		
		if(hpi())
			puts("YES");
		else puts("NO");
	}
	return 0;
}

2.

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;
const double eps = 1e-5;
const int N = 1e2+10; 
int sgn(double x)
{
	if(fabs(x)<eps) return 0;
	else if(x<0) return -1;
	else return 1;
}
 
struct Point
{
	double x,y;
	Point(){}
	Point(double x,double y):x(x),y(y){}
	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;
	}
};
struct Line
{
	Point s,e;
	Line(){}
	Line(Point ss,Point ee)
	{
		s=ss,e=ee;
	}
	pair<Point,int> operator &(const Line& b)const//兩直線相對關係 
	{
		Point res=s;
		if(sgn((s-e)^(b.s-b.e))==0)
		{
			if(sgn((b.s-s)^(b.e-s))==0)//兩直線重合 
				return make_pair(res,0);
			else return make_pair(res,1);//兩直線平行 
		}
		double t=((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
		res.x+=(e.x-s.x)*t;
		res.y+=(e.y-s.y)*t;
		return make_pair(res,2);//兩直線相交 
	}
};
Point ps[N];
Line ls[N],q[N];
double x;
int n;
double dis(Point a,Point b)
{
	return sqrt((a-b)*(a-b));
}
//注意,這樣並不能把點按照逆時針排序 
/*
bool cmp(const Point& a,const Point& b)
{
	x=(a-ps[0])^(b-ps[0]);
	if(sgn(x)==0)
		return dis(ps[0],a)<dis(ps[0],b);
	else if(x>0) return 1;
	else return 0;
}
void anticlock()
{
	int pos=0;
	for(int i=1;i<n;i++)
		if(ps[i].y<ps[pos].y||(ps[i].y==ps[pos].y&&ps[i].x<ps[pos].x))
			pos=i;
	swap(ps[0],ps[pos]);
	sort(ps+1,ps+n,cmp);		
}
*/
//獲得極角 
double getA(Line a)
{
	return atan2(a.e.y-a.s.y,a.e.x-a.s.x);
}
//當極角相同時,最靠左的在後面 
bool hpicmp(Line a,Line b)
{
	double A=getA(a),B=getA(b);
	if(sgn(A-B)==0)	
		return ((a.e-a.s)^(b.e-a.s))>=0;
	else return A<B;
}
//判斷l1與l2的交點是否在l3右邊 
bool onright(Line l1,Line l2,Line l3)
{
	Point p=(l1&l2).first;
	x=((l3.e-l3.s)^(p-l3.s));
	if(sgn(x)<0) return 1;
	else return 0;
}
//半平面交 
bool hpi()
{
	int he=0,ta=0,cnt=0;
	//按極角排序 
	sort(ls,ls+n,hpicmp);
	//去重   
	for(int i=0;i<n-1;i++)
	{
		if(sgn(getA(ls[i])-getA(ls[i+1]))==0)
			continue;
		ls[cnt++]=ls[i];
	}
	ls[cnt++]=ls[n-1];
	//求半平面交 
	for(int i=0;i<cnt;i++)
	{
		while(ta-he>1&&onright(q[ta-2],q[ta-1],ls[i])) ta--;
		while(ta-he>1&&onright(q[he],q[he+1],ls[i])) he++;
		q[ta++]=ls[i];
	}
	while(ta-he>1&&onright(q[ta-2],q[ta-1],q[he])) ta--;
	while(ta-he>1&&onright(q[he],q[he+1],q[ta-1])) he++;
	if(ta-he>2) return 1;
	else return 0;  
}
bool judge() 
{
	double ans=0;
	for (int i=1;i<n-1;i++) 
		ans+=((ps[i]-ps[0])^(ps[i+1]-ps[0]));
	return ans<0;
}
int main(void)
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=0;i<n;i++)
			scanf("%lf%lf",&ps[i].x,&ps[i].y);
	    if(judge()) 
		{
			for (int i=0;i<n;i++) 
				ls[i]=Line(ps[(i+1)%n],ps[i]);
	    } 
		else 
		{
			for (int i=0;i<n;i++) 
				ls[i]=Line(ps[i],ps[(i+1)%n]);
	    }		
		if(hpi())
			puts("YES");
		else puts("NO");
	}
	
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章