FZU 2153 A simple geometric problems 凸包 + DP

给出五十个点,构造出一个凸包,让尽可能多的点在上面。


首先要说一下凸包的构造过程。

极角:设参考点为R,对于点P的极角即为向量RP与X正方向的夹角。

显然极角的取值范围为 [0,180] ,所以参考点R应选择 y 坐标最小的,若有多个,则选x最小的。

然后按极角大小升序排列。参考点不参与排序,或者说默认为最小。

设置空栈。

从前向后枚举点 p[i]。

当栈中的元素个数小于 2 时,直接将p[i] 放入栈内。

当栈中元素个数大于等于 2 时。

         设栈顶的两个元素为 pa , pb(pb为后入栈的那个)。

        若向量papb[i] 叉乘 向量papb 小于 0,则将 p[i] 入栈,i++。否则将pb弹出,i 不变,继续计算当前点。

以上即是就是葛立恒算法的整个流程。

其实明白了上面流程就可以发现,只要确定了参考点和末尾的两个点就可知一个新点是否可以加入此凸包。

所以只需要枚举 参考点,两个末尾点 和 等待加入的点。时间复杂度o[n^4]。稍微剪剪指就可以了。

枚举参考点时应注意 对于参考点R,P.y < R.y || (P.y == R.y && P.x< R.x) ,则p不参与计算,这是因为极角的计算问题。


PS :思路是学姐点拨的,本菜充其量也就算个码农。。。


http://blog.csdn.net/zmx354/article/details/23004375


#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <stack>

#pragma comment(linker, "/STACK:1024000000");
#define EPS (1e-8)
#define LL long long
#define ULL unsigned long long int
#define _LL __int64
#define _INF 0x3f3f3f3f
#define Mod 1000000007
#define LM(a,b) (((ULL)(a))<<(b))
#define RM(a,b) (((ULL)(a))>>(b))

using namespace std;

const double PI = acos(-1.0);

struct P
{
    double x,y,a;
}p[1100],tp[1100];

int dp[51][51];

bool cmpxy(P p1,P p2)
{
    return ( p1.y < p2.y || (fabs(p1.y-p2.y) < EPS && p1.x < p1.x ) );
}

bool cmp_angle(P p1,P p2)
{
    return (p1.a < p2.a || (fabs(p1.a-p2.a) < EPS && p1.y < p2.y) ||(fabs(p1.a-p2.a) < EPS && fabs(p1.y-p2.y) < EPS && p1.x < p2.x)   );
}

double X_Mul(P a1,P a2,P b1,P b2)
{
    P v1 = {a2.x-a1.x,a2.y-a1.y},v2 = {b2.x-b1.x,b2.y-b1.y};
    return v1.x*v2.y - v1.y*v2.x;
}

double Cal_Angle(P t,P p)
{
    return ((t.x-p.x)*(t.x-p.x) + 1 - (t.x-p.x-1)*(t.x-p.x-1))/(2*sqrt((t.x-p.x)*(t.x-p.x) + (t.y-p.y)*(t.y-p.y)));
}

void Sort_By_Angle(P *p,int n)
{
    P re = p[0];

    p[0].a = -2;

    for(int i = 1;i < n; ++i)
    {
        p[i].a = Cal_Angle(re,p[i]);
    }

    sort(p,p+n,cmp_angle);
}

int Max;

void Updata_Max(P *p,int n)
{
    int i,j,k;

    for(i = 0;i < n; ++i)
    {
        tp[i] = p[i];
    }

    Sort_By_Angle(tp,n);

    for(i = 1;i < n; ++i)
    {
        for(j = i+1;j < n; ++j)
            dp[i][j] = 3;
    }

    for(i = 1;i < n; ++i)
    {
        for(j = i+1;j < n; ++j)
        {
           
            for(k = j+1;k < n; ++k)
            {
                if(dp[i][j] >= dp[j][k] &&  X_Mul(tp[i],tp[j],tp[i],tp[k]) > 0)
                {
                    dp[j][k] = dp[i][j]+1;

                    Max = max(dp[j][k],Max);
                }
            }
           
        }
    }
}

int main()
{
    int icase = 1,T,n,i;

    scanf("%d",&T);

    while(T--)
    {
        scanf("%d",&n);

        for(i = 0;i < n; ++i)
        {
            scanf("%lf %lf",&p[i].x,&p[i].y);
        }

        sort(p,p+n,cmpxy);

        Max = 3;

        for(i = 0;i < n ; ++i)
        {
            if(n-i <= Max)
                break;
            Updata_Max(&p[i],n-i);
        }
        
        printf("Case#%d: %d\n",icase++,Max);
    }
    return 0;
}








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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值