算法-分治法-凸包问题

35 篇文章 2 订阅

蛮力法的凸包问题:https://blog.csdn.net/qq_40452317/article/details/87976833

第一步、把给定的点集中在横坐标上排序,就能得到p[0]和p[n-1]的直线,将求解凸包问题分解为求解上凸包和下凸包两个问题。

//第一步:排序
//排序,如果,横坐标相同,则按照纵坐标排序
int compxy(point a,point b)
{
	if(a.x!=b.x)
		return a.x<b.x;
	else
		return a.y<b.y;
}

 

第二步、求三角形的面积,假设三个点,A(x1,y1),B(x2,y2), C(x3,y3),以下行列式对于平面上任意三角形, 求解面积都很方便, 所得结果是三角形ABC面积的两倍,A->B->C为顺时针顺序时,该值为正,,反之则为负。

//第二步:计算三角形面积
int S(point a1, point a2, point a3)
{
	return a1.x*a2.y + a3.x*a1.y + a2.x*a3.y - a3.x*a2.y - a2.x*a1.y - a1.x*a3.y;
}

 

第三步、找到一个点Pmax,使三角形P0-Pmax-Pn-1面积最大,也就是距离直线P1Pn-1最远的点。函数deal(first,last),first代表直线最小的点即p[0],last代表直线最大的点,即[n-1]。寻找pmax点,就是面积最大时的点,放进mark数组,即他们就是凸包的点,递归求解。

 

void DealLeft(int first, int last)
{
    int max = 0, index = -1;
    int i = first;
    if (first < last)
    {
        for (i = first+1; i < last; i++) //注意两端,对于first和last,没必要再进行计算
        {
            int calcu = Djudge(p[first], p[i], p[last]);
            if (calcu == 0) {  visit[i] = 1; } //
            if (calcu > max)
            {
                max = calcu;
                index = i;
            }
 
        }
    }
    else
    {
        for (i-1; i >last; i--) //如果first>last,重复上述过程,注意这里下界不是0.
        {
            int calcu = Djudge(p[first], p[i], p[last]);
            if (calcu == 0) {visit[i] = 1;} //
            if (calcu >  max)
            {
                max = calcu;
                index = i;
            }
        }
    }
    if (index != -1)
    {
        visit[index] = 1; //对取到的点进行标注  
        DealLeft(first, index);
        DealLeft(index, last);//分治的部分
    }
}

完整代码 

#include <iostream>
using namespace std;
#include <algorithm>
#include <stdlib.h>
#define N 10000
int n = 0;
struct POINT
{
    int x, y;
}p[N],ans[N];
int visit[N],mark[N];
int Djudge(POINT a1, POINT a2, POINT a3)
{
    int calculate = a1.x*a2.y + a3.x*a1.y + a2.x*a3.y - a3.x*a2.y - a2.x*a1.y - a1.x*a3.y;
    return calculate;
}
bool cmpxy(const POINT a, const POINT b) //按x轴排序,如果x相同,按y轴排序
{
    if (a.x != b.x)
        return a.x < b.x;
    else
        return a.y < b.y;
}
void DealLeft(int first, int last)
{
    int max = 0, index = -1;
    int i = first;
    if (first < last)
    {
        for (i = first+1; i < last; i++) //注意两端,对于first和last,没必要再进行计算
        {
            int calcu = Djudge(p[first], p[i], p[last]);
            if (calcu == 0) {  visit[i] = 1; } //
            if (calcu > max)
            {
                max = calcu;
                index = i;
            }
 
        }
    }
    else
    {
        for (i-1; i >last; i--) //如果first>last,重复上述过程,注意这里下界不是0.
        {
            int calcu = Djudge(p[first], p[i], p[last]);
            if (calcu == 0) {visit[i] = 1;} //
            if (calcu >  max)
            {
                max = calcu;
                index = i;
            }
        }
    }
    if (index != -1)
    {
        visit[index] = 1; //对取到的点进行标注  
        DealLeft(first, index);
        DealLeft(index, last);//分治的部分
    }
}
 
int main()
{
	cout<<"请输入点数:"<<endl;
    cin >> n;
	cout<<"请输入点的坐标:"<<endl;
    for (int i = 0; i < n; i++)
    {
        cin >> p[i].x >> p[i].y;
        visit[i] = 0;
    }
    visit[0] = 1;
    visit[n - 1] = 1;
    sort(p, p + n, cmpxy);
    DealLeft(0, n - 1); //查找上凸包;
    DealLeft(n - 1, 0); //查找下凸包;
    int t = 0;
    for (int i = 0; i < n; i++)
    {
        if (visit[i] == 1)
        {
            ans[t].x = p[i].x;
            ans[t].y = p[i].y;
            t++;
        }
    }
//顺时针输出
    mark[0] = mark[t - 1] = 1; //数组mark避免重复检查降低效率
    for (int i = 1; i < t - 1; i++)
    {
        mark[i] = 0;
    }
	cout<<"凸包点的坐标:"<<endl;
    cout << ans[0].x << " " <<ans[0].y<< endl;
    for (int i =1; i < t-1; i++)
    {
        int d = Djudge(ans[0], ans[t-1], ans[i]);
        if (d >= 0)
        {
            cout << ans[i].x << " " << ans[i].y << endl;
            mark[i] = 1;
        }
    }
    cout << ans[t - 1].x << " " << ans[t - 1].y << endl;
    for (int i = 1; i < t; i++)
    {
        if (mark[i] != 1)
        {
            int d = Djudge(ans[0], ans[t - 1], ans[i]);
            if (d < 0)
            {
                cout << ans[i].x << " " << ans[i].y << endl;
            }
        }
    }
    return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值