二维凸包可以用来解决围栏问题、城市规划问题、聚类分析等等。
原文链接:http://blog.csdn.net/bone_ace/article/details/46239187
分治法
时间复杂度:O(n㏒n)。
思路:应用分治法思想,把一个大问题分成几个结构相同的子问题,把子问题再分成几个更小的子问题……。然后我们就能用递归的方法,分别求这些子问题的解。最后把每个子问题的解“组装”成原来大问题的解。
步骤:
把所有的点都放在二维坐标系里面。那么横坐标最小和最大的两个点 P1 和 Pn 一定是凸包上的点(为什么呢?用反证法很容易证明,这里不详讲)。直线 P1Pn 把点集分成了两部分,即 X 轴上面和下面两部分,分别叫做上包和下包。
对上包:求距离直线 P1Pn 最远的点,即下图中的点 Pmax 。
作直线 P1Pmax 、PnPmax,把直线 P1Pmax 左侧的点当成是上包,把直线 PnPmax 右侧的点也当成是上包。
重复步骤 2、3。
对下包也作类似操作。
然而怎么求距离某直线最远的点呢?我们用到公式:
设有一个点 P3 和直线 P1P2 。(坐标:p1(x1,y1),p2(x2,y2),p3(x3,y3))
当上式结果为正时,p3在直线 p1p2 的左侧;当结果为负时,p3在直线 p1p2 的右边
对上式的结果取绝对值,绝对值越大,则距离直线越远。
注意:在步骤一,如果横坐标最小的点不止一个,那么这几个点都是凸包上的点,此时上包和下包的划分就有点不同了,需要注意。(但在代码中无需特殊处理,详见代码注释)
我对原作者的代码增加了注释,然后更改了一些代码,并写明了原因,希望原作者不要介意,如果有人看到我粘的代码有问题,希望能不吝赐教,谢谢
#include#includeint g_result[240][2];
/*getResult()实现功能:以坐标P0(x1,y1)和Pn(x2,y2)为直线,找出pack里面离这条直线最远的点Pmax
*并找出直线P0Pmax和PmaxPn的上包,进行递归。
*注:Pack[0][0]存放点的个数,pack[1]开始存放点的坐标。
*全局变量g_result[][]用来存放凸包上的点,即最终所要的答案。同样g_result[0][0]存放的是已找到的点的个数。
**/
void getResult(int Pack[240][2], int x1, int y1, int x2, int y2)
{
int i,t,x3,y3,R,Rmax,tmax;
int ResultPack[240][2];
ResultPack[0][0] = 0;
if(Pack[0][0] <= 2)
//if(Pack[0][0] <= 1)//从主函数递归调用来看,Pack用的是含有线上两点的上包总点数,所以如果是0或1或2,就可以直接返回
//这里没用<=2是因为后面的递归算法中的ResultPack没有记录Pack[1][0](这好像产生一个大问题),所以为了解决这里,下面的for仍应该从1开始吧?
//就算x3暂时不是凸包点,但如果计算所得的R大于0就很可能成为凸包点,所以我确切认为是从1开始,然后这里也应该相应改成<=2,不然多跑了一趟浪费时间
return;
x3 = Pack[1][0];//这里的Pack[1][0]不一定是x1的
y3 = Pack[1][1];
R = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;
Rmax = R;
tmax = 1;
//for(i=2;i<=Pack[0][0];i++)//我把CSDN搞下来上的代码改了
for(i=1;i<=Pack[0][0];i++)
{
x3 = Pack[i][0];
y3 = Pack[i][1];
R = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;//R的绝对值越大,那么x3这个点距离x1,x2所连成的线越远(其实不知道公式哪来的,但目前暂时不重要,就是矩阵如下)
/*
|x1 y1 1|
|x2 y2 1|
|x3 y3 1|
*/
if(R >= 0)//R>=0在左边,也就是只处理上包
{
t = ++ResultPack[0][0];
ResultPack[t][0] = x3;
ResultPack[t][1] = y3;
}
if(R > Rmax)
{
Rmax = R;
tmax = i;
}
}
if(Rmax <= 0)//这里就是处理凸包一边上有三点的情况
{
//for(i=1;ix2)
{
x2 = x3;
y2 = y3;
}
}
g_result[1][0] = x1;
g_result[1][1] = y1;
g_result[2][0] = x2;
g_result[2][1] = y2;
g_result[0][0] += 2;
getResult(Point, x1, y1, x2, y2);//上包递归
getResult(Point, x2, y2, x1, y1);//下包递归
printf("\n\n构成凸包的点有:\n");
for(i=1;i<=g_result[0][0];i++)
printf("(%d,%d)\n",g_result[i][0],g_result[i][1]);
system("pause");//提交记得去掉这一行
return 0;//HDOJ选手代码一般没有这一行
}
2018年8月8日18:02:51