2018 Multi-University Training Contest 3: G. Interstellar Travel(凸包)

题意:给你平面上n个点,第一个点一定在(0, 0),第n个点的y坐标一定为0,除此之外中间所有点的x坐标一定大于0且小于最后一个点的x坐标,你从一号点开始出发,中间从第i个点到第j个点必须满足xj>xi,会消耗xi*yj-xj*yi点能量,这个值可以为负,为负相当于获得能量,求出一条字典序最小的路径,满足从1出发,终点为n,消耗的能量最少(或者获得的能量最多)

  • 首先这道题肯定不用消耗能量的,因为你最坏情况下从1直接到n
  • 可以发现式子xi*yj-xj*yi其实就是两个向量(x1, y1), (x2, y2)的叉积,而叉积正是两个向量构成的平行四边形面积
  • 接上,如果向量(x2, y2)在向量(x1, y1)的下面,那么贡献就为正(相当于获得能量),否则为负
  • 知道这点后,你就会发现当且仅当走出一个凸包时,获得的能量是最多的,正好等于凸包的面积
  • 所以这题就是求一下凸包就行了,所有在凸包上的点,就是你依次要经过的点

可是并没有这么简单,因为题目还要求字典序最小,比如下面这种情况(每个数字代表点的编号):

  • 其中1,2,4,7是必经点,3,5,6于可走可不走的,剩下的在凸包内部的点全部不能走
  • 这样就有不止一种走法了,其中字典序最小的走法为:0 2 4 5 7
  • 所以还要再贪心一下
#include<stdio.h>
#include<limits.h>
#include<stdlib.h>
#include<string.h>
#include<deque>
#include<algorithm>
using namespace std;
#define LL long long
typedef struct
{
	LL x, y;
	LL id;
}Point;
LL n, temp, net[200005], Min[200005], ans[200005];
Point top2, top1, s[200005];
deque<Point> q;
bool comp2(Point a, Point b)	/*按幅角排序*/
{
	if(a.x*b.y-a.y*b.x>0)
		return 1;
	else if(a.x*b.y-a.y*b.x==0 && a.x>b.x || a.x*b.y-a.y*b.x==0 && a.x==b.x && a.id<b.id)
		return 1;
	return 0;
}
int main(void)
{
	LL T, cnt, i, now, p;
	scanf("%lld", &T);
	while(T--)
	{
		scanf("%lld", &n);
		p = n;
		memset(net, 0, sizeof(net));
		for(i=1;i<=n;i++)
		{
			scanf("%lld%lld", &s[i].x, &s[i].y);
			Min[i] = i;
			s[i].id = i;
		}
		sort(s+1, s+n+1, comp2);
		cnt = 0;
		for(i=1;i<=n;i++)
		{
			if(s[i].x==s[i-1].x && s[i].y==s[i-1].y)
				continue;
			s[++cnt] = s[i];
		}
		n = cnt;
		q.push_back(s[1]);
		q.push_back(s[2]);
		temp = 3;
		while(temp<=n)
		{
			top1 = q.back();
			q.pop_back();
			if(q.empty())
			{
				q.push_back(top1);
				q.push_back(s[temp]);
				temp++;
				continue;
			}
			top2 = q.back();
			if((top2.x-top1.x)*(s[temp].y-top1.y)-(top2.y-top1.y)*(s[temp].x-top1.x)<=0)
			{
				if((top2.x-top1.x)*(s[temp].y-top1.y)-(top2.y-top1.y)*(s[temp].x-top1.x)<0)
				{
					q.push_back(top1);
					q.push_back(s[temp]);
					temp++;
				}
				else
				{
					net[s[temp].id] = top1.id;
					Min[s[temp].id] = min(s[temp].id, Min[top1.id]);
					q.push_back(s[temp]);
					temp++;
				}
			}
		}
		cnt = 0;
		while(q.empty()==0)
		{
			top1 = q.back();
			ans[++cnt] = top1.id;
			q.pop_back();
		}
		for(i=1;i<=cnt-1;i++)
		{
			printf("%lld ", ans[i]);
			now = net[ans[i]];
			while(now)
			{
				if(Min[now]>ans[i+1])
					break;
				else if(Min[now]==now)
					printf("%lld ", now);
				now = net[now];
			}
		}
		printf("%lld\n", p);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Jaihk662/article/details/81290528