【算法设计与分析】分治法和暴力——最近对问题

问题描述

设 p1(x1,y1),p2(x2,y2), …… ,pn(xn,yn)是平面上n个点构成的集合S,最近对问题就是找出集合S中距离最近的点对。

分治法思想

分治法总体来说分为三个步骤:划分、求解子问题、合并

这题的思路是,将集合S分为左右两个子集S1和S2,两个子集分别含有n/2个点,先在每个子集中递归地求其最接近的点对,再求最近的两点分别在两个集合中的点对,最后比较得出对短距离。

最近对问题的算法效率取决于划分点m的选取
如果选取m =(max{S}+min{S})/2,则有可能因集合S中点分布的不均匀而造成子集S1和S2的不平衡,如果使用各中点坐标的中位数(即S的中值)作为分割点,则会得到一个平衡的分割点,使得子集S1和S2中有个数大致相同的点。

暴力法代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iomanip>
void BF(const vector<Point>& v)
{
	double ans = maxn;
	int r1 = -1, r2 = -1;
	int len = v.size();
	for(int i=0; i<len; ++i)
	{
		for(int j=i+1; j<len; ++j)
		{
			double d = Dis(v[i],v[j]);
			if(d<ans)
			{
				ans = d;
				r1 = i, r2 = j;
			}
		}
	}
	cout<<"n^2暴力枚举的蛮力法:\n";
	cout<<"最近的两个点为:"<<v[r1]<<"和"<<v[r2]<<endl;
	cout<<"最近对的距离为:"<<ans<<endl;
}
int main()
{
	//freopen("out.txt","w",stdout);
	vector<Point> points;
	srand(time(NULL));
	int n = 15;
	for(int i=0;i<n;i++)
	{
		Point point;
		point.x = rand()%71;
		point.y = rand()%61;
		points.push_back(point);
	}
	BF(points);
	return 0;
}

得到结果:
在这里插入图片描述

分治法代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iomanip>
using namespace std;
const int maxn = 0x3f3f3f3f;
struct Point
{
	int x,y;
	friend ostream & operator << (ostream & out,const Point& p);
	Point(int _x=0,int _y=0):x(_x),y(_y){}
	bool operator < (const Point& rhs)const{
		return x < rhs.x;
	}
};
ostream & operator << (ostream & out,const Point& p){
	out<<"("<<setw(2)<<p.x<<","<<setw(2)<<p.y<<")";
	return out;
}
bool cmp(const Point&a,const Point&b) //按x坐标排序
{
	return a.x<b.x;
}
bool cmp2(const Point&a,const Point&b) //按y坐标排序
{
	return a.y<b.y;
}

double Dis(Point a,Point b)
{
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double min(double a,double b,double c)
{
	return min(min(a,b),c));  
}

double ClosestPoint(vector<Point> points,int m,int n)  //左闭右开
{
	if(n-m<2) return maxn;
	if(n-m==2)
	{
		return Dis(points[m],points[n-1]);
	}
	if(n-m==3)
	{
		double a = Dis(points[m],points[n-1]);
		double b = Dis(points[m],points[n-2]);
		double c = Dis(points[n-1],points[n-2]);
		return min(a,b,c);
	}
	int mid = (m+n)/2;
	int mm = points[mid].x;

	double d1 = ClosestPoint(points,m,mid); // 左边区域最短距离
	double d2 = ClosestPoint(points,mid,n);  // 右边区域最短距离
	double minn = min(d1,d2);
	vector<Point> left,right;
	for(int i=m;i<mid;++i)
	{
		if(points[i].x>mm-minn)
			left.push_back(points[i]);
	}
	for(int i=mid;i<n;++i)
	{
		if(points[i].x<=mm+minn)
			right.push_back(points[i]);
	}
	sort(right.begin(),right.end(),cmp2); //按y坐标排序
	double mindist = 100000;
	for(int i=0;i<(int)left.size();i++)   //遍历左边所有点求与右边最短距离
	{
		for(int j=0;j<(int)right.size();j++)
		{
			if(abs(left[i].y-right[j].y)<minn)
			{
				double d = Dis(left[i],right[j]);
				if(d<min) mindist = d;
			}
		}
	}
	return min(minn,mindist);
}
int main()
{
	//freopen("out.txt","w",stdout);
	vector<Point> points;
	srand(time(NULL));
	int n = 15;
	for(int i=0;i<n;i++)
	{
		Point point;
		point.x = rand()%71;
		point.y = rand()%61;
	//	cin>>point.x>>point.y;
		points.push_back(point);
	}
	sort(points.begin(),points.end(),cmp);
	for(int i=0;i<n;i++)
	{
		//cout << points[i].x <<" "<< points[i].y <<endl;
		cout<<points[i]<<endl;
	}
	cout << "分治法求得的答案为:"<< ClosestPoint(points,0,points.size()) << endl ;
	return 0;
}


结果图为:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值