计算几何模板(自己打的参与修正)(刘汝佳)
// // main.cpp // demo // // Created by Yanbin GONG on 14/4/2018. // Copyright © 2018 Yanbin GONG. All rights reserved. // //向量的基本运算 #include <cmath> #include <vector> using namespace std; //基本定义 struct Point{ double x,y; Point(double x=0, double y=0):x(x),y(y){}//构造函数方便代码编写 }; typedef Point Vector; //程序实现上, Vector只是Point的别名(因为起点挪到了原点) Vector operator + (Vector A, Vector B) {return Vector(A.x+B.x,A.y+B.y);} Vector operator - (Vector A, Vector B) {return Vector(A.x-B.x,A.y-B.y);} Vector operator * (Vector A, double p) {return Vector(A.x*p,A.y*p);} Vector operator / (Vector A, double p) {return Vector(A.x/p,A.y/p);} // const &的作用是直接引用但是不改变,会节约内存 bool operator < (const Point& a, const Point& b){ return a.x<b.x || (a.x==b.x&&a.y<b.y); } const double eps = 1e-10; //设置精度在小数点后十位 //如果两个数的差距小于这个数字就当做他们相等 //判断这个数是为0,还是小于0,还是大于0 int dcmp(double x){ //fabs为绝对值函数 if(fabs(x)<eps)return 0; //fabs在cmath里 else return x<0? -1:1; } bool operator == (const Point& a, const Point& b){ return (dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0); } //向量基本运算 double Dot(Vector A, Vector B) {return A.x*B.x + A.y*B.y;}//点积 double Length(Vector A) {return sqrt(Dot(A,A));}//自身乘积再开根号保证绝对值稳定性 double Angle(Vector A, Vector B) {return acos(Dot(A,B)/Length(A)/Length(B));} //叉乘 double Cross(Vector A, Vector B) {return A.x*B.y-A.y*B.x;} double Area2(Point A, Point B, Point C) {return Cross(B-A, C-A);}//相当于上面的为原点,为面积的两倍 //角度转弧度 double torad(double deg) { return deg/180*acos(-1); } //旋转 Vector Rotate(Vector A, double rad) { return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad)); } //单位法线 Vector Normal(Vector A){ double L = Length(A); return Vector(-A.y/L,A.x/L); } //点和直线 //两条直线的交点 //一条直线可以写成一个点和一个向量(方向) // Point GetLineIntersection(Point P, Vector v, Point Q, Vector w){ Vector u = P-Q; double t = Cross(w,u)/Cross(v,w); return P+v*t; } //点到直线的距离 double DistanceToLine(Point P, Point A, Point B){ Vector v1=B-A, v2=P-A; return fabs(Cross(v1,v2))/Length(v1); //如果不取绝对值,得到的是有向距离 } //点到线段的距离 double DistanceToSegment(Point P, Point A, Point B){ if(A==B) return Length(P-A); Vector v1=B-A, v2=P-A, v3=P-B; //投影不在线段上的情况 if(dcmp(Dot(v1,v2))<0) return Length(v2); //P在靠A侧 else if(dcmp(Dot(v1,v3))>0) return Length(v3); //在靠近B的一侧 else return fabs(Cross(v1,v2))/Length(v1); } //点在直线上的投影 Point GetLineProjection(Point P, Point A, Point B){ Vector v=B-A; return A+v*(Dot(v,P-A)/Dot(v,v)); //从A移动到投影 } //线段相交判定 相交为1 (交点不为任何一线段的端点) bool SegmentProperIntersection(Point a1, Point a2, Point b1, Point b2){ double c1 = Cross(a2-a1,b1-a1); double c2 = Cross(a2-a1,b2-a1); double c3 = Cross(b2-b1,a1-b1); double c4 = Cross(b2-b1,a2-b1); return dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0; } //判断一个点是否在一条线段上(用于判断一个端点是否在另一个线段上) //如果c1 c2窦唯0,则线段共线 bool OnSegment(Point p, Point a1, Point a2){ return dcmp(Cross(a1-p, a2-p))==0 && dcmp(Dot(a1-p, a2-p))<0; } //与圆和球有关的计算问题 struct Line{ Point p;//线上一点 Vector v;//方向向量 double ang; //极角,从x正半轴旋转到v所需要的角(弧度) Line(Point p, Vector v):p(p),v(v){ang = atan2(v.y,v.x);} Point point(double t){return p+v*t;}; bool operator < (const Line& L) const{ //排序用的比较运算符 return ang < L.ang; } }; struct Circle{ Point c; double r; Circle(Point c, double r):c(c),r(r){} Point point(double a){ //通过圆心角求坐标的函数 return Point(c.x+cos(a)*r,c.y+sin(a)*r); } }; //直线与圆的交点 //sol存放的是交点本身,代码没有清空sol,就很方便:可以反复调用把所有交点放在一个sol里 int getLineCircleIntersection(Line L, Circle C, double& t1, double& t2, vector<Point>& sol){ double a=L.v.x, b=L.p.x-C.c.x, c=L.v.y, d=L.p.y-C.c.y; double e=a*a+c*c, f=2*(a*b+c*d), g=b*b+d*d-C.r*C.r; double delta = f*f - 4*e*g;//判别式 if(dcmp(delta)<0) return 0; //相离 if(dcmp(delta)==0){ t1=t2=-f/(2*e); sol.push_back(L.point(t1)); return 1; } //相交 t1 = (-f-sqrt(delta))/(2*e); sol.push_back(L.point(t1)); t2 = (-f+sqrt(delta))/(2*e); sol.push_back(L.point(t2)); return 2; } //计算向量极角 double angle(Vector v){return atan2(v.y,v.x);} //两圆相交 int getCircleCircleIntersection(Circle C1, Circle C2, vector<Point>& sol){ double d=Length(C1.c-C2.c); if(dcmp(d)==0){ if(dcmp(C1.r-C2.r)==0) return -1; //两圆重合 return 0; } if(dcmp(C1.r+C2.r-d)<0) return 0; //内含 if(dcmp(fabs(C1.r-C2.r)-d)>0) return 0; //外离 double a = angle(C2.c-C1.c); //向量C1C2的极角 double da = acos((C1.r*C1.r+d*d-C2.r*C2.r)/(2*C1.r*d)); //C1C2到C1P1的角 Point p1 = C1.point(a-da), p2 = C1.point(a+da); sol.push_back(p1); if(p1==p2)return 1; sol.push_back(p2); return 2; } //过定点作圆切线,v[i]是第i条切线,返回切线条数 int getTangents(Point p, Circle C, Vector* v){ Vector u = C.c-p; double dist = Length(u); if(dist<C.r) return 0; else if(dcmp(dist-C.r)==0){ //p在圆上,只有一条切线 v[0]=Rotate(u,M_PI/2); return 1; } else{ double ang = asin(C.r/dist); v[0] = Rotate(u, -ang); v[1] = Rotate(u, +ang); return 2; } } //两圆的公切线 int getTangents(Circle A, Circle B, Point* a, Point* b){ int cnt=0; if(A.r<B.r){ swap(A,B); swap(a,b); } int d2=(A.c.x-B.c.x)*(A.c.x-B.c.x)+(A.c.y-B.c.y)*(A.c.y-B.c.y); int rdiff=A.r-B.r; int rsum=A.r+B.r; if(d2<rdiff*rdiff) return 0; //内含 double base = atan2(B.c.y-A.c.y,B.c.x-A.c.x); if(d2==0&&A.r==B.r) return -1; //无限多条切线 if(d2==rdiff*rdiff){//内切,一条切线 a[cnt]=A.point(base); b[cnt]=B.point(base); cnt++; return 1; } //有外共切线 double ang = acos(A.r-B.r)/sqrt(d2); a[cnt] = A.point(base+ang); b[cnt] = B.point(base+ang); cnt++; a[cnt] = A.point(base+ang); b[cnt] = B.point(base-ang); cnt++; if(d2==rsum*rsum){ a[cnt]=A.point(base); b[cnt]=B.point(M_PI+base); cnt++; } else if(d2>rsum*rsum){ double ang=acos((A.r+B.r)/sqrt(d2)); a[cnt]=A.point(base+ang); b[cnt]=B.point(M_PI+base+ang); cnt++; a[cnt]=A.point(base-ang); b[cnt]=B.point(M_PI+base-ang); cnt++; } return cnt; } //三角形外接圆(三点保证不共线) Circle CircumscribedCircle(Point p1, Point p2, Point p3){ double Bx = p2.x-p1.x, By = p2.y-p1.y; double Cx = p3.x-p1.x, Cy = p3.y-p1.y; double D = 2*(Bx*Cy-By*Cx); double cx = (Cy*(Bx*Bx+By*By)-By*(Cx*Cx+Cy*Cy))/D+p1.x; double cy = (Bx*(Cx*Cx+Cy*Cy)-Cx*(Bx*Bx+By*By))/D+p1.y; Point p = Point(cx,cy); return Circle(p,Length(p1-p)); } //三角形内切圆 Circle InscribedCircle(Point p1, Point p2, Point p3){ double a = Length(p2-p3); double b = Length(p3-p1); double c = Length(p1-p2); Point p = (p1*a+p2*b+p3*c)/(a+b+c); return Circle(p, DistanceToLine(p, p1, p2)); } //二维几何常用算法 typedef vector<Point> Polygon; //多边形的有向面积 double PolygonArea(Polygon po) { int n = po.size(); double area = 0.0; for(int i = 1; i < n-1; i++) { area += Cross(po[i]-po[0], po[i+1]-po[0]); } return area * 0.5; } //点在多边形内判定 int isPointInPolygon(Point p, Polygon poly){ int wn = 0; //绕数 int n = poly.size(); for(int i=0;i<n;i++){ if(OnSegment(p,poly[i],poly[(i+1)%n])) return -1;//边界上 int k = dcmp(Cross(poly[(i+1)%n]-poly[i], p-poly[i])); int d1 = dcmp(poly[i].y-p.y); int d2 = dcmp(poly[(i+1)%n].y-p.y); if(k>0&&d1<=0&&d2>0) wn++; if(k<0&&d2<=0&&d1>0) wn--; } if(wn!=0) return 1;//内部 return 0;//外部 } //凸包 //Andrew算法 bool myCmp(Point A, Point B) { if(A.x != B.x) return A.x < B.x; else return A.y < B.y; } int ConvexHall (Point* p, int n, Point* ch){ sort(p,p+n,myCmp); //先比较x坐标,再比较y坐标 int m = 0; for(int i=0;i<n;i++){ while(m>1&&Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2])<=0) m--; ch[m++] = p[i]; } if(n>1)m--; return m; }
//凸包(Andrew算法) //如果不希望在凸包的边上有输入点,把两个 <= 改成 < //如果不介意点集被修改,可以改成传递引用 Polygon ConvexHull(vector<Point> p) { //预处理,删除重复点 sort(p.begin(), p.end()); p.erase(unique(p.begin(), p.end()), p.end()); int n = p.size(), m = 0; Polygon res(n+1); for(int i = 0; i < n; i++) { while(m > 1 && Cross(res[m-1]-res[m-2], p[i]-res[m-2]) <= 0) m--; res[m++] = p[i]; } int k = m; for(int i = n-2; i >= 0; i--) { while(m > k && Cross(res[m-1]-res[m-2], p[i]-res[m-2]) <= 0) m--; res[m++] = p[i]; } m -= n > 1; res.resize(m); return res; }
1 // 2 // main.cpp 3 // demo 4 // 5 // Created by Yanbin GONG on 14/4/2018. 6 // Copyright © 2018 Yanbin GONG. All rights reserved. 7 // 8 9 //向量的基本运算 10 11 #include <cmath> 12 #include <vector> 13 14 using namespace std; 15 16 17 //基本定义 18 struct Point{ 19 double x,y; 20 Point(double x=0, double y=0):x(x),y(y){}//构造函数方便代码编写 21 }; 22 typedef Point Vector; //程序实现上, Vector只是Point的别名(因为起点挪到了原点) 23 24 Vector operator + (Vector A, Vector B) {return Vector(A.x+B.x,A.y+B.y);} 25 Vector operator - (Vector A, Vector B) {return Vector(A.x-B.x,A.y-B.y);} 26 Vector operator * (Vector A, double p) {return Vector(A.x*p,A.y*p);} 27 Vector operator / (Vector A, double p) {return Vector(A.x/p,A.y/p);} 28 29 // const &的作用是直接引用但是不改变,会节约内存 30 bool operator < (const Point& a, const Point& b){ 31 return a.x<b.x || (a.x==b.x&&a.y<b.y); 32 } 33 34 const double eps = 1e-10; //设置精度在小数点后十位 35 //如果两个数的差距小于这个数字就当做他们相等 36 37 //判断这个数是为0,还是小于0,还是大于0 38 int dcmp(double x){ 39 //fabs为绝对值函数 40 if(fabs(x)<eps)return 0; //fabs在cmath里 41 else return x<0? -1:1; 42 } 43 44 bool operator == (const Point& a, const Point& b){ 45 return (dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0); 46 } 47 48 //向量基本运算 49 double Dot(Vector A, Vector B) {return A.x*B.x + A.y*B.y;}//点积 50 double Length(Vector A) {return sqrt(Dot(A,A));}//自身乘积再开根号保证绝对值稳定性 51 double Angle(Vector A, Vector B) {return acos(Dot(A,B)/Length(A)/Length(B));} 52 53 //叉乘 54 double Cross(Vector A, Vector B) {return A.x*B.y-A.y*B.x;} 55 double Area2(Point A, Point B, Point C) {return Cross(B-A, C-A);}//相当于上面的为原点,为面积的两倍 56 57 //角度转弧度 58 double torad(double deg) 59 { 60 return deg/180*acos(-1); 61 } 62 //旋转 63 Vector Rotate(Vector A, double rad) { 64 return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad)); 65 } 66 67 //单位法线 68 Vector Normal(Vector A){ 69 double L = Length(A); 70 return Vector(-A.y/L,A.x/L); 71 } 72 73 //点和直线 74 75 //两条直线的交点 76 //一条直线可以写成一个点和一个向量(方向) 77 // 78 Point GetLineIntersection(Point P, Vector v, Point Q, Vector w){ 79 Vector u = P-Q; 80 double t = Cross(w,u)/Cross(v,w); 81 return P+v*t; 82 } 83 84 //点到直线的距离 85 double DistanceToLine(Point P, Point A, Point B){ 86 Vector v1=B-A, v2=P-A; 87 return fabs(Cross(v1,v2))/Length(v1); //如果不取绝对值,得到的是有向距离 88 } 89 90 //点到线段的距离 91 double DistanceToSegment(Point P, Point A, Point B){ 92 if(A==B) return Length(P-A); 93 Vector v1=B-A, v2=P-A, v3=P-B; 94 //投影不在线段上的情况 95 if(dcmp(Dot(v1,v2))<0) return Length(v2); //P在靠A侧 96 else if(dcmp(Dot(v1,v3))>0) return Length(v3); //在靠近B的一侧 97 else return fabs(Cross(v1,v2))/Length(v1); 98 } 99 100 //点在直线上的投影 101 Point GetLineProjection(Point P, Point A, Point B){ 102 Vector v=B-A; 103 return A+v*(Dot(v,P-A)/Dot(v,v)); //从A移动到投影 104 } 105 106 //线段相交判定 相交为1 (交点不为端点) 107 bool SegmentProperIntersection(Point a1, Point a2, Point b1, Point b2){ 108 double c1 = Cross(a2-a1,b1-a1); 109 double c2 = Cross(a2-a1,b2-a1); 110 double c3 = Cross(b2-b1,a1-b1); 111 double c4 = Cross(b2-b1,a2-b1); 112 return dcmp(c1)*dcmp(c2)<0 && dcmp(c3)*dcmp(c4)<0; 113 } 114 //判断一个点是否在一条线段上(用于判断一个端点是否在另一个线段上) 115 //如果c1 c2窦唯0,则线段共线 116 bool OnSegment(Point p, Point a1, Point a2){ 117 return dcmp(Cross(a1-p, a2-p))==0 && dcmp(Dot(a1-p, a2-p))<0; 118 } 119 120 //与圆和球有关的计算问题 121 122 struct Line{ 123 Point p;//线上一点 124 Vector v;//方向向量 125 double ang; //极角,从x正半轴旋转到v所需要的角(弧度) 126 Line(Point p, Vector v):p(p),v(v){ang = atan2(v.y,v.x);} 127 Point point(double t){return p+v*t;}; 128 bool operator < (const Line& L) const{ //排序用的比较运算符 129 return ang < L.ang; 130 } 131 }; 132 133 struct Circle{ 134 Point c; 135 double r; 136 Circle(Point c, double r):c(c),r(r){} 137 Point point(double a){ //通过圆心角求坐标的函数 138 return Point(c.x+cos(a)*r,c.y+sin(a)*r); 139 } 140 }; 141 142 //直线与圆的交点 143 //sol存放的是交点本身,代码没有清空sol,就很方便:可以反复调用把所有交点放在一个sol里 144 int getLineCircleIntersection(Line L, Circle C, double& t1, double& t2, vector<Point>& sol){ 145 double a=L.v.x, b=L.p.x-C.c.x, c=L.v.y, d=L.p.y-C.c.y; 146 double e=a*a+c*c, f=2*(a*b+c*d), g=b*b+d*d-C.r*C.r; 147 double delta = f*f - 4*e*g;//判别式 148 if(dcmp(delta)<0) return 0; //相离 149 if(dcmp(delta)==0){ 150 t1=t2=-f/(2*e); 151 sol.push_back(L.point(t1)); 152 return 1; 153 } 154 //相交 155 t1 = (-f-sqrt(delta))/(2*e); 156 sol.push_back(L.point(t1)); 157 t2 = (-f+sqrt(delta))/(2*e); 158 sol.push_back(L.point(t2)); 159 return 2; 160 } 161 162 //计算向量极角 163 double angle(Vector v){return atan2(v.y,v.x);} 164 165 //两圆相交 166 int getCircleCircleIntersection(Circle C1, Circle C2, vector<Point>& sol){ 167 double d=Length(C1.c-C2.c); 168 if(dcmp(d)==0){ 169 if(dcmp(C1.r-C2.r)==0) return -1; //两圆重合 170 return 0; 171 } 172 if(dcmp(C1.r+C2.r-d)<0) return 0; //内含 173 if(dcmp(fabs(C1.r-C2.r)-d)>0) return 0; //外离 174 175 double a = angle(C2.c-C1.c); //向量C1C2的极角 176 double da = acos((C1.r*C1.r+d*d-C2.r*C2.r)/(2*C1.r*d)); 177 //C1C2到C1P1的角 178 Point p1 = C1.point(a-da), p2 = C1.point(a+da); 179 180 sol.push_back(p1); 181 if(p1==p2)return 1; 182 sol.push_back(p2); 183 return 2; 184 } 185 186 //过定点作圆切线,v[i]是第i条切线,返回切线条数 187 int getTangents(Point p, Circle C, Vector* v){ 188 Vector u = C.c-p; 189 double dist = Length(u); 190 if(dist<C.r) return 0; 191 else if(dcmp(dist-C.r)==0){ //p在圆上,只有一条切线 192 v[0]=Rotate(u,M_PI/2); 193 return 1; 194 } 195 else{ 196 double ang = asin(C.r/dist); 197 v[0] = Rotate(u, -ang); 198 v[1] = Rotate(u, +ang); 199 return 2; 200 } 201 } 202 203 //两圆的公切线 204 int getTangents(Circle A, Circle B, Point* a, Point* b){ 205 int cnt=0; 206 if(A.r<B.r){ 207 swap(A,B); 208 swap(a,b); 209 } 210 int d2=(A.c.x-B.c.x)*(A.c.x-B.c.x)+(A.c.y-B.c.y)*(A.c.y-B.c.y); 211 int rdiff=A.r-B.r; 212 int rsum=A.r+B.r; 213 if(d2<rdiff*rdiff) return 0; //内含 214 double base = atan2(B.c.y-A.c.y,B.c.x-A.c.x); 215 if(d2==0&&A.r==B.r) return -1; //无限多条切线 216 if(d2==rdiff*rdiff){//内切,一条切线 217 a[cnt]=A.point(base); 218 b[cnt]=B.point(base); 219 cnt++; 220 return 1; 221 } 222 //有外共切线 223 double ang = acos(A.r-B.r)/sqrt(d2); 224 a[cnt] = A.point(base+ang); 225 b[cnt] = B.point(base+ang); 226 cnt++; 227 a[cnt] = A.point(base+ang); 228 b[cnt] = B.point(base-ang); 229 cnt++; 230 if(d2==rsum*rsum){ 231 a[cnt]=A.point(base); 232 b[cnt]=B.point(M_PI+base); 233 cnt++; 234 } 235 else if(d2>rsum*rsum){ 236 double ang=acos((A.r+B.r)/sqrt(d2)); 237 a[cnt]=A.point(base+ang); 238 b[cnt]=B.point(M_PI+base+ang); 239 cnt++; 240 a[cnt]=A.point(base-ang); 241 b[cnt]=B.point(M_PI+base-ang); 242 cnt++; 243 } 244 return cnt; 245 } 246 247 //三角形外接圆(三点保证不共线) 248 Circle CircumscribedCircle(Point p1, Point p2, Point p3){ 249 double Bx = p2.x-p1.x, By = p2.y-p1.y; 250 double Cx = p3.x-p1.x, Cy = p3.y-p1.y; 251 double D = 2*(Bx*Cy-By*Cx); 252 double cx = (Cy*(Bx*Bx+By*By)-By*(Cx*Cx+Cy*Cy))/D+p1.x; 253 double cy = (Bx*(Cx*Cx+Cy*Cy)-Cx*(Bx*Bx+By*By))/D+p1.y; 254 Point p = Point(cx,cy); 255 return Circle(p,Length(p1-p)); 256 } 257 //三角形内切圆 258 Circle InscribedCircle(Point p1, Point p2, Point p3){ 259 double a = Length(p2-p3); 260 double b = Length(p3-p1); 261 double c = Length(p1-p2); 262 Point p = (p1*a+p2*b+p3*c)/(a+b+c); 263 return Circle(p, DistanceToLine(p, p1, p2)); 264 } 265 266 267 //二维几何常用算法 268 typedef vector<Point> Polygon; 269 //多边形的有向面积 270 double PolygonArea(Polygon po) { 271 int n = po.size(); 272 double area = 0.0; 273 for(int i = 1; i < n-1; i++) { 274 area += Cross(po[i]-po[0], po[i+1]-po[0]); 275 } 276 return area * 0.5; 277 } 278 279 //点在多边形内判定 280 int isPointInPolygon(Point p, Polygon poly){ 281 int wn = 0; //绕数 282 int n = poly.size(); 283 for(int i=0;i<n;i++){ 284 if(OnSegment(p,poly[i],poly[(i+1)%n])) return -1;//边界上 285 int k = dcmp(Cross(poly[(i+1)%n]-poly[i], p-poly[i])); 286 int d1 = dcmp(poly[i].y-p.y); 287 int d2 = dcmp(poly[(i+1)%n].y-p.y); 288 if(k>0&&d1<=0&&d2>0) wn++; 289 if(k<0&&d2<=0&&d1>0) wn--; 290 } 291 if(wn!=0) return 1;//内部 292 return 0;//外部 293 } 294 295 //凸包 296 //Andrew算法 297 bool myCmp(Point A, Point B) 298 { 299 if(A.x != B.x) return A.x < B.x; 300 else return A.y < B.y; 301 } 302 303 int ConvexHall (Point* p, int n, Point* ch){ 304 sort(p,p+n,myCmp); //先比较x坐标,再比较y坐标 305 int m = 0; 306 for(int i=0;i<n;i++){ 307 while(m>1&&Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2])<=0) m--; 308 ch[m++] = p[i]; 309 } 310 if(n>1)m--; 311 return m; 312 }
double PolygonArea(Point *p,int n) { double area=0; for(int i=1;i<n-1;i++) area += Cross(p[i]-p[0],p[i+1]-p[0]); return fabs(area)/2; } int ConvexHull(Point *p,int n,Point *ch) { sort(p,p+n); n=unique(p,p+n)-p; int m=0; for(int i=0;i<n;i++) { while(m>1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2])<=0) m--; ch[m++]=p[i]; } int k=m; for(int i=n-2;i>=0;i--) { while(m>k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2])<=0) m--; ch[m++]=p[i]; } if(n>1) m--; return m; }