1. 程式人生 > >Problem G. Interstellar Travel  HDU

Problem G. Interstellar Travel  HDU

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 nn planets in space, labeled by 11 to nn. To his surprise, these planets are all coplanar. So to simplify, Little Q put these nnplanets on a plane coordinate system, and calculated the coordinate of each planet (xi,yi)(xi,yi).  Little Q plans to start his journey at the 11-th planet, and end at the nn-th planet. When he is at the ii-th planet, he can next fly to the jj-th planet only if xi<xjxi<xj, which will cost his spaceship xi×yj−xj×yixi×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)T(1≤T≤10), denoting the number of test cases.  In each test case, there is an integer n(2≤n≤200000)n(2≤n≤200000) in the first line, denoting the number of planets.  For the next nn lines, each line contains 22 integers xi,yi(0≤xi,yi≤109)xi,yi(0≤xi,yi≤109), denoting the coordinate of the ii-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<xny1=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)p1,p2,...,pm(1≤pi≤n), denoting the route you chosen is p1→p2→...→pm−1→pmp1→p2→...→pm−1→pm. Obviously p1p1 should be 11 and pmpm should be nn. 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 aa is lexicographically smaller than a sequence of bb if there exists such index jj that ai=biai=bi for all i<ji<j, but aj<bjaj<bj. 

Sample Input

1
3
0 0
3 0
4 0

Sample Output

1 2 3

題意:給定平面上n個點,起點橫座標最小,終點橫座標最大。每次可以飛到一個橫座標嚴格更大的點,代價為兩個座標的叉積。 求起點到終點總代價最小的飛行路線,並輸出字典序最小的路線。

思路:因為凸包是求逆時針的時候面積包住所有點的面積最大,那麼我們題目這樣是順時針(橫座標越來越大),相反的就是面積最小,所以這題就是求順時針的凸包,那肯定就包括起點、終點、凸包的拐點了,只要把上凸包求出來就好。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
#define ll long long
struct node
{
    ll x,y;
    int id;
}a[N];
int s[N];
bool cmp(node a,node b)
{
    if(a.x==b.x)
    {
        if(a.y==b.y)
        {
            return a.id<b.id;
        }
       return a.y>b.y;
    }
    else return a.x<b.x;
}
bool judge(int x,int y,int z)
{
    if((a[z].y-a[x].y)*(a[y].x-a[x].x)==(a[z].x-a[x].x)*(a[y].y-a[x].y))
        return a[z].id<a[y].id;
    if((a[z].y-a[x].y)*(a[y].x-a[x].x)>(a[z].x-a[x].x)*(a[y].y-a[x].y))
        return true;
    return false;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld%lld",&a[i].x,&a[i].y);
            a[i].id=i;
        }
        sort(a+1,a+1+n,cmp);
        int top=0;
        s[top++]=1;
        for(int i=2;i<=n;i++)
        {
            if(a[i].x==a[i-1].x&&a[i].y==a[i-1].y)
                continue;
            while(top>=2&&judge(s[top-2],s[top-1],i))
                top--;
            s[top++]=i;
        }
        for(int i=0;i<top;i++)
        {
            if(i)printf(" ");
            printf("%d",a[s[i]].id);
        }
        printf("\n");
    }
    return 0;
}

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 200000 + 10;

struct point
{
	ll x, y;
	int id;
} a[maxn], p[maxn], b[maxn];

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

ll cross(point p0, point p1, point p2)//計算叉乘,注意p0,p1,p2的位置,這個決定了方向
{
	return (p1.x - p0.x)*(p2.y - p0.y) - (p1.y - p0.y)*(p2.x - p0.x);
}

ll dis(point a, point b)//計算距離,這個用在了當兩個點在一條直線上
{
	return (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);
}

bool cmp(point p1, point 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(point p1, point 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;
}