SWUST OJ 249: 凸包面积(分治法求凸包面积)

好蠢哦,很多细节错误,搞得wa好几次

题目描述

原题链接点我
在这里插入图片描述

总结一下,就是简单的分治法求凸包面积

思路

如何使用分治法求凸包面积

分治法:将问题划分为同一类型的若干个子问题,问题足够小时求解,最后合并结果

首先,我们将点集划分为两个部分,然后逐步求解

如何划分
为了最好的划分效果,我们希望找到两个点,能划分点集,同时是凸包的定点。
我们选择水平方向上最两边的点,作为第一次划分的依据
在这里插入图片描述

第二步求子集的解

暂时只考虑上半部分(因为下半部分可以相同方法解决)
我们希望能找到一个点,能划分子集,同时是顶点,最能肯定的一件事是
距离第一次划分的边最远的点一定是我们需要的点

在这里插入图片描述
如何计算
考虑到距离涉及开方、除法等操作,我们可以利用三角形的面积,
如下图,找到面积最大的点即可,
面积怎么算?
我们在线性代数中,得到过这个问题的答案,给出三点坐标,得到面积

通过三个点的坐标求出三角形面积的公式 当三个点A、B、C的坐标分别为A(x1,y1)、B(x2,y2)、C(x3、y3)时,三角形面积为, S=(x1y2-x1y3+x2y3-x2y1+x3y1-x2y2)。

在这里插入图片描述

下一步划分

观察上图,我们发现:三角形内部的点我们是不用考虑的,可以假设当前连线就是三角形的边,你就能得到上面的结论,
于是我们再次缩小
在这里插入图片描述
现在,只需要仿照前一步就能得到我们要的顶点。

如何解决SWUST OJ 249?

注意到题中需要的是面积,我们稍加观察就能发现:
上半部分的面积可以通过三部分的三角形面积相加得到!!
这三部分分别是左端点,右端点,最高点以及由他们划分出来的子三角形构成,
你只需要将计算得来的面积保留下来,在最后一步合并,就做到了
(另外,上述求三角形面积的公式中,设计除2,这将带来小数,所以我们可以在最后对所有的结果和相加后再除2,这样避免了因为精度导致问题的可能
在这里插入图片描述代码如下:
C++11去掉abs函数,并将创建对象修改为{}初始化即可
另一种利用指针的解法在我另一篇文章中SWUST OJ 249凸包面积 分治法解法二,指针升级版

#include<iostream>
#include <iomanip>  //不要忘了头文件
using namespace std;
//点类
int abs(int num) {
	if (num < 0) {
		num *= (-1);
	}
	return num;
}
class Point {
	int x;
	int y;
public:
	Point() = default;
	Point(int x, int y) {
		this->x = x;
		this->y = y;
	}
	int getX() {
		return this->x;
	}
	int getY() {
		return this->y;
	}
	bool isXBiggerThan(Point amb) {
		if (this->x > amb.getX()) {
			return true;
		}
		return false;
	}
};
//点集合类,方便修改点数组
class NodeList {
private:
	int num;
public:

	Point pointList[105];
	Point left;
	Point right;
	NodeList() {
		num = 0;
	}
	NodeList(Point left, Point right) {
		num = 0;
		this->left = left;
		this->right = right;
	}
	void add(Point p) {
		pointList[num] = p;
		num += 1;
	}
	bool isEmpty() {
		return num == 0;
	}
	int getLength() {
		return num;
	}
};

bool isAbove(Point point,Point left,Point right) {
	int isAbove = (point.getY() - left.getY()) * (right.getX() - left.getX()) - (point.getX() - left.getX()) * (right.getY() - left.getY());
	if (isAbove > 0) {
		return true;
	}
	else {
		return false;
	}
}
int calS(Point a, Point b, Point c) {
	//利用行列式
	int toAdd = a.getX() * b.getY() + b.getX() * c.getY() + c.getX() * a.getY();
	int toMinus = a.getY() * b.getX() + b.getY() * c.getX() + c.getY() * a.getX();
	return abs(toAdd - toMinus);
}
//该模块为核心,在每次遍历查找,返回最大三角形的面积,这里不除2,达到精确要求
int doSearch(NodeList list) {
	//结束点
	if (list.isEmpty()) {
		return 0;
	}
	//计算最大面积,然后迭代,必须遍历两边出结果,让添加节点和寻找一同进行
	int max = 0;
	int biggestP = 0;
	int biggetS = calS(list.left, list.right, list.pointList[biggestP]);
	for (int i = 1; i < list.getLength(); i++) {

		int s = calS(list.left, list.right, list.pointList[i]);
		if (s > biggetS) {
			biggestP = i;
			biggetS = s;
		}

	}
	//生成子节点集
	NodeList left( list.left,list.pointList[biggestP] );
	NodeList right( list.pointList[biggestP] ,list.right );
	int maxX = list.pointList[biggestP].getX();
	for (int i = 0; i < list.getLength(); i++) {
		if (i == biggestP) {
			continue;
		}
		if (list.pointList[i].getX() < maxX) {
			if (isAbove(list.pointList[i],left.left,left.right)) {
				left.add(list.pointList[i]);
			}
			
		}
		else {
			if (isAbove(list.pointList[i],right.left,right.right)) {
				right.add(list.pointList[i]);
			}
		}
	}
	return doSearch(left) + doSearch(right) + biggetS;
}

void cal() {
	int n;
	cin >> n;
	Point points[105];
	for (int i = 0; i < n; i++) {
		int x, y;
		cin >> x >> y;
		points[i] = Point{ x,y };
	}
	int leftx = 0, rightx = 0;
	//划分上下包
	//找两端
	for (int i = 1; i < n; i++) {
		Point point = points[i];
		if (point.isXBiggerThan(points[rightx])) {
			rightx = i;
		}
		if (!point.isXBiggerThan(points[leftx])) {
			leftx = i;
		}
	}
	//生成上下包
	Point left = points[leftx];
	Point right = points[rightx];
	NodeList above( left,right );
	NodeList below( left,right );
	//添加节点
	for (int i = 0; i < n; i++) {
		if (i == leftx || i == rightx) {
			continue;
		}
		Point point = points[i];
		bool isAmbAbove = isAbove(point,left,right);
		if (isAmbAbove) {
			above.add(point);
		}
		else {
			below.add(point);
		}
	}
	//执行分治查找
	double answer = (doSearch(above) + doSearch(below)) / 2.0;
	cout << fixed << setprecision(1) << answer << endl;
}
int main() {
	/*
	多组测试数据。第一行是一个整数T,表明一共有T组测试数据。
每组测试数据的第一行是一个正整数N(0< N < = 105),表明了墨点的数量。
接下来的N行每行包含了两个整数Xi和Yi(0<=Xi,Yi<=2000),表示每个墨点的坐标。
每行的坐标间可能包含多个空格。
*/	
	int t;
	cin >> t;
	while (--t != -1) {
		cal();
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_44494373/article/details/105519296