POJ 1696(Space Ant) 多次求凸包

题意:
有一只类似于蚂蚁之类的动物,由于 他的身体缺陷只可以左转不可右转,每天他它都要吃一个植物才可以活下去,如果没有食物可以吃了他就死,给你植物的坐标你要求的就是问他怎么选择吃食物的顺序才可以使自己活得长久。
最开始他的坐标在(0,y),y是所有节点中纵坐标的最小值,所以第一个食物我i们直着走过去不用转弯就可以吃到。
思路:
因为只可以左转不可以右转所以我们每次吃的都应该是转的角度最小的那个,因为只有这样我们才可以吃到更多的食物。因为这个特性我们就像到凸包, 外面的凸包就是外面这几个点的顺序,用一个数组做一下标记,标记一下这个食物是否已经被吃过, 然后对没吃过的食物继续求凸包即可,但是在求凸包的时候应该注意的一个点就是每一次求极角排序的的参照点应该是上一个凸包结果的最后一个点。
还有细节部分看一下代码注释。
G++ AC 代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const double eps = 1e-8;
const  int maxn = 110;
int vis[maxn];
int sgn(double x){
    if(fabs(x)<0)return 0;
    if(x<0)return -1;
    return 1;
}
struct Point {
    double x,y;
    int num;  //点的标号
    Point (){};
    Point (double _x,double _y){
        x=_x,y=_y;
    }
    void input(){
        scanf("%d %lf %lf",&num,&x,&y);
    }
    bool operator == (Point b)const{
        return sgn(x-b.x)==0&&sgn(y-b.y)==0;
    }
    double operator ^ (const Point &b)const{
        return x*b.y-y*b.x;//返回模长 带正负
    }
    bool operator < (Point b)const{
        return sgn(x-b.x)==0?sgn(y-b.y)<0:x<b.x;
    }
    Point operator - (const Point &b)const{
        return Point(x-b.x,y-b.y);
    }
    double distance (Point p){
        return hypot(x-p.x,y-p.y);
    }
};

struct polygon{
    int n;
    Point p[maxn];
    struct cmp{
        Point p;
        cmp(const Point &p0){
            p=p0;
        }
        bool operator () (const Point &aa,const Point &bb){
            Point a=aa,b=bb;
            int d=sgn((a-p)^(b-p));
            if(d==0){
                return sgn(a.distance(p)-b.distance(p))<0;
            }
            return d>0;
        }
    };
    void norm(Point mi){   //mi直接表示的就是求凸包的基础点
        sort(p+1,p+n,cmp(mi));
    }
    void Graham(polygon &convex,Point k){
        norm(k);
        int &top=convex.n;
        top=0;
        if(n==1){
            top=1;
            convex.p[0]=p[0];
            return ;
        }
        if(n==2){
            top=2;
            convex.p[0]=p[0];
            convex.p[1]=p[1];
            if(convex.p[0]==convex.p[1])top--;
            return ;
        }
        convex.p[0]=p[0];
        convex.p[1]=p[1];
        top=2;
        for(int i=2;i<n;i++){
            while(top>1&&sgn((convex.p[top-1]-convex.p[top-2])^(p[i]-convex.p[top-2]))<=0)
            top--;
            convex.p[top++]=p[i];
        }
        if(convex.n==2&&(convex.p[0]==convex.p[1]))convex.n--;
    }
};
polygon a,ap,tempa;
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        int cnt=0,cnt1=0,cont=maxn;
        scanf("%d",&n);
        int ans[maxn];
        for(int i=0;i<n;i++){
            vis[i]=0;
            tempa.p[i].input();
        }
        tempa.n=n;
        a=tempa;
        Point kk = a.p[0];
        int g=0;
        for(int i=1;i<n;i++){
            if(sgn(a.p[i].y-kk.y)<0){
                kk=a.p[i];
                g=i;
            }
            else if(sgn(a.p[i].y-kk.y)==0&&sgn(a.p[i].x-kk.x)<0){
                kk=a.p[i];
                g=i;
            }
        }//找到第一个食物节点。
        //以此作为求解凸包的基础。
        swap(a.p[g],a.p[0]);
        while(cont>0){
            a.Graham(ap,kk);
            cont=0;
            //cout<<ap.n<<endl;
            //对凸包内的的点做标记
            if(cnt1==0){
                for(int i=0;i<ap.n;i++){
                    ans[cnt++]=ap.p[i].num;
                    for(int j=0;j<tempa.n;j++){
                        if(ap.p[i].num==tempa.p[j].num){
                            vis[j]=1;
                            break;
                        }
                    }
                }
            }
            else{
                for(int i=1;i<ap.n;i++){
                    ans[cnt++]=ap.p[i].num;
                    for(int j=0;j<tempa.n;j++){
                        if(ap.p[i].num==tempa.p[j].num){
                            vis[j]=1;
                            break;
                        }
                    }
                }
            }
            //准备节点左线一次凸包。
            for(int i=0;i<tempa.n;i++){
                if(!vis[i]){
                    a.p[++cont]=tempa.p[i];
                }
            }
            if(cnt1==0)
            cnt1+=ap.n;
            else cnt1+=(ap.n-1);
            a.n=cont+1;
            kk=ap.p[ap.n-1];
            a.p[0]=kk;
        }
        printf("%d",cnt1);
        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、付费专栏及课程。

余额充值