Solution of Convex Hull Problem by Divide and Conquer Method

Problem introduction

Given a set of some points on the plane, find some points so that these points form a convex enclosure, enclosing all points, as shown in the figure
Insert picture description here

Ideas

Using the divide-and-conquer method, the point set is divided into two, and the overall convex hull problem can be divided into [find the convex hull of the upper half] + [find the convex hull of the lower half]

Sub-strategy

The strategy to divide the set into two is to sort the points in ascending order by x, and ascending order by y for the same x, and then select 0 and the last subscript to make a straight line with these two points. On the right , we use pa and pb to denote these two points
Insert picture description here

Divide the scope of the subproblem solution

The set has been divided into two, so how should the solution interval of the subproblem be delimited?

Traverse all points

  • Find the point pmax that is farthest from the "straight line pa pb" above the "straight line pa pb"
  • Find the point pmin that is farthest from [straight line pa pb] under [straight line pa pb]

The farthest distance

This distance can be expressed by the area of ​​the triangle formed by their three points. This determinant can be solved. It is worth noting that p1 must be on the left of p2, p1 and p2 form a straight line , and p3 is the point we want to judge.

  • The final value is greater than 0, indicating that p3 is above [straight line p1 p2]
  • The final value is less than 0, indicating that p3 is below [straight line p1 p2]
  • The final value is equal to 0, indicating that p3 on [straight line p1 p2]
    Insert picture description here
    we find the point pmax above [straight line pa pb], the furthest distance from [straight line pa pb], and below [straight line pa pb], the distance [ pa straight line farthest point Pb] Pmin, the pa, pb, pmax, pmin put together to obtain the following graph
    Insert picture description here
    the straight line [pa] pmax above the point as the next set of s1 find
    the linear [Pb] pmax above the point As the next search set s2 , the point below
    [straight line pa pmin] is taken as the next search set s3, and the point below [straight line pmin pb] is taken as the next search set s4.

Insert picture description here

Recursively collect a set of four regional points, it is worth noting that

Use the points array to store points.
Use the vis array to indicate whether the point with index i is a point on the convex hull, and vis [i] = 1 is

  • Recursive boundary conditions, the number of point sets is less than 3, indicating that all points are on the convex hull, vis is set to 1
  • The points on the line should also be added to the next point set
  • If the recursion is the set of the upper half, then all subsequent recursions are only for the upper half, recursive s1 s2
  • If the recursion is the lower half of the set, then all subsequent recursion is only for the lower half, recursion s3 s4
  • If the recursion is the whole set (only the first recursion will send this case), you need to recurse the upper and lower parts at the same time, that is, recursively s1 s2 s3 s4

Code

Enter:

12
1 1
1 2
2 0
2 1
2 3
3 1
3 3
4 0
4 2
5 1
5 4
6 2

Output

(1, 1)
(1, 2)
(2, 0)
(2, 3)
(4, 0)
(5, 1)
(5, 4)
(6, 2)
#include <bits/stdc++.h>

using namespace std;

// 结构定义 
typedef struct p
{
	int x, y;	
}p;

bool cmp(const p& p1, const p& p2)
{
	if(p1.x==p2.x) return p1.y<p2.y;
	return p1.x<p2.x;
}

// 存放点 
p points[100];
int vis[100];

// 计算p1,p2,pi三个点组成的三角形面积 
int peak(p p1, p p2, p pi)
{
	return p1.x*p2.y + pi.x*p1.y + p2.x*pi.y - pi.x*p2.y - p2.x*p1.y - p1.x*pi.y;
}

// 递归求凸包 
// ps 是当前要求解的点的集合,ps保存这些点在points数组中的下标 
// mode 表示递归s1 s2,还是递归s3,s4,还是同时递归 s1,s2,s3,s4
// mode = 3 递归 s1,s2,s3,s4,只有第一次调用会出现该情况 
// mode = 2 递归s3,s4
// mode = 1 递归s1 s2
void hull(vector<int> &ps, int mode)
{
	// 边界处理:少于两个点的集合一定是凸包上的点 
	if(ps.size()<=2)
	{
		for(int i=0; i<ps.size(); i++) vis[ps[i]]=1;
		return;
	}
	
	// 最左右一定是凸包上的点, pa最左点,pb最右点 
	vis[ps.front()]=1, vis[ps.back()]=1;
	p pa=points[ps.front()], pb=points[ps.back()];
	
	// 找距离 pa,pb组成的直线最远的点,imax是上方最远,imin是下方最远 
	int maxs=INT_MIN, mins=INT_MAX, imax=-1, imin=-1;
	for(int i=1; i<ps.size()-1; i++)
	{
		int s = peak(pa, pb, points[ps[i]]);
		if(s>maxs && s>=0) maxs=s, imax=ps[i];
		if(s<mins && s<=0) mins=s, imin=ps[i];
	}
	
	// pa,pb与imax,imin的连线,分割出下一趟递归的点集合 s1 s2 s3 s4
	vector<int> s1, s2, s3, s4;
	for(int i=0; i<ps.size()-1; i++)
	{
		if(peak(pa, points[imax], points[ps[i]])>=0) s1.push_back(ps[i]);
		if(peak(pa, points[imin], points[ps[i]])<=0) s3.push_back(ps[i]);
	}
	for(int i=1; i<ps.size(); i++)
	{
		if(peak(points[imax], pb, points[ps[i]])>=0) s2.push_back(ps[i]);
		if(peak(points[imin], pb, points[ps[i]])<=0) s4.push_back(ps[i]);
	}
	 
	if(mode==3)
		hull(s1, 1), hull(s2, 1), hull(s3, 2), hull(s4, 2);
	else if(mode==1) hull(s1, 1), hull(s2, 1);
	else if(mode==2) hull(s3, 2), hull(s4, 2);
} 

int main()
{
	
	int n, x, y;
	cin>>n;
	vector<int> ps(n);
	for(int i=0; i<n; i++)
		cin>>points[i].x>>points[i].y, ps[i]=i;
	sort(points, points+n, cmp);
	
	hull(ps, 3);
	
	for(int i=0; i<n; i++)
		if(vis[i]==1) cout<<"("<<points[i].x<<", "<<points[i].y<<")"<<endl;
		
	return 0;
}

/*
12
1 1
1 2
2 0
2 1
2 3
3 1
3 3
4 0
4 2
5 1
5 4
6 2
*/
Published 262 original articles · won 11 · 10 thousand views

Guess you like

Origin blog.csdn.net/weixin_44176696/article/details/105291858