Interstellar Travel(凸包 hdu6325)

题目:Problem G. Interstellar Travel

官方题解:

显然坐标相同的点里只保留编号最小的点最优。

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

显然起点、终点、凸壳的拐点必须要作为降落点。对于共线的点a1,a2,...,am,若一个点ii的编号是[i,m]中最小的,那么在此处降落可以最小化字典序。

时间复杂度O(nlog n)。

#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <algorithm>
using namespace std;

typedef long long ll;

const int MAX = 200000 + 10;

struct node
{
	ll x, y;
	int id;
} a[MAX], p[MAX], b[MAX];

int top, n, tot;
int ans[MAX];
bool vis[MAX];

ll cross(node p0, node p1, node p2)//计算叉乘,注意p0,p1,p2的位置,这个决定了方向
{
	return (p1.x - p0.x)*(p2.y - p0.y) - (p1.y - p0.y)*(p2.x - p0.x);
}

ll dis(node a, node b)//计算距离,这个用在了当两个点在一条直线上
{
	return (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);
}

bool cmp(node p1, node p2)//去重
{
	if (p1.x == p2.x)
	{
		if (p1.y == p2.y)
			return p1.id<p2.id;
		return p1.y<p2.y;
	}
	return p1.x<p2.x;
}

bool cmp2(node p1, node p2)//极角排序
{
	ll z = cross(a[0], p1, p2);
	if (z>0 || (z == 0 && dis(a[0], p1)<dis(a[0], p2)))
		return 1;
	return 0;
}

void Graham()
{
	sort(a + 1, a + tot, cmp2);
	top = 1;
	p[0] = a[0];
	p[1] = a[1];
	for (int i = 2; i<tot; i++)
	{
		while (cross(p[top - 1], p[top], a[i])<0 && top)
			top--;
		top++;
		p[top] = a[i];
	}
}

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		memset(vis, false, sizeof(vis));
		scanf("%d", &n);
		int flag = 0;
		for (int i = 0; i<n; i++)
		{
			scanf("%lld%lld", &b[i].x, &b[i].y);
			b[i].id = i + 1;
			if (b[i].y != 0)
				flag = 1;
		}

		//去重
		sort(b + 1, b + n, cmp);
		a[0] = b[0];
		tot = 1;
		for (int i = 1; i<n; i++)
		{
			if (b[i].x == b[i - 1].x&&b[i].y == b[i - 1].y)
			{
				continue;
			}
			else
			{
				a[tot++] = b[i];
			}
		}

		//计算凸包
		Graham();

		//特判全在x轴上的情况
		if (flag == 0)
		{
			int cnt = 0;
			ans[++cnt] = p[top].id;
			for (int i = top - 1; i >= 1; i--)
			{
				if (p[i].id<ans[cnt])
					ans[++cnt] = p[i].id;
			}
			ans[++cnt] = p[0].id;
			printf("%d", ans[cnt]);
			for (int i = cnt - 1; i >= 1; i--)
			{
				printf(" %d", ans[i]);
			}
			printf("\n");
			continue;
		}

		//把共线但没包含在凸包p[]中的点加进来
		//这些点只会出现在p[0]和p[top]的连线上
		int now = top;
		int nowid = p[top].id;
		for (int i = tot - 1; i>0; i--)
		{
			ll z = cross(p[now], p[0], a[i]);
			if (z == 0 && a[i].id != nowid) {
				p[++top] = a[i];
			}
		}

		//记录拐点
		sort(p, p + top + 1, cmp);
		for (int i = 1; i < top; i++)
		{
			if (cross(p[i - 1], p[i], p[i + 1]) != 0)
				vis[i] = true;
		}


		int cnt = 0;
		ans[++cnt] = p[top].id;
		for (int i = top - 1; i >= 0; i--)
		{
			if (vis[i] == true)
			{
				ans[++cnt] = p[i].id;
				continue;
			}
			if (p[i].id < ans[cnt])
				ans[++cnt] = p[i].id;
		}

		printf("%d", ans[cnt]);
		for (int i = cnt - 1; i >= 1; i--)
		{
			printf(" %d", ans[i]);
		}
		printf("\n");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/luyehao1/article/details/81382354
今日推荐