UESTC oj 797 Eh? Beautiful Stone!(计算几何)

这道题的意思就是给你一个凸包,然后再给一个在凸包外的点,求站在这个点,能看到的凸包的周长,题意很简单,做法也很明白。。就是实现起来有点坑。。。。

需要考虑的就是凸包上与给出的点共线的点,这个情况一定要考虑,最开始我是用的atan2对凸包进行分情况极角排序。。但是好像是有精度问题什么的。

写了一天写了5000+b都A不了。。太坑了,

后来换了一种极角排序方法,就是用象限极角排序,如下图,然后根据凸包横跨象限的情况来讨论,因为给出的点是逆时针的,所以比较好处理。

代码贴上~~~~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define inf 0x7ffffff
using namespace std;
const double eps = 1e-10;
bool dy(double x,double y)	{	return x > y + eps;}	// x > y
bool xy(double x,double y)	{	return x < y - eps;}	// x < y
bool dyd(double x,double y)	{ 	return x > y - eps;}	// x >= y
bool xyd(double x,double y)	{	return x < y + eps;} 	// x <= y
bool dd(double x,double y) 	{	return fabs( x - y ) < eps;}  // x == y
struct P
{
    double x,y;
    int index;
};
P r;
int quat(P a)
{
    if(dy(a.x,0.0) && dyd(a.y,0.0)) return 1;
    if(xyd(a.x,0.0) && dy(a.y,0.0))return 2;
    if(xy(a.x,0.0)&& xyd(a.y,0.0)) return 3;
    if(dyd(a.x,0.0)&& xy(a.y,0.0)) return 4;
}
double det(P r,P a,P b)
{
    return (a.x-r.x)*(b.y-r.y)-(b.x-r.x)*(a.y-r.y);
}
bool cmp(P a,P b)
{
    P p1,p2;
    p1.x=a.x-r.x;
    p1.y=a.y-r.y;
    p2.x=b.x-r.x;
    p2.y=b.y-r.y;
    int l1=quat(p1),l2=quat(p2);
    if(l1==l2)
    {
        double c=det(r,b,a);
        if(dd(c,0.0)) //同一象限共线的点,按照与r的距离从小到大排序
        {
            if(dd(fabs(p1.x),fabs(p2.x)))
				return xy(fabs(p1.y),fabs(p2.y));
            return xy(fabs(p1.x),fabs(p2.x));
        }
        return xy(c,0.0);
    }
    return l1<l2;
}
double dis(P a,P b)
{
    double c=(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
    return sqrt(c);
}
int mn,mk;
double sum;
P qs[1000000],ps[1000000];
int n;
int vis[10];
void solve()
{
	int i=mn;
	while(1)
	{
		sum+=dis(ps[(i%n+n)%n],ps[((i+1)%n+n)%n]);
		if(((i+1)%n+n)%n==mk)
			break;
		i++;
	}
}
int main()
{
    while(~scanf("%d",&n))
    {
        sum=0.0;
		int i;
        memset(vis,0,sizeof(vis));
		for( i=0;i<n;i++)
		{
			scanf("%lf%lf",&qs[i].x,&qs[i].y);
			qs[i].index=i;
			ps[i]=qs[i];
		}
		scanf("%lf%lf",&r.x,&r.y);
		sort(qs,qs+n,cmp);
		int l=0;
		for( i=0;i<n;i++)
		{
		    P a;
		    a.x=qs[i].x-r.x;
		    a.y=qs[i].y-r.y;
		    vis[quat(a)]=1;
		}
		for( i=1;i<=4;i++)
		l+=vis[i];
		if(l==1)
		{
		    mk=qs[0].index;
		    mn=qs[n-1].index;
		}
		else
		{
		    if(vis[1]==vis[4] &&  vis[1]==1 && l==2) //只横跨1 4象限特殊考虑
		    {
		        for( i=0;i<n;i++)
		        {
		            P a;
                    a.x=qs[i].x-r.x;
                    a.y=qs[i].y-r.y;
		            if(quat(a)==1)
		            mn=qs[i].index;
		        }
		        for( i=0;i<n;i++)
		        {
		            P a;
                    a.x=qs[i].x-r.x;
                    a.y=qs[i].y-r.y;
		            if(quat(a)==4)
		            {
		                mk=qs[i].index;
		                break;
		            }
		        }

		    }
		    else
		    {
		        mk=qs[0].index;
		        int a=n-1;
				mn=qs[a].index;
		        while(dd(det(r,qs[a],qs[a-1]),0.0))
		        {
                    mn=qs[a-1].index;
                    a--;
		        }
		    }
		}
		solve();
		printf("%.3lf\n",sum);

    }
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值