HDU 6325 Problem G. Interstellar Travel [凸包]

题意

给你n个点,求一个序列p1,p2,..pm,使得其相邻两个点到原点的向量的叉乘和最小,若两个序列答案相同,那么输出字典序最小的序列。

题解

可以很容易的得出,最后围成的形状是一个上凸包,因此我们维护一个上凸包,由于存在重点与三点共线的情况,那么对于重点,我们只取id最小的点就可以了。对于三点共线的话,那么对于这一段共线的点,两端的端点肯定必须要取,对于中间的情况,我们只需要从后往前维护一个递减序列即可。

AC代码

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<stack>
#define N 200005
using namespace std;
typedef long long ll;
vector<ll>ans,gg;
stack<ll>sta;
struct point
{
    ll x,y,id;
    point(){}
    point(ll x,ll y)
    {
        this->x=x;
        this->y=y;
    }
}p[N],tubao[N];
ll mark[N];
point operator-(point A,point B)  
{  
    return point(A.x-B.x,A.y-B.y);  
}  
bool cmp(point a,point b)
{
    if(a.x==b.x)
    {
        if(a.y==b.y)return a.id<b.id; 
        return a.y<b.y;
    }
    return a.x<b.x;
}
ll Cross(point A,point B)  
{  
    return A.x*B.y-A.y*B.x;  
}  
ll ConvexHull(point* p,ll n,point* ch) 
{  
    ll m=0; 
    for(ll i=0;i<n;i++)
    {  
        if(mark[i])continue;
        while(m>=2&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])>0)m--;  
        ch[m++]=p[i];  
    }    
    return m;  
}  
int main()
{
    ll T;
    scanf("%lld",&T);
    while(T--)
    {
        memset(mark,0,sizeof(mark));
        ans.clear();
        ll n;
        scanf("%lld",&n);
        for(ll i=0;i<n;i++)
            scanf("%lld%lld",&p[i].x,&p[i].y),p[i].id=i+1;
        sort(p,p+n,cmp);
        for(int i=1;i<n;i++)
            if(p[i].x==p[i-1].x&&p[i].y==p[i-1].y)
                mark[i]=1;
        ll tot=ConvexHull(p,n,tubao);
        for(ll i=0;i<tot;i++)
        {
            ans.push_back(tubao[i].id);
            while(i<tot-2&&Cross(tubao[i+1]-tubao[i],tubao[i+2]-tubao[i])==0)
                gg.push_back(tubao[i+1].id),i++;
            ll now=tubao[i+1].id;
            for(int j=gg.size()-1;j>=0;j--)
                if(gg[j]<now)
                    sta.push(gg[j]),now=gg[j];
            gg.clear();
            while(!sta.empty())
            {
                ans.push_back(sta.top());
                sta.pop();
            }
        }
        for(int i=0;i<ans.size()-1;i++)
            printf("%lld ",ans[i]);
        printf("%lld\n",ans[ans.size()-1]);
    }

}

猜你喜欢

转载自blog.csdn.net/ACTerminate/article/details/81291223