HDU 6325 Problem G. Interstellar Travel(凸包)

题目链接

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,所以不可能在原地飞行。

将起点到终点的路径补全为终点往下走到无穷远处,再往左走到起点正下方,再往上回到起点。任意路径中回到起点部分的代价相同,观察代价和的几何意义,就是走过部分的面积的相反数。代价和最小等价于面积最大,故一定是沿着上凸壳行走。

显然起点、终点、凸壳的拐点必须要作为降落点。对于共线的点a​1​​,a​2​​,...,a​m​​,若一个点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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值