POJ 1696 Space Ant(凸包变形)
http://poj.org/problem?id=1696
题意:
给你平面的一个点集,然后要你从y坐标最小的点开始以水平方向向下一个点连线,每次只能直走或左转,问你最多能走多少点?(输出所有走过的点,且不走回头路,所有点坐标均唯一)
分析:
本题其实就是用极角排序,每次都有一个你的当前点,然后每次都贪心的走以当前点为中心的极角最小的那个点(如果有多个,就走距离当前点最近的那个点即可.)
这样,我们能保证能走过的点数是最多的.
为什么上述贪心算法是对的呢?其实这就是每次都在找最外层的凸包.
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
//精度控制
const double eps=1e-10;
int dcmp(double x)
{
if(fabs(x)<eps) return 0;
return x<0?-1:1;
}
//点
struct Point
{
int id;
double x,y;
Point(){}
Point(int id,double x,double y):id(id),x(x),y(y){}
}cur_point;//cur_point是当前参照点
//向量
typedef Point Vector;
//点-点==向量
Vector operator-(Point A,Point B)
{
return Vector(-1,A.x-B.x,A.y-B.y);
}
//叉积
double Cross(Vector A,Vector B)
{
return A.x*B.y-A.y*B.x;
}
//距离的平方
double Length2(Point A,Point B)
{
return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y);
}
//用于排序的比较函数,这里对比的是与参照点的极角序
bool cmp(const Point& A,const Point& B)
{
double c=Cross(A-cur_point, B-cur_point);
if(c>0) return true;
else if(c<0) return false;
double len1=Length2(A,cur_point);
double len2=Length2(B,cur_point);
return len1<len2;
}
const int maxn=50+5;
Point p[maxn];
int main()
{
int T; scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
cur_point=Point(0,1e10,1e10);
for(int i=0;i<n;i++)
{
scanf("%d%lf%lf",&p[i].id,&p[i].x,&p[i].y);
if(p[i].y<cur_point.y || (p[i].y==cur_point.y&&p[i].x<cur_point.x) )
cur_point=Point(p[i].id,p[i].x,p[i].y);
}
swap(p[0],p[cur_point.id-1]);//将y坐标最小的点交换到0号去
int ans[maxn];//按顺序记录所有点的原始编号
int cnt=0;
ans[cnt++]=cur_point.id;//第1个点
for(int i=1;i<n;i++)
{
sort(p+i,p+n,cmp);//把剩下的点按参照点从新极角排序
cur_point=p[i];
ans[cnt++]=cur_point.id;
}
printf("%d",cnt);
for(int i=0;i<cnt;i++)
printf(" %d",ans[i]);
printf("\n");
}
return 0;
}