定义1:平面上的点集,如果以该集合中的任意两点P和Q为端点构成的线段属于该集合,就称该集合是凸的。
定义2:一个点集S的凸包是包含S的最小凸集合。
定理:任意包含n > 2个点的集合S的凸包是以S中的某些点为顶点的凸多边形。(如果所有点是共线的,多边形退化为线段)
因此,直观看来,任意的凸多边形都是凸集合。
凸包问题是为一个包含n个点的集合构造一个凸包。
根据上面的定理设计了一个基于线性规划的算法来判断能否构造凸包。算法描述如下:
两点确定一条直线(线段),因此,在n个点的集合中的点i和j可以确定一条直线,当且仅当其余n-2个点位于该直线上或者是该直线同一侧时,点i和j的连线才是凸包的一部分边界。直线的方程:f=ax+by-c=0;其中a=y2-y1;b=x1-x2;c=x1*y2-y1*x2,将剩余的点的坐标带入该方程,如果都是f >= 0或者都是f <= 0,说明(x1,y1),(x2,y2)构成的线段是凸包的边界。代码如下:
#include <iostream>
#include <complex>
#include <cstdlib>
using namespace std;
void fun(complex<int> *point, int n);
int main()
{
complex<int> point[5] = { complex<int>(0,0),complex<int>(2,0),complex<int>(2,2),complex<int>(0,2),complex<int>(1,1) };
fun(point, 5);
system("pause");
}
void fun(complex<int> *point, int n)
{
int count1,count2;
int k;
int temp;
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++) //遍历所有可能的线段
{
count1 = 0;
count2 = 0;
for (k = 0; k < n; k++)
{
if (k != i && k != j)
{
//计算公式
temp = point[i].real()*point[j].imag() +
point[k].real()*point[i].imag() +
point[j].real()*point[k].imag() -
point[k].real()*point[j].imag() -
point[j].real()*point[i].imag() -
point[i].real()*point[k].imag();
if (temp > 0)
{
count1++;
}
if (0 == temp)
{
count2++;
}
}
}
if (n - 2 == count2) //在一条直线上的时候
{
cout << "凸多边形退化为线段,两个端点分别是:" <<
point[i] << "和" << point[j] << endl;
return;
}
if ((count1 == n - 2) || (0 == count1)) //不在一条直线上
{
cout << "凸多边形其中一条边的两个端点是:" <<
point[i] << "和" << point[j] << endl;
}
}
}
cout << "凸包构造完毕!" << endl;
}
上述算法的时间复杂度是O(n³),很不理想。有待改进。