HDU1007———作业题

只愿长相守 提交于 2020-03-21 05:51:59

服务器崩了,资料还没备份,回到博客园

HDU1007

题意:
给你一些在一个平面上的点的坐标,让你找出这堆点中,距离最短的两个点,距离的一半为多少?


题解:
数据 \(n=10^6\),如果我们枚举每个点的话,很明显复杂度将会达到\(n^2\),这样必定会超时。


考虑使用分治。

  1. 首先我们把点按照\(x\)轴坐标从小到大排序
  2. 选取一个中点\(mid\),将目前所有的点分成左右两边\((1,mid),(mid+1,n)\)
    • 目前可以推出结果只有三种情况
      • 两个点都在左边
      • 两个点都在右边
      • 一个在左边,一个在右边
  3. 针对结果\(1\)和结果\(2\)我们都能递归求出,递归边界为当目前点的个数为\(2\)或者\(3\)的时候,我们可以直接算出结果。
    这样我们目前得到了\(Lmin\)\(Rmin\)

接下来处理第三种结果

由于我们先前得出了\(Lmin\)\(Rmin\),由此得\(dis=min(Lmin,Rmin)\)

如果要验证第三种结果,必然由是一个左边的点连接右边的点,并且距离最短,那么我们自然是选取\(mid\)点。

接下来在目前所有点选出和\(mid\)点的 \(x\)轴距离 不大于\(dis\)的所有点。(如果连\(x\)轴距离都大于dis,算上\(y\)轴距离只会更远)

选出来的点\(y\)轴坐标从小到大排序,然后枚举这些点即可。

中间还可进一步剪枝,如果\(v\)点到达基点的距离已经大于\(dis\),那么就可以更换下一个基点。


额外知识点(有兴趣可以了解)

在刚才的最后一步枚举中,在算法导论中有解释过。

我们将点选出来后,这些点自然是处于一个 \(x\)轴距离为 \(2*dis\)的一块区域。

picture

选取一个点作为 基点 ,和他距离小于 \(dis\)的其它点,自然 \(y\)轴距离也不会大于 \(dis\)
于是我们针对每一个 基点可以得出一个区域,并且这个区域内左右两边最多4个点,总和为8个点。

picture

因为如果你左右任意一边多出一个点,那么不满足之前求出 \(Lmin\).

也就是说每个基点最多也才枚举8次。

#include <bits/stdc++.h>
//freopen("in.txt", "r", stdin);

using namespace std;
typedef double dou;
typedef long long ll;
typedef pair<ll, ll> pii;

#define M 200050
#define inf 0x3f3f3f3f
#define mod 998244353
#define W(a) while(a)
#define lowbit(a) a&(-a)
#define left k<<1
#define right k<<1|1
#define ms(a,b) memset(a,b,sizeof(a))
#define debug(a) cout<<#a<<" == "<<a<<endl
#define false_stdio ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)

int n;
struct Data {
    dou x;
    dou y;
}node[M];

bool cmpx(Data &a, Data &b) { return a.x < b.x; }
bool cmpy(Data &a, Data &b) { return a.y < b.y; }

dou dis(Data& a, Data &b) { return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y)); }

dou solve(int L, int R) {
    if (L + 1 == R)return dis(node[L], node[R]);//如果目前只有两个点
    if (L + 2 == R)return min(dis(node[L], node[L + 1]), min(dis(node[L], node[R]), dis(node[L + 1], node[R])));//如果目前只有三个点
    int mid = L + R >> 1;
    dou ans = min(solve(L, mid), solve(mid + 1, R));//递归求左右两边最小距离

    //选点
    vector<Data>tmp;
    for (; L <= R; L++) {
        if (fabs(node[mid].x - node[L].x) <= ans) {
            tmp.push_back(node[L]);
        }
    }
    sort(tmp.begin(), tmp.end(), cmpy);//按y坐标排序

    //枚举点
    for (int i = 0; i < tmp.size(); i++) {
        for (int j = i + 1; j < tmp.size(); j++) {
            if (tmp[j].y - tmp[i].y >= ans)break;
            ans = min(ans, dis(tmp[i], tmp[j]));
        }
    }
    return ans;
}

int main() {
    false_stdio;//HDU貌似关闭同步cin还是慢,所以还是用scanf了
    W(scanf("%d", &n) != EOF && n) {
        for (int i = 1; i <= n; i++) scanf("%lf%lf", &node[i].x, &node[i].y);
        sort(node + 1, node + n + 1, cmpx);//按x坐标排序
        printf("%.2lf\n", solve(1, n) / 2.0);
    }
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!