当前位置: 首页 > article >正文

AcWing119 袭击

目录

  • AcWing119 袭击
    • 题目描述
      • 背景
      • 输入
      • 输出
      • 数据范围
    • 题解
      • 解法
      • 优化
    • 打赏

AcWing119 袭击

题目描述

背景

特工进入据点突袭发电站,已知所有发电站的位置和所有特工的降落位置,求任意特工距离任意核电站的最短距离

输入

  1. 第一行一个整数 T T T,表示有 T T T组数据;
  2. 对于每组数据,第一行一个整数 N N N
  3. 接下来 N N N行,每行两个整数 X , Y X , Y X,Y,代表每个核电站位置的 X , Y X , Y X,Y坐标;
  4. 再接下来 N N N行,每行两个整数 X , Y X , Y X,Y,代表每名特工位置的 X , Y X , Y X,Y坐标;

输出

对于每组数据,输出一个最短距离值,结果保留三位小数且每个输出结果占一行

数据范围

1 ≤ N ≤ 1 e 5 , 0 ≤ X , Y ≤ 1 e 9 1 \le N \le 1e5 , 0 \le X , Y \le 1e9 1N1e5,0X,Y1e9

题解

解法

假设先计算了某个区域内的最小距离,那么任意一对特工和核电站的坐标如果在 x x x y y y方向上超出了这个距离,就不用计算他们之间的距离,由此可以想到分治,先分别算出两个独立点集内的最小距离,再考虑跨越了这两个点集的最小距离,这样就可以得到两个点集合并所得点集内的最小距离
先定义一个结构体数组用于储存位置(无论核电站还是特工的),数组的每个元素包含 x , y x , y x,y坐标,以及一个标签( b o o l bool bool变量)用于表示该位置是核电站的还是特工的
为了方便计算跨越点集的最小距离,先将定义的结构体数组按照 x x x y y y值排序(本文按照 x x x
这样排好序后可以采用二分的方式,每一部分先分别算好其左右两个子部分内的最小距离,比较得到二者中的较小值 a n s ans ans,那么如果想要通过跨越两个部分对 a n s ans ans产生贡献,考虑的点的 x x x值与二分中点的 x x x值之间的差距就必须低于 a n s ans ans
这样筛选出了部分点后,把这些点分为特工和核电站两个部分,分别存入两个数组,对于特工数组中的每一个元素,分别考虑在核电站数组中是否存在元素到它的距离能产生贡献(对于核电站数组中的每一个元素考虑特工数组也可以),这至少需要满足二者 y y y值之间的差距低于 a n s ans ans,为了快速在核电站数组中找到满足条件的元素,可以在二分求解的过程中把按照 x x x值排序的数组再按照 y y y值排序,这样对于特工数组中的每一个元素,只需要在核电站数组中找到满足条件的上下界的下标即可
由此通过一边归并排序一边计算最小值就可以得到最终的答案
代码如下:

#include<cstdio>
#include<cmath>

using namespace std;

#define il inline
#define db double

const int M = 2e5 + 5;
const int inf = 15e8;

typedef struct {
    int x;
    int y;
    bool tag;
}Point;

Point pos[M], t1[M], t2[M];

il void pswap(Point &a, Point &b) {     //交换位置
    a.x^=b.x^=a.x^=b.x;
    a.y^=b.y^=a.y^=b.y;
    a.tag^=b.tag^=a.tag^=b.tag;
}

il db dmin(db a, db b) {
    return a < b ? a : b;
}

il db dis(Point a, Point b) {     //计算两点距离,同tag的点之间距离无限大
    return a.tag == b.tag ? inf : sqrt((db)(a.x - b.x) * (a.x - b.x) + (db)(a.y - b.y) * (a.y - b.y));
}

il void quickSort(int l, int r) {     //按照x值排序
    if(l == r) return ;
    if(l + 1 == r) {
        if(pos[l].x > pos[r].x) pswap(pos[l], pos[r]);
        return ;
    }

    int mid = l + r >> 1, i = l, j = mid + 1, k = 0;
    quickSort(l, mid), quickSort(mid + 1, r);

    while(i <= mid && j <= r) {
        while(i <= mid && pos[i].x <= pos[j].x) t1[++k] = pos[i++];
        while(j <= r && pos[i].x >= pos[j].x) t1[++k] = pos[j++];
    }
    while(i <= mid) t1[++k] = pos[i++];
    while(j <= r) t1[++k] = pos[j++];
    for(i = l, j = 1; j <= k; ++j, ++i) pos[i] = t1[j];
}

il db divideSolve(int l, int r) {     //按照y值排序并计算答案
    if(l == r) return inf;
    if(l + 1 == r) {
        if(pos[l].y > pos[r].y) pswap(pos[l], pos[r]);
        return dis(pos[l], pos[r]);
    }
    
    int mid = l + r >> 1;
    db ans = dmin(divideSolve(l, mid), divideSolve(mid + 1, r));
    int i = l, j = mid + 1;
    int axs = pos[mid].x, k1 = l, k2;
    
    while(i <= mid && j <= r) {
        while(i <= mid && pos[i].y <= pos[j].y) t1[k1++] = pos[i++];
        while(j <= r && pos[i].y >= pos[j].y) t1[k1++] = pos[j++];
    }
    while(i <= mid) t1[k1++] = pos[i++];
    while(j <= r) t1[k1++] = pos[j++];
    k1 = k2 = 0;
    for(i = l; i <= r; ++i) {     //筛选并分类
        pos[i] = t1[i];
        if(t1[i].x > axs - ans && t1[i].x < axs + ans)
            t1[i].tag ? t1[++k1] = t1[i] : t2[++k2] = t1[i];
    }
    if(k1 && k2) {     //找上下界
        t2[k2 + 1].y = inf;
        for(i = 1; i <= k1; ++i) {
            db mxx = dmin(inf, t1[i].y + ans), mnn = t1[i].y - ans;
            if(t2[1].y < mxx) {
                int p1 = 1, p2 = 1;
                for(; t2[p2].y < mxx; ++p2);
                if(t2[1].y <= mnn)
                    for(; t2[p1].y <= mnn; ++p1);
                for(j = p1; j < p2; ++j) ans = dmin(ans, dis(t1[i], t2[j]));
            }
        }
    }
    return ans;
}

int main() {
    int t, n, m;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n), m = n << 1;
        for(int i = 1; i <= n; ++i) scanf("%d%d", &pos[i].x, &pos[i].y), pos[i].tag = 0;
        for(int i = n + 1; i <= m; ++i) scanf("%d%d", &pos[i].x, &pos[i].y), pos[i].tag = 1;
        quickSort(1, m);
        printf("%.3lf\n", divideSolve(1, m));
    }
    return 0;
}

优化

按照 x x x值排序时,对于重复的元素只保留一个
对于存在特工和核电站位置相同的数据,可以在一得到 ! a n s !ans !ans时直接 r e t u r n 0 return 0 return0
对于筛选得到的特工数组中的每一个元素,在核电站数组中找上下界时可以二分查找,并且由于这些元素是排序后筛选的,所以对于特工数组中的每个元素,上下界可以在前一元素的基础上寻找,不用每次都初始化为 0 0 0
代码如下:

#include<cstdio>
#include<cmath>

using namespace std;

#define il inline
#define db double

const int M = 2e5 + 5;
const int inf = 15e8;

typedef struct {
    int x;
    int y;
    bool tag;
}Point;

Point pos[M], t1[M], t2[M];

il bool operator == (Point a, Point b) {
    return a.tag == b.tag && a.x == b.x && a.y == b.y;
}

il bool operator != (Point a, Point b) {
    return a.tag != b.tag || a.x != b.x || a.y != b.y;
}

il int quickRead() {
    int x = 0, f = 1;
    char c = getchar();
    while((c < '0' || c > '9') && c != '-') c = getchar();
    if(c == '-') f = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return x * f;
}

il void pswap(Point &a, Point &b) {
    a.x^=b.x^=a.x^=b.x;
    a.y^=b.y^=a.y^=b.y;
    a.tag^=b.tag^=a.tag^=b.tag;
}

il db dmin(db a, db b) {
    return a < b ? a : b;
}

il db dis(Point a, Point b) {
    return a.tag == b.tag ? inf : sqrt((db)(a.x - b.x) * (a.x - b.x) + (db)(a.y - b.y) * (a.y - b.y));
}

il int quickSort(int l, int r) {     //返回值表示去重后从l到多大下标是有效值
    if(l == r) return l;
    if(l + 1 == r) {
        if(pos[l] == pos[r]) return l;
        if(pos[l].x > pos[r].x) pswap(pos[l], pos[r]);
        return r;
    }

    int L = quickSort(l, l + r >> 1), R = quickSort((l + r >> 1) + 1, r);
    int i = l, j = (l + r >> 1) + 1, k = 0;

    while(i <= L && j <= R) {
        while(i <= L && pos[i].x <= pos[j].x) {
            if(pos[i] != t1[k]) t1[++k] = pos[i];
            ++i;
        }
        while(j <= R && pos[i].x >= pos[j].x) {
            if(pos[j] != t1[k]) t1[++k] = pos[j];
            ++j;
        }
    }
    while(i <= L) {
        if(pos[i] != t1[k]) t1[++k] = pos[i];
        ++i;
    }
    while(j <= R) {
        if(pos[j] != t1[k]) t1[++k] = pos[j];
        ++j;
    }
    for(i = l, j = 1; j <= k; ++j, ++i) pos[i] = t1[j];
    return l + k - 1;
}

il db divideSolve(int l, int r) {
    if(l == r) return inf;
    if(l + 1 == r) {
        if(pos[l].y > pos[r].y) pswap(pos[l], pos[r]);
        return dis(pos[l], pos[r]);
    }
    
    int mid = l + r >> 1;
    db ans = dmin(divideSolve(l, mid), divideSolve(mid + 1, r));
    if(!ans) return 0;     //得到0立马返回
    int i = l, j = mid + 1;
    int axs = pos[mid].x, k1 = l, k2;
    
    while(i <= mid && j <= r) {
        while(i <= mid && pos[i].y <= pos[j].y) t1[k1++] = pos[i++];
        while(j <= r && pos[i].y >= pos[j].y) t1[k1++] = pos[j++];
    }
    while(i <= mid) t1[k1++] = pos[i++];
    while(j <= r) t1[k1++] = pos[j++];
    k1 = k2 = 0;
    for(i = l; i <= r; ++i) {
        pos[i] = t1[i];
        if(t1[i].x > axs - ans && t1[i].x < axs + ans)
            t1[i].tag ? t1[++k1] = t1[i] : t2[++k2] = t1[i];
    }
    if(k1 && k2) {
        int p1 = 0, p2 = 0;     //共用p1,p2
        t2[k2 + 1].y = inf;
        for(i = 1; i <= k1; ++i) {
            db mxx = dmin(inf, t1[i].y + ans), mnn = t1[i].y - ans;
            if(t2[p2 + 1].y < mxx) {     //判断是否更新p2
                for(j = k2 - p2; j != 1; ++j >>= 1)     //注意k2-p2,“j!=1”防止死循环,“++j >>= 1”保证可以得到所有数
                    if(t2[p2 + j].y < mxx) 
                        if(t2[(p2 += j) + 1].y >= mxx) break;     //下一项超了直接退出
                if(t2[p2 + 1].y < mxx) ++p2;     //对j==1的特判,保证可以得到所有数
            }
            if(t2[p1 + 1].y <= mnn) {     //判断是否更新p1
                for(j = p2 - p1; j != 1; ++j >>= 1)     //注意p2-p1
                    if(t2[p1 + j].y <= mnn) 
                        if(t2[(p1 += j) + 1].y > mnn) break;
                if(t2[p1 + 1].y <= mnn) ++p1;
            }
            for(j = p1 + 1; j <= p2; ++j) ans = dmin(ans, dis(t1[i], t2[j]));     //注意p1+1
        }
    }
    return ans;
}

int main() {
    int t, n, m;
    t1[0].x = -1, t2[0].y = -inf;     //“t1[0].x = -1”用于去重,“t2[0].y = -inf”用于求下界的二分查找
    t = quickRead();
    while(t--) {
        n = quickRead(), m = n << 1;
        for(int i = 1; i <= n; ++i) pos[i] = {quickRead(), quickRead(), 0};
        for(int i = n + 1; i <= m; ++i) pos[i] = {quickRead(), quickRead(), 1};
        printf("%.3lf\n", divideSolve(1, quickSort(1, m)));
    }
    return 0;
}

打赏

制作不易,若有帮助,欢迎打赏!
赞赏码
支付宝付款码


http://www.kler.cn/a/301656.html

相关文章:

  • django解决跨域问题
  • SpringBoot+React养老院管理系统 附带详细运行指导视频
  • 【windows笔记】08-Windows中的各种快捷方式、符号链接、目录联接、硬链接的区别和使用方法
  • Redis性能优化——针对实习面试
  • 关于强化学习的一份介绍
  • ISP是什么?
  • 韩语中的多义词 (치다)柯桥学韩语到蓝天广场附近
  • Python的学习(三十二)---- ctypes库的使用整理
  • LSP协议:打造流动性管理的市场新标杆
  • [前端][js]获取当前正在执行的Javascript脚本文件的路径
  • 项目实现:云备份(一)
  • 【数字集成电路与系统设计】一些Chisel语法的介绍
  • 二、Maven工程的创建--JavaSEJavaEE
  • Element UI按钮组件:构建响应式用户界面的秘诀
  • vue3 ref
  • ffmpeg7.0 AVFrame的分配与释放
  • 使用 DBeaver 创建 MySQL 数据库
  • 第十五届蓝桥杯图形化省赛题目及解析
  • 前端 PDF 预览技巧:标签 vs 插件,如何优雅地展示 PDF 文件
  • 6、多线程
  • 如何使用python运行Flask开发框架并实现无公网IP远程访问
  • 力扣刷题之2555.两个线段获得的最多奖品
  • 装杯 之 Linux 指令1
  • 哈希表及算法
  • xLSTM模型学习笔记
  • 高性能计算机A100会带有BMC功能 ;BMC;SSH