POJ 1696 Space Ant(凸包变形)

558 篇文章 0 订阅
273 篇文章 0 订阅

POJ 1696 Space Ant(凸包变形)

http://poj.org/problem?id=1696

题意:

       给你平面的一个点集,然后要你从y坐标最小的点开始以水平方向向下一个点连线,每次只能直走或左转,问你最多能走多少点?(输出所有走过的点,且不走回头路,所有点坐标均唯一)

分析:

       本题其实就是用极角排序,每次都有一个你的当前点,然后每次都贪心的走以当前点为中心的极角最小的那个点(如果有多个,就走距离当前点最近的那个点即可.)

       这样,我们能保证能走过的点数是最多的.

       为什么上述贪心算法是对的呢?其实这就是每次都在找最外层的凸包.


AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
//精度控制
const double eps=1e-10;
int dcmp(double x)
{
    if(fabs(x)<eps) return 0;
    return x<0?-1:1;
}

//点
struct Point
{
    int id;
    double x,y;
    Point(){}
    Point(int id,double x,double y):id(id),x(x),y(y){}
}cur_point;//cur_point是当前参照点

//向量
typedef Point Vector;

//点-点==向量
Vector operator-(Point A,Point B)
{
    return Vector(-1,A.x-B.x,A.y-B.y);
}

//叉积
double Cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}

//距离的平方
double Length2(Point A,Point B)
{
    return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y);
}

//用于排序的比较函数,这里对比的是与参照点的极角序
bool cmp(const Point& A,const Point& B)
{
    double c=Cross(A-cur_point, B-cur_point);
    if(c>0) return true;
    else if(c<0) return false;

    double len1=Length2(A,cur_point);
    double len2=Length2(B,cur_point);
    return len1<len2;
}

const int maxn=50+5;
Point p[maxn];
int main()
{
    int T; scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        cur_point=Point(0,1e10,1e10);
        for(int i=0;i<n;i++)
        {
            scanf("%d%lf%lf",&p[i].id,&p[i].x,&p[i].y);
            if(p[i].y<cur_point.y || (p[i].y==cur_point.y&&p[i].x<cur_point.x) )
                cur_point=Point(p[i].id,p[i].x,p[i].y);
        }
        swap(p[0],p[cur_point.id-1]);//将y坐标最小的点交换到0号去

        int ans[maxn];//按顺序记录所有点的原始编号
        int cnt=0;
        ans[cnt++]=cur_point.id;//第1个点
        for(int i=1;i<n;i++)
        {
            sort(p+i,p+n,cmp);//把剩下的点按参照点从新极角排序
            cur_point=p[i];
            ans[cnt++]=cur_point.id;
        }

        printf("%d",cnt);
        for(int i=0;i<cnt;i++)
            printf(" %d",ans[i]);
        printf("\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值