poj-1279 Art Gallery

75 篇文章 0 订阅
30 篇文章 0 订阅

题意:

给出一个n个点的多边形,求多边形核的面积;

多边形的核即为多边形内部的点集,使其中的点可以看到多边形的任意顶点和边;

n<=1500;


题解:

这应该也算是模板题的一种吧;

求多边形核的方法现在是O(nlogn)的;

将所有的直线按极角排序(math库中atan2()函数),然后线性的在双端队列里面搞搞;

这样求出了半平面交的边集,也顺带求出了点集;

求面积的话就直接叉积扫一遍就好了;

但是这道题不一样的地方是没有说顶点按什么序给出;

所以可以先顺时针求一边,如果半平面交无解再逆时针求,都无解那多边形不存在核,答案为0;

半平面交无解就是求出的边集小于三;

为了省事我两次直接粘的代码= =,可以写的函数化一些以缩短码长;


代码:


#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
using namespace std;
typedef long long ll;
const double EPS=1e-10;
const double INF=1e100;
struct Point
{
	double x,y;
	Point(){}
	Point(const double &_,const double &__):x(_),y(__){}
	friend Point operator + (const Point &a,const Point &b)
	{
		return Point(a.x+b.x,a.y+b.y);
	}
	friend Point operator - (const Point &a,const Point &b)
	{
		return Point(a.x-b.x,a.y-b.y);
	}
	friend double operator * (const Point &a,const Point &b)
	{
		return a.x*b.x+a.y*b.y;
	}
	friend double operator ^ (const Point &a,const Point &b)
	{
		return a.x*b.y-a.y*b.x;
	}
	friend Point operator * (const double a,const Point &b)
	{
		return Point(a*b.x,a*b.y);
	}
}poi[N],p[N];
struct Line
{
	Point p,v;
	double alpha;
	Line(){};
	Line(const Point &_,const Point &__):p(_),v(__-_){alpha=atan2(v.y,v.x);}
	friend bool operator < (const Line &a,const Line &b)
	{
		return a.alpha<b.alpha;
	}
	friend Point getP(const Line &a,const Line &b)
	{
		Point u=a.p-b.p;
		double temp=(b.v^u)/(a.v^b.v);
		return a.p+temp*a.v;
	}
}l[N];
int q[N],st,en;
bool Onleft(const Line &l,const Point &a)
{
	return (l.v^(a-l.p))>EPS;
}
void HPI(int n)
{
	sort(l+1,l+n+1);
	q[st=en=1]=1;
	for(int i=2;i<=n;i++)
	{
		while(st<en&&!Onleft(l[i],p[en]))
			en--;
		while(st<en&&!Onleft(l[i],p[st+1]))
			st++;
		if(fabs(l[q[en]].alpha-l[i].alpha)>EPS)
			q[++en]=i;
		else
			q[en]=Onleft(l[i],l[q[en]].p)?q[en]:i;
		p[en]=getP(l[q[en-1]],l[q[en]]);
	}
	while(st<en&&!Onleft(l[q[st]],p[en]))
		en--;
}	
int main()
{
	int c,T,n,i,j;
	double ans;
	scanf("%d",&T);
	for(c=1;c<=T;c++)
	for(c=1;c<=T;c++)
	{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
			scanf("%lf%lf",&poi[i].x,&poi[i].y);
		for(i=2;i<=n;i++)
		{
			l[i]=Line(poi[i],poi[i-1]);
		}
		l[1]=Line(poi[1],poi[n]);
		HPI(n);
		bool flag=1;
		if(en-st+1<3)
		{
			for(i=2;i<=n;i++)
				l[i]=Line(poi[i-1],poi[i]);
			l[1]=Line(poi[n],poi[1]);
			HPI(n);
			if(en-st+1<3)
				flag=0;
		}
		if(flag==0)
		{
			puts("0.00");
			continue;
		}
		p[st]=getP(l[q[st]],l[q[en]]);
		for(i=st,ans=0;i<en;i++)
			ans+=p[i]^p[i+1];
		ans+=p[en]^p[st];
		if(ans<0)	ans=-ans;
		printf("%.2f\n",ans/2+EPS);
	}
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值