题目链接:哆啦A梦传送门
题意:给出n个点的坐标,现在要从第一个点走到最后一个点,从第i个点走到第j个点需要花费 ai.x+b.y-a.y*b.x,要使得走的路线花费最少,输出路线。
题解:参考题解:https://blog.youkuaiyun.com/a1325136367/article/details/81297176
一看到那个式子,就马上能看出是个叉积公式,而我们走的路线又是一个半多边形,并且我们也知道叉积的几何意义就为多边形面积的一半,也就是说此时路线的花费跟路线所围成的多边形的面积相关,那么我们知道凸包是把点围住的最小面积,也就是我们能走的最大面积,因为我们是顺时针走的(叉积为负),故这道题就可看做是为求上凸包。
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int maxn=200010;
struct point{
LL x,y;
int id;
bool operator < (const point &a) const { ///排序
if(x!=a.x) return x<a.x;
if(y!=a.y) return y<a.y;
return id<a.id;
}
friend point operator - (const point &a,const point &b){
point c;
c.x=a.x-b.x;
c.y=a.y-b.y;
c.id=a.id;
return c;
}
LL operator * (point &a){ ///叉积
return x*a.y-a.x*y;
}
}p[maxn],ch[maxn];
bool operator != (const point &a,const point &b){
return a.x!=b.x||a.y!=b.y;
}
bool judge(point a,point b,point c) ///求上凸包判断
{
c=c-a;
b=b-a;
if(b*c>0) return 1;
else if(b*c==0&&c.id<b.id) return 1;
else return false;
}
int main()
{
int ncase,n;
scanf("%d",&ncase);
while(ncase--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&p[i].x,&p[i].y),p[i].id=i;
sort(p+2,p+n); ///除掉头尾,排序
int tot=0;
ch[++tot]=p[1];
ch[++tot]=p[2];
for(int i=3;i<=n;i++) ///求上凸包
{
if(p[i]!=p[i-1]){ ///去重
while(tot>=2&&judge(ch[tot-1],ch[tot],p[i])) tot--;
ch[++tot]=p[i];
}
}
for(int i=1;i<tot;i++)
printf("%d ",ch[i].id);
printf("%d\n",ch[tot].id);
}
return 0;
}