• 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;
    }
    
  • 相关阅读:
    Intellij idea安装
    c# .net 我的Application_Error 全局异常抓取处理
    c# .net Global.asax文件的作用
    ASP.NET机制详细的管道事件流程(转)
    正则表达式_学习笔记
    c# .net获取随机字符串!
    c# 动态调用WCF方法笔记!
    Web Service和WCF的区别。其实二者不属于一个范畴!!!
    c# .net获取文件夹下的所有文件(多层递归),并获取区间数据(Jsion,xml等数据)写出到处理文件,学习分享~
    c#.net单例模式的学习记录!
  • 原文地址:https://www.cnblogs.com/caibingxu/p/12536369.html
Copyright © 2020-2023  润新知