分治法:
分治法在每一层递归上都有三个步骤:
分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;
合并:将各个子问题的解合并为原问题的解。
实例:
1:求最近点对:
在二维空间里,可用分治法求解最近点对问题。预处理:
分别根据点的x轴和y轴坐标进行排序,得到X和Y,很显然此时X和Y中的点就是S中的点。
情况(1):点数小于等于三时:
情况(2):点数大于三时:
首先划分集合S为SL和SR,使得集合分为左右两边,尽量使L分割出两个相同点数的集合。然后在SL与SR中解决最近点对问题,得出SL中最近点对距离为DL,SR中最近点对距离为DR,然后D=MIN(DL,DR),再用D与[L-D,L+D]间的最近点对(P1,P2)作比较。得出较小的点对,即为S中的最近点对。
具体可以分为以下步骤:
步骤1:根据点的y值和x值对S中的点排序。
步骤2:找出中线L将S划分为SL和SR
步骤3:将步骤2递归的应用解决SL和SR的最近点对问题,并令d=min(dL,dR)。
步骤4:将L-d~L+d内的点以y值排序,对于每一个点(x1,y1)找出y值在y1-d~y1+d内的所有点,计算距离为d’。如果d'小于d,令d=d',最后的d值就是答案。
2:凸包问题:
时间复杂度:O(n㏒n)。
思路:应用分治法思想,把一个大问题分成几个结构相同的子问题,把子问题再分成几个更小的子问题……。然后我们就能用递归的方法,分别求这些子问题的解。最后把每个子问题的解“组装”成原来大问题的解。
步骤:
1:把所有的点都放在二维坐标系里面。那么横坐标最小和最大的两个点 P1 和 Pn 一定是凸包上的点。直线 P1Pn 把点集分成了两部分,即 X 轴上面和下面两部分,分别叫做上包和下包。
2:对上包:求距离直线 P1Pn 最远的点,即下图中的点 Pmax 。
3:作直线 P1Pmax 、PnPmax,把直线 P1Pmax 左侧的点当成是上包,把直线 PnPmax 右侧的点也当成是上包。
4:重复步骤 2、3。
5:对下包也作类似操作。
点到直线距离:
设有一个点 P3 和直线 P1P2 。(坐标:p1(x1,y1),p2(x2,y2),p3(x3,y3))
对上式的结果取绝对值,绝对值越大,则距离直线越远。
注意:在步骤一,如果横坐标最小的点不止一个,那么这几个点都是凸包上的点,此时上包和下包的划分就有点不同了,需要注意
#include<iostream>
#include<stdlib.h>
#include <time.h>
#include <vector>
#include <math.h>
#include <stdio.h>
#include <sys/timeb.h>
#include <ctime>
#include <climits>
using namespace std;
#define TOP 100
#define BOTTOM -100
static int number;
class ThreeDimensionalPoint{
public :
ThreeDimensionalPoint(double x1, double y1, double z1){
x = x1;
y = y1;
z = z1;
}
void print(){
cout << "x:" << x << " y:" << y << " z:" << z<<endl;
}
double x;
double y;
double z;
};
vector<ThreeDimensionalPoint *>* GetDataSet(int NUM_DATA,int Max = 100, int Min = -100){
vector<ThreeDimensionalPoint *>* DataSet = new vector<ThreeDimensionalPoint *>();
srand((unsigned)time(NULL));
int RANDNUM = Max - Min;
for (int i = 0; i < NUM_DATA; i++){
double X_DATA = rand() % RANDNUM - RANDNUM / 2;
double Y_DATA = rand() % RANDNUM - RANDNUM / 2;
double Z_DATA = rand() % RANDNUM - RANDNUM / 2;
ThreeDimensionalPoint* randpoint = new ThreeDimensionalPoint(X_DATA, Y_DATA, Z_DATA);
DataSet->push_back(randpoint);
}
vector<ThreeDimensionalPoint *>* FinalDataSet = new vector<ThreeDimensionalPoint *>();
int num = DataSet->size();
double *p = new double[num];
int *q = new int[num];
for (int i = 0; i < num; i++){
q[i] = i;
}
for (int i = 0; i < num; i++){
p[i] = DataSet->at(i)->x;
}
for (int i = 1; i < num; i++) //从第2个数据开始插入
{
int j = 0;
while (j < i && p[j] <= p[i]) //寻找插入的位置
j++;
if (j < i) //i位置之前,有比pDataArray[i]大的数,则进行挪动和插入
{
int k = i;
int temp = q[i];
double temp1 = p[i];
while (k > j) //挪动位置
{
p[k] = p[k - 1];
q[k] = q[k - 1];
k--;
}
q[k] = temp; //插入
p[k] = temp1;
}
}
for (int i = 0; i < num; i++){
FinalDataSet->push_back(DataSet->at(q[i]));
}
return FinalDataSet;
}
double min(double num1, double num2){
if (num2 < num1)
num1 = num2;
return num1;
}
double max(double num1, double num2){
if (num2 > num1)
num1 = num2;
return num1;
}
double Threepartition(double x1, double x2, double x3){
double Split_X = x1;
if (x1 > x2 || x1 == x2){
if (x1 > x3 || x1 == x3){
if (x2 > x3 || x2 == x3)
Split_X = x2;
else
Split_X = x3;
}
else{
Split_X = x1;
}
}
else{
if (x2 > x3 || x2 == x3){
if (x1 > x3 || x1 == x3)
Split_X = x1;
else
Split_X = x3;
}
else{
Split_X = x2;
}
}
return Split_X;
}
//不开平方,方便比较节省效率
double Distance_N(ThreeDimensionalPoint* point1, ThreeDimensionalPoint* point2){
return pow((point1->x - point2->x), 2) + pow((point1->y - point2->y), 2) + pow((point1->z - point2->z), 2);
}
//开平方,获得实际距离
double Distance_Y(ThreeDimensionalPoint* point1, ThreeDimensionalPoint* point2){
return sqrt(pow((point1->x - point2->x), 2) + pow((point1->y - point2->y), 2) + pow((point1->z - point2->z), 2));
}
//蛮力法解决最近点对问题
double Brute_PointDistance(vector<ThreeDimensionalPoint *>* SUBSET){
double PointDistance = double(INT_MAX);
for (int i = 0; i < SUBSET->size(); i++){
for (int j = i + 1; j < SUBSET->size(); j++){
double Distance = Distance_N(SUBSET->at(i), SUBSET->at(j));
if (Distance < PointDistance)
PointDi