凸包問題
阿新 • • 發佈:2020-07-18
(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