继上次ACM几何问题,这次在这里讲一下凸包,作为给大一努力的礼物。
凸包即为一个最小凸多边形满足所有的点都在其内或其边上。
下面将讲述Andrew的算法,其比Graham算法更快更稳定。
对于输入的点p,先按照x从小到大排序(如果x相同,按照y从小到大排序)。
然后进行两次扫描,第一次从左到右,第二次从右到左,每次扫描时间复杂度为O(n),排序为O(nlogn)。
第一次扫描:先将前面的两个点加入,第一个点记X1,第二个点记X2,然后对于第三个点X3,此时产生两个向量v1=X2-X1,v2=X3-X1。
如果v1叉积v2为正,则加入X3这个点,否则删去X2这个点。然后重复上面的步骤,直到遍历完所有的点。
上面的描述也可以这样表述:第一个点记X1,第二个点记X2,然后对于第三个点X3,若X3在向量X1X2的左边,那么加入该点,否则删除X2。
第二次扫描同理,将第一次扫描的最后一个点作为第一个点,然后再加入一个点,重复上面的步骤。
代码如下:
看完上面的内容,我们来想一想下面的题目。
一:坐标系里有一些长方形,只能用直线,使长方形都在你所创建的凸多边形内。
二:平面上有N个红点和M个蓝点,是否存在一条直线,使得直线的左边都是同一种颜色的点,右边都是另一种颜色的点,且点不能再所划的线上。
有兴趣的童鞋可以回复这两个题目在评论版。
凸包即为一个最小凸多边形满足所有的点都在其内或其边上。
下面将讲述Andrew的算法,其比Graham算法更快更稳定。
对于输入的点p,先按照x从小到大排序(如果x相同,按照y从小到大排序)。
然后进行两次扫描,第一次从左到右,第二次从右到左,每次扫描时间复杂度为O(n),排序为O(nlogn)。
第一次扫描:先将前面的两个点加入,第一个点记X1,第二个点记X2,然后对于第三个点X3,此时产生两个向量v1=X2-X1,v2=X3-X1。
如果v1叉积v2为正,则加入X3这个点,否则删去X2这个点。然后重复上面的步骤,直到遍历完所有的点。
上面的描述也可以这样表述:第一个点记X1,第二个点记X2,然后对于第三个点X3,若X3在向量X1X2的左边,那么加入该点,否则删除X2。
第二次扫描同理,将第一次扫描的最后一个点作为第一个点,然后再加入一个点,重复上面的步骤。
代码如下:
//p[]为所有点的点集,n为所有点的个数,ch[]为存储凸包上的点的点集
int Andrew(Point *p, int n, Point *ch)
{
sort(p, p + n);
int m = 0;
//第一次扫描
for (int i = 0; i < n; i++)
{
//Cross函数为两向量叉积函数
while (m > 1 && Cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0) m--;
ch[m++] = p[i];
}
int k = m;
//第二次扫描
for (int i = n - 2; i >= 0; i--)
{
while (m > k && Cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0) m--;
ch[m++] = p[i];
}
//得到的点集中最后一个跟第一个一样,所以减一,返回凸包顶点数
return m - 1;
}
看完上面的内容,我们来想一想下面的题目。
一:坐标系里有一些长方形,只能用直线,使长方形都在你所创建的凸多边形内。
二:平面上有N个红点和M个蓝点,是否存在一条直线,使得直线的左边都是同一种颜色的点,右边都是另一种颜色的点,且点不能再所划的线上。
有兴趣的童鞋可以回复这两个题目在评论版。