这两周一直在做计算几何的题,感觉计算几何题就是读懂题意,并且能有清晰的解题思路,剩下的就是套用模板了,所以模板相当重要!下面整理了一些模板以便以后做题用
说明:下面的eps 都是1e-8;
目录:
㈠ Pick定理:
㈡ 在二维空间中,已知四个点的坐标,求交点的坐标
㈢ 已知多边形的顶点的坐标,求多边形的面积利用叉积
㈣ 求三角形面积
㈤ 判断一个多边形是否是一个凸包
㈥ 求凸包的周长(在若干个点钟选取点组成凸包并求其周长)
㈦ 判断点是否在凸包内
㈧ 凸包内的点到凸包每条边的距离
㈨ 跨立试验--判断两条线段是否相交(含顶点)
㈩ 判断点是否在矩形的内部
①.Pick定理:如图给定顶点坐标都是整点,所围成的多边形的面积area、多边形边上的点的个数on以及多边形内部点的个数in满足area = in + on / 2 – 1;
POJ 1265 Area
代码如下:
int m, n, on, in; double area; struct node { int x, y; }point[MAX]; int Gcd(int a, int b)//求a和b的最大公约数的函数 { int temp; if (a < b)//把a和b中较大的数字放到a中 { temp = a; a = b; b = temp; } if (b == 0)//找到两者的最大公约数,直接返回a { return a; } return Gcd(b, a % b);//否则继续往下找 } int Area(int i)//叉积计算多边形的面积 { return double(point[i - 1].x * point[i].y - point[i].x * point[i - 1].y); } int main() { int i, j, xx, yy; scanf("%d", &m); for (i = 1; i <= m; ++i) { printf("Scenario #%d:\n", i); scanf("%d", &n); memset(point, 0, sizeof(point)); on = 0; in = 0; area = 0; for (j = 1; j <= n; ++j) { scanf("%d%d", &xx, &yy); point[j].x = xx + point[j - 1].x; point[j].y = yy + point[j - 1].y; on += Gcd(abs(xx), abs(yy));//求在边上的点的个数 area += Area(j); } area = area / 2.0; in = int(area) + 1 - on / 2; printf("%d %d %.1lf\n", in, on, area); printf("\n"); } return 0; }
②在二维空间中,已知四个点的坐标,求交点的坐标
核心代码如下:
Point GetPoint(Point p1, Point p2, Point p3, Point p4)//已知四个点求直线相交的点的坐标 //(这种求法包括了直线斜率不存在或为0的情况下) { Point p0;//p0就是所求的交点的坐标 double A1 = p2.y - p1.y; double B1 = p1.x - p2.x; double C1 = p1.y * (-B1) - p1.x * A1; double A2 = p4.y - p3.y; double B2 = p3.x - p4.x; double C2 = p3.y *(-B2) - p3.x * A2; p0.x = (C2 * B1 - C1 * B2) / (A1 * B2 - A2 * B1); p0.y = (C2 * A1 - C1 * A2) / (B1 * A2 - B2 * A1); return p0; }
③已知多边形的顶点的坐标,求多边形的面积利用叉积
核心代码实现如下:
for (int i = 0; i < N; ++i) { int j = (i + 1) % N; area += point[i].x * point[j].y - point[i].y * point[j].x; } area = area / 2; if (area < 0) { area = - area; }
④求三角形的面积
⑴ 利用的是海伦公式求的三角形面积(已知三角形的三个顶点的坐标)
原理假设有一个三角形,边长分别为a、b、c,三角形的面积Area可由以下公式求得:设p是周长的一半,p=(a+b+c)/2,则三角形的面积为:Area =sqrt〔p*(p-a)*(p-b)*(p-c)〕
代码如下:
double GetArea(int m, Point tr[]) { double a[m]; double pp = 0; for (int i = 0; i < m; ++i) { a[i] = Dis(tr[i], tr[(i + 1) % m]); pp += a[i]; } pp = pp / 2.0; return sqrt(pp * (pp - a[0]) * (pp - a[1]) * (pp -a[2])); }
⑵ 利用叉积求三角形面积(已知三角形的三个顶点的坐标)
代码如下:
double GetArea(int m, Point tr[])//其中这儿的m就是3了 { int i; double area = tr[0].y * (tr[m - 1].x - tr[1].x); for (i = 1; i < m; ++i)//求三角形的面积 { area += tr[i].y * (tr[i - 1].x - tr[(i + 1) % m].x); } if (area < 0) { area = -area; } return area = area / 2.0; }
⑤判断一个多边形是否是一个凸包(有若干个点是否能构成凸包)
核心代码如下:
for (i = 0; i + 2 < n; ++i) { dis = Multi(p[i + 1], p[i + 2], p[i]); if (abs(dis) >= eps)//先确定多边形的输入是逆时针还是顺时针 { break; } } flag = 1; for (i = 0; i < n && flag; ++i) { for (j = 0, k = i + 2; j < n - 2; j ++, k ++) { double q = Multi(p[(i + 1) % n], p[k % n], p[i]); if (q * dis < -eps)//出现了旋转方向相反的点了 { flag = 0; break; } } } if (flag == 0)//不是凸包 { printf("不是凸包\n"); }
⑥求凸包的周长(在若干个点钟选取点组成凸包并求其周长)
核心代码如下:
int convex[MAX];//储存选取的点 bool cmp(Point a, Point b)//先按y的从小到大排序,若y的大小相等的时候,再按x从小到大排序!这个函数在#include<algorithm>头文件中 { if(a.y == b.y) return a.x < b.x; return a.y < b.y; } double Multi(Point p1, Point p2, Point p3) { return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y); } void Graham()//选取点构成凸包 { int i, count; top = 1; sort(p, p + N, cmp);//排序函数 for (i = 0; i < 3; ++i)//先把前三个点压入栈中 { convex[i] = i; } for (i = 2; i < N; ++i) { while (top && Multi(p[i], p[convex[top]], p[convex[top - 1]]) >= 0) { top --; } convex[++ top] = i; } count = top; convex[++ top] = N - 2; for (i = N - 3; i >= 0; --i) { while (top != count && Multi(p[i], p[convex[top]], p[convex[top - 1]]) >= 0) { top --; } convex[++ top] = i; } } for (i = 0; i < top; ++i)//求周长 { ans += Dis(convex[i], convex[(i + 1) % top]); } printf("%.0lf\n", ans);
⑦判断点是否在凸包内
int Judge() { double a, b; int i; Point p3; p3.x = x; p3.y = y; b = Multi(p[0], p[1], p3); for (i = 1; i < n; ++i) { a = Multi(p[i], p[(i + 1) % n], p3); if (a * b < - eps) { return 1;//说明点(x, y)不在凸包内 } b = a; } return 0; }
⑧凸包内的点到凸包每条边的距离
实现的原理是:求到多边形每条边的距离利用了三角形的面积 = (底边的边长 * 高) / 2;即 dis = 2.0 * area / h;
triangle[0].x = x; triangle[0].y = y;//(x, y)是凸包内的一个点 for (i = 0; i < n; ++i) { triangle[1] = p[i]; triangle[2] = p[(i + 1) % n]; dis = 2.0 * GetArea(3, triangle) / Dis(triangle[1], triangle[2]); printf("%.2lf\n", dis); }
⑨跨立试验--判断两条线段是否相交(含顶点)
bool Isintersect(Point a1, Point a2, Point b1, Point b2)//判断两条线段是否相交(含顶点) { if (Min(a1.x, a2.x) <= Max(b1.x, b2.x) && Min(a1.y, a2.y) <= Max(b1.y, b2.y) && Min(b1.x, b2.x) <= Max(a1.x, a2.x) && Min(b1.y, b2.y) <= Max(a1.y, a2.y) && Multi(a1, a2, b1) * Multi(a1, a2, b2) <= 0 && Multi(b1, b2, a1) * Multi(b1, b2, a2) <= 0 ) return true;//说明两线段之间相交 return false; }
⑩判断点是否在矩形的内部
其中seg是代表点,rec[1],rec[3]代表了矩形的对角点 bool Inrectangle(int i)//判断点是否在矩形的内部 { if (seg[i].x > Max(rec[1].x, rec[3].x)) return false; if (seg[i].y > Max(rec[1].y, rec[3].y)) return false; if (seg[i].x < Min(rec[1].x, rec[3].x)) return false; if (seg[i].y < Min(rec[1].y, rec[3].y)) return false; //如果满足上面的,说明线段在矩形的内部并且没有和矩形没有交点(含线段的顶点) return true; } ⑾ 已知两条相交直线,知道每条直线的的两个点,求交点的坐标 Point GetPoint(Point p1, Point p2, Point p3, Point p4)//已知四个点求直线相交的点的坐标 //(这种求法包括了直线斜率不存在或为0的情况下) { Point p0; double A1 = p2.y - p1.y; double B1 = p1.x - p2.x; double C1 = p1.y * (-B1) - p1.x * A1; double A2 = p4.y - p3.y; double B2 = p3.x - p4.x; double C2 = p3.y *(-B2) - p3.x * A2; p0.x = (C2 * B1 - C1 * B2) / (A1 * B2 - A2 * B1); p0.y = (C2 * A1 - C1 * A2) / (B1 * A2 - B2 * A1); return p0; }