[计算几何]计算几何相关知识及基本算法 (C语言版)
2008-05-28 10:54
399 查看
计算几何相关知识及基本算法 (C语言版)
/**//*
计算几何相关知识及基本算法
这些东西是自己写的,哪里有错请指出来,谢谢!
*/
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#define MaxNode 100
//自定义常量,类型及通用函数
const double eps = 1e-6;
typedef struct TPoint
...{
//平面点
double x;
double y;
}TPoint;
typedef struct TTriangle
...{
TPoint t[2];
}TTriangle;
typedef struct TPolygon
...{
//多边形
TPoint p[MaxNode];
}TPolygon;
typedef struct TLine
...{
//直线方程的系数
double a, b, c;
}TLine;
typedef struct TCircle
...{
//圆
double r;
TPoint centre;
}TCircle;
bool same(double x, double y)
...{
//判断两个实数是否相等
if(fabs(x - y) < eps) return true;
return false;
}
double max(double x, double y)
...{
//比较两个数的大小,返回大的数
if(x > y) return x;
else return y;
}
double min(double x, double y)
...{
//比较两个数的大小,返回小的数
if(x < y) return x;
else return y;
}
double distance(TPoint p1, TPoint p2)
...{
//计算平面上两个点之间的距离
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
double multi(TPoint p1, TPoint p2, TPoint p0)
...{
//求矢量[p0, p1], [p0, p2]的叉积
//p0是顶点
return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
//若结果等于0,则这三点共线
//若结果大于0,则p0p2在p0p1的逆时针方向
//若结果小于0,则p0p2在p0p1的顺时针方向
}
/**//*
折线的拐向的判断(从p0向p1看过去的左边)
若 (p2 - p1) 叉乘 (p1 - p0) < 0 ,则p0p1在p1点拐向左侧后得到p1p2
若 (p2 - p1) 叉乘 (p1 - p0) = 0 ,则 p0, p1, p2三点共线
若 (p2 - p1) 叉乘 (p1 - p0) > 0 ,则p0p1在p1点拐向右侧后得到p1p2
*/
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;
}
//角形面积的计算
// S = ah / 2
// S = absinC / 2
// S = sqrt(p * (p - a) * (p - b) * (p - c)), p = (a + b + c) / 2
// S = abc / R / 4
double triangleArea(TTriangle t)
...{
//已知三角形三个顶点的坐标,求三角形的面积
return fabs(t.t[0].x * t.t[1].y + t.t[1].x * t.t[2].y + t.t[2].x * t.t[0].y
- t.t[1].x * t.t[0].y - t.t[2].x * t.t[1].y - t.t[0].x * t.t[2].y) / 2;
}
double polygonArea(TPolygon p, int n)
...{
//已知多边形各顶点的坐标,求其面积
double area;
int i;
area = 0;
for(i = 1;i <= n;i++)...{
area += (p.p[i - 1].x * p.p[i % n].y - p.p[i % n].x * p.p[i - 1].y);
}
return fabs(area) / 2;
}
TCircle circumcircleOfTriangle(TTriangle t)
...{
//三角形的外接圆
TCircle tmp;
double a, b, c, c1, c2;
double xA, yA, xB, yB, xC, yC;
a = distance(t.t[0], t.t[1]);
b = distance(t.t[1], t.t[2]);
c = distance(t.t[2], t.t[0]);
//根据S = a * b * c / R / 4;求半径R
tmp.r = a * b * c / triangleArea(t) / 4;
xA = t.t[0].x; yA = t.t[0].y;
xB = t.t[1].x; yB = t.t[1].y;
xC = t.t[2].x; yC = t.t[2].y;
c1 = (xA * xA + yA * yA - xB * xB - yB * yB) / 2;
c2 = (xA * xA + yA * yA - xC * xC - yC * yC) / 2;
tmp.centre.x = (c1 * (yA - yC) - c2 * (yA - yB)) /
((xA - xB) * (yA - yC) - (xA - xC) * (yA - yB));
tmp.centre.y = (c1 * (xA - xC) - c2 * (xA - xB)) /
((yA - yB) * (xA - xC) - (yA - yC) * (xA - xB)); //这里改一下,先写掉了二个括号
return tmp;
}
TCircle incircleOfTriangle(TTriangle t)
...{
//三角形的内接圆
TCircle tmp;
double a, b, c, angleA, angleB, angleC, p, p2, p3, f1, f2;
double xA, yA, xB, yB, xC, yC;
a = distance(t.t[0], t.t[1]);
b = distance(t.t[1], t.t[2]);
c = distance(t.t[2], t.t[0]);
/**//*
S = p * r
p = (a + b + c) / 2;
r = S / P = 2 * S / (a + b + c)
*/
tmp.r = 2 * triangleArea(t) / (a + b +c);
angleA = acos((b * b + c * c - a * a) / (2 * b * c));
angleB = acos((a * a + c * c - b * b) / (2 * a * c));
angleC = acos((a * a + b * b - c * c) / (2 * a * b));
p = sin(angleA / 2);
p2 = sin(angleB / 2);
p3 = sin(angleC / 2);
xA = t.t[0].x; yA = t.t[0].y;
xB = t.t[1].x; yB = t.t[1].y;
xC = t.t[2].x; yC = t.t[2].y;
f1 = ((tmp.r / p2) * (tmp.r / p2) - (tmp.r / p) * (tmp.r / p) +
xA * xA - xB * xB + yA * yA - yB * yB) / 2;
f2 = ((tmp.r / p3) * (tmp.r / p3) - (tmp.r / p) * (tmp.r / p) +
xA * xA - xC * xC + yA * yA - yC * yC) / 2;
tmp.centre.x = (f1 * (yA - yC) - f2 * (yA - yB)) /
((xA - xB) * (yA - yC) - (xA - xC) * (yA - yB));
tmp.centre.y = (f1 * (xA - xC) - f2 * (xA - xB)) /
((yA - yB) * (xA - xC) - (yA - yC) * (xA - xB));
return tmp;
}
bool isPointOnSegment(TPoint p, TPoint p1, TPoint p2)
...{
// 判断p点是否在线段p1p2上
//1.p是否在直线p1p2上
//2.p是否在以p1p2为对角线的矩形中
if(multi(p1, p2, p) != 0) return false ;
if((p.x > p1.x && p.x > p2.x) || (p.x < p1.x && p.x < p2.x)) return false;
if((p.y > p1.y && p.y > p2.y) || (p.y < p1.y && p.y < p2.y)) return false;
return true;
}
bool isIntersected(TPoint s1, TPoint e1, TPoint s2, TPoint e2)
...{
//判断线段是否相交
//1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交
//2.跨立试验
if(
(max(s1.x, e1.x) >= min(s2.x, e2.x)) &&
(max(s2.x, e2.x) >= min(s1.x, e1.x)) &&
(max(s1.y, e1.y) >= min(s2.y, e2.y)) &&
(max(s2.y, e2.y) >= min(s1.y, e1.y)) &&
(multi(s2, e1, s1) * multi(e1, e2, s1) > 0) &&
(multi(s1, e2, s2) * multi(e2, e1, s2) > 0)
) return true;
return false;
}
//判断线段是否和直线相交。比如要判断线段[s1, e1]和直线 L 是否相交,
//只要判断线段[s1, e1]是否跨立 L 即可。
//判断点是否在三角形内
//1.面积判断
//2.线段的拐向判断
//判断点是否在多边形内,如果多边形为凸多边形,
//下面的两个方法还是适用的,只需要做少量的修改
// 若需要判断的点在凹多边形内,就需采用完全不同
//的方法
bool isPointInTriangle1(TPoint p, TTriangle t)
...{
//判断点是否在三角形内,面积判断
TTriangle tmp;
double area;
int i, j;
area = 0;
for(i = 0;i <= 2;i++)...{
for(j = 0;j <= 2;j++)...{
if(i == j) tmp.t[j] = p;
else tmp.t[j] = t.t[j];
}
area += triangleArea(tmp);
}
return same(area, triangleArea(t));
}
bool isPointInTriangle2(TPoint p, TTriangle t)
...{
//判断点是否在三角形内,线段的拐向判断
//APB, BPC, CPA 的拐向都是相同的
double k1, k2, k3;
k1 = multi(t.t[0], t.t[1], p);
k2 = multi(t.t[1], t.t[2], p);
k3 = multi(t.t[2], t.t[0], p);
if(k1 * k2 * k3 != 0)...{
if(k1 * k2 < 0) return false;
if(k1 * k3 < 0) return false;
}
return true;
}
TPoint symmetricalPoint(TPoint p1, TPoint p2)
...{
//求p1关于p2的对称点
TPoint p3;
p3.x = 2 * p2.x - p1.x;
p3.y = 2 * p2.y - p1.y;
return p3;
}
TPoint symmetricalPointofLine(TPoint p, TLine L)
...{
//p点关于直线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;
}
//点关于线段的对称点
//首先可以根据线段的两个端点求出线段所在的直线L,然后再来求指定
//点关于直线L的对称点
/**//*
凸包( Convex Hull )
凸包是对平面是上的某个点集而言的,凸包是一个最小凸多边形,满足点集
中的所有点都在该凸多边形内(或在该多边形的边上)。
通常,我们采用Graham扫描法来求点集的凸包。首先,排序选出点集中最左下
角点(先取y坐标最小的点,若有多个再在其中取x坐标最小的点),设该点为p0;
然后,将其余的按以p0为中心的极角坐标逆时针排序,多于相同极角的点只保留
距离p0最远的一个,这样就可以得到一个点的序列p1,p2, p2.....,pn;接下来,
将p0, p1, p2压入栈,一次处理pi(i >= 2 && i <= n),不断让栈顶的元素出
栈,直到栈顶的下一个元素,栈顶元素,以及pi构成的折线不拐向左侧,将pi压
入栈中;最后栈中的元素即为所求的凸包的顶点序列
*/
void swap(TPoint p[], int i, int j)
...{
TPoint tmp;
tmp = p[i];
p[i] = p[j];
p[j] = tmp;
}
int stack[MaxNode];
int top;
int cmp(const void *a, const void *b)
...{
TPoint *c = (TPoint *)a;
TPoint *d = (TPoint *)b;
double k = multi(*c, *d, point[0]);
if(k< 0) return 1;
else if(k == 0 && distance(*c, point[0]) >= distance(*d, point[0])) return 1;
else return -1;
}
void grahamScan(TPoint p[], int n)
...{
//Graham扫描求凸包
int i;
//将最左下的点调整到p[0]的位置
for(i = 1;i <= n - 1;i++)...{
if((p[i].y < p[0].y) || (p[i].y == p[0].y && p[i].x < p[0].x))
swap(p, 0, i);
}
//将平p[1]到p[n - 1]按按极角排序,可采用快速排序
qsort(p + 1, , n - 1, sizeof(p[0]), cmp);
for(i = 0;i <= 2;i++) stack[i] = i;
top = 2;
for(i = 3;i <= n - 1;i++)...{
while(multi(p[i], p[stack[top]], p[stack[top - 1]]) > 0)...{
top--;
if(top == 0) break;
}
}
top++;
stack[top] = i;
}
int main()
...{
}
/**//*
计算几何相关知识及基本算法
这些东西是自己写的,哪里有错请指出来,谢谢!
*/
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#define MaxNode 100
//自定义常量,类型及通用函数
const double eps = 1e-6;
typedef struct TPoint
...{
//平面点
double x;
double y;
}TPoint;
typedef struct TTriangle
...{
TPoint t[2];
}TTriangle;
typedef struct TPolygon
...{
//多边形
TPoint p[MaxNode];
}TPolygon;
typedef struct TLine
...{
//直线方程的系数
double a, b, c;
}TLine;
typedef struct TCircle
...{
//圆
double r;
TPoint centre;
}TCircle;
bool same(double x, double y)
...{
//判断两个实数是否相等
if(fabs(x - y) < eps) return true;
return false;
}
double max(double x, double y)
...{
//比较两个数的大小,返回大的数
if(x > y) return x;
else return y;
}
double min(double x, double y)
...{
//比较两个数的大小,返回小的数
if(x < y) return x;
else return y;
}
double distance(TPoint p1, TPoint p2)
...{
//计算平面上两个点之间的距离
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
double multi(TPoint p1, TPoint p2, TPoint p0)
...{
//求矢量[p0, p1], [p0, p2]的叉积
//p0是顶点
return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
//若结果等于0,则这三点共线
//若结果大于0,则p0p2在p0p1的逆时针方向
//若结果小于0,则p0p2在p0p1的顺时针方向
}
/**//*
折线的拐向的判断(从p0向p1看过去的左边)
若 (p2 - p1) 叉乘 (p1 - p0) < 0 ,则p0p1在p1点拐向左侧后得到p1p2
若 (p2 - p1) 叉乘 (p1 - p0) = 0 ,则 p0, p1, p2三点共线
若 (p2 - p1) 叉乘 (p1 - p0) > 0 ,则p0p1在p1点拐向右侧后得到p1p2
*/
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;
}
//角形面积的计算
// S = ah / 2
// S = absinC / 2
// S = sqrt(p * (p - a) * (p - b) * (p - c)), p = (a + b + c) / 2
// S = abc / R / 4
double triangleArea(TTriangle t)
...{
//已知三角形三个顶点的坐标,求三角形的面积
return fabs(t.t[0].x * t.t[1].y + t.t[1].x * t.t[2].y + t.t[2].x * t.t[0].y
- t.t[1].x * t.t[0].y - t.t[2].x * t.t[1].y - t.t[0].x * t.t[2].y) / 2;
}
double polygonArea(TPolygon p, int n)
...{
//已知多边形各顶点的坐标,求其面积
double area;
int i;
area = 0;
for(i = 1;i <= n;i++)...{
area += (p.p[i - 1].x * p.p[i % n].y - p.p[i % n].x * p.p[i - 1].y);
}
return fabs(area) / 2;
}
TCircle circumcircleOfTriangle(TTriangle t)
...{
//三角形的外接圆
TCircle tmp;
double a, b, c, c1, c2;
double xA, yA, xB, yB, xC, yC;
a = distance(t.t[0], t.t[1]);
b = distance(t.t[1], t.t[2]);
c = distance(t.t[2], t.t[0]);
//根据S = a * b * c / R / 4;求半径R
tmp.r = a * b * c / triangleArea(t) / 4;
xA = t.t[0].x; yA = t.t[0].y;
xB = t.t[1].x; yB = t.t[1].y;
xC = t.t[2].x; yC = t.t[2].y;
c1 = (xA * xA + yA * yA - xB * xB - yB * yB) / 2;
c2 = (xA * xA + yA * yA - xC * xC - yC * yC) / 2;
tmp.centre.x = (c1 * (yA - yC) - c2 * (yA - yB)) /
((xA - xB) * (yA - yC) - (xA - xC) * (yA - yB));
tmp.centre.y = (c1 * (xA - xC) - c2 * (xA - xB)) /
((yA - yB) * (xA - xC) - (yA - yC) * (xA - xB)); //这里改一下,先写掉了二个括号
return tmp;
}
TCircle incircleOfTriangle(TTriangle t)
...{
//三角形的内接圆
TCircle tmp;
double a, b, c, angleA, angleB, angleC, p, p2, p3, f1, f2;
double xA, yA, xB, yB, xC, yC;
a = distance(t.t[0], t.t[1]);
b = distance(t.t[1], t.t[2]);
c = distance(t.t[2], t.t[0]);
/**//*
S = p * r
p = (a + b + c) / 2;
r = S / P = 2 * S / (a + b + c)
*/
tmp.r = 2 * triangleArea(t) / (a + b +c);
angleA = acos((b * b + c * c - a * a) / (2 * b * c));
angleB = acos((a * a + c * c - b * b) / (2 * a * c));
angleC = acos((a * a + b * b - c * c) / (2 * a * b));
p = sin(angleA / 2);
p2 = sin(angleB / 2);
p3 = sin(angleC / 2);
xA = t.t[0].x; yA = t.t[0].y;
xB = t.t[1].x; yB = t.t[1].y;
xC = t.t[2].x; yC = t.t[2].y;
f1 = ((tmp.r / p2) * (tmp.r / p2) - (tmp.r / p) * (tmp.r / p) +
xA * xA - xB * xB + yA * yA - yB * yB) / 2;
f2 = ((tmp.r / p3) * (tmp.r / p3) - (tmp.r / p) * (tmp.r / p) +
xA * xA - xC * xC + yA * yA - yC * yC) / 2;
tmp.centre.x = (f1 * (yA - yC) - f2 * (yA - yB)) /
((xA - xB) * (yA - yC) - (xA - xC) * (yA - yB));
tmp.centre.y = (f1 * (xA - xC) - f2 * (xA - xB)) /
((yA - yB) * (xA - xC) - (yA - yC) * (xA - xB));
return tmp;
}
bool isPointOnSegment(TPoint p, TPoint p1, TPoint p2)
...{
// 判断p点是否在线段p1p2上
//1.p是否在直线p1p2上
//2.p是否在以p1p2为对角线的矩形中
if(multi(p1, p2, p) != 0) return false ;
if((p.x > p1.x && p.x > p2.x) || (p.x < p1.x && p.x < p2.x)) return false;
if((p.y > p1.y && p.y > p2.y) || (p.y < p1.y && p.y < p2.y)) return false;
return true;
}
bool isIntersected(TPoint s1, TPoint e1, TPoint s2, TPoint e2)
...{
//判断线段是否相交
//1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交
//2.跨立试验
if(
(max(s1.x, e1.x) >= min(s2.x, e2.x)) &&
(max(s2.x, e2.x) >= min(s1.x, e1.x)) &&
(max(s1.y, e1.y) >= min(s2.y, e2.y)) &&
(max(s2.y, e2.y) >= min(s1.y, e1.y)) &&
(multi(s2, e1, s1) * multi(e1, e2, s1) > 0) &&
(multi(s1, e2, s2) * multi(e2, e1, s2) > 0)
) return true;
return false;
}
//判断线段是否和直线相交。比如要判断线段[s1, e1]和直线 L 是否相交,
//只要判断线段[s1, e1]是否跨立 L 即可。
//判断点是否在三角形内
//1.面积判断
//2.线段的拐向判断
//判断点是否在多边形内,如果多边形为凸多边形,
//下面的两个方法还是适用的,只需要做少量的修改
// 若需要判断的点在凹多边形内,就需采用完全不同
//的方法
bool isPointInTriangle1(TPoint p, TTriangle t)
...{
//判断点是否在三角形内,面积判断
TTriangle tmp;
double area;
int i, j;
area = 0;
for(i = 0;i <= 2;i++)...{
for(j = 0;j <= 2;j++)...{
if(i == j) tmp.t[j] = p;
else tmp.t[j] = t.t[j];
}
area += triangleArea(tmp);
}
return same(area, triangleArea(t));
}
bool isPointInTriangle2(TPoint p, TTriangle t)
...{
//判断点是否在三角形内,线段的拐向判断
//APB, BPC, CPA 的拐向都是相同的
double k1, k2, k3;
k1 = multi(t.t[0], t.t[1], p);
k2 = multi(t.t[1], t.t[2], p);
k3 = multi(t.t[2], t.t[0], p);
if(k1 * k2 * k3 != 0)...{
if(k1 * k2 < 0) return false;
if(k1 * k3 < 0) return false;
}
return true;
}
TPoint symmetricalPoint(TPoint p1, TPoint p2)
...{
//求p1关于p2的对称点
TPoint p3;
p3.x = 2 * p2.x - p1.x;
p3.y = 2 * p2.y - p1.y;
return p3;
}
TPoint symmetricalPointofLine(TPoint p, TLine L)
...{
//p点关于直线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;
}
//点关于线段的对称点
//首先可以根据线段的两个端点求出线段所在的直线L,然后再来求指定
//点关于直线L的对称点
/**//*
凸包( Convex Hull )
凸包是对平面是上的某个点集而言的,凸包是一个最小凸多边形,满足点集
中的所有点都在该凸多边形内(或在该多边形的边上)。
通常,我们采用Graham扫描法来求点集的凸包。首先,排序选出点集中最左下
角点(先取y坐标最小的点,若有多个再在其中取x坐标最小的点),设该点为p0;
然后,将其余的按以p0为中心的极角坐标逆时针排序,多于相同极角的点只保留
距离p0最远的一个,这样就可以得到一个点的序列p1,p2, p2.....,pn;接下来,
将p0, p1, p2压入栈,一次处理pi(i >= 2 && i <= n),不断让栈顶的元素出
栈,直到栈顶的下一个元素,栈顶元素,以及pi构成的折线不拐向左侧,将pi压
入栈中;最后栈中的元素即为所求的凸包的顶点序列
*/
void swap(TPoint p[], int i, int j)
...{
TPoint tmp;
tmp = p[i];
p[i] = p[j];
p[j] = tmp;
}
int stack[MaxNode];
int top;
int cmp(const void *a, const void *b)
...{
TPoint *c = (TPoint *)a;
TPoint *d = (TPoint *)b;
double k = multi(*c, *d, point[0]);
if(k< 0) return 1;
else if(k == 0 && distance(*c, point[0]) >= distance(*d, point[0])) return 1;
else return -1;
}
void grahamScan(TPoint p[], int n)
...{
//Graham扫描求凸包
int i;
//将最左下的点调整到p[0]的位置
for(i = 1;i <= n - 1;i++)...{
if((p[i].y < p[0].y) || (p[i].y == p[0].y && p[i].x < p[0].x))
swap(p, 0, i);
}
//将平p[1]到p[n - 1]按按极角排序,可采用快速排序
qsort(p + 1, , n - 1, sizeof(p[0]), cmp);
for(i = 0;i <= 2;i++) stack[i] = i;
top = 2;
for(i = 3;i <= n - 1;i++)...{
while(multi(p[i], p[stack[top]], p[stack[top - 1]]) > 0)...{
top--;
if(top == 0) break;
}
}
top++;
stack[top] = i;
}
int main()
...{
}
相关文章推荐
- 计算几何相关算法介绍
- 计算几何基本知识整理
- 计算几何的相关知识
- 计算几何算法--点线面相关算法--目录
- POJ 1408 Fishnet(计算几何,多边形相关算法)
- 【算法总结】计算几何相关
- 数字图像处理:基本算法-卷积和相关
- 计算几何常用算法概览
- mysql 相关基本知识
- (转)计算几何算法大全
- linux中线程的基本相关知识
- 计算几何基本函数
- 计算几何的基本应用~~~~
- 关于Java中基本类型的长度相关基础知识
- 计算几何知识
- 多核程序设计的相关基础知识----以误差扩散算法为例
- [22] 计算几何图形包围盒与包围球的算法
- Redis相关基本知识
- 马遍历棋盘高效算法(10*10),10*10以下的棋盘基本可以迅速计算出结果。
- 【算法学习笔记】52.一道题的三种方法..二分答案、动态规划、计算几何 SJTU OJ 1250 BestSubsequence