题意:给你平面上n个点,第一个点一定在(0, 0),第n个点的y坐标一定为0,除此之外中间所有点的x坐标一定大于0且小于最后一个点的x坐标,你从一号点开始出发,中间从第i个点到第j个点必须满足xj>xi,会消耗xi*yj-xj*yi点能量,这个值可以为负,为负相当于获得能量,求出一条字典序最小的路径,满足从1出发,终点为n,消耗的能量最少(或者获得的能量最多)
- 首先这道题肯定不用消耗能量的,因为你最坏情况下从1直接到n
- 可以发现式子xi*yj-xj*yi其实就是两个向量(x1, y1), (x2, y2)的叉积,而叉积正是两个向量构成的平行四边形面积
- 接上,如果向量(x2, y2)在向量(x1, y1)的下面,那么贡献就为正(相当于获得能量),否则为负
- 知道这点后,你就会发现当且仅当走出一个凸包时,获得的能量是最多的,正好等于凸包的面积
- 所以这题就是求一下凸包就行了,所有在凸包上的点,就是你依次要经过的点
可是并没有这么简单,因为题目还要求字典序最小,比如下面这种情况(每个数字代表点的编号):
- 其中1,2,4,7是必经点,3,5,6于可走可不走的,剩下的在凸包内部的点全部不能走
- 这样就有不止一种走法了,其中字典序最小的走法为:0 2 4 5 7
- 所以还要再贪心一下
#include<stdio.h>
#include<limits.h>
#include<stdlib.h>
#include<string.h>
#include<deque>
#include<algorithm>
using namespace std;
#define LL long long
typedef struct
{
LL x, y;
LL id;
}Point;
LL n, temp, net[200005], Min[200005], ans[200005];
Point top2, top1, s[200005];
deque<Point> q;
bool comp2(Point a, Point b) /*按幅角排序*/
{
if(a.x*b.y-a.y*b.x>0)
return 1;
else if(a.x*b.y-a.y*b.x==0 && a.x>b.x || a.x*b.y-a.y*b.x==0 && a.x==b.x && a.id<b.id)
return 1;
return 0;
}
int main(void)
{
LL T, cnt, i, now, p;
scanf("%lld", &T);
while(T--)
{
scanf("%lld", &n);
p = n;
memset(net, 0, sizeof(net));
for(i=1;i<=n;i++)
{
scanf("%lld%lld", &s[i].x, &s[i].y);
Min[i] = i;
s[i].id = i;
}
sort(s+1, s+n+1, comp2);
cnt = 0;
for(i=1;i<=n;i++)
{
if(s[i].x==s[i-1].x && s[i].y==s[i-1].y)
continue;
s[++cnt] = s[i];
}
n = cnt;
q.push_back(s[1]);
q.push_back(s[2]);
temp = 3;
while(temp<=n)
{
top1 = q.back();
q.pop_back();
if(q.empty())
{
q.push_back(top1);
q.push_back(s[temp]);
temp++;
continue;
}
top2 = q.back();
if((top2.x-top1.x)*(s[temp].y-top1.y)-(top2.y-top1.y)*(s[temp].x-top1.x)<=0)
{
if((top2.x-top1.x)*(s[temp].y-top1.y)-(top2.y-top1.y)*(s[temp].x-top1.x)<0)
{
q.push_back(top1);
q.push_back(s[temp]);
temp++;
}
else
{
net[s[temp].id] = top1.id;
Min[s[temp].id] = min(s[temp].id, Min[top1.id]);
q.push_back(s[temp]);
temp++;
}
}
}
cnt = 0;
while(q.empty()==0)
{
top1 = q.back();
ans[++cnt] = top1.id;
q.pop_back();
}
for(i=1;i<=cnt-1;i++)
{
printf("%lld ", ans[i]);
now = net[ans[i]];
while(now)
{
if(Min[now]>ans[i+1])
break;
else if(Min[now]==now)
printf("%lld ", now);
now = net[now];
}
}
printf("%lld\n", p);
}
return 0;
}