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; }