hdu 1007 -- Quoit Design

Quoit Design

题目页面:戳这里

题意理解:

给定一组二维平面中的点(坐标),计算距离最近的两个点之间的距离的一半。

初始思路:

  1. 暴力,O(n^2)肯定超时;
  2. 降低复杂度(使用 分治算法,将复杂度降低到 O(nlogn))

应用 分治算法 思路详解:

也是看了别人的解题报告,学习到了新的搜索方法,总结于此,与大家分享学习。

使用 分治算法,顾名思义,就是要:二分+递归
由于所要求解的是两点之间的距离,既然是距离,那么就可以想到按照 x坐标 或者 y坐标 排序再分治(为什么排序,详见下面需要剪枝的那个部分)

  1. 那么不妨,首先将所有的点按照 x坐标 排序,之后将排好序的点数组从中间分成两段,那么,构成我们所要求解的最小值的两个点,要么在第一段中,要么在第二段中,要么一边一个

  2. 假设构成我们所要求解的最小值的两个点在同一段中,那么对每一段进行递归计算,得到两个最小距离,更新answer

  3. 下面在对一边一个的情况进行处理:
    注意:这里需要剪枝,不然暴力的复杂度还是O(n^2)

    由于我们要找点与点之间的距离小于answer的点对,那么这样的点对的 x坐标 的距离一定小于answer
    现在已经假设两个点分别在两段内,而且 我们已经把点按照 x坐标 排好序了!

    所以,第一段中的点都在第二段中的点的左面!

    所以,我们要找的点的横向范围在 以中间的点为中心,左右两边距离为answer的范围内,O(n)遍历一边,将这些点复制出来(第一次剪枝)
    效果如图(红点为中间点,蓝点在范围内,黑点显然不构成解):
    第一次剪枝

    但是这样的点可能还是很多,所以我们还需要在 y坐标 方向上再次剪枝(第二次剪枝)
    方法:按照 y坐标,将上一步复制出来的点进行排序,然后再遍历计算最小距离,遍历的第二层循环的终止条件加入判断,两点的 y坐标 距离差要小于answer
    遍历效果如下图:

    第一次循环:
    第二次剪枝,第一次循环

    第二次循环
    第二次剪枝,第二次循环

    最后更新并返回answer(或者实时更新,将answer定义为全局变量)

C++代码:

#include <stdio.h>
#include <math.h>
#include <algorithm>

using namespace std;

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

class Node {
public:
    double x;
    double y;
};

const int N_MAX = 100001;

int N;
Node nd[N_MAX], ntmp[N_MAX];
double ans;

bool cmpX(const Node & na, const Node & nb) {
    return na.x < nb.x;
}

bool cmpY(const Node & na, const Node & nb) {
    return na.y < nb.y;
}

inline double dist(const Node & na, const Node & nb) {
    return sqrt((na.x-nb.x)*(na.x-nb.x) + (na.y-nb.y)*(na.y-nb.y));
}

void DACsolve(int nstart, int nend) {
    if (nstart >= nend) return;

    int mid = (nstart + nend) / 2;
    DACsolve(nstart, mid);
    DACsolve(mid+1, nend);

    int i, j, c;
    for (i = nstart, c = 0; i <= nend; i++) {
        if (fabs(nd[i].x - nd[mid].x) < ans) {
            ntmp[c++] = nd[i];
        }
    }
    sort(ntmp, ntmp+c, cmpY);
    for (i = 0; i < c; i++) {
        for (j = i+1; j < c && fabs(ntmp[i].y - ntmp[j].y) < ans; j++) {
            ans = min(ans, dist(ntmp[i], ntmp[j]));
        }
    }
}

int main() {
    while (1) {
        scanf("%d", &N);
        if (N == 0) break;

        int i, a, b;
        for (i = 0; i < N; i++) {
            scanf("%lf %lf", &nd[i].x, &nd[i].y);
        }
        sort(nd, nd+N, cmpX);

        ans = 1000000000;
        DACsolve(0, N-1);

        if (N == 1) ans = 0;
        printf("%.2lf\n", ans/2);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值