计算几何 常用算法模版

/*
计算几何

目录 
㈠ 点的基本运算 
1. 平面上两点之间距离 1 
2. 判断两点是否重合 1 
3. 矢量叉乘 1 
4. 矢量点乘 2 
5. 判断点是否在线段上 2 
6. 求一点饶某点旋转后的坐标 2 
7. 求矢量夹角 2 

㈡ 线段及直线的基本运算 
1. 点与线段的关系 3 
2. 求点到线段所在直线垂线的垂足 4 
3. 点到线段的最近点 4 
4. 点到线段所在直线的距离 4 
5. 点到折线集的最近距离 4 
6. 判断圆是否在多边形内 5 
7. 求矢量夹角余弦 5 
8. 求线段之间的夹角 5 
9. 判断线段是否相交 6 
10.判断线段是否相交但不交在端点处 6 
11.求线段所在直线的方程 6 
12.求直线的斜率 7 
13.求直线的倾斜角 7 
14.求点关于某直线的对称点 7 
15.判断两条直线是否相交及求直线交点 7 
16.判断线段是否相交,如果相交返回交点 7 

㈢ 多边形常用算法模块 
1. 判断多边形是否简单多边形 8 
2. 检查多边形顶点的凸凹性 9 
3. 判断多边形是否凸多边形 9 
4. 求多边形面积 9 
5. 判断多边形顶点的排列方向,方法一 10 
6. 判断多边形顶点的排列方向,方法二 10 
7. 射线法判断点是否在多边形内 10 
8. 判断点是否在凸多边形内 11 
9. 寻找点集的graham算法 12 
10.寻找点集凸包的卷包裹法 13 
11.判断线段是否在多边形内 14 
12.求简单多边形的重心 15 
13.求凸多边形的重心 17 
14.求肯定在给定多边形内的一个点 17 
15.求从多边形外一点出发到该多边形的切线 18 
16.判断多边形的核是否存在 19 

㈣ 圆的基本运算 
1 .点是否在圆内 20 
2 .求不共线的三点所确定的圆 21 

㈤ 矩形的基本运算 
1.已知矩形三点坐标,求第4点坐标 22 

㈥ 常用算法的描述 22 

㈦ 补充 
1.两圆关系: 24 
2.判断圆是否在矩形内: 24 
3.点到平面的距离: 25 
4.点是否在直线同侧: 25 
5.镜面反射线: 25 
6.矩形包含: 26 
7.两圆交点: 27 
8.两圆公共面积: 28 
9. 圆和直线关系: 29 
10. 内切圆: 30 
11. 求切点: 31 
12. 线段的左右旋: 31 
13.公式: 32 
*/
/* 需要包含的头文件 */ 
#include  

/* 常用的常量定义 */ 
const double INF  = 1E200    
const double EP  = 1E-10 
const int  MAXV = 300 
const double PI  = 3.14159265 

/* 基本几何结构 */ 
struct POINT 
{ 
 double x; 
 double y; 
 POINT(double a=0, double b=0) { x=a; y=b;} //constructor 
}; 
struct LINESEG 
{ 
 POINT s; 
 POINT e; 
 LINESEG(POINT a, POINT b) { s=a; e=b;} 
 LINESEG() { } 
}; 
struct LINE           // 直线的解析方程 a*x+b*y+c=0  为统一表示,约定 a >= 0 
{ 
   double a; 
   double b; 
   double c; 
   LINE(double d1=1, double d2=-1, double d3=0) {a=d1; b=d2; c=d3;} 
}; 

/**********************
 *                    * 
 *   点的基本运算     * 
 *                    * 
 **********************/ 

double dist(POINT p1,POINT p2)                // 返回两点之间欧氏距离 
{ 
 return( sqrt( (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y) ) ); 
} 
bool equal_point(POINT p1,POINT p2)           // 判断两个点是否重合  
{ 
 return ( (abs(p1.x-p2.x)0:ep在矢量opsp的逆时针方向; 
r=0:opspep三点共线; 
r<0:ep在矢量opsp的顺时针方向 
*******************************************************************************/ 
double multiply(POINT sp,POINT ep,POINT op) 
{ 
 return((sp.x-op.x)*(ep.y-op.y)-(ep.x-op.x)*(sp.y-op.y)); 
} 
/* 
r=dotmultiply(p1,p2,op),得到矢量(p1-op)和(p2-op)的点积,如果两个矢量都非零矢量 
r<0:两矢量夹角为钝角;
r=0:两矢量夹角为直角;
r>0:两矢量夹角为锐角 
*******************************************************************************/ 
double dotmultiply(POINT p1,POINT p2,POINT p0) 
{ 
 return ((p1.x-p0.x)*(p2.x-p0.x)+(p1.y-p0.y)*(p2.y-p0.y)); 
} 
/****************************************************************************** 
判断点p是否在线段l上
条件:(p在线段l所在的直线上) && (点p在以线段l为对角线的矩形内)
*******************************************************************************/ 
bool online(LINESEG l,POINT p) 
{ 
 return( (multiply(l.e,p,l.s)==0) &&( ( (p.x-l.s.x)*(p.x-l.e.x)<=0 )&&( (p.y-l.s.y)*(p.y-l.e.y)<=0 ) ) ); 
} 
// 返回点p以点o为圆心逆时针旋转alpha(单位:弧度)后所在的位置 
POINT rotate(POINT o,double alpha,POINT p) 
{ 
 POINT tp; 
 p.x-=o.x; 
 p.y-=o.y; 
 tp.x=p.x*cos(alpha)-p.y*sin(alpha)+o.x; 
 tp.y=p.y*cos(alpha)+p.x*sin(alpha)+o.y; 
 return tp; 
} 
/* 返回顶角在o点,起始边为os,终止边为oe的夹角(单位:弧度) 
 角度小于pi,返回正值 
 角度大于pi,返回负值 
 可以用于求线段之间的夹角 
原理:
 r = dotmultiply(s,e,o) / (dist(o,s)*dist(o,e))
 r'= multiply(s,e,o)

 r >= 1 angle = 0;
 r <= -1 angle = -PI
 -10 angle = arccos(r)
 -1=  1.0 ) return 0; 
 if (cosfi <= -1.0 ) return -3.1415926; 

 fi=acos(cosfi); 
 if (dsx*dey-dsy*dex>0) return fi;      // 说明矢量os 在矢量 oe的顺时针方向 
 return -fi; 
} 
  /*****************************\ 
  *                             * 
  *      线段及直线的基本运算   * 
  *                             * 
  \*****************************/ 

/* 判断点与线段的关系,用途很广泛 
本函数是根据下面的公式写的,P是点C到线段AB所在直线的垂足 

                AC dot AB 
        r =     --------- 
                 ||AB||^2 
             (Cx-Ax)(Bx-Ax) + (Cy-Ay)(By-Ay) 
          = ------------------------------- 
                          L^2 

    r has the following meaning: 

        r=0      P = A 
        r=1      P = B 
        r<0   P is on the backward extension of AB 
  r>1      P is on the forward extension of AB 
        01) 
 { 
  np=l.e; 
  return dist(p,l.e); 
 } 
 np=perpendicular(p,l); 
 return dist(p,np); 
} 
// 求点p到线段l所在直线的距离,请注意本函数与上个函数的区别  
double ptoldist(POINT p,LINESEG l) 
{ 
 return abs(multiply(p,l.e,l.s))/dist(l.s,l.e); 
} 
/* 计算点到折线集的最近距离,并返回最近点. 
注意:调用的是ptolineseg()函数 */ 
double ptopointset(int vcount,POINT pointset[],POINT p,POINT &q) 
{ 
 int i; 
 double cd=double(INF),td; 
 LINESEG l; 
 POINT tq,cq; 

 for(i=0;i= 0。
//判断Q1Q2跨立P1P2的依据是:( Q1 - P1 ) × ( P2 - P1 ) * ( P2 - P1 ) × ( Q2 - P1 ) >= 0。
bool intersect(LINESEG u,LINESEG v) 
{ 
 return( (max(u.s.x,u.e.x)>=min(v.s.x,v.e.x))&&                     //排斥实验 
   (max(v.s.x,v.e.x)>=min(u.s.x,u.e.x))&& 
   (max(u.s.y,u.e.y)>=min(v.s.y,v.e.y))&& 
   (max(v.s.y,v.e.y)>=min(u.s.y,u.e.y))&& 
   (multiply(v.s,u.e,u.s)*multiply(u.e,v.e,u.s)>=0)&&         //跨立实验 
   (multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s)>=0)); 
} 
//  (线段u和v相交)&&(交点不是双方的端点) 时返回true    
bool intersect_A(LINESEG u,LINESEG v) 
{ 
 return ((intersect(u,v))&& 
   (!online(u,v.s))&& 
   (!online(u,v.e))&& 
   (!online(v,u.e))&& 
   (!online(v,u.s))); 
} 
// 线段v所在直线与线段u相交时返回true;方法:判断线段u是否跨立线段v  
bool intersect_l(LINESEG u,LINESEG v) 
{ 
 return multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s)>=0; 
} 
// 根据已知两点坐标,求过这两点的直线解析方程: a*x+b*y+c = 0  (a >= 0)  
LINE makeline(POINT p1,POINT p2) 
{ 
 LINE tl; 
 int sign = 1; 
 tl.a=p2.y-p1.y; 
 if(tl.a<0) 
 { 
  sign = -1; 
  tl.a=sign*tl.a; 
 } 
 tl.b=sign*(p1.x-p2.x); 
 tl.c=sign*(p1.y*p2.x-p1.x*p2.y); 
 return tl; 
} 
// 根据直线解析方程返回直线的斜率k,水平线返回 0,竖直线返回 1e200 
double slope(LINE l) 
{ 
 if(abs(l.a) < 1e-20)
  return 0; 
 if(abs(l.b) < 1e-20)
  return INF; 
 return -(l.a/l.b); 
} 
// 返回直线的倾斜角alpha ( 0 - pi) 
double alpha(LINE l) 
{ 
 if(abs(l.a)< EP)
  return 0; 
 if(abs(l.b)< EP)
  return PI/2; 
 double k=slope(l); 
 if(k>0) 
  return atan(k); 
 else 
  return PI+atan(k); 
} 
// 求点p关于直线l的对称点  
POINT symmetry(LINE l,POINT p) 
{ 
   POINT tp; 
   tp.x=((l.b*l.b-l.a*l.a)*p.x-2*l.a*l.b*p.y-2*l.a*l.c)/(l.a*l.a+l.b*l.b); 
   tp.y=((l.a*l.a-l.b*l.b)*p.y-2*l.a*l.b*p.x-2*l.b*l.c)/(l.a*l.a+l.b*l.b); 
   return tp; 
} 
// 如果两条直线 l1(a1*x+b1*y+c1 = 0), l2(a2*x+b2*y+c2 = 0)相交,返回true,且返回交点p  
bool lineintersect(LINE l1,LINE l2,POINT &p) // 是 L1,L2 
{ 
 double d=l1.a*l2.b-l2.a*l1.b; 
 if(abs(d)=0 ) 
   bc[(index+1)%vcount]=1; 
  else 
   bc[(index+1)%vcount]=0; 
  index++; 
  count--; 
 } 
}
// 返回值:多边形polygon是凸多边形时,返回true  
bool isconvex(int vcount,POINT polygon[]) 
{ 
 bool bc[MAXV]; 
 checkconvex(vcount,polygon,bc); 
 for(int i=0;i0; 
} 
// 另一种判断多边形顶点排列方向的方法  
bool isccwize(int vcount,POINT polygon[]) 
{ 
 int i,index; 
 POINT a,b,v; 
 v=polygon[0]; 
 index=0; 
 for(i=1;i0; 
} 
/********************************************************************************************   
射线法判断点q与多边形polygon的位置关系,要求polygon为简单多边形,顶点逆时针排列 
   如果点在多边形内:   返回0 
   如果点在多边形边上: 返回1 
   如果点在多边形外: 返回2 
*********************************************************************************************/ 
int insidepolygon(int vcount,POINT Polygon[],POINT q) 
{ 
 int c=0,i,n; 
 LINESEG l1,l2; 
 bool bintersect_a,bonline1,bonline2,bonline3; 
 double r1,r2; 

 l1.s=q; 
 l1.e=q; 
 l1.e.x=double(INF); 
 n=vcount; 
 for (i=0;i0) ||    
  (bonline3=online(l1,Polygon[(i+2)%n]))&&     /* 下一条边是水平线,前一个端点和后一个端点在射线两侧  */ 
   ((r2=multiply(Polygon[i],Polygon[(i+2)%n],l1.s)*multiply(Polygon[(i+2)%n], 
  Polygon[(i+3)%n],l1.s))>0) 
    ) 
   ) 
  ) c++; 
 } 
 if(c%2 == 1) 
  return 0; 
 else 
  return 2; 
} 
//点q是凸多边形polygon内时,返回true;注意:多边形polygon一定要是凸多边形  
bool InsideConvexPolygon(int vcount,POINT polygon[],POINT q) // 可用于三角形! 
{ 
 POINT p; 
 LINESEG l; 
 int i; 
 p.x=0;p.y=0; 
 for(i=0;i0 ||  // 极角更小    
    (multiply(PointSet[j],PointSet[k],PointSet[0])==0) && /* 极角相等,距离更短 */        
    dist(PointSet[0],PointSet[j])=0) 
   top--; 
  ch[++top]=PointSet[i]; 
 } 
 len=top+1; 
} 
// 卷包裹法求点集凸壳,参数说明同graham算法    
void ConvexClosure(POINT PointSet[],POINT ch[],int n,int &len) 
{ 
 int top=0,i,index,first; 
 double curmax,curcos,curdis; 
 POINT tmp; 
 LINESEG l1,l2; 
 bool use[MAXV]; 
 tmp=PointSet[0]; 
 index=0; 
 // 选取y最小点,如果多于一个,则选取最左点 
 for(i=1;icurmax || fabs(curcos-curmax)<1e-6 && dist(l2.s,l2.e)>curdis) 
   { 
    curmax=curcos; 
    index=i; 
    curdis=dist(l2.s,l2.e); 
   } 
  } 
  use[first]=false;            //清空第first个顶点标志,使最后能形成封闭的hull 
  use[index]=true; 
  ch[top++]=PointSet[index]; 
  l1.s=ch[top-2]; 
  l1.e=ch[top-1]; 
  l2.s=ch[top-1]; 
 } 
 len=top-1; 
} 
/*********************************************************************************************  
 判断线段是否在简单多边形内(注意:如果多边形是凸多边形,下面的算法可以化简) 
    必要条件一:线段的两个端点都在多边形内; 
 必要条件二:线段和多边形的所有边都不内交; 
 用途: 1. 判断折线是否在简单多边形内 
   2. 判断简单多边形是否在另一个简单多边形内 
**********************************************************************************************/ 
bool LinesegInsidePolygon(int vcount,POINT polygon[],LINESEG l) 
{ 
 // 判断线端l的端点是否不都在多边形内 
 if(!insidepolygon(vcount,polygon,l.s)||!insidepolygon(vcount,polygon,l.e)) 
  return false; 
 int top=0,i,j; 
 POINT PointSet[MAXV],tmp; 
 LINESEG s; 

 for(i=0;iPointSet[j].x || fabs(PointSet[i].x-PointSet[j].x)PointSet[j].y ) 
   { 
    tmp=PointSet[i]; 
    PointSet[i]=PointSet[j]; 
    PointSet[j]=tmp; 
   } 
  } 
 } 

 for(i=0;i x1 ) 
 { 
  AddPosPart ((x2+x1)/2, y1/2, (x2-x1) * y1,xtr,ytr,wtr); /* rectangle 全局变量变化处 */ 
  AddPosPart ((x1+x2+x2)/3, (y1+y1+y2)/3, (x2-x1)*(y2-y1)/2,xtr,ytr,wtr);    
  // triangle 全局变量变化处 
 } 
 else 
 { 
  AddNegPart ((x2+x1)/2, y1/2, (x2-x1) * y1,xtl,ytl,wtl);  
  // rectangle 全局变量变化处 
  AddNegPart ((x1+x2+x2)/3, (y1+y1+y2)/3, (x2-x1)*(y2-y1)/2,xtl,ytl,wtl);  
  // triangle  全局变量变化处 
 } 
} 
POINT cg_simple(int vcount,POINT polygon[]) 
{ 
 double xtr,ytr,wtr,xtl,ytl,wtl;        
 //注意: 如果把xtr,ytr,wtr,xtl,ytl,wtl改成全局变量后这里要删去 
 POINT p1,p2,tp; 
 xtr = ytr = wtr = 0.0; 
 xtl = ytl = wtl = 0.0; 
 for(int i=0;i=4的简单多边形至少有一条对角线 
结论 : x坐标最大,最小的点肯定是凸顶点 
 y坐标最大,最小的点肯定是凸顶点            
************************************************/ 
POINT a_point_insidepoly(int vcount,POINT polygon[]) 
{ 
 POINT v,a,b,r; 
 int i,index; 
 v=polygon[0]; 
 index=0; 
 for(i=1;i=0;                // p is to the left of pre edge 
  bln=multiply(en.e,p,en.s)>=0;                // p is to the left of next edge 
  if(!blp&&bln) 
  { 
   if(multiply(polygon[i],rp,p)>0)           // polygon[i] is above rp 
   rp=polygon[i]; 
  } 
  if(blp&&!bln) 
  { 
   if(multiply(lp,polygon[i],p)>0)           // polygon[i] is below lp 
   lp=polygon[i]; 
  } 
 } 
 return ; 
} 
// 如果多边形polygon的核存在,返回true,返回核上的一点p.顶点按逆时针方向输入  
bool core_exist(int vcount,POINT polygon[],POINT &p) 
{ 
 int i,j,k; 
 LINESEG l; 
 LINE lineset[MAXV]; 
 for(i=0;i0)      
     //多边形顶点按逆时针方向排列,核肯定在每条边的左侧或边上 
     break; 
    } 
    if(k == vcount)             //找到了一个核上的点 
    break; 
   } 
  } 
  if(j r1+r2 ) 
  return 1; 
 if( d < fabs(r1-r2) ) 
  return 5; 
 if( fabs(r1-r2) < d && d < r1+r2 ) 
  return 3; 
 return 0; // indicate an error! 
} 
//判断圆是否在矩形内:
// 判定圆是否在矩形内,是就返回true(设矩形水平,且其四个顶点由左上开始按顺时针排列) 
// 调用ptoldist函数,在第4页 
bool CircleRecRelation(POINT pc, double r, POINT pr1, POINT pr2, POINT pr3, POINT pr4) 
{ 
 if( pr1.x < pc.x && pc.x < pr2.x && pr3.y < pc.y && pc.y < pr2.y ) 
 { 
  LINESEG line1(pr1, pr2); 
  LINESEG line2(pr2, pr3); 
  LINESEG line3(pr3, pr4); 
  LINESEG line4(pr4, pr1); 
  if( r 0; 
}
//镜面反射线:
// 已知入射线、镜面,求反射线。 
// a1,b1,c1为镜面直线方程(a1 x + b1 y + c1 = 0 ,下同)系数;  
//a2,b2,c2为入射光直线方程系数;  
//a,b,c为反射光直线方程系数. 
// 光是有方向的,使用时注意:入射光向量:<-b2,a2>;反射光向量:. 
// 不要忘记结果中可能会有"negative zeros" 
void reflect(double a1,double b1,double c1,double a2,double b2,double c2,double &a,double &b,double &c) 
{ 
 double n,m; 
 double tpb,tpa; 
 tpb=b1*b2+a1*a2; 
 tpa=a2*b1-a1*b2; 
 m=(tpb*b1+tpa*a1)/(b1*b1+a1*a1); 
 n=(tpa*b1-tpb*a1)/(b1*b1+a1*a1); 
 if(fabs(a1*b2-a2*b1)<1e-20) 
 { 
  a=a2;b=b2;c=c2; 
  return; 
 } 
 double xx,yy; //(xx,yy)是入射线与镜面的交点。 
 xx=(b1*c2-b2*c1)/(a1*b2-a2*b1); 
 yy=(a2*c1-a1*c2)/(a1*b2-a2*b1); 
 a=n; 
 b=-m; 
 c=m*yy-xx*n; 
} 
//矩形包含: 
// 矩形2(C,D)是否在1(A,B)内
bool r2inr1(double A,double B,double C,double D)  
{ 
 double X,Y,L,K,DMax; 
 if (A < B) 
 { 
  double tmp = A; 
  A = B; 
  B = tmp; 
 } 
 if (C < D) 
 { 
  double tmp = C; 
  C = D; 
  D = tmp; 
 } 
 if (A > C && B > D)                 // trivial case  
  return true; 
 else 
  if (D >= B) 
   return false; 
  else 
  { 
   X = sqrt(A * A + B * B);         // outer rectangle's diagonal  
   Y = sqrt(C * C + D * D);         // inner rectangle's diagonal  
   if (Y < B) // check for marginal conditions 
    return true; // the inner rectangle can freely rotate inside 
   else 
    if (Y > X) 
     return false; 
    else 
    { 
     L = (B - sqrt(Y * Y - A * A)) / 2; 
     K = (A - sqrt(Y * Y - B * B)) / 2; 
     DMax = sqrt(L * L + K * K); 
     if (D >= DMax) 
     return false; 
     else 
     return true; 
    } 
  } 
} 
//两圆交点: 
// 两圆已经相交(相切) 
void  c2point(POINT p1,double r1,POINT p2,double r2,POINT &rp1,POINT &rp2) 
{ 
 double a,b,r; 
 a=p2.x-p1.x; 
 b=p2.y-p1.y; 
 r=(a*a+b*b+r1*r1-r2*r2)/2; 
 if(a==0&&b!=0) 
 { 
  rp1.y=rp2.y=r/b; 
  rp1.x=sqrt(r1*r1-rp1.y*rp1.y); 
  rp2.x=-rp1.x; 
 } 
 else if(a!=0&&b==0) 
 { 
  rp1.x=rp2.x=r/a; 
  rp1.y=sqrt(r1*r1-rp1.x*rp2.x); 
  rp2.y=-rp1.y; 
 } 
 else if(a!=0&&b!=0) 
 { 
  double delta; 
  delta=b*b*r*r-(a*a+b*b)*(r*r-r1*r1*a*a); 
  rp1.y=(b*r+sqrt(delta))/(a*a+b*b); 
  rp2.y=(b*r-sqrt(delta))/(a*a+b*b); 
  rp1.x=(r-b*rp1.y)/a; 
  rp2.x=(r-b*rp2.y)/a; 
 } 

 rp1.x+=p1.x; 
 rp1.y+=p1.y; 
 rp2.x+=p1.x; 
 rp2.y+=p1.y; 
} 
//两圆公共面积:
// 必须保证相交 
double c2area(POINT p1,double r1,POINT p2,double r2) 
{ 
 POINT rp1,rp2; 
 c2point(p1,r1,p2,r2,rp1,rp2); 

 if(r1>r2) //保证r2>r1 
 { 
  swap(p1,p2); 
  swap(r1,r2); 
 } 
 double a,b,rr; 
 a=p1.x-p2.x; 
 b=p1.y-p2.y; 
 rr=sqrt(a*a+b*b); 

 double dx1,dy1,dx2,dy2; 
 double sita1,sita2; 
 dx1=rp1.x-p1.x; 
 dy1=rp1.y-p1.y; 
 dx2=rp2.x-p1.x; 
 dy2=rp2.y-p1.y; 
 sita1=acos((dx1*dx2+dy1*dy2)/r1/r1); 

 dx1=rp1.x-p2.x; 
 dy1=rp1.y-p2.y; 
 dx2=rp2.x-p2.x; 
 dy2=rp2.y-p2.y; 
 sita2=acos((dx1*dx2+dy1*dy2)/r2/r2); 
 double s=0; 
 if(rr0) 
  return -1; 
 else 
  return 1; 
} 
/*
公式: 

球坐标公式: 
直角坐标为 P(x, y, z) 时,对应的球坐标是(rsinφcosθ, rsinφsinθ, rcosφ),其中φ是向量OP与Z轴的夹角,范围[0,π];是OP在XOY面上的投影到X轴的旋角,范围[0,2π]  

直线的一般方程转化成向量方程: 
ax+by+c=0 
x-x0     y-y0 
   ------ = ------- // (x0,y0)为直线上一点,m,n为向量 
m        n 
转换关系: 
a=n;b=-m;c=m·y0-n·x0; 
m=-b; n=a; 

三点平面方程: 
三点为P1,P2,P3 
设向量  M1=P2-P1; M2=P3-P1; 
平面法向量:  M=M1 x M2 () 
平面方程:    M.i(x-P1.x)+M.j(y-P1.y)+M.k(z-P1.z)=0 
*/

你可能感兴趣的:(ACM)