题目:

Have you ever played quoit in a playground? Quoit is a game in which flat rings are pitched at some toys, with all the toys encircled awarded. In the field of Cyberground, the position of each toy is fixed, and the ring is carefully designed so it can only encircle one toy at a time. On the other hand, to make the game look more attractive, the ring is designed to have the largest radius. Given a configuration of the field, you are supposed to find the radius of such a ring. Assume that all the toys are points on a plane. A point is encircled by the ring if the distance between the point and the center of the ring is strictly less than the radius of the ring. If two toys are placed at the same point, the radius of the ring is considered to be 0.

Input The input consists of several test cases. For each case, the first line contains an integer N (2 <= N <= 100,000), the total number of toys in the field. Then N lines follow, each contains a pair of (x, y) which are the coordinates of a toy. The input is terminated by N = 0.

Output For each test case, print in one line the radius of the ring required by the Cyberground manager, accurate up to 2 decimal places.

测试用例:

输入:                                                        输出:

4                                                                 1.12

0 3

3 2

4 0

7 1

0

长话短说,题目的大概意思就是输入N个坐标点,要求你求这些点中最短的距离,其中如果有重合的点,就输出0。需要注意的是,题目有多组用例,当输入0时表示输入结束。

如果博主没有猜错,那么看这篇文章的你应该是北理工的学生吧。如果猜错的话就当我没说。

那么进入正题,这其实是一个典型的分治法求最小对的问题,不过这道题对时间的要求比较严格,需要你尽可能的去做优化。

如果你没有彻底掌握分治法求最小对的问题,那么耐心看完这篇文章,相信你会有所收获;如果你是TLE来找优化方法的,相信你看完也能找到答案。

接下来博主将逐段讲解代码,以便掌握该类问题的求解方法。

(因为博主也是学生,因此在讲解时可能会有些啰嗦;但如果你需要学习分治法,那么讲解详细一点也并不是一件坏事)

简要思路

首先此题不能使用蛮力法,明显会TLE。因此我们使用分治法来解决。

解题思路是,先把坐标按照x升序(从小到大)排列,并找到x的中位数,来分为两部分。

对每一部分继续划分,递归处理,直到坐标数到达1或2,return结果,对两部分的结果取最小值,作为宽度d。

以上不能处理最小值的点恰好在分界线两侧的情况。

因此我们要再对分界线上的点进行处理,找一个宽度d,在分界线两侧形成一个总宽度为2d的宽度带,并对宽度带中的点进行遍历计算,更新最小距离值。

具体实现:

一、主函数部分

首先来看我们的主函数部分,这边其实没有什么好说的,值得注意的就是要使用double类型来储存坐标数据,如果你使用float类型,将会因精度不够而WA。

然后,数据的存储方式可以采用自定义的结构体,也可以像博主一样直接使用pair类型的vector进行储存,后者相对来说比较方便,可以不用指定数组的大小。

注意:如果你想使用map来解决这道题,注意要使用multimap;如果单纯使用map并以x作为key值,可能会因为有重复的x坐标而造成数据的丢失。

接下来为了后面的分治,我们需要将坐标按照x的大小进行排序,这里要写一个针对x的比较函数,虽然比较简单,但后面依然会把代码展示并做略微讲解。

最后的clear是为了下一轮的数据处理。

#include<vector>
#include<utility>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdbool>using namespace std;
vector <pair<double, double>> P;        //定义pair类型的vectorint main() {int n, i;double dis, x, y;while (1) {scanf("%d", &n);if (n == 0) {break;        //输入0结束}i = n;while (i--) {scanf("%lf %lf", &x, &y);P.push_back(pair<double, double>(x, y));}sort(P.begin(), P.end(), cmp1);        //预备工作,将坐标按x排序dis = shortestdis(0, n - 1);        //核心步骤,找最小对dis = sqrt(dis) / 2;        //因为题目要求半径,因此要除以二printf("%.2lf\n", dis);P.clear();        //清空vector,以便进行下一组数据的输入}return 0;}

二、非递归的其他函数部分

这一部分有三个函数。

前两个都是比较函数,以bool为类型,static可加可不加,const也可以不加。

优化:下面一个是计算距离的函数,加inline是为了提高一点点效率。另外值得一提的是,这里没有对距离进行sqrt运算,原因之一是为了保证精度;原因之二是开平方在计算机内部并不是一件消耗低的事情,因此我们要尽量减少使用平方根。

static bool cmp1(const pair<int, int>& a, const pair<int, int>& b) {return a.first < b.first;        //比较函数1,比x}
static bool cmp2(const pair<int, int>& a, const pair<int, int>& b) {return a.second < b.second;        //比较函数2,比y}
inline double calculatee(pair<double,double>a, pair<double, double>b) {return ((a.first - b.first) * (a.first - b.first) + (a.second - b.second) * (a.second - b.second));        //计算距离的函数}

三、递归函数部分

这一部分是该程序的核心。

首先我们根据left和right来确定数组的元素个数,如果是1,那么无法计算距离,我们返回极大值;如果是2,我们借助上面的计算函数来确定距离的平方值。

接下来,如果元素个数大于2,首先我们获取中间元素,利用(left + right) / 2来取得中间元素的下标。

然后将左右两侧递归,进行分治。

优化:接下来获取左右两侧最小平方距离的最小值mind,这里我们获取以后开一次根号得到sqmd,以便后面进行比较,否则跟距离的平方(即mind比较的值还要进行平方运算,这样造成的资源消耗会得不偿失。

然后我们以上面的中位元素做基准,将它左右sqmd距离的元素纳入临时数组中。

然后对该临时数组按y排序,原因是我们按y排序以后,后面进行遍历时可以大大提高效率。在下文中我们详细讨论。

接下来就是遍历,我们先取临时数组里面的第一个元素(暂时叫a),然后将它和它后面的元素(暂时叫bi)逐一求距离。

优化:这里注意while循环的第二个条件,它求的是abi的y方向距离,我们知道,经过前面的y排序,bi的y值是非递减的。举个例子,也就是说b3的y值一定大于等于b2的y值的。这样,当我们在进行while循环时,发现某一bia的y方向距离已经大于等于我们的sqmd了,那也没有必要计算bia的x方向与y方向的总距离了;同时,对于bi后面的所有元素,它们与a的y方向的距离一定不会比bia的y方向的距离小,那么我们就可以不必继续进行while循环,以此来提高效率。

double shortestdis(int left, int right) {int s = right - left + 1;        //s是元素的个数//这里是对于元素个数为2或1的情况if (s == 2) {double disone;disone = calculatee(P[left], P[right]);return disone;}if(s==1) {return 0x3f3f3f3f;}//这里是其他情况vector <pair<double, double>> SPV;        //创建一个临时数组int i, j, k, capav;double d1, d2, mind, sqmd, spx;//对数组分治,递归k = (left + right) / 2;d1 = shortestdis(left, k);d2 = shortestdis(k + 1, right);//取上面二者最小,并开根号mind = min(d1, d2);sqmd = sqrt(mind);//处理分界线两端的元素,只需要处理分界线左右sqmd距离的元素即可spx = P[k].first;        //以分界线的x坐标为基准//将元素存入临时数组for (i = left; i <= right; i++) {if (abs(P[i].first - spx) < sqmd) {SPV.push_back(P[i]);}}//临时数组按y排序sort(SPV.begin(), SPV.end(), cmp2);capav = SPV.size();        //获得临时数组的大小//遍历分界线附近的元素,查看有无更小的距离for (i = 0; i < capav - 1; i++) {j = i + 1;while ((j < capav) && ((SPV[j].second - SPV[i].second) < sqmd)) {        //遍历继续的条件是j要小于总元素个数,并且y方向的距离要小于最小距离mind = min(((SPV[j].first - SPV[i].first) * (SPV[j].first - SPV[i].first) + (SPV[j].second - SPV[i].second) * (SPV[j].second - SPV[i].second)), mind);        //判断是否更小j++;}}return mind;        //返回最小值}

完整代码

不含注释。

#include<vector>
#include<utility>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdbool>using namespace std;vector <pair<double, double>> P;static bool cmp1(const pair<int, int>& a, const pair<int, int>& b) {return a.first < b.first;}static bool cmp2(const pair<int, int>& a, const pair<int, int>& b) {return a.second < b.second;}inline double calculatee(pair<double,double>a, pair<double, double>b) {return ((a.first - b.first) * (a.first - b.first) + (a.second - b.second) * (a.second - b.second));}double shortestdis(int left, int right) {int s = right - left + 1;if (s == 2) {double disone;disone = calculatee(P[left], P[right]);return disone;}if(s==1) {return 0x3f3f3f3f;}vector <pair<double, double>> SPV;int i, j, k, capav;double d1, d2, mind, sqmd, spx;k = (left + right) / 2;d1 = shortestdis(left, k);d2 = shortestdis(k + 1, right);mind = min(d1, d2);sqmd = sqrt(mind);spx = P[k].first;for (i = left; i <= right; i++) {if (abs(P[i].first - spx) < sqmd) {SPV.push_back(P[i]);}}sort(SPV.begin(), SPV.end(), cmp2);capav = SPV.size();for (i = 0; i < capav - 1; i++) {j = i + 1;while ((j < capav) && ((SPV[j].second - SPV[i].second) < sqmd)) {mind = min(((SPV[j].first - SPV[i].first) * (SPV[j].first - SPV[i].first) + (SPV[j].second - SPV[i].second) * (SPV[j].second - SPV[i].second)), mind);j++;}}return mind;}int main() {int n, i;double dis, x, y;while (1) {scanf("%d", &n);if (n == 0) {break;}i = n;while (i--) {scanf("%lf %lf", &x, &y);P.push_back(pair<double, double>(x, y));}sort(P.begin(), P.end(), cmp1);dis = shortestdis(0, n - 1);dis = sqrt(dis) / 2;printf("%.2lf\n", dis);P.clear();}return 0;}

【数据结构与算法设计-基础向】C/C++编程练习 - 套圈(分治法解决最近对问题)相关推荐

  1. 算法设计 (分治法应用实验报告)基于分治法的合并排序、快速排序、最近对问题

    一.名称 分治法应用 二.目的 1.掌握分治法的基本思想: 2.学会运用分治法解决实际系统设计应用中碰到的问题. 三.要求 1.实现基于分治法思想的合并排序: 2.实现基于分治法思想的快速排序: 3. ...

  2. 数据结构与算法之基础概述

    目录 数据结构和算法的重要性 数据结构概述 逻辑结构 存储结构 算法概述 如何理解"大O记法" 时间复杂度 空间复杂度 数据结构和算法的重要性 算法是程序的灵魂,优秀的程序可以在海 ...

  3. 【数据结构和算法设计】算法篇(7) 贪心法

    文章目录 7.1 贪心法概述 7.1.1 什么是贪心法 7.1.2 用贪心法求解的问题应具有的性质 1. 贪心选择性质 2. 最优子结构性质 7.1.3 贪心法的一般求解过程 7.2 求解活动安排问题 ...

  4. 快速排序 php内存超限,数据结构与算法设计

    spContent=学了一门编程语言不知道能干啥?来学数据结构就对啦! 学会编程相当于会砌猪圈的泥瓦匠,学完数据结构就会盖个双层小楼啦~ 同时还可以一窥构筑摩天大厦的奇门武功! 欢迎勤奋的小白活泼乱入 ...

  5. 大话数据结构与算法:基础篇

    1.数据结构的重要性 数据结构是计算机软件相关专业的基础课程,几乎可以说,要想从事编程工作,无论是否是科班出身(比如我,标准的非科班人员,我是学医的,哈哈)都不可以绕过数据结构与算法这部分知识. 数据 ...

  6. 7、算法设计(基础)

    算法设计 算法设计:迭代.穷举搜索.递推.递归.回溯.贪心.动态规划.分治等算法设计. 在算法设计中,主要考查动态规划法.分治法.回溯法.递归法.贪心法. 1. 算法特性 算法有一些基本特性要求掌握, ...

  7. 数据结构与算法设计思路和考察点

    网上摘录,想分专题放到一起.数据结构常见的问题包括字符串方面.链表的各种操作.树的各种操作,以及各种变形和与其它数据结构的结合使用. 面试题目 字符串专题 1.将字符串转换成整数,将整数转换为字符串, ...

  8. 数据结构与算法学习——基础知识(一)

    数据结构与算法 线性结构和非线性结构 基本概念 线性结构 非线性结构 稀疏数组 基本概念 稀疏数组的处理方法 举例说明 应用实例 代码实例 队列 基本介绍 示意图 数组模拟队列 思路分析 代码实现 数 ...

  9. 【数据结构和算法设计】算法篇(11) 概率算法和近似算法

    文章目录 12.1 概率算法 12.1.1 什么是概率算法 1. 概率算法的特点 2. 概率算法的分类 3. 随机数生成器 12.1.2 蒙特卡罗类型概率算法 12.1.3 拉斯维加斯类型概率算法 1 ...

最新文章

  1. python 去掉文件头部几行_批量重命名文件的python代码
  2. Java基础知识强化26:Object类之hashCode()方法、getClass()方法
  3. 分享Silverlight/WPF/Windows Phone一周学习导读(4月4日-4月9日)
  4. 你真的懂 timeout 吗?
  5. CF280C-Game on Tree【数学期望】
  6. cocos2dx3.2文件结构和代码结构
  7. Chrome谷歌浏览器私密浏览自动开启功能获批专利
  8. 【Elasticsearch】如何在生产中执行Elasticsearch的零停机升级
  9. 【老生谈算法】matlab实现图像复原算法源码——图像复原
  10. VLAN 间路由配置
  11. iPhone所有屏幕分辨率
  12. 在 vue中使用 html5 的 drag和 drop 拖拽功能
  13. 二十四节气-秋分 | 文案、海报
  14. 【热搜词方案】android/java热搜词方案设计
  15. 邓仰东专栏|机器学习的那些事儿(一)
  16. rangeOfString
  17. 用endnote x9在Word 2016中插入参考文献到特定位置
  18. Hive数据类型、数据库相关操作、表的相关操做、数据的导入导出
  19. 推荐16个下载超酷脚本的免费热门网站
  20. ios点击大头针气泡不弹出_高德 ios 自定义气泡添加点击事件无效问题

热门文章

  1. Matlab求解非线性规划(fmincon函数的使用)
  2. Windows10 1803版本以上找回控制面板语言设置的方法
  3. java删除字符串最后一个字符
  4. np.linalg.norm()用法总结
  5. mac安装并使用tldr
  6. python智能识别 病虫草害_Python图片识别——人工智能篇
  7. Linux的adduser和useradd
  8. 生信 反相蛋白质阵列 RPPA的相关学习记录
  9. 图论与网络流理论 3. 匹配理论 2:二部图的匹配
  10. Spring Bean生命周期,就像人的一生