POJ 3130 How I Mathematician Wonder What You Are! & 3335 Rotating Scoreboard (半平面交求多边形的核)

传送门

1.POJ 3335

2.POJ 3130


前置技能

1.半平面交判断多边形核是否存在,请移步我的博客:半平面交算法原理及应用的讲解。

2.reverse函数,用于将数组中的元素颠倒位置。


题目大意其问题都是判断多边形的核是否存在,具体思路当然是用半平面交来解决了。要注意的是两个题给出的点的数序不同,一个是顺时针一个是逆时针,所以代码中有些地方也会有所差异。


思路:用多边形的每条边切割平面,最终剩下的就是多边形的核,结果可能为凸多边形、无穷平面、点、直线、线段等。只有为凸多边形时才存在核,这时最终凸多边形点的个数肯定 >1 .


下面每个题都提供两个模板,第一个主要用来理解半平面交的代码思路,第二个是从kuangbin模板上摘下来的,好多题都可以直接用这个模板,可以用作比赛时的模板。

kuangbin的模板默认是按照逆时针顺序的,如果题目给的点是顺时针顺序只需要加个 reverse 函数将点的顺序颠倒一下就可以了。


代码

POJ 3130

//点是逆时针给出的 
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#define eps 1e-8
using namespace std;

int n,tol,bot,top; //bot和top指向双端队列的底部和顶部 
int dq[55],order[55]; //双端队列和存储排序后的极角 

struct point
{ //点 
	double x,y;
} p[55];

struct vct
{ //向量(直线)
	point start,end;
	double angle; //极角 
} v[55];

int dbcmp(double k)
{ //判断为0,还是为正负 
	if(fabs(k)<eps) return 0;
	return k>0? 1:-1;
}

//用叉积判断点在直线的哪一侧,>0则在直线左侧,<0则在直线右侧 
double Multi(point p0,point p1,point p2)
{ //向量 p0p1 和 p0p2 相乘 
	return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}

int cmp(int x,int y)
{ //排序的比较函数 
	int d=dbcmp(v[x].angle-v[y].angle);
	if(!d) return dbcmp(Multi(v[x].start,v[y].start,v[y].end))>0;
	 //大于0取向量左半部分为半平面,小于0,取右半部分  
	return d<0;
}

void addline(double x1,double y1,double x2,double y2)
{ //增加一个向量(直线) 
	v[tol].start.x=x1;
	v[tol].start.y=y1;
	v[tol].end.x=x2;
	v[tol].end.y=y2;
	//atan2是求原点到(x,y)的直线与x轴的夹角,比atan稳定 
	v[tol].angle=atan2(y2-y1,x2-x1);
	order[tol]=tol;
	tol++;
}

void GetIntersect(vct v1,vct v2,point &p)
{ //获取交点并返回给 p 
	double dot1,dot2;
	dot1=Multi(v1.start,v2.end,v1.end);
	dot2=Multi(v1.start,v2.start,v1.end);
	p.x=(dot1*v2.start.x-dot2*v2.end.x)/(dot1-dot2);		
	p.y=(dot1*v2.start.y-dot2*v2.end.y)/(dot1-dot2);
}

int Judge(vct v0,vct v1,vct v2)
{ //判断 v1 和 v2 的交点 pt 在半平面 v0内 
	point pt;
	GetIntersect(v1,v2,pt);
	return dbcmp(Multi(pt,v0.start,v0.end))<0;
	//叉积大于0 pt在 v0 的左面,小于0 pt 在 v0 的右面
	//如果 pt 在半平面 v0 右侧则返回真 
}

void HalfPlaneIntersection()
{ //求解半平面交 
	int i,j;
	sort(order,order+tol,cmp); //极角排序 
	for(i=1,j=0;i<tol;i++) //排除极角相同的 
		if(dbcmp(v[order[i]].angle-v[order[j]].angle)>0)
			order[++j]=order[i]; 
	tol=j+1; //个数 
	dq[0]=order[0]; //开始时入队两条直线 
	dq[1]=order[1];
	bot=0;
	top=1;
	for(i=2;i<tol;i++)
	{ //如果双端队列底端或顶端两个半平面的交点在当前半平面之外,则删除 
		while(bot<top&&Judge(v[order[i]],v[dq[top-1]],v[dq[top]]))
			top--;
		while(bot<top&&Judge(v[order[i]],v[dq[bot+1]],v[dq[bot]]))
			bot++;
		dq[++top]=order[i]; //将当前半平面加入队列的顶端 
	}
	while(bot<top&&Judge(v[dq[bot]],v[dq[top-1]],v[dq[top]])) top--;
	while(bot<top&&Judge(v[dq[top]],v[dq[bot+1]],v[dq[bot]])) bot++;	
}

int main()
{
	int i;
	while(~scanf("%d",&n)&&n)
	{
		for(i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
		tol=0;
		for(i=0;i<n-1;i++) addline(p[i].x,p[i].y,p[i+1].x,p[i+1].y);		
		addline(p[i].x,p[i].y,p[0].x,p[0].y);
		HalfPlaneIntersection();
		if(top-bot>1) printf("1\n");
		else printf("0\n");
	}
	return 0;
}


#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#define eps 1e-8
#define PI acos(-1.0)
#define MAXN 110
using namespace std;

int n;

int sgn(double x)
{ //符号函数
	if(fabs(x) < eps) 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;
	}
	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 k; //斜率 
	Line(){}
	Line(Point _s,Point _e)
	{ //构造
		s = _s; e = _e;
		k = atan2(e.y - s.y,e.x - s.x);
	}
	Point operator &(const Line &b)const
	{ //求两直线交点
		Point res = s;
		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 res;
	}
};

Line Q[MAXN];
Point p[MAXN]; //记录最初给的点集
Line line[MAXN]; //由最初的点集生成直线的集合
Point pp[MAXN]; //记录半平面交的结果的点集

//半平面交,直线的左边代表有效区域
bool HPIcmp(Line a,Line b)
{ //直线排序函数
	if(fabs(a.k - b.k) > eps)return a.k < b.k; //斜率排序
	//斜率相同我也不知道怎么办
	return ((a.s - b.s)^(b.e - b.s)) < 0;
}

void HPI(Line line[], int n, Point res[], int &resn)
{ //line是半平面交的直线的集合 n是直线的条数 res是结果
//的点集 resn是点集里面点的个数
	int tot = n;
	sort(line,line+n,HPIcmp);
	tot = 1;
	for(int i = 1;i < n;i++)
		if(fabs(line[i].k - line[i-1].k) > eps) //去掉斜率重复的
			line[tot++] = line[i];
	int head = 0, tail = 1;
	Q[0] = line[0];
	Q[1] = line[1];
	resn = 0;
	for(int i = 2; i < tot; i++)
	{ //如果双端队列底端或顶端两个半平面的交点在当前半平面之外,则删除 
		if(fabs((Q[tail].e-Q[tail].s)^(Q[tail-1].e-Q[tail-1].s)) < eps ||
		fabs((Q[head].e-Q[head].s)^(Q[head+1].e-Q[head+1].s)) < eps)
			return;
		while(head < tail && (((Q[tail]&Q[tail-1]) -
		line[i].s)^(line[i].e-line[i].s)) > eps)
			tail--;
		while(head < tail && (((Q[head]&Q[head+1]) -
		line[i].s)^(line[i].e-line[i].s)) > eps)
			head++;
		Q[++tail] = line[i];
	}
	while(head < tail && (((Q[tail]&Q[tail-1]) -
	Q[head].s)^(Q[head].e-Q[head].s)) > eps)
		tail--;
	while(head < tail && (((Q[head]&Q[head-1]) -
	Q[tail].s)^(Q[tail].e-Q[tail].e)) > eps)
		head++;
	if(tail <= head + 1) return;
	//保存点到 res数组 
	for(int i = head; i < tail; i++)
		res[resn++] = Q[i]&Q[i+1];
	if(head < tail - 1)
		res[resn++] = Q[head]&Q[tail];
}

int main()
{
	int i,resn;
	while(~scanf("%d",&n)&&n)
	{			
		for(i = 0;i < n;i++)
			scanf("%lf%lf",&p[i].x,&p[i].y);
		//将相邻点连成直线(向量) 
		for(i=0;i<n-1;i++) line[i]=Line(p[i],p[i+1]);
		line[i]=Line(p[i],p[0]);
		HPI(line,n,pp,resn);
		if(resn) printf("1\n"); //如果最终点集中点的个数不为 0 
		else printf("0\n");
	}
	return 0;
}

POJ 3335

//点是顺时针顺序给出的 
#include<stdio.h> 
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#define eps 1e-8
using namespace std;

int n,tol,bot,top; //bot和top指向双端队列的底部和顶部 
int dq[55],order[55]; //双端队列和排序后的…… 

struct point
{ //点 
	double x,y;
} p[55];

struct vct
{ //向量(直线)
	point start,end;
	double angle; //极角 
} v[55];

int dbcmp(double k)
{ //判断为0,还是为正负 
	if(fabs(k)<eps) return 0;
	return k>0? 1:-1;
}

//用叉积判断点在直线的哪一侧,>0则在直线左侧,<0则在直线右侧 
double Multi(point p0,point p1,point p2)
{ //向量 p0p1 和 p0p2 相乘 
	return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}

int cmp(int x,int y)
{ //排序的比较函数 
	int d=dbcmp(v[x].angle-v[y].angle);
	if(!d) return dbcmp(Multi(v[x].start,v[y].start,v[y].end))<0;
	//大于0取向量左半部分为半平面,小于0,取右半部分
	return d<0;
}

void addline(double x1,double y1,double x2,double y2)
{ //增加一个向量(直线) 
	v[tol].start.x=x1;
	v[tol].start.y=y1;
	v[tol].end.x=x2;
	v[tol].end.y=y2;
	//atan2是求原点到(x,y)的直线与x轴的夹角,比atan稳定 
	v[tol].angle=atan2(y2-y1,x2-x1);
	order[tol]=tol;
	tol++;
}

void GetIntersect(vct v1,vct v2,point &p)
{ //获取交点并返回给 p 
	double dot1,dot2;
	dot1=Multi(v1.start,v2.end,v1.end);
	dot2=Multi(v1.start,v2.start,v1.end);
	p.x=(dot1*v2.start.x-dot2*v2.end.x)/(dot1-dot2);		
	p.y=(dot1*v2.start.y-dot2*v2.end.y)/(dot1-dot2);
}

int Judge(vct v0,vct v1,vct v2)
{ //判断 v1 和 v2 的交点 pt 在半平面 v0内 
	point pt;
	GetIntersect(v1,v2,pt);
	return dbcmp(Multi(pt,v0.start,v0.end))>0;
	//叉积大于0 pt在 v0 的左面,小于0 pt 在 v0 的右面
	//如果 pt 在半平面 v0 左侧则返回真 
}

void HalfPlaneIntersection()
{ //求解半平面交 
	int i,j;
	sort(order,order+tol,cmp); //极角排序 
	for(i=1,j=0;i<tol;i++) //排除极角相同的 
		if(dbcmp(v[order[i]].angle-v[order[j]].angle)>0)
			order[++j]=order[i]; 
	tol=j+1; //个数 
	dq[0]=order[0]; //开始时入队两条直线 
	dq[1]=order[1];
	bot=0;
	top=1;
	for(i=2;i<tol;i++)
	{ //如果双端队列底端或顶端两个半平面的交点在当前半平面之外,则删除 
		while(bot<top&&Judge(v[order[i]],v[dq[top-1]],v[dq[top]]))
			top--;
		while(bot<top&&Judge(v[order[i]],v[dq[bot+1]],v[dq[bot]]))
			bot++;
		dq[++top]=order[i]; //将当前半平面加入队列的顶端 
	}
	while(bot<top&&Judge(v[dq[bot]],v[dq[top-1]],v[dq[top]])) top--;
	while(bot<top&&Judge(v[dq[top]],v[dq[bot+1]],v[dq[bot]])) bot++;	
}

int main()
{
	int i,t,n;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
		tol=0;
		for(i=0;i<n-1;i++) addline(p[i].x,p[i].y,p[i+1].x,p[i+1].y);
		addline(p[i].x,p[i].y,p[0].x,p[0].y);
		HalfPlaneIntersection();
		if(top-bot>1) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#define eps 1e-8
#define PI acos(-1.0)
#define MAXN 110
using namespace std;

int n;

int sgn(double x)
{ //符号函数
	if(fabs(x) < eps) 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;
	}
	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 k; //斜率 
	Line(){}
	Line(Point _s,Point _e)
	{ //构造
		s = _s; e = _e;
		k = atan2(e.y - s.y,e.x - s.x);
	}
	Point operator &(const Line &b)const
	{ //求两直线交点
		Point res = s;
		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 res;
	}
};

Line Q[MAXN];
Point p[MAXN]; //记录最初给的点集
Line line[MAXN]; //由最初的点集生成直线的集合
Point pp[MAXN]; //记录半平面交的结果的点集

//半平面交,直线的左边代表有效区域
bool HPIcmp(Line a,Line b)
{ //直线排序函数
	if(fabs(a.k - b.k) > eps)return a.k < b.k; //斜率排序
	//斜率相同我也不知道怎么办
	return ((a.s - b.s)^(b.e - b.s)) < 0;
}

void HPI(Line line[], int n, Point res[], int &resn)
{ //line是半平面交的直线的集合 n是直线的条数 res是结果
//的点集 resn是点集里面点的个数
	int tot = n;
	sort(line,line+n,HPIcmp);
	tot = 1;
	for(int i = 1;i < n;i++)
		if(fabs(line[i].k - line[i-1].k) > eps) //去掉斜率重复的
			line[tot++] = line[i];
	int head = 0, tail = 1;
	Q[0] = line[0];
	Q[1] = line[1];
	resn = 0;
	for(int i = 2; i < tot; i++)
	{ //如果双端队列底端或顶端两个半平面的交点在当前半平面之外,则删除 
		if(fabs((Q[tail].e-Q[tail].s)^(Q[tail-1].e-Q[tail-1].s)) < eps ||
		fabs((Q[head].e-Q[head].s)^(Q[head+1].e-Q[head+1].s)) < eps)
			return;
		while(head < tail && (((Q[tail]&Q[tail-1]) -
		line[i].s)^(line[i].e-line[i].s)) > eps)
			tail--;
		while(head < tail && (((Q[head]&Q[head+1]) -
		line[i].s)^(line[i].e-line[i].s)) > eps)
			head++;
		Q[++tail] = line[i];
	}
	while(head < tail && (((Q[tail]&Q[tail-1]) -
	Q[head].s)^(Q[head].e-Q[head].s)) > eps)
		tail--;
	while(head < tail && (((Q[head]&Q[head-1]) -
	Q[tail].s)^(Q[tail].e-Q[tail].e)) > eps)
		head++;
	if(tail <= head + 1) return;
	//保存点到 res数组 
	for(int i = head; i < tail; i++)
		res[resn++] = Q[i]&Q[i+1];
	if(head < tail - 1)
		res[resn++] = Q[head]&Q[tail];
}

int main()
{
	int i,t,resn;
	scanf("%d",&t);
	while(t--)
	{	
		scanf("%d",&n);
		for(i = 0;i < n;i++)
			scanf("%lf%lf",&p[i].x,&p[i].y);
		reverse(p,p+n); //将点位置颠倒
		//将相邻点连成直线(向量)  
		for(i=0;i<n-1;i++) line[i]=Line(p[i],p[i+1]);
		line[i]=Line(p[i],p[0]);
		HPI(line,n,pp,resn);
		if(resn) printf("YES\n"); //如果最终点集中点的个数不为 0 
		else printf("NO\n");
	}
	return 0;
}

可以发现这两个题除了给出点的顺序之外都是一样的,代码也差不多都是一样的。第一个模板中除了 cmp函数和 Judge函数中的 > 和 < 正好相反外。

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下 4载使用后,可先查看README.md或论文文件(如有),本项目仅用作流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值