http://acm.hdu.edu.cn/showproblem.php?pid=6325
从凸包上面走会更优,所以答案的解肯定包括起点,终点,凸包拐点。
假如凸包一条边上有多点共线,那么就要看边上的点编号是否比边的终点编号小,是的话放到答案里解会更优。因为题目求的是字典序最小。
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL long long
const int maxn = 200000+33;
struct point
{
LL x,y;
int id;
bool friend operator < (point a,point b)
{
if(a.x!=b.x) return a.x<b.x;
if(a.y!=b.y) return a.y>b.y;
return a.id<b.id;
}
}a[maxn];
point operator -(point A, point B) {
point a;
a.x=A.x-B.x;
a.y=A.y-B.y;
return a;
}
LL Cross(point A, point B) {return A.x*B.y-A.y*B.x;}
point top[maxn];
int n;
int G_tb()
{
int m=0;
for(int i=1;i<=n;i++){
if(i!=1&&a[i].x==a[i-1].x) continue;
while (m>1 && Cross(top[m]-top[m-1],a[i]-top[m-1]) > 0) m--;
top[++m]=a[i];
}
return m;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&a[i].x,&a[i].y);
a[i].id=i;
}
sort(a+1,a+1+n);
int m=G_tb();
int vis[maxn],ans[maxn];
memset(vis,0,sizeof vis);
vis[1]=vis[m]=1;
for(int i=2;i<m;i++){
if(Cross(top[i]-top[i-1],top[i+1]-top[i-1])!=0)
vis[i]=1;
}
ans[m]=top[m].id;
for(int i=m-1;i>=1;i--){
if(vis[i]) ans[i]=top[i].id;
else{
ans[i]=min(ans[i+1],top[i].id);
}
}
for(int i=1;i<m;i++){
if(ans[i]==top[i].id)
printf("%d ",ans[i]);
}
printf("%d\n",n);
}
return 0;
}