Problem G. Interstellar Travel
题意:
给定平面上n个点,起点1 为(0,0),终点 n 为(Xn, 0),其它点的横坐标 0
题解:
显然坐标相同的点里只保留编号最小的点最优。
将起点到终点的路径补全为终点往下走到无穷远处,再往左走到起点正下方,再往上回到起点。
任意路径中回到起点部分的代价相同,观察代价和的几何意义,就是走过部分的面积的相反数。
代价和最小等价于面积最大,故一定是沿着上凸壳行走。
显然起点、终点、凸壳的拐点必须要作为降落点。
对于共线的点a1,a2,...,am,若一个点i的编号是[i,m]中最小的,那么在此处降落可以最小化字典序。时间复杂度O(nlogn)。
1、其实首先要明确从点1 直接到点 n 的花费是 0,所以再加入其它点的代价必须为负。
2、根据叉积的几何意义,面积要最大,那就是求凸包。
3、最皮的是要字典序最小输出,也就是在凸包边界线上的点也有可能要取。所以我们记录一下边界线上的点是否小于它到拐点之间的点。
#include
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.x1 && 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=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