博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

计算几何相关问题

Posted on 2011-09-08 16:23  各种不会  阅读(116)  评论(0)    收藏  举报

1.最近点

// 最近点对.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <algorithm>
#include <WINDOWS.H>
#include <SET>
using namespace std;
/************************************************************************/
/*最近点  基本的分治思想 
                                                                     */
/************************************************************************/

struct Point{int x; int y;};

typedef vector<Point> PointsVec;

//#define min(a,b) a<b?a:b 

void PrintVector(const vector<Point>& src)
{
	vector<Point>::const_iterator it;
	for (it=src.begin();it!=src.end();it++)
	{
		printf("%d  %d\n",it->x, it->y);
	}
}

bool operator==(const Point& p1, const Point& p2){
	return p1.x==p2.x && p1.y==p2.y;
}

bool operator<(const Point& p1, const Point& p2){
	return p1.x<p2.x;
}
bool operator>(const Point& p1, const Point& p2){
	return p1.x>p2.x;
}

int distance(Point p1, Point p2){
	return (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y);
}

int merge(PointsVec Sy, int miniDist, int midValue){
	PointsVec::iterator it;
	for (it=Sy.begin();it!=Sy.end();)
	{
		if(it->x <= midValue+miniDist && it->x >= midValue-miniDist)
			it++;
		else
			it = Sy.erase(it);
	}

	for (int i=0; i<Sy.size(); i++)
	{
		for (int j=i+1; j<=i+7&&j<Sy.size();j++)
		{
			int tmp = distance(Sy[i],Sy[j]);
			if(tmp<miniDist)
					miniDist = tmp;
		}
	}
	return miniDist;
}

int ClosestPoints(PointsVec Sx, PointsVec Sy){
	int miniDist = INT_MAX;
	if(Sx.size()<=3){
		for (int i=0; i<Sx.size(); i++)
		{
			for (int j=i+1; j<Sx.size(); j++)
			{
				int tmp = distance(Sx[i],Sx[j]);
				if(tmp<miniDist)
					miniDist = tmp;
			}
		}
	}else{
		int midIndex = (Sx.size()-1)/2;
		int midValue = Sx[midIndex].x;
		PointsVec Sxl, Sxr, Syl, Syr;
		for (int i=0; i<Sx.size(); i++){
			if(Sx[i].x<=midValue)
				Sxl.push_back(Sx[i]);
			else
				Sxr.push_back(Sx[i]);

			if(Sy[i].x<=midValue)
				Syl.push_back(Sy[i]);
			else
				Syr.push_back(Sy[i]);
		}
		int minL = ClosestPoints(Sxl,Syl);
		int minR = ClosestPoints(Sxr,Syr);
		miniDist = min(minL,minR);
		miniDist = merge(Sy,miniDist,midValue);
	}
	return miniDist;
}

int NativeClosePoints(PointsVec srcPoints){
	int miniDist = INT_MAX;
	for (int i=0; i<srcPoints.size(); i++)
	{
		for (int j=i+1; j<srcPoints.size(); j++)
		{
			int tmp = distance(srcPoints[i],srcPoints[j]);
			if(tmp<miniDist)
				miniDist = tmp;
		}
	}
	return miniDist;
}

bool cmp1(Point a, Point b){
	return a.x < b.x;
}

bool cmp2(Point a, Point b){
	return a.y < b.y;
}

int main(int argc, char* argv[])
{
	PointsVec srcPoint,Sy;

	srand(GetTickCount());

	set<Point> points;
	for (int i=0; i<100; i++)
	{
		Point pt;
		pt.x = rand()%10;
		pt.y = rand()%10;
		points.insert(pt);//.push_back(pt);
	}
	for (set<Point>::iterator it=points.begin(); it!=points.end(); it++)
	{
		srcPoint.push_back(*it);
	}

	Sy = srcPoint;
	sort(srcPoint.begin(), srcPoint.end(), cmp1);
	//PrintVector(srcPoint);
	sort(Sy.begin(), Sy.end(), cmp2);
	//PrintVector(Sy);
	cout << ClosestPoints(srcPoint,Sy) << endl;
	cout << NativeClosePoints(srcPoint) << endl;

	return 0;
}
2.凸包及其直径
// 凸包.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <VECTOR>
#include <algorithm>

using namespace std;

struct Point{int x;int y;};

bool operator==(const Point& a, const Point& b){
	return a.x==b.x && a.y ==b.y;
}

bool compare(const Point& p1, const Point& p2)
{
	int temp = p1.x*p2.y-p1.y*p2.x;
	if(temp<0)
		return false;
	else if(temp==0 && p1.x*p1.x+p1.y*p1.y>p2.x*p2.x+p2.y*p2.y)
		return false;
	else
		return true;

}

void PrintVector(const vector<Point>& src)
{
	vector<Point>::const_iterator it;
	for (it=src.begin();it!=src.end();it++)
	{
		printf("%d  %d\n",it->x, it->y);
	}
}

bool isLeft(Point p1, Point p2, Point p3)
{
	Point pt1, pt2;
	pt1.x = p1.x - p2.x;
	pt1.y = p1.y - p2.y;

	pt2.x = p3.x - p2.x;
	pt2.y = p3.y - p2.y;
	
	int temp = pt2.x*pt1.y - pt1.x*pt2.y;
	if(temp > 0)
		return true;
	else
		return false;
}

/************************************************************************/
/*计算凸包:主要解决两个问题
1.找到基准点之后,要按照每个点和基准点组成的向量和X轴横坐标的夹角,升序排序
这里判断的依据是例如pi,pj和p0    则(pi-p0)*(pj-p0)>0   则pi的夹角小于pj   (pi-p0)*(pj-p0)<0   则pi的夹角大于pj
(pi-p0)*(pj-p0)=0 说明三点在一条直线上,只取离基准点最远的那个点,其他点可以忽略
2.第二个难点就是判断是否左转   例如pi,pi+1和pi+2三个点
(p[i+1])-p[i])*(p[i-1]-p[i])>0   加入p[i+1]
否则 弹出p[i]   继续回溯
                                                                     */
/************************************************************************/
void CalcConvexHull(vector<Point>& srcPoints, vector<Point>& desPoints){
	if(srcPoints.size()<3)
		return;
	Point ptBase = srcPoints.front();
	vector<Point>::iterator it;
	for (it=srcPoints.begin(); it!=srcPoints.end();it++)
	{
		if(it->y < ptBase.y)
			ptBase = *it;
		else if(it->y==ptBase.y && it->x<ptBase.x)
			ptBase = *it;
	}

	for (it=srcPoints.begin(); it!=srcPoints.end();)
	{
		it->x -= ptBase.x;
		it->y -= ptBase.y;
		it++;
	}

	sort(srcPoints.begin(),srcPoints.end(),compare);
	PrintVector(srcPoints);

	it = srcPoints.begin();
	desPoints.push_back(*it);
	it++;
	desPoints.push_back(*it);
	it++;
	desPoints.push_back(*it);
	it++;
	for (;it!=srcPoints.end();it++)
	{
		Point p1, p2, p3;
		p1 = desPoints.at(desPoints.size()-2);
		p2 = desPoints.at(desPoints.size()-1);
		p3 = *it;
		while(!isLeft(p1,p2,p3)){
			desPoints.pop_back();
			p1 = desPoints.at(desPoints.size()-2);
			p2 = desPoints.at(desPoints.size()-1);
		}
		desPoints.push_back(p3);
	}
	printf("******************************\n");
	PrintVector(desPoints);
}

int cross(Point p1, Point p2, Point p3){
	Point pt1, pt2;
	pt1.x = p1.x - p2.x;
	pt1.y = p1.y - p2.y;
	
	pt2.x = p3.x - p2.x;
	pt2.y = p3.y - p2.y;
	
	int temp = pt2.x*pt1.y - pt1.x*pt2.y;
	return temp;
}

int distance(Point p1, Point p2){
	return (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y);
}

int _max(int a, int b){
	return a>b?a:b;
}

/************************************************************************/
/*旋转卡壳法 求凸多边形的直径,基本思想就是对每一条边,遍历每一个顶点到该边的两个断点的距离;
距离该边最远的顶点到两个端点的距离也最远,又因为顶点到边的距离是单峰函数,所以当下一个顶点小于
当前顶点时就退出,由于按逆时针方向输入各条边,所以最大的顶点也随逆时针反向旋转,不需要每次都遍历一遍
所有顶点,使用叉积来代表顶点到边的距离                                                                     */
/************************************************************************/
int rotating_calipers(vector<Point> &ch)
{
	ch.push_back(ch[0]);
	int size = ch.size();
	int p=1, result=0;
	for (int i=0; i<size-1; i++)
	{
		while(cross(ch[p+1],ch[i], ch[i+1])>cross(ch[p],ch[i], ch[i+1]))
			p = (p+1)%(size-1);
		result = _max(result,_max(distance(ch[p],ch[i]), distance(ch[p+1],ch[i+1])));
	}
	return result;
}

int main(int argc, char* argv[])
{
	Point p1, p2, p3, p4, p5, p6;
	p1.x = 1; p1.y=1;
	p2.x = 2; p2.y=5;
	p3.x = 4; p3.y=1;
	p4.x = 4; p4.y = 3;
	p5.x = 2; p5.y = 2;
	p6.x = 3; p6.y = 2;
	vector<Point> src, result;
	src.push_back(p1);
	src.push_back(p2);
	src.push_back(p3);
	src.push_back(p4);
	src.push_back(p5);
	src.push_back(p6);
	CalcConvexHull(src,result);
	cout << rotating_calipers(result) << endl;
	return 0;
}

参考资料:

算法导论

http://www.cnblogs.com/DreamUp/archive/2010/09/16/1828131.html
http://www.cnblogs.com/Booble/archive/2011/04/03/2004865.html
http://www.cppblog.com/staryjy/archive/2010/09/25/101412.html