HDU1007 平面最近点对

2 篇文章 0 订阅
1 篇文章 0 订阅
  • 题目

  • 题意

开始复习自己之前的代码。竟然没有想到,我竟然学过平面最近点对。

题目的背景是套圈游戏。作为一名商人,为了最大化自己的利益,这个圈只能套住一个玩具。但是又要吸引更多的顾客,这个圈就得尽可能的大。

求这个圈的半径。

最近平面点对的裸题了。

  • 平面最近点对  (分治法)

不需要什么鸽巢原理的解析

首先,将所有的点按照x坐标排序,将坐标尽量分成相等数量的两部分。

然后,通过递归求出这两部分的最近点对的距离d1,d2(两个,三个的时候可以直接求得)。

但是,所有点的最近点对并不一定等于 min(d1,d2)。需要进行分治后的合并操作,如下(没有用到7,鸽巢的优化):

     第一,把所有的点中,可能形成的最小值点放入一个集合possible中。这个可能形成的点的横坐标范围在

              [  中间点坐标 - min(d1,d2) , 中间点坐标 + min(d1,d2)]。

     第二, 直接对possible集合中的点,一对一对进行遍历,求出最小值。最糟糕的情况下,复杂度堪比直接暴力搜索。所以,进                   行优化。

     第三,优化就是,将possible集合中的点,按照y坐标进行排序。在一对一对顺序地进行遍历的过程中,如果两点的纵坐标大于了min(d1,d2),break后进行下一次循环。

  • 代码

//1007 套圈,给出N个物体的坐标,让圈一次只能套到一个物体,求圈长度的最大值
//最近点对问题---分治法
//暴力循环--tle
//AC 如果参数总是0,注意有没有传进去参数,是不是重复定义了!
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100005
#define INf 0x3f3f3f
using namespace std;
struct point
{
	double x;
	double y;
};
point p[N];
int possible[N];
double dis(point a,point b)
{
	double xx=a.x-b.x;
	double yy=a.y-b.y;
	return sqrt(xx*xx+yy*yy);
}
bool cmpx(point a,point b)
{
	return a.x<b.x;
}
bool cmpy(int a,int  b)
{
	return p[a].y<p[b].y;
}
double divide(int l,int r)
{

	if(r==l+1)
        return dis(p[l],p[r]);
	if(r==l+2)
        return min(min(dis(p[l],p[l+1]),dis(p[l],p[r])),dis(p[l+1],p[r]));

	int mid=(l+r)/2;
	double mindis=min(divide(l,mid),divide(mid+1,r));


	int pos_num=0;
	for(int i=l;i<=r;i++)
	{
		if(p[i].x>=p[mid].x-mindis && p[i].x<=p[mid].x+mindis)
		{
			possible[pos_num++]=i;
		}
	}

	sort(possible,possible+pos_num,cmpy);

	for(int i=0;i<pos_num;i++)
	{
		for(int j=i+1;j<pos_num;j++)
		{
			if(p[possible[j]].y-p[possible[i]].y>=mindis)
                break;
			mindis=min(mindis,dis(p[possible[i]],p[possible[j]]));
		}
	}

	return mindis;
}
int main()
{
	int n;
//	freopen("a.txt","r",stdin);
	while(scanf("%d",&n)!=EOF&&n)
	{
	//	point p[N];
		for(int i=0;i<n;i++)
		{
			scanf("%lf%lf",&p[i].x,&p[i].y);
		//	cout<<"dis"<<dis(p[0],p[i])<<" ";
		}
		sort(p,p+n,cmpx);
	//	cout<<endl;
	//	for(int i=0;i<n;i++)
	//	cout<<p[i].x<<" "<<p[i].y<<endl;
		double ans=divide(0,n-1);
		printf("%.2f\n",ans/2);
	 }
 }

鸽巢原理算法与代码

#include <ctime>
#include <cmath>
#include <iostream>  
#include <algorithm>

using namespace std;

// 分治法求解最近点对问题
// @`13
// 2017年4月21日
// 参考 : http://blog.csdn.net/to_baidu/article/details/50315607
// 参考 : http://www.cnblogs.com/king1302217/archive/2010/07/08/1773413.html

#define INFINITE_DISTANCE 65535    // 无限大距离
#define COORDINATE_RANGE 100.0    // 横纵坐标范围为[-100,100]

#ifndef Closest_pair

typedef struct Point
{// 二维坐标上的点Point
    double x;
    double y;
}Point;

double Distance(Point a, Point b)
{//平面上任意两点对之间的距离公式计算
    return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}

bool compareX(Point a, Point b)
{//自定义排序规则:依照结构体中的x成员变量升序排序
    return a.x < b.x;
}

bool compareY(Point a, Point b)
{//自定义排序规则:依照结构体中的x成员变量升序排序
    return a.y < b.y;
}

float ClosestPair(Point points[], int length, Point &a, Point &b)
{// 求出最近点对记录,并将两点记录再a、b中
    double distance;                   //记录集合points中最近两点距离 
    double d1, d2;                     //记录分割后两个子集中各自最小点对距离
    int i = 0, j = 0, k = 0, x = 0;    //用于控制for循环的循环变量
    Point a1, b1, a2, b2;              //保存分割后两个子集中最小点对

    if (length < 2)
        return INFINITE_DISTANCE;    //若子集长度小于2,定义为最大距离,表示不可达
    else if (length == 2)
    {//若子集长度等于2,直接返回该两点的距离
        a = points[0];
        b = points[1];
        distance = Distance(points[0], points[1]);
    }
    else
    {//子集长度大于3,进行分治求解
        Point *pts1 = new Point[length];     //开辟两个子集
        Point *pts2 = new Point[length];

        sort(points, points + length, compareX);    //调用algorithm库中的sort函数对points进行排序,compareX为自定义的排序规则
        double mid = points[(length - 1) / 2].x;    //排完序后的中间下标值,即中位数

        for (i = 0; i < length / 2; i++)
            pts1[i] = points[i];
        for (int j = 0, i = length / 2; i < length; i++)
            pts2[j++] = points[i];

        d1 = ClosestPair(pts1, length / 2, a1, b1);             //分治求解左半部分子集的最近点  
        d2 = ClosestPair(pts2, length - length / 2, a2, b2);    //分治求解右半部分子集的最近点  

        if (d1 < d2) { distance = d1; a = a1; b = b1; }            //记录最近点,最近距离
        else { distance = d2; a = a2; b = b2; }

        //merge - 进行子集合解合并
        //求解跨分割线并在δ×2δ区间内的最近点对
        Point *pts3 = new Point[length];

        for (i = 0, k = 0; i < length; i++)                        //取得中线2δ宽度的所有点对共k个    
            if (abs(points[i].x - mid) <= distance)
                pts3[k++] = points[i];

        sort(pts3, pts3 + k, compareY);                                       // 以y排序矩形阵内的点集合

        for (i = 0; i < k; i++)
        {
            if (pts3[j].x - mid >= 0)                                             // 只判断左侧部分的点
                continue;
            x = 0;
            for (j = i + 1; j <= i + 6 + x && j < k; j++)            //只需与有序的领接的的6个点进行比较
            {
                if (pts3[j].x - mid < 0)
                {//  假如i点是位于mid左边则只需判断在mid右边的j点即可
                    x++;
                    continue;
                }
                if (Distance(pts3[i], pts3[j]) < distance)
                {//如果跨分割线的两点距离小于已知最小距离,则记录该距离和两点
                    distance = Distance(pts3[i], pts3[j]);
                    a = pts3[i];
                    b = pts3[j];
                }
            }
        }
    }
    return distance;
}

void SetPoints(Point *points, int length)
{//随机函数对点数组points中的二维点进行初始化
    srand(unsigned(time(NULL)));
    for (int i = 0; i < length; i++)
    {
        points[i].x = (rand() % int(COORDINATE_RANGE * 200)) / COORDINATE_RANGE - COORDINATE_RANGE;
        points[i].y = (rand() % int(COORDINATE_RANGE * 200)) / COORDINATE_RANGE - COORDINATE_RANGE;
    }
}

int main()
{
    int num;            //随机生成的点对个数
    Point a, b;            //最近点对
    double diatance;    //点对距离

    cout << "请输入二维点对个数:";
    cin >> num;
    if (num < 2)
        cout << "请输入大于等于2的点个数!!" << endl;
    else
    {
        cout << endl << "随机生成的" << num << "个二维点对如下:" << endl;
        Point *points = new Point[num];

        SetPoints(points, num);
        for (int i = 0; i < num; i++)
            cout << "(" << points[i].x << "," << points[i].y << ")" << endl;
        diatance = ClosestPair(points, num, a, b);
        cout << endl << endl << "按横坐标排序后的点对:" << endl;
        for (int i = 0; i < num; i++)
            cout << "(" << points[i].x << "," << points[i].y << ")" << endl;

        cout << endl << "最近点对为:" << "(" << a.x << "," << a.y << ")和" << "(" << b.x << "," << b.y << ")" << endl << "最近点对距离为:" << diatance << endl;
    }
    system("pause");
}

#endif // !Closest_pair

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值