第一题

2019-03-24  本文已影响0人  Dreamindream

分治:点对最短距离
要求:求平面上距离最近的点间的距离
思路参照了一篇解释的非常优秀的博客:
[寻找距离最小的平面点对——分治方法](https://blog.csdn.net/qq_40707370/article/details/85268256

其中的分治思想就是:每次把n个点分成两份,最短距离dm一定出现在左边n/2个点的最短距离d1,右边n/2个点的最短距离d2,左右配对的最短距离d3这三个值之间,其中最后一个要是穷举的话,会有n/2*n/2=n^2/4种组合,于是想要减少配对的可能,就是对于一边的点来说,可能出现最短距离的点只会在一个宽n/2,长n/2的矩形内,而且这个点必须和作为划分的midX的距离不超过min(d1,d2),于是就可以减小问题规模。
上面的博客+书上有一些要求,代码里没有实现:
1.为了平衡问题规模其实最好应取中位数
(我每次取的平均数)
2.确定好x属于左边的区域[midX-dm,minX],应依次对这个里面的点,检查右侧[midX,midX+dm]内的点是否符合情况
(我直接把横坐标在[midX-dm,midX+dm]的k个点全部存入辅助数组,写了个O(n)=n^2二重循环)
还有一点不足是:开了三个大数组,以为会超时超内存,万幸的是过了
因为老师不允许用快排,使用了MergeSort,没想到上学期学的东西很快就忘了,一开始就被卡在这里,同样参照了一篇博客:
[C语言 归并排序](https://www.cnblogs.com/942267027wzmblog/p/6882267.html

总的来说:一个负责分,一个负责连接,因为怕出错,就直接定义了一个全局的数组

代码如下:

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;

class St{
    public:
    double x;
    double y;
};
St Site[100000];
St Temp[100000];
St Add[100000];
double CalDis(St M,St N)
{
    double dx=M.x-N.x;
    double dy=M.y-N.y;
    return sqrt(dx*dx+dy*dy);
}
void MergeAdd_X(St A[],int left,int mid,int right,St T[])
{
    int i=left;
    int j=mid+1;
    int k=left;
    while(i<=mid&&j<=right)
    {
        if(A[i].x<=A[j].x)
        {
            T[k].x=A[i].x;
            T[k].y=A[i].y;
            k++;
            i++;
        }
        else
        {
            T[k].x=A[j].x;
            T[k].y=A[j].y;
            k++;
            j++;
        }
    }
    while(i<=mid)
    {
            T[k].x=A[i].x;
            T[k].y=A[i].y;
            k++;
            i++;
    }
    while(j<=right)
    {
             T[k].x=A[j].x;
            T[k].y=A[j].y;
            k++;
            j++;
    }
    for(int f=left;f<k;f++)
    {
        A[f].x=T[f].x;
        A[f].y=T[f].y;
    }
}
void MergeAdd_Y(St A[],int left,int mid,int right,St T[])
{
    int i=left;
    int j=mid+1;
    int k=left;
    while(i<=mid&&j<=right)
    {
        if(A[i].y<=A[j].y)
        {
            T[k].x=A[i].x;
            T[k].y=A[i].y;
            k++;
            i++;
        }
        else
        {
            T[k].x=A[j].x;
            T[k].y=A[j].y;
            k++;
            j++;
        }
    }
    while(i<=mid)
    {
            T[k].x=A[i].x;
            T[k].y=A[i].y;
            k++;
            i++;
    }
    while(j<=right)
    {
             T[k].x=A[j].x;
            T[k].y=A[j].y;
            k++;
            j++;
    }
    for(int f=left;f<k;f++)
    {
        A[f].x=T[f].x;
        A[f].y=T[f].y;
    }
}
void MergeSort(St A[],int left,int right,St T[],bool flag)
{
    if(flag)   //对X排序 
    {
    if(left<right)
    {
        int mid=(left+right)/2;
        MergeSort(A,left,mid,T,flag);
        MergeSort(A,mid+1,right,T,flag);
        MergeAdd_X(A,left,mid,right,T);
    }   
    }
    else
    {
        if(left<right)  //对Y排序 
    {
        int mid=(left+right)/2;
        MergeSort(A,left,mid,T,flag);
        MergeSort(A,mid+1,right,T,flag);
        MergeAdd_Y(A,left,mid,right,T);
    }   
    }
    
}
double MIN(double a,double b)
{
    return a<b?a:b;
}

double FindAns(St A[],int left,int right,St T[])
{
    if(right-left==0)
    {
        return -100000.0;
    }
    else if(right-left==1)
    {
        return CalDis(A[left],A[right]);
    }
    else if(right-left==2)
    {
        double d1=CalDis(A[left],A[left+1]);
        double d2=CalDis(A[left+1],A[right]);
        double d3=CalDis(A[left],A[right]);
        double dm=0.0;
        dm=MIN(d1,d2);
        dm=MIN(dm,d3);
        return dm;
    }
    else
    {
        int mid=(left+right)/2;
        double dm=0.0;
        double d1=FindAns(A,left,mid,T);
        double d2=FindAns(A,mid+1,right,T);
        dm=MIN(d1,d2);
        
        int i=left;
        int j=right;
        int k=0;
        for(i=mid;i>=left;i--)
        {
            double t=fabs(A[i].x-A[mid].x);
            if(t<=dm)
            {
                T[k].x=A[i].x;
                T[k].y=A[i].y;
                k++; 
            }
            else
            {
                break;
            }
        }
        for(int i=mid+1;i<=right;i++)
        {
            double t=fabs(A[i].x-A[mid].x);
            if(t<=dm)
            {
                T[k++]=A[i];
            }
            else
                break;
        }
        MergeSort(T,0,k-1,Add,0);
    
        for(i=0;i<k;i++)
        {
            for(j=i+1;j<k;j++)
            {
                if(fabs(T[i].y-T[j].y)<dm)
                {
                    double op=CalDis(T[i],T[j]);
                    dm=MIN(op,dm);
                }
                 else
                     break;
            }
        }

        return dm;
    }
}

int main()
{
    int n;
    while(scanf("%d",&n)!=0&&n!=0)
    {
        double a,b;
    for(int i=0;i<n;i++)
    {
       scanf("%lf %lf",&a,&b);
       Site[i].x=a;
       Site[i].y=b; 
    }
    MergeSort(Site,0,n-1,Temp,1);  //pre sort by x
    double dm=FindAns(Site,0,n-1,Temp);
    dm=dm/2;   //Required by the task need the half of the smallest distence 
    printf("%.2lf\n",dm);
    }
    
    
    return 0;
    
}

中间遇到的坑:
1、归并排序搞错,老老实实写了一维,然后二维,检验通过了才往后写
2、一开始没想清流程,何时对x,y排序,现筛出x再对y排序还是反过来,归并排序要写几个,d1,d2,d3求的顺序、递归要怎么写
3、没想出如何求d3,二重循环都没想到
4、辅助数组没想到要用两个,没想到把符合要求的x要存进辅助数组,有点混乱
5、各种小问题,i,j写反,赋值左右两边写反
6、甚至忘了怎么写输入0停止
写了两天,被自己菜哭T T

上一篇 下一篇

猜你喜欢

热点阅读