HDOJ1007Quoit Design——日常折磨

原题链接:HDOJ1007
本来看到这样的题我应该是一脸懵逼的,但是幸运的是由于太菜没来得及做,导致做到这题的时候已经被科普解法了。没错!就是按照x+y排序 分治!
按照了解到的解法,我知道了每块区域要划分为左半区,右半区,一左一右的中间区。(还没学到分治
于是用到递归写出了这样的代码

#include "iostream"
#include <algorithm>
#include "math.h"
#include "string.h"
using namespace std;
struct  poi;
struct poi{
    double x,y;
};
poi a[999999];
//double dtc[9999][9999];


double dis(const poi &a, const poi &b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool eq(const double& a,const double& b){
    if(fabs(a-b)<0.000000001) return true;
    else return false;
}
double mindis(int start,int end){//一个区域中的最小距离=min(左半区最小距离,右边区最小距离,一左一右最小距离)
    int cap=end-start+1;
    int left=start,right=start+cap/2;
    int leftcap=cap/2,rightcap=cap-cap/2,lefte=start+leftcap-1,righte=right+rightcap-1;
    if(cap==1) return 1e+300;
    else if(cap==2) return dis(a[start],a[end]);
    double minlr=min(mindis(left,lefte),mindis(right,righte));//左半区和右半区最小距离

    //计算中间区域最小距离
    int i,j;
    for(i=right;(fabs(a[i].x-a[right].x))<minlr||eq(a[right].x-a[i].x,minlr);i--){if(i<left) break;}//令i+1为中间区域的第一个物体
    for(j=right;(fabs(a[j].x-a[right].x))<minlr||eq(a[j].x-a[right].x,minlr);j++){if(j>righte) break;}//令j-1 为中央区域的最后一个物体
    i++;j--;
    if(i!=j) {
        double minmid = dis(a[i], a[j]);

        for (int k = i; k <= j&&k<=lefte; k++)
            for (int t = right; t>=right&&t <= j; t++) {
                if (dis(a[k], a[t]) < minmid) minmid = dis(a[k], a[t]);
            }
        return  min(minmid, minlr);
    }
    else return minlr;
}
bool cmp(const poi& x,const poi &y){
    return x.x<y.x;
}
int main(){

    int N;
    while(cin>>N&&N!=0){
        memset(a,0, sizeof(poi)*N);
        for(int i=0;i<N;i++) {cin>>a[i].x>>a[i].y;}
        sort(a,a+N,cmp);
        printf("%.2lf\n",mindis(0,N-1)/2);
    }

    return 0;
}

交上去之后TLE了。。。
于是想到是不是求中间区域的时候项太多了
于是将求中间区域的代码改成递归

//计算中间区域最小距离
    int i,j;
    for(i=right;(fabs(a[i].x-a[right].x))<minlr||eq(a[right].x-a[i].x,minlr);i--){if(i<left) break;}//令i+1为中间区域的第一个物体
    for(j=right;(fabs(a[j].x-a[right].x))<minlr||eq(a[j].x-a[right].x,minlr);j++){if(j>righte) break;}//令j-1 为中央区域的最后一个物体
    i++;j--;
    return min(mindis(i,j),minlr)

然后发现毫无l用,对于一些数据可以无限递归下去
这时我坚信问题就出在这个中间区域中,于是跑去百度

果不其然
在这里找到了关于中间区域的解法
https://blog.csdn.net/lishuhuakai/article/details/9161843在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述
然后我就把中间区域的代码改成了这样:

//计算中间区域最小距离
    int i,j;
    for(i=right;(fabs(a[i].x-a[right].x))<minlr||eq(a[right].x-a[i].x,minlr);i--){if(i<left) break;}//令i+1为中间区域的第一个物体
    for(j=right;(fabs(a[j].x-a[right].x))<minlr||eq(a[j].x-a[right].x,minlr);j++){if(j>righte) break;}//令j-1 为中央区域的最后一个物体
    i++;j--;
    if(i!=j) {
        int p=0;
        for(int k=i;k<right;k++) {m[p].x=a[k].x;m[p].y=a[k].y;m[p].id=0;p++;}
        for(int k=right;k<=j;k++) {m[p].x=a[k].x;m[p].y=a[k].y;m[p].id=1;p++;}//将中间区域的点放入m数组中以便排序
        sort(m,m+p,cmpy);//对中间区域的店按y进行排序
        for(int k=0;k<p;k++)//从中间区域中每一点都找出之后7个点,与它们求最短距离
            for(int t=1;(k+t)<p&&t<8;t++){
                if(dis(m[k],m[k+t])<minlr&&m[k].id!=m[k+t].id) minlr=dis(m[k],m[k+t]);

            }
        return minlr;
    }
    else return minlr;

然后!
还是TLE~~
这时看着那篇博客的我一脸茫然,这不是和他写的思路一样了吗?为什么他就能过我过不了

于是我先生成了10w对随机数

int main() {
   double  a,b;
   srand(time(0));
   for(int i=0;i<100000;i++) {
       a=(rand()%999+1)*1.0;
       b=(rand()%999+1)*1.0;
       cout<<a<<' '<<b<<endl;
   }
}

在这里插入图片描述
再将它们输入到程序里,发现
在这里插入图片描述
根本发现不了什么
然后我又注释掉这里

//        memset(a,0, sizeof(poi)*N);
//        memset(m,0, sizeof(m));

仍然TLE

后来我又找到了百度
继续搜索帖子,然后我在一个帖子里注意到了这里
在这里插入图片描述
(不会是这里吧
|
|
|

AC

至于为什么之前那个帖子的代码可以AC,我也有同样的好奇
后来我再把他的代码提交,发现TLE

在这里插入图片描述
没人看的代码附上:

#include "iostream"
#include <algorithm>
#include "math.h"
#include "string.h"
using namespace std;
struct  poi;
struct poi{
    double x,y;
};
struct poi_id{
    double x,y;
    int id;
};
poi a[999999];
//double dtc[9999][9999];
poi_id m[100100];

bool cmpx(const poi& x, const poi &y){
    return x.x<y.x;
}
bool cmpy(const poi_id &x,const poi_id &y){
    return x.y<y.y;
}
template <class T>
double dis(T &a, T &b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool eq(const double& a,const double& b){
    if(fabs(a-b)<0.000000001) return true;
    else return false;
}
double mindis(int start,int end){//一个区域中的最小距离=min(左半区最小距离,右边区最小距离,一左一右最小距离)
    int cap=end-start+1;
    int left=start,right=start+cap/2;
    int leftcap=cap/2,rightcap=cap-cap/2,lefte=start+leftcap-1,righte=right+rightcap-1;
    if(cap==1) return 1e+300;
    else if(cap==2) return dis(a[start],a[end]);
    else if(cap==3) return min(min(dis(a[start],a[end]),dis(a[start+1],a[end])),dis(a[start+1],a[start]));
    double minlr=min(mindis(left,lefte),mindis(right,righte));//左半区和右半区最小距离

    //计算中间区域最小距离
    int i,j;
    for(i=right;(fabs(a[i].x-a[right].x))<minlr||eq(a[right].x-a[i].x,minlr);i--){if(i<left) break;}//令i+1为中间区域的第一个物体
    for(j=right;(fabs(a[j].x-a[right].x))<minlr||eq(a[j].x-a[right].x,minlr);j++){if(j>righte) break;}//令j-1 为中央区域的最后一个物体
    i++;j--;
    if(i!=j) {
        int p=0;
        for(int k=i;k<right;k++) {m[p].x=a[k].x;m[p].y=a[k].y;m[p].id=0;p++;}
        for(int k=right;k<=j;k++) {m[p].x=a[k].x;m[p].y=a[k].y;m[p].id=1;p++;}//将中间区域的点放入m数组中以便排序
        sort(m,m+p,cmpy);//对中间区域的店按y进行排序
        for(int k=0;k<p;k++)//从中间区域中每一点都找出之后7个点,与它们求最短距离
            for(int t=1;(k+t)<p&&t<8;t++){
                if(dis(m[k],m[k+t])<minlr&&m[k].id!=m[k+t].id) minlr=dis(m[k],m[k+t]);

            }
        return minlr;
    }
    else return minlr;
}

int main(){

    int N;
    while(cin>>N&&N!=0){
//        memset(a,0, sizeof(poi)*N);
//        memset(m,0, sizeof(m));
        for(int i=0;i<N;i++) {scanf("%lf%lf",&a[i].x,&a[i].y);}
        sort(a, a + N, cmpx);
        printf("%.2lf\n",mindis(0,N-1)/2);
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值