Problem G. Interstellar Travel
Time Limit: 4000/2000 MS (Java/Others)
Memory Limit: 524288/524288 K (Java/Others)
Problem Description
After trying hard for many years, Little Q has finally received an astronaut license. To celebrate the fact, he intends to buy himself a spaceship and make an interstellar travel.
Little Q knows the position of
planets in space, labeled by
to
. To his surprise, these planets are all coplanar. So to simplify, Little Q put these n planets on a plane coordinate system, and calculated the coordinate of each planet
.
Little Q plans to start his journey at the
planet, and end at the
planet. When he is at the
planet, he can next fly to the
planet only if
, which will cost his spaceship
units of energy. Note that this cost can be negative, it means the flight will supply his spaceship.
Please write a program to help Little Q find the best route with minimum total cost.
Input
The first line of the input contains an integer
, denoting the number of test cases.
In each test case, there is an integer
in the first line, denoting the number of planets.
For the next
lines, each line contains
integers
, denoting the coordinate of the i-th planet. Note that different planets may have the same coordinate because they are too close to each other. It is guaranteed that
.
Output
For each test case, print a single line containing several distinct integers
, denoting the route you chosen is
. Obviously
should be
and
should be
. You should choose the route with minimum total cost. If there are multiple best routes, please choose the one with the smallest lexicographically.
A sequence of integers
is lexicographically smaller than a sequence of
if there exists such index
that
for all
, but
.
Sample Input
1
3
0 0
3 0
4 0
Sample Output
1 2 3
思路:观察代价
。可以发现这是向量
与
的叉积,也就是由
,
,
三点构成的三角形的有向面积的
倍。熟悉这个的话,就比较容易想到从这
个点中选出一些点构成一个上半凸包是最优的。
然后排序求个上半凸包。
接下来就是字典序的问题了。
因为求出来的是一个凸包,如果凸包上没有三点或三点以上共线的情况,那么肯定就把凸包上的点全部选中就OK了。
如果有,在多点共线的情况下,线段的2个端点肯定是必选的(不然就不是线段了)。只是对于处于线段中的点,如果选了那个点能使字典序变小,则选上,否则不选。
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+10;
typedef long long ll;
struct Point
{
ll x,y,id;
}p[MAX],q[MAX];
int cmp(const Point& A,const Point& B)
{
if(A.x!=B.x)return A.x<B.x;
if(A.y!=B.y)return A.y>B.y;
return A.id<B.id;
}
Point operator-(Point A,Point B){return (Point){A.x-B.x,A.y-B.y,0};}
ll cross(Point A,Point B){return A.x*B.y-A.y*B.x;}
int check(Point A,Point B,Point C){return cross(B-A,C-A)!=0;}
int v[MAX];
int ans[MAX];
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&p[i].x,&p[i].y);
p[i].id=i;
}
sort(p+1,p+n+1,cmp);
int R=0;
for(int i=1;i<=n;i++) //排序后求上半凸包
{
if(i>1&&p[i].x==p[i-1].x)continue;
while(R>=2&&(q[R].y-q[R-1].y)*(p[i].x-q[R].x)<(q[R].x-q[R-1].x)*(p[i].y-q[R].y))R--;
q[++R]=p[i];
}
for(int i=1;i<=R;i++)v[i]=0;
v[1]=v[R]=1;
for(int i=2;i<R;i++)v[i]=check(q[i-1],q[i],q[i+1]);//确定必选的点
for(int i=R;i>=1;i--) //对于没选中的点,和后面已经选的点进行比较,取字典序较小的
{
if(v[i])ans[i]=q[i].id;
else ans[i]=min((int)q[i].id,ans[i+1]);
}
for(int i=1;i<=R;i++)
{
if(ans[i]==q[i].id)printf("%d%c",ans[i],i==R?'\n':' ');
}
}
return 0;
}