求凸包点和面积及周长

/**
Author: LinZhiQ
Date: 2018-10-01 17:28
Graham扫描法  求凸包
*/
#include<bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
const double PI = acos(-1.0);

struct Point {
	double x,y;

	Point() {}

	Point(double _x, double _y) {
		x = _x;
		y = _y;
	}

	Point operator -(const Point &b)const {
		return Point(x - b.x, y - b.y);
	}// (点 a - 点 b) 表示以 b 为坐标轴原点重新定义 a 的坐标

	double operator ^(const Point &b)const {
		return x * b.y - y * b.x;
	}//Cross 比较 点 a 与 点 b 到原点 连线的斜率 ,a的斜率小则返回值 大于零,等于零表示在同一直线
	double operator *(const Point &b)const {
		return x * b.x + y * b.y;
	}//Dot	点自己 乘 自己 表示 点到原点距离的平方 也就是点乘

	void transXY(double B) {
		double tx = x,ty = y;
		x = tx * cos(B) - ty * sin(B);
		y = tx * sin(B) + ty * cos(B);
	}
} p[105], lis[105];

bool CheckCross(Point a, Point b, Point c){   // 判断点 c 是否在 直线 ab 左侧 
	if((c ^ a) >= 0 && (b ^ c) >= 0 && ((a - b) ^ (c - b)) >= 0){
		return true; 
	}
	return false;
// 求交叉积(ab,ac)
//	return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x); 
// 返回值小于0表示c在ab右侧,大于0则在左侧
} 

bool cmp1(const Point &a, const Point &b) {
	return (a.x != b.x ? a.x < b.x : a.y < b.y);
}	// 按照点的 y 坐标从小到大,x 坐标从小到大 (逆时针)

bool cmp2(const Point &a, const Point &b) {
	return ((a ^ b) == 0 ? (a * a) < (b * b) : (a ^ b) > 0);
}	// 极角排序,如果 a与b 在同一直线,那就与原点距离近的优先,否则极角小的优先

int T, n, m;

void PolarAngleSort(){	// 极角排序  
	sort(p + 1, p + 1 + n, cmp1);
	for(int i = 2;i <= n;i++){
		p[i] = p[i] - p[1];
	}
	p[1].x = p[1].y = 0;
	sort(p + 1, p + 1 + n, cmp2);
}

void ConvexHull() {
	PolarAngleSort();
	lis[1] = p[1];	// 起点 
	lis[2] = p[2];	// 前两个点与起点三点可构成面 
	lis[3] = p[3];
	m = 3;
	for(int i = 4;i <= n;i++){
		while(CheckCross(p[i],lis[m - 1],lis[m]) && m > 2){ // 把被新点包围的点全部删去 
			m--;
		}
		lis[++m] = p[i];
	}
}

double getArea() { // 求面积 
	double area = 0;
	for(int i = 2; i <= m - 1; i++) {
		area += ((lis[i] - lis[1]) ^ (lis[i + 1] - lis[1]));
	}
	return fabs(area) / 2.0;
}

double dis(const Point &a, const Point &b){
	return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

double getPerimeter(){ //求周长 
	double sum = 0;
    for(int i = 2; i <= m; i++){
        sum += dis(lis[i - 1],lis[i]);
    }
    sum += dis(lis[m], lis[1]);
    return sum;
} 

int main() {
	scanf("%d",&T);
	while(T--) {
		scanf("%d",&n);
		for(int i = 1; i <= n; i++) {
			scanf("%lf %lf",&p[i].x,&p[i].y);
		}
		ConvexHull();
		for(int i = 1; i <= m; i++) {
			printf("(%lf,%lf)\n",lis[i].x,lis[i].y);
		}
		printf("%lf %lf\n",getArea(),getPerimeter());
	}
	return 0;
}

 

### 计算任意四边形周长面积的Python代码 为了计算任意四边形的周长面积,可以基于几何学原理来设计程序。对于周长,只需累加各条边的长度即可;而对于面积,则可以通过将其划分为两个三角形并分别解其面积后相加以得到。 以下是具体的实现方式: #### 定义四边形类 通过定义一个 `Quadrilateral` 类,我们可以封装计算周长面积的功能。假设已知四个顶坐标 `(x1, y1), (x2, y2), (x3, y3), (x4, y4)`,则可通过如下公式完成计算: - **周长**:依次连接相邻两的距离之。 - **面积**:利用鞋带定理(Shoelace Formula),即给定集按顺序排列后的有向面积公式[^1]。 ```python import math class Quadrilateral: def __init__(self, points): self.points = points # List of tuples [(x1,y1),(x2,y2),(x3,y3),(x4,y4)] def distance(self, p1, p2): """Calculate the Euclidean distance between two points.""" return math.sqrt((p2[0] - p1[0])**2 + (p2[1] - p1[1])**2) def perimeter(self): """Calculate the perimeter of the quadrilateral.""" total_distance = 0 n = len(self.points) for i in range(n): total_distance += self.distance(self.points[i], self.points[(i + 1) % n]) return total_distance def area(self): """Calculate the area using Shoelace formula.""" n = len(self.points) sum1 = sum(self.points[i][0] * self.points[(i + 1) % n][1] for i in range(n)) sum2 = sum(self.points[i][1] * self.points[(i + 1) % n][0] for i in range(n)) return abs(sum1 - sum2) / 2 # Example usage points = [(0, 0), (4, 0), (4, 3), (0, 3)] quad = Quadrilateral(points) print(f"Perimeter: {quad.perimeter()}") # 输出周长 print(f"Area: {quad.area()}") # 输出面积 ``` 上述代码实现了以下功能: - 初始化函数接受一组表示四边形顶坐标的列表作为输入参数; - 提供了用于计算欧几里得距离的帮助方法 `distance()`; - 方法 `perimeter()` 负责返回整个多边形的边界总长度; - 方法 `area()` 利用了鞋带公式的逻辑来得出封闭区域内的总面积。 #### 关于凸包的概念补充说明 如果考虑的是更复杂的形状或者不确定是否为简单四边形的情况时,可能需要用到凸壳算法如 Graham Scan 来处理数据预加工阶段中的去重与排序等问题[^2]。不过这超出了当前需范围,在此不做深入探讨。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值