zoukankan      html  css  js  c++  java
  • 计算几何:POJ 1696 Space Ant 极角排序

    The most exciting space discovery occurred at the end of the 20th century. In 1999, scientists traced down an ant-like creature in the planet Y1999 and called it M11. It has only one eye on the left side of its head and just three feet all on the right side of its body and suffers from three walking limitations:

    1. It can not turn right due to its special body structure.
    2. It leaves a red path while walking.
    3. It hates to pass over a previously red colored path, and never does that.


    The pictures transmitted by the Discovery space ship depicts that plants in the Y1999 grow in special points on the planet. Analysis of several thousands of the pictures have resulted in discovering a magic coordinate system governing the grow points of the plants. In this coordinate system with x and y axes, no two plants share the same x or y.
    An M11 needs to eat exactly one plant in each day to stay alive. When it eats one plant, it remains there for the rest of the day with no move. Next day, it looks for another plant to go there and eat it. If it can not reach any other plant it dies by the end of the day. Notice that it can reach a plant in any distance.
    The problem is to find a path for an M11 to let it live longest.
    Input is a set of (x, y) coordinates of plants. Suppose A with the coordinates (xA, yA) is the plant with the least y-coordinate. M11 starts from point (0,yA) heading towards plant A. Notice that the solution path should not cross itself and all of the turns should be counter-clockwise. Also note that the solution may visit more than two plants located on a same straight line.

    Input

    The first line of the input is M, the number of test cases to be solved (1 <= M <= 10). For each test case, the first line is N, the number of plants in that test case (1 <= N <= 50), followed by N lines for each plant data. Each plant data consists of three integers: the first number is the unique plant index (1..N), followed by two positive integers x and y representing the coordinates of the plant. Plants are sorted by the increasing order on their indices in the input file. Suppose that the values of coordinates are at most 100.

    Output

    Output should have one separate line for the solution of each test case. A solution is the number of plants on the solution path, followed by the indices of visiting plants in the path in the order of their visits.

    Sample Input

    2
    10
    1 4 5
    2 9 8
    3 5 9
    4 1 7
    5 3 2
    6 6 3
    7 10 10
    8 8 1
    9 2 4
    10 7 6
    14
    1 6 11
    2 11 9
    3 8 7
    4 12 8
    5 9 20
    6 3 2
    7 1 6
    8 2 13
    9 15 1
    10 14 17
    11 13 19
    12 5 18
    13 7 3
    14 10 16
    

    Sample Output

    10 8 7 3 4 9 5 6 2 1 10
    14 9 10 11 5 12 8 7 6 13 4 14 1 3 2
    这道题算是凸包的一个前置理解,如果能理解极角排序,就能比较轻松的理解凸包的做法。
    首先要确定一个必然在凸包(最外围)上的点,作为原点,我们记录y坐标最小的点中x坐标最小的点就可以作为原点,然后利用叉积的性质,把其他所有点从右往左排,在一条线上的点按距离排。
    极角排序的写法:
    double xmult(point p0,point p1,point p2)//求叉积
    {
        return(p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
    }
    
    bool Tro(point p1,point p2)//极角排序 
    {
    	int tmp=xmult(p[0],p1,p2);
    	if(tmp>0) return true;
    	else if(tmp==0&&distan(p[0],p1)<distan(p[0],p2)) return true;
    	else return false;
    }
    

     ac代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<stack>
    #include<map>
    #include<vector>
    #include<set>
    #include<queue>
    using namespace std;
    const int MAXN=1e3+10;
    const double eps=1e-4;
    const double mod=1e9+7;
    #define INF 0x7fffffff
    #define ll long long
    #define edl putchar('
    ')
    #define useit  ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    #define FOR(i,a,b) for(int i=a;i<=b;i++)
    #define ROF(i,a,b) for(int i=a;i>=b;i--)
    #define mst(a) memset(a,0,sizeof(a))
    #define mstn(a,n) memset(a,n,sizeof(a))
    #define offset 10000
    #define eps 1e-8
    #define PI acos(-1.0)//3.14159265358979323846
    //判断一个数是否为0,是则返回true,否则返回false
    #define zero(x)(((x)>0?(x):-(x))<eps)
    //返回一个数的符号,正数返回1,负数返回2,否则返回0
    #define _sign(x)((x)>eps?1:((x)<-eps?2:0))
    struct point 
    {
    	int num;
        double x,y;
    };
    struct line
    {
        point a,b;
    };//直线通过的两个点,而不是一般式的三个系数
    //求矢量[p0,p1],[p0,p2]的叉积
    //p0是顶点 
    //若结果等于0,则这三点共线
    //若结果大于0,则p0p2在p0p1的逆时针方向
    //若结果小于0,则p0p2在p0p1的顺时针方向
    double xmult(point p1,point p2,point p0)
    {
        return(p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
    }
    //计算dotproduct(P1-P0).(P2-P0)
    double dmult(point p1,point p2,point p0)
    {
        return(p1.x-p0.x)*(p2.x-p0.x)+(p1.y-p0.y)*(p2.y-p0.y);
    }
    //两点距离
    double distan(point p1,point p2)
    {
        return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
    }
    //判三点共线
    int dots_inline(point p1,point p2,point p3)
    {
        return zero(xmult(p1,p2,p3));
    }
    //判点是否在线段上,包括端点
    int dot_online_in(point p,line l)
    {
        return zero(xmult(p,l.a,l.b))&&(l.a.x-p.x)*(l.b.x-p.x)<eps&&(l.a.y-p.y)*(l.b.y-p.y)<eps;
    }
    //判点是否在线段上,不包括端点
    int dot_online_ex(point p,line l)
    {
        return dot_online_in(p,l)&&(!zero(p.x-l.a.x)||!zero(p.y-l.a.y))&&(!zero(p.x-l.b.x)||!zero(p.y-l.b.y));
    }
    //判两点在线段同侧,点在线段上返回0
    int same_side(point p1,point p2,line l)
    {
        return xmult(l.a,p1,l.b)*xmult(l.a,p2,l.b)>eps;
    }
    //判两点在线段异侧,点在线段上返回0
    int opposite_side(point p1,point p2,line l)
    {
        return xmult(l.a,p1,l.b)*xmult(l.a,p2,l.b)<-eps;
    }
    //判两直线平行
    int parallel(line u,line v)
    {
        return zero((u.a.x-u.b.x)*(v.a.y-v.b.y)-(v.a.x-v.b.x)*(u.a.y-u.b.y));
    }
    //判两直线垂直
    int perpendicular(line u,line v)
    {
        return zero((u.a.x-u.b.x)*(v.a.x-v.b.x)+(u.a.y-u.b.y)*(v.a.y-v.b.y));
    }
    //判两线段相交,包括端点和部分重合
    int intersect_in(line u,line v)
    {
        if(!dots_inline(u.a,u.b,v.a)||!dots_inline(u.a,u.b,v.b))
            return!same_side(u.a,u.b,v)&&!same_side(v.a,v.b,u);
        return dot_online_in(u.a,v)||dot_online_in(u.b,v)||dot_online_in(v.a,u)||dot_online_in(v.b,u);
    }
    //判两线段相交,不包括端点和部分重合
    int intersect_ex(line u,line v)
    {
        return opposite_side(u.a,u.b,v)&&opposite_side(v.a,v.b,u);
    }
    //计算两直线交点,注意事先判断直线是否平行!
    //线段交点请另外判线段相交(同时还是要判断是否平行!)
    point intersection(line u,line v)
    {
        point ret=u.a;
        double t=((u.a.x-v.a.x)*(v.a.y-v.b.y)-(u.a.y-v.a.y)*(v.a.x-v.b.x))/((u.a.x-u.b.x)*(v.a.y-v.b.y)-(u.a.y-u.b.y)*(v.a.x-v.b.x));
        ret.x+=(u.b.x-u.a.x)*t;
        ret.y+=(u.b.y-u.a.y)*t;
        return ret;
    }
    //点到直线上的最近点
    point ptoline(point p,line l)
    {
        point t=p;
        t.x+=l.a.y-l.b.y,t.y+=l.b.x-l.a.x;
        line s;
        s.a=p;
    	s.b=t; 
        return intersection(s,l);
    }
    //点到直线距离
    double disptoline(point p,line l)
    {
        return fabs(xmult(p,l.a,l.b))/distan(l.a,l.b);
    }
    //点到线段上的最近点
    point ptoseg(point p,line l)
    {
        point t=p;
        t.x+=l.a.y-l.b.y,t.y+=l.b.x-l.a.x;
        if(xmult(l.a,t,p)*xmult(l.b,t,p)>eps)
            return distan(p,l.a)<distan(p,l.b)?l.a:l.b;
        line s;
        s.a=p;
        s.b=t;
        return intersection(s,l);
    }
    //点到线段距离
    double disptoseg(point p,line l)
    {
        point t=p;
        t.x+=l.a.y-l.b.y,t.y+=l.b.x-l.a.x;
        if(xmult(l.a,t,p)*xmult(l.b,t,p)>eps)
            return distan(p,l.a)<distan(p,l.b)?distan(p,l.a):distan(p,l.b);
        return fabs(xmult(p,l.a,l.b))/distan(l.a,l.b);
    }
    struct TPoint
    {
        double x,y;
        TPoint operator-(TPoint&a)
        {
            TPoint p1;
            p1.x=x-a.x;
            p1.y=y-a.y;
            return p1;
        }
    };
    
    struct TLine
    {
        double a,b,c;
    };
    
    //求p1关于p2的对称点
    TPoint symmetricalPoint(TPoint p1,TPoint p2)
    {
        TPoint p3;
        p3.x=2*p2.x-p1.x;
        p3.y=2*p2.y-p1.y;
        return p3;
    }
    //p点关于直线L的对称点
    TPoint symmetricalPointofLine(TPoint p,TLine L)
    {
        TPoint p2;
        double d;
        d=L.a*L.a+L.b*L.b;
        p2.x=(L.b*L.b*p.x-L.a*L.a*p.x-2*L.a*L.b*p.y-2*L.a*L.c)/d;
        p2.y=(L.a*L.a*p.y-L.b*L.b*p.y-2*L.a*L.b*p.x-2*L.b*L.c)/d;
        return p2;
    }
    //求线段所在直线,返回直线方程的三个系数
    //两点式化为一般式
    TLine lineFromSegment(TPoint p1,TPoint p2)
    {
        TLine tmp;
        tmp.a=p2.y-p1.y;
        tmp.b=p1.x-p2.x;
        tmp.c=p2.x*p1.y-p1.x*p2.y;
        return tmp;
    }
    //求直线的交点
    //求直线的交点,注意平行的情况无解,避免RE
    TPoint LineInter(TLine l1,TLine l2)
    {
        //求两直线得交点坐标
        TPoint tmp;
        double a1=l1.a;
        double b1=l1.b;
        double c1=l1.c;
        double a2=l2.a;
        double b2=l2.b;
        double c2=l2.c;
        //注意这里b1=0
        if(fabs(b1)<eps){
            tmp.x=-c1/a1;
            tmp.y=(-c2-a2*tmp.x)/b2;
        }
        else{
            tmp.x=(c1*b2-b1*c2)/(b1*a2-b2*a1);
            tmp.y=(-c1-a1*tmp.x)/b1;
        }
        //cout<<"交点坐标"<<endl;
        //cout<<a1*tmp.x+b1*tmp.y+c1<<endl;
        //cout<<a2*tmp.x+b2*tmp.y+c2<<endl;
        return tmp;
    }
    //矢量(点)V以P为顶点逆时针旋转angle(弧度)并放大scale倍
    point rotate(point v,point p,double angle,double scale){
        point ret=p;
        v.x-=p.x,v.y-=p.y;
        p.x=scale*cos(angle);
        p.y=scale*sin(angle);
        ret.x+=v.x*p.x-v.y*p.y;
        ret.y+=v.x*p.y+v.y*p.x;
        return ret;
    }
    //矢量(点)V以P为顶点逆时针旋转angle(弧度)
    point rotate(point v,point p,double angle){
        double cs=cos(angle),sn=sin(angle);
        v.x-=p.x,v.y-=p.y;
        p.x+=v.x*cs-v.y*sn;
        p.y+=v.x*sn+v.y*cs;
        return p;
    }
    struct kk
    {
    	double cos,dis;
    	int num;
    };
    int T,n,pos,j;
    point p[55],res[55];
    
    bool cmp(const point &a,const point &b)
    {
    	double temp;
    	temp=xmult(a,b,p[pos]);
    	if(temp>eps)
    	{
    		return true;
    	}
    	else if(zero(temp)&&distan(p[pos],a)<distan(p[pos],b))
    	{
    		return true;
    	}
    	return false;
    }
    
    int main()
    {
    	cin>>T;
    	while(T--)
    	{
    		j=pos=1;
    		scanf("%d",&n);
    		FOR(i,1,n)
    		{
    			cin>>p[i].num>>p[i].x>>p[i].y;
    			if(p[i].y<p[1].y)
    			swap(p[i],p[1]);
    		}
    		sort(p+2,p+n+1,cmp);
    		res[j++]=p[pos++];
    		FOR(i,2,n)
    		{
    			sort(p+pos,p+n+1,cmp);
    			res[j++]=p[pos++];
    		}
    		res[j++]=p[pos++];
    		printf("%d",n);
    		FOR(i,1,n)
    		{
    			printf(" %d",res[i].num);
    		}
    		printf("
    ");
    	}
    }
    

      

  • 相关阅读:
    java对象序列化机制
    进程和线程的区别
    关于Java中的String类的不可变
    计算机编码ASCII、UNICODE和UTF-8
    mybatis中的一级缓存和二级缓存
    SQL语句之EXSITS谓词
    git学习笔记
    hdu1542-扫描线求矩形面积并
    首场ACM总结——2019JXCPC(CCPC江西省省赛)
    hdu1199(离散化线段树)
  • 原文地址:https://www.cnblogs.com/qq936584671/p/7794922.html
Copyright © 2011-2022 走看看