Problem G. Interstellar Travel
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 1199 Accepted Submission(s): 283
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 n planets in space, labeled by 1 to n. 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 (xi,yi).
Little Q plans to start his journey at the 1-th planet, and end at the n-th planet. When he is at the i-th planet, he can next fly to the j-th planet only if xi<xj, which will cost his spaceship xi×yj−xj×yi 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 T(1≤T≤10), denoting the number of test cases.
In each test case, there is an integer n(2≤n≤200000) in the first line, denoting the number of planets.
For the next n lines, each line contains 2 integers xi,yi(0≤xi,yi≤109), 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 y1=yn=0,0=x1<x2,x3,...,xn−1<xn.
Output
For each test case, print a single line containing several distinct integers p1,p2,...,pm(1≤pi≤n), denoting the route you chosen is p1→p2→...→pm−1→pm. Obviously p1 should be 1 and pm should be n. 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 a is lexicographically smaller than a sequence of b if there exists such index j that ai=bi for all i<j, but aj<bj.
Sample Input
1
3
0 0
3 0
4 0
Sample Output
1 2 3
题意:
给你n个点,第一个点一定是(0,0),最后一个点纵坐标yn一定是0,中间的点的横坐标一定都是在(0,xn)之间的
然后从第一个点开始飞行,每次飞到下一个点j,你花费的价值就是xi*yj-xj*yi,并且这里每一次飞行必须满足xi<xj
让你求出你所花费的最小的价值(可以为负)下,飞行的路径,如果有多种情况,输出路径字典序最小的那个
解析:
官方题解讲的挺清楚的
显然坐标相同的点里只保留编号最小的点最优。这里是因为题目严格要求xi<xj,所以不可能在原地飞行。
将起点到终点的路径补全为终点往下走到无穷远处,再往左走到起点正下方,再往上回到起点。任意路径中回到起点部分的代价相同,观察代价和的几何意义,就是走过部分的面积的相反数。代价和最小等价于面积最大,故一定是沿着上凸壳行走。
显然起点、终点、凸壳的拐点必须要作为降落点。对于共线的点a1,a2,...,am,若一个点ii的编号是[i,m]中最小的,那么在此处降落可以最小化字典序。
时间复杂度O(nlogn)。
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <queue>
#include <algorithm>
#define lch root<<1
#define rch (root<<1)|1
using namespace std;
typedef long long ll;
const int MAXN = 200000+100;
const ll MOD = 1e9+7;
typedef struct node
{
ll x;
ll y;
int id;
}node;
node point[MAXN];
node result[MAXN];
int n,top;
ll Multiply(node p1,node p2,node p3)
{
return ((p2.x-p1.x)*(p3.y-p1.y)-(p2.y-p1.y)*(p3.x-p1.x));
}
ll Distance(node p1,node p2)
{
return ((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
bool cmp(node p1,node p2)
{
ll m=Multiply(point[1],p1,p2) ;
if(m>0) return false;
else if(m==0&&(Distance(point[1],p1)>Distance(point[1],p2))) //相同斜率的,近的排在前面
return false;
else if(m==0&&(Distance(point[1],p1)==Distance(point[1],p2)))
return p1.id<p2.id;
else return true;
}
void Tubao()
{
int i;
result[0].x=point[1].x;
result[0].y=point[1].y;
result[0].id=point[1].id;
result[1].x=point[2].x;
result[1].y=point[2].y;
result[1].id=point[2].id;
result[2].x=point[3].x;
result[2].y=point[3].y;
result[2].id=point[3].id;
top=3;
for(i=4;i<=n;i++)
{
while(Multiply(result[top-2],result[top-1],point[i])>0&&top>1)
top--;
while(Multiply(result[top-2],result[top-1],point[i])==0&&top>1&&point[i].id<result[top-1].id)
top--;
result[top].x=point[i].x;
result[top].y=point[i].y;
result[top].id=point[i].id;
top++;
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&point[i].x,&point[i].y);
point[i].id=i;
}
sort(point+2,point+n+1,cmp);
int cnt=1;
for(int i=2;i<=n;i++) //去相同点
if(point[cnt].x!=point[i].x||point[cnt].y!=point[i].y)
point[++cnt]=point[i];
n=cnt;
/*point[n+1].x=point[1].x;
point[n+1].y=point[1].y;
point[n+1].id=point[1].id;*/
Tubao();
for(int i=0;i<top;i++)
{
if(i==0) printf("%d",result[i].id);
else printf(" %d",result[i].id);
}
printf("\n");
}
return 0;
}