凸包问题
(1)问题描述
用分分治法编写一个求解凸包问题的算法,并测试算法的正确性。
【注:凸包问题】给定平面上n个点,从中找出一个最小点集,使该点集所组成的凸多边形包围所有的n个点。
(2)问题分析
将n个点分为两部分,则每一部分可以形成一个凸包,重复分下去最后将多个凸包合并即可得到n个点的凸包。
(3)算法设计
将n个点按横坐标从小到大排列,则x1,xn必定属于最小点集,x1,xn连线将n个点分成两部分,下面只需找各自的凸包即可。以找上凸包为例,在上半部分的点中找到距离x1,xn连线最远的点,将该点与x1,xn相连则可将上半部分分为3块,则找到上凸包的问题又转化为找出另外两个凸包的问题。上凸包和下凸包要分开处理。递归结束的条件是连线的上方(上凸包)/下方(下凸包)没有点。重复此操作即可得到最后整个的凸包。
(4)算法实现
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
#include<fstream>
#include<sstream>
using namespace std;
struct Point {
double x;
double y;
};
inline bool Compx(const Point &p1, const Point &p2)
{
return p1.x < p2.x;
}
/*
函数:求点p在以p1和p2决定直线的上侧还是下侧
返回值:上侧:>0 下侧:<0 在直线上:=0
*/
inline double Line(const Point &p1, const Point &p2, const Point &p)
{
return (p1.y - p2.y) * p.x + (p2.x - p1.x) * p.y + (p1.x * p2.y - p1.y * p2.x);
}
/*
函数:求点p到以p1和p2决定直线的距离
*/
double Dist(const Point &p1, const Point &p2, const Point &p)
{
double A = p1.y - p2.y;
double B = p2.x - p1.x;
double C = p1.x * p2.y - p1.y * p2.x;
return abs(A*p.x + B * p.y + C) / sqrt(A*A + B * B);
}
/*
函数:求解直线p1p2上点集的上包
参数:v:直线p1p2上方的点集 vo:上包点集
*/
void UpHull(const vector<Point> &v, vector<Point> &vo, const Point &p1, const Point &p2)
{
if (v.size() == 0)
return;
if (v.size() == 1) {
vo.push_back(v[0]);
return;
}
double d = 0;
int k;
for (size_t i = 0; i < v.size(); ++i) {
double t = Dist(p1, p2, v[i]);
if (t > d) {
d = t;
k = i;
}
}
vo.push_back(v[k]);
vector<Point> vl;
vector<Point> vr;
for (size_t i = 0; i < v.size(); ++i) {
if (Line(p1, v[k], v[i]) > 0)
vl.push_back(v[i]);
else if (Line(v[k], p2, v[i]) > 0)
vr.push_back(v[i]);
}
UpHull(vl, vo, p1, v[k]);
UpHull(vr, vo, v[k], p2);
}
/*
函数:求解直线p1p2下点集的下包
参数:v:直线p1p2下的点集 vo:下包点集
*/
void DownHull(const vector<Point> &v, vector<Point> &vo, const Point &p1, const Point &p2)
{
if (v.size() == 0)
return;
if (v.size() == 1) {
vo.push_back(v[0]);
return;
}
double d = 0;
int k;
for (size_t i = 0; i < v.size(); ++i) {
double t = Dist(p1, p2, v[i]);
if (t > d) {
d = t;
k = i;
}
}
vo.push_back(v[k]);
vector<Point> vl;
vector<Point> vr;
for (size_t i = 0; i < v.size(); ++i) {
if (Line(p1, v[k], v[i]) < 0)
vl.push_back(v[i]);
else if (Line(v[k], p2, v[i]) < 0)
vr.push_back(v[i]);
}
DownHull(vl, vo, p1, v[k]);
DownHull(vr, vo, v[k], p2);
}
/*
函数:求解点集v的凸包
*/
void ConvexHull(vector<Point> &v, vector<Point> &vo)
{
sort(v.begin(), v.end(), Compx);
vo.push_back(v[0]);
vo.push_back(v[v.size() - 1]);
vector<Point> vu;
vector<Point> vd;
for (size_t i = 1; i < v.size() - 1; ++i) {
if (Line(v[0], v[v.size() - 1], v[i]) >= 0)
vu.push_back(v[i]);
else if (Line(v[0], v[v.size() - 1], v[i]) < 0)
vd.push_back(v[i]);
}
UpHull(vu, vo, v[0], v[v.size() - 1]);
DownHull(vd, vo, v[0], v[v.size() - 1]);
}
int main()
{
vector<Point> v;
ifstream input("Points.txt", ifstream::in);
string line;
Point p;
while (getline(input, line)) {
stringstream liness(line);
liness >> p.x >> p.y;
v.push_back(p);
}
vector<Point> vo;
ConvexHull(v, vo);
for (auto p : vo)
cout << "<" << p.x << "," << p.y << ">" << endl;
system("pause");
return 0;
}
输入文件Points.txt:
0 0
1 5
2 2
3 -1
4 5
5 -3
6 0