题目描述

给定平面上n个点(x_1,y_1),...,(x_n,y_n)(x1​,y1​),...,(xn​,yn​),找出2个半径相同的圆R_1R1​和R_2R2​,覆盖给定的n个点,且半径最小。

设计一个算法,计算出所求最小覆盖双圆 R_1R1​ 和 R_2R2​ 的半径。

输入格式

输入有多个测试实例。每个实例的第1行中给出正整数n,n<1000,表示平面上有n个点。

接下来的n行中每行给出2个实数(x, y),-100000≤x≤100000,-100000≤y≤100000。

最后一行有一个0表示结束。

输出格式

对于每组数据,输出最小的符合题意的圆的半径,保留两位小数。

输入输出样例

输入 #1
3

0.00 0.00
1.00 0.00
0.00 4.00
10
0.00 0.00
0.00 3.00
1.00 6.00
2.00 2.00
3.00 5.00
5.00 3.00
6.00 3.00
9.00 5.00
10.00 5.00
11.00 3.00
0

输出 #1
0.50

3.05

说明/提示

对于100%的数据,n<=1000n<=1000,|x_i|,|y_i|<=100000∣xi​∣,∣yi​∣<=100000,(T<=10T<=10)

【题目大意】

n个点,现在给两个半径相同的圆去覆盖,求最小半径

【解题思路】

我们需要尽量可能的减少重复的点,即同时被两个圆覆盖的点

所以我们二分枚举这个中间点,让一个圆覆盖一部分

使点没有重复的被覆盖

一般情况下,我们直接按 x 排序二分

选择半径更大的一边,然后继续二分

但是这不行

因为这样实际上是用一根垂直于 x 轴的直线分两边的点

而这个直线可能会有一定斜率

所以这个中间点的查找就是一个问题

二分枚举中间的分界点,因为通过图可知

两个圆总会关于一条直线对称

两圆不相交时是一条,两圆相交时是他们的交线

我们需要一条直线将所有点分成两半

但是正像上面所说,是被交线分开的

所以两边的点并不会一定被这根线分成两半

于是我们枚举这根直线的斜率,同时为了更好的排序

我们直接旋转坐标系

旋转坐标系的过程参见线性代数

旋转这一步我觉得是这道题的关键,而其他题解都没有太说明

注意:

对于变量的使用,要注意之前他是否被变更过

调试代码的时候可以通过调整查看顺序达到更高的效率

(先看main ---> 顺序往下看)

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
const int    MAXN = 2*1e3 + 5            ;
const double INF  = 1e18                 ;
const double an   = 1.0 / 180 * acos(-1);
const double eps  = 1e-9;
const double si   = sin(an), co = cos(an);
int n;
double reps() { return (1.0*rand() / RAND_MAX - 0.5)*eps; }
struct V
{double x, y;V() {}V(double a, double b) :x(a), y(b) {}V      operator+(const V&b) const { return V(x + b.x , y + b.y);}V      operator-(const V&b) const { return V(x - b.x , y - b.y);}bool   operator<(const V&b) const { return x < b.x; }double operator*(const V&b) const { return x * b.y - y * b.x;   }double operator^(const V&b) const { return x * b.x + y * b.y;   }//叉乘V      operator*(double b)  const { return V(x * b   , y*b);    }V      operator/(double b)  const { return V(x / b   , y /b);   }void   rota()                      { double x1 = x, y1 = y;x=x1 * co - y1 * si, y=x1 * si + y1 * co;        }V      rot()                      { return V(-y, x); }double len()                      { return (x*x + y * y);       }}p[MAXN],o;
typedef V P;
struct L
{V s, t;L(P a,P b):s(a),t(b){}friend P cross(L a, L b) { return a.s + a.t*(b.t*(b.s - a.s)) / (b.t*a.t); }//求交点
};
P circle(P a,P b, P c)
{return cross(L((a + b)*0.5, (b - a).rot()), L((a + c)*0.5, (c - a).rot()));
}
//求圆心
P s[MAXN];
int sgn(double x) { return x<-eps ? -1 : x>eps; }
double solve(int l,int r)
{double r1;if (l > r)    return 0;int cnt = 0;for (int i = l; i <= r; i++)s[++cnt] = p[i];random_shuffle(s + 1, s + 1 + cnt);r1 = 0, o = s[1];for (int i = 1; i <= cnt; i++){if (sgn((s[i] - o).len() - r1)>0){o = s[i], r1 = 0;for (int j = 1; j <= i - 1; j++)if (sgn((s[j] - o).len()-r1) > 0){o = (s[i] + s[j])*0.5, r1 = (s[j] - o).len();for (int k = 1; k <= j - 1; k++)if (sgn((s[k] - o).len() - r1)>0)o = circle(s[i], s[j], s[k]), r1 = (s[i] - o).len();}}}return r1;
}
int main()
{srand(20030719);while (scanf("%d", &n) && n){for (int i = 1; i <= n; i++){scanf("%lf%lf", &p[i].x, &p[i].y);}double ans = INF;///double cnt = 180;for (int _ = 1; _ <= 181; _++){sort(p + 1, p + 1 + n);int l = 1, r = n;//printf("**\n");while (l <= r){int mid = (l + r) >> 1;double retl = solve(1, mid); double retr = solve(mid + 1, n);//printf("**\n");double tmp = max(retl, retr);//if (retr + retl - tmp > ans)    break;ans = min(tmp, ans);if (retl > retr)  r = mid - 1;else               l = mid + 1;}for (int i = 1; i <= n; i++)p[i].rota();}printf("%.2lf\n", sqrt(ans));}return 0;
}

View Code


纪念我写完的第一道黑题

转载于:https://www.cnblogs.com/rentu/p/11347191.html

【FJOI2015】最小覆盖双圆问题相关推荐

  1. 697. Degree of an Array 频率最高元素的最小覆盖子数组

    [抄题]: Given a non-empty array of non-negative integers nums, the degree of this array is defined as ...

  2. hdu 1528+hdu 1962(最小覆盖)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1528 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1 ...

  3. 二分图最小覆盖的Konig定理及其证明

    二分图: 顶点可以分类两个集合X和Y,所有的边关联在两个顶点中,恰好一个属于集合X,另一个属于集合Y. 最小覆盖: 最小覆盖要求用最少的点(X集合或Y集合的都行)让每条边都至少和其中一个点关联.可以证 ...

  4. Leetcode 76最小覆盖子串77组合78子集

    新人公众号(求支持):bigsai 专注于Java.数据结构与算法,一起进大厂不迷路! 算法文章题解全部收录在github仓库bigsai-algorithm,求star! 关注回复进群即可加入力扣打 ...

  5. 双指针算法之滑动窗口 | 力扣76.最小覆盖字串

    本文讲解力扣76.最小覆盖字串问题 主要用到的是滑动窗口的思想 目录 76.最小覆盖字串 题目: 分析: 步骤描述: 复杂度分析: 结果 76.最小覆盖字串 题目: 给定字符串 S 以及字符串 T , ...

  6. 最小覆盖圆的增量算法

    题意:给出平面上的一些点,要求用一个最小的圆,把所有的点包围起来. 最小覆盖圆, 增量法: 假设圆O是前i-1个点得最小覆盖圆,加入第i个点,如果在圆内或边上则什么也不做.否,新得到的最小覆盖圆肯定经 ...

  7. hdu3007(最小覆盖圆问题)

    题目:Buried memory 最小圆覆盖,很经典的问题. 题目大概是,平面上n个点,求一个半径最小的圆,能够覆盖所有的点. 如果要求一个最小覆盖圆,这个圆至少要由三个点确定.有一种算法就是任意取三 ...

  8. cstring判断是否包含子串_leetcode76. 最小覆盖子串

    leetcode76. 最小覆盖子串 给你一个字符串 S.一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串. 示例: 输入: S = "ADOBECODEBANC&qu ...

  9. P5024-保卫王国【动态dp,最小覆盖集】

    正题 题目链接:https://www.luogu.org/problem/P5024 题目大意 一棵树,每次有要求 axby:a\ x\ b\ y:a x b y:表示aaa点是否必选和bbb点是否 ...

最新文章

  1. 深入理解argparse模块中的add_argument的参数(如action等)
  2. 国赛来咯,全国大学生智能汽车竞赛百度赛道正式开启
  3. 在.net 2.0中使用了masterpager 重写WebForm_OnSubmit()
  4. STM8启动分析及IAP
  5. win7(windows 7)系统下安装SQL2005(SQL Server 2005)图文教程
  6. 读【36岁IT老人再次随笔】的读后感,你会哪些计算机语言?
  7. android的动态注册,Android JNI 函数注册的两种方式(静态注册/动态注册)
  8. (学习笔记1)可见光与红外图像的特征融合(Feature Fusion)
  9. mysql 插入毫秒数据_MySQL存储毫秒数据的方法
  10. 职业学校计算机课评课,中职计算机评课稿
  11. 链表(c语言),c语言链表(c语言链表详解)
  12. 高并发之阿里云弹性伸缩的使用记录
  13. 接口调用频繁限制,接口限制流量
  14. Android微博平台设计,基于Android平台的微博系统设计与开发
  15. 《智能对话机器人开发实战20讲》--学习笔记--AIML基础功能拓展-与互联网的集成
  16. JavaScript进阶 - 第9章 DOM对象,控制HTML元素
  17. COALESCE()函数 一个非常有用的函数
  18. 积木盒子工作室第一次博客汇总
  19. qq空间、微信好友、邮件、短信分享
  20. 申请软件著作权登记没有源代码怎么办?如果有源代码怎么快速下证?

热门文章

  1. 微软推出VS Code新特性,为TypeScript和JavaScript用户提供AI辅助开发功能
  2. Android 通过反射让SQlite建表
  3. Linux 系统 rpm安装ipvsadm.src.rpm
  4. Silverlight 5.0 之前的SaveFileDialog的FilterIndex 错误
  5. 寻找U2OS中表达的基因及其promoter并用于后续annotation
  6. 代码质量与规范,那些年你欠下的技术债
  7. ZigBee TI ZStack CC2530 2.4 IAR软件版本
  8. TYVJ P1069 cowtour 看不懂题意
  9. 解决在Ubuntu下打开txt文件乱码的问题
  10. PL/SQL程序设计 第一章 PL/SQL 程序设计简介