PS:首先这样的路线是按照顺时针走的,那么两个点的叉积必定是负数,负数最小,则代表面积最大,因为叉积还可以表示成三角形的面积,想象一下,路线与x轴围成的面积最大,那么路线应该绕着上凸包。重点来了,字典序最小。因为凸包的拐点、起点、终点是必须要的,那么为了最小化字典序,在同一条边上共线的点中(除了这条边的起点和终点)选一个编号最小的点作降落点M,然后从M点开始,选某个字典序最小的递增序列。比如 3 1 4 2 7,选3 1 2 7,M点是编号为1的点。
//#include<bits/stdc++.h> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<bitset> #include<vector> #include<queue> #include<map> #include<string> #include<stack> #define ll long long #define P pair<int, int> #define PP pair<int,pair<int, int>> #define pb push_back #define pp pop_back #define lson root << 1 #define INF (int)2e9 + 7 #define rson root << 1 | 1 #define LINF (unsigned long long int)1e18 #define mem(arry, in) memset(arry, in, sizeof(arry)) using namespace std; const int N = 200005; int n, T, ans[N]; bool use[N]; struct node { int x, y, id; } a[N], p[N]; bool cmp(const node& aa, const node& bb) { if(aa.x != bb.x) return aa.x < bb.x; if(aa.y != bb.y) return aa.y > bb.y; return aa.id < bb.id; } ll Cross(const node& aa, const node& bb, const node& cc) { return 1ll * (bb.y - aa.y) * (cc.x - bb.x) - 1ll * (bb.x - aa.x) * (cc.y - bb.y); } int main() { cin >> T; while(T--) { scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d %d", &a[i].x, &a[i].y), a[i].id = i; sort(a + 1, a + n + 1, cmp); int t = 0; for(int i = 1; i <= n; i++) { if(i > 1 && a[i].x == a[i - 1].x) continue; while(t > 1 && Cross(p[t - 1], p[t], a[i]) < 0) t--; p[++t] = a[i]; } mem(use, 0); use[1] = use[t] = 1; for(int i = 2; i < t; i++) { if(Cross(p[i - 1], p[i], p[i + 1]) != 0) use[i] = 1; } for(int i = t; i; i--) { if(use[i]) ans[i] = p[i].id; else ans[i] = min(ans[i + 1], p[i].id); } for(int i = 1; i < t; i++) if(ans[i] == p[i].id) printf("%d ", ans[i]); printf("%d ", ans[t]); } return 0; }