欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

2018 Multi-University Training Contest 3 1007 / hdu6325 Problem G. Interstellar Travel 凸包

程序员文章站 2022-03-30 08:58:16
...

Problem G. Interstellar Travel

题意:
给定平面上n个点,起点1 为(0,0),终点 n 为(Xn, 0),其它点的横坐标 0 <Xi<Xn,纵坐标 Yi >=0。每次可以飞到一个横坐标严格更大的点,代价为两个坐标的叉积。求起点到终点总代价最小的飞行路线,并输出字典序最小的路线。2≤n≤200000。 Shortest judge solution: 979 bytes
题解:
显然坐标相同的点里只保留编号最小的点最优。
将起点到终点的路径补全为终点往下走到无穷远处,再往左走到起点正下方,再往上回到起点。
任意路径中回到起点部分的代价相同,观察代价和的几何意义,就是走过部分的面积的相反数。
代价和最小等价于面积最大,故一定是沿着上凸壳行走。
显然起点、终点、凸壳的拐点必须要作为降落点。
对于共线的点a1,a2,...,am,若一个点i的编号是[i,m]中最小的,那么在此处降落可以最小化字典序。时间复杂度O(nlogn)。


1、其实首先要明确从点1 直接到点 n 的花费是 0,所以再加入其它点的代价必须为负。
2、根据叉积的几何意义,面积要最大,那就是求凸包。
3、最皮的是要字典序最小输出,也就是在凸包边界线上的点也有可能要取。所以我们记录一下边界线上的点是否小于它到拐点之间的点。


#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 200005;

struct Point {
    ll  x, y;  int id;
    Point(ll x=0, ll y=0):x(x),y(y){} //构造函数,方便代码编写
};
typedef Point Vector;
Vector operator +(Vector A, Vector B){ return Vector(A.x+B.x, A.y+B.y); }
Vector operator -(Point A, Point B){ return Vector(A.x-B.x, A.y-B.y); }
bool operator <(const Point& a, const Point& b){
    if(a.x==b.x && a.y==b.y) return a.id < b.id;
    return a.x<b.x || (a.x==b.x && a.y<b.y);
}
ll  Cross(Vector A, Vector B){ return A.x*B.y - A.y*B.x; }

bool used[N];
int mi[N];
void ConvexHull(Point* p, int n, Point* ch)
{
    sort(p, p+n);
    int m = 0;
    for(int i=0; i<n; ++i) {  //维护上凸包
        if(i>1 && p[i].x==p[i-1].x && p[i].y==p[i-1].y) continue;
        while(m>1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2])>0) --m;
        ch[m++] = p[i];
    }
    mes(used, false);
    used[0] = used[m-1] = true;
    for(int i=1; i<m; ++i) {
        if(Cross(ch[i]-ch[i-1], ch[i+1]-ch[i-1]) != 0)
            used[i] = true;
    }
    for(int i=m; i>=0; --i) {
        if(used[i]) mi[i] = ch[i].id;
        else  mi[i] = min(mi[i+1], ch[i].id);
    }
    for(int i=0; i<m; ++i)
        if(mi[i] == ch[i].id)
            printf("%d%c", ch[i].id, " \n"[i==m-1]);
}
Point p[N];
Point vertex[N];
int main()
{
    int T;  scanf("%d", &T);
    while(T--)
    {
        int n;  scanf("%d", &n);
        rep(i,1,n) scanf("%lld%lld", &p[i].x, &p[i].y), p[i].id=i;
        ConvexHull(p+1, n, vertex);
    }

    return 0;
}