zoukankan      html  css  js  c++  java
  • HDU1007———作业题

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

    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 Rmin).

    也就是说每个基点最多也才枚举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
    ", solve(1, n) / 2.0);
    	}
    	return 0;
    }
    
  • 相关阅读:
    2015531 网络攻防 Exp1 PC平台逆向破解(5)M
    2017-2018-1 20155331 嵌入式C语言
    20155330 《网络对抗》 Exp9 web安全基础实践
    20155330 《网络对抗》 Exp8 Web基础
    20155330 《网络对抗》 Exp7 网络欺诈防范
    20155330 《网络对抗》 Exp6 信息搜集与漏洞扫描
    20155330 《网络对抗》 Exp5 MSF基础应用
    20155330 《网络攻防》 Exp4 恶意代码分析
    20155330 《网络攻防》 Exp3 免杀原理与实践
    20155330 《网络对抗》 Exp2 后门原理与实践
  • 原文地址:https://www.cnblogs.com/caibingxu/p/12536369.html
Copyright © 2011-2022 走看看