题解:P4586 [FJOI2015] 最小覆盖双圆问题
写了这么久终于过了,发篇题解记录一下。
第一次写黑题题解,写的不好请见谅。
目录
- 本题思路
- 三点定圆
- 最小圆覆盖
- 关于最小圆覆盖时间复杂度
- 回到本题
- 二分法划分点集
- 总时间复杂度
- 最小覆盖双圆问题代码
本题思路
首先,这道题叫做最小覆盖双圆问题,这道题涉及到一个叫做最小圆覆盖的问题。
一个容易想到的思路:若我们能做出最小圆覆盖的问题,就可以把本题分成两个圆,也就是两个点集来处理。
那我们如何处理最小圆覆盖的问题呢?
三点定圆
首先,不共线的三个点可以确定一个圆。
作任意两点所在线段的中垂线,三条中垂线的交点就是这三个点共圆的圆心。这个圆心到三点中的任意一点就是这个圆的半径。当三点不在同一条直线时。形成一个三角形,而三角形有且只有一个外接圆。
数学原理是中垂线上的点到线段两端的距离相等。两条中垂线的交点,到两条线段的距离都相等。所以,不在同一条直线的三点可以确定一个圆。
附三角形的外切圆半径公式:\(\frac{abc}{4S}\),其中,\(S\) 表示三角形的面积,\(a\) 和 \(b\) 和 \(c\) 分别为三角形的三条边。
本部分代码:
Point circle_center(const Point a,const Point b,const Point c){
Point center;
double a1=b.x-a.x,b1=b.y-a.y,c1=(a1*a1+b1*b1)/2;
double a2=c.x-a.x,b2=c.y-a.y,c2=(a2*a2+b2*b2)/2;
double d=a1*b2-a2*b1;
center.x=a.x+(c1*b2-c2*b1)/d;
center.y=a.y+(a1*c2-a2*c1)/d;
return center;
}
最小圆覆盖
根据以上结论,我们知道对于任意三个不共线的点,我们可以求出三点定圆,所以一个明显的想法就是枚举三个点。
具体步骤如下:
枚举第一个点 \(i\),若不在目前圆内,设它为圆心。
枚举第二个点 \(j\),若不在当前圆内,设当前圆为以 \(i\) 和 \(j\) 为直径的圆。
枚举第三个点 \(k\),若不在当前圆内,设当前圆为 \(i\) 和 \(j\) 和 \(k\) 的外接圆。
显然最优解一定是两个点为直径的圆或者一个三角形的外接圆,否则肯定能缩的更小。这样做就是正确的。
这就是所谓的增量法。
关于最小圆覆盖时间复杂度
显然,按上面的做法,时间复杂度是 \(O(n^3)\) 的,会被不良心的出题人卡掉。
所以,我们需要打乱数据,从而提升时间复杂度。
这就是所谓的随机增量法。
时间复杂度期望 \(O(n)\)。
最小圆覆盖代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define eps 1e-8
const int N=1e5+1;
int sgn(double x){
if(fabs(x)<eps) return 0;
else return x<0?-1:1;
}
struct Point{double x,y;};
double Distance(Point A,Point B){return hypot(A.x-B.x,A.y-B.y);};
Point circle_center(const Point a,const Point b,const Point c){
Point center;
double a1=b.x-a.x,b1=b.y-a.y,c1=(a1*a1+b1*b1)/2;
double a2=c.x-a.x,b2=c.y-a.y,c2=(a2*a2+b2*b2)/2;
double d=a1*b2-a2*b1;
center.x=a.x+(c1*b2-c2*b1)/d;
center.y=a.y+(a1*c2-a2*c1)/d;
return center;
}
void min_cover_circle(Point *p,int n,Point &c,double &r){
random_shuffle(p,p+n);
c=p[0],r=0;
for(int i=1;i<n;i++){
if(sgn(Distance(p[i],c)-r)>0){
c=p[i];r=0;
for(int j=0;j<i;j++){
if(sgn(Distance(p[j],c)-r)>0){
c.x=(p[i].x+p[j].x)/2;
c.y=(p[i].y+p[j].y)/2;
r=Distance(p[j],c);
for(int k=0;k<j;k++){
if(sgn(Distance(p[k],c)-r)>0){
c=circle_center(p[i],p[j],p[k]);
r=Distance(p[i],c);
}
}
}
}
}
}
}
Point p[N];
signed main(){
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>p[i].x>>p[i].y;
Point c;double r;
min_cover_circle(p,n,c,r);
printf("%.10f\n%.10f %.10f\n",r,c.x,c.y);
return 0;
}
最小圆覆盖例题:这里。
回到本题
之前说若我们能做出最小圆覆盖的问题,就可以把本题分成两个圆,也就是两个点集来处理。
现在我们已经会了最小圆覆盖的问题,如何划分点集呢?
如果暴力枚举,显然不行。
这时引出我们的二分法。
二分法划分点集
用二分求出一条与 \(X\) 轴垂直的直线,然后再求解。
如果正解是斜着画一条直线呢?
这时,我们旋转坐标系!
每次旋转之后都求一次最小双圆覆盖,然后再取最优值即是答案。如何对每个点进行“旋转”?

可以看图来理解。
坐标系旋转 \(180\) 度就是原坐标系了,所以只要转 \(180\) 度就好了。
总时间复杂度
首先,要转 \(180\) 次,每次中要二分直线,再做最小圆覆盖。
总时间复杂度 \(O(n \log n)\)。
最小覆盖双圆问题代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define eps 1e-8
int n;
const int N=1e5+1;
struct Point{double x,y;};
Point p[N],a[N];
double Distance(Point A,Point B){return hypot(A.x-B.x,A.y-B.y);};
int cmp(Point a, Point b){return a.x<b.x;}
int sgn(double x){
if(fabs(x)<eps) return 0;
else return x<0?-1:1;
}
Point rotate(const Point &b) {
Point t;
t.x=b.x*cos(1.0/180*acos(-1))+b.y*sin(1.0/180*acos(-1));
t.y=b.y*cos(1.0/180*acos(-1))-b.x*sin(1.0/180*acos(-1));
return t;
}
Point circle_center(const Point a,const Point b,const Point c){
Point center;
double a1=b.x-a.x,b1=b.y-a.y,c1=(a1*a1+b1*b1)/2;
double a2=c.x-a.x,b2=c.y-a.y,c2=(a2*a2+b2*b2)/2;
double d=a1*b2-a2*b1;
center.x=a.x+(c1*b2-c2*b1)/d;
center.y=a.y+(a1*c2-a2*c1)/d;
return center;
}
double min_cover_circle(int n1,int n2){
if(n1>n2) return 0;
for(int i=n1;i<=n2;i++) a[i]=p[i];
random_shuffle(a+n1+1,a+n2+1);
Point c=a[n1];
double r=0;
for(int i=n1;i<=n2;i++){
if(sgn(Distance(a[i],c)-r)>0){
c=a[i];r=0;
for(int j=n1;j<i;j++){
if(sgn(Distance(a[j],c)-r)>0){
c.x=(a[i].x+a[j].x)/2;
c.y=(a[i].y+a[j].y)/2;
r=Distance(a[j],c);
for(int k=n1;k<=j;k++){
if(sgn(Distance(a[k],c)-r)>0){
c=circle_center(a[i],a[j],a[k]);
r=Distance(a[i],c);
}
}
}
}
}
}
return r;
}
void init(int n){
double R=1e9;
for(int i=1;i<=n;i++) cin>>p[i].x>>p[i].y;
for(int i=1;i<=180;i++){
for(int i=1;i<=n;i++) p[i]=rotate(p[i]);
sort(p+1,p+n+1,cmp);
int l=1,r=n;
while(l<=r) {
int mid=(l+r)/2;
double R1=min_cover_circle(1,mid),R2=min_cover_circle(mid+1,n);
if(min(R1,R2)>R) break;
if(R1<R2)l=mid+1;
else r=mid-1;
R=min(R,max(R1, R2));
}
}
printf("%.2f\n",R);
}
signed main(){
while(cin>>n&&n!=0){init(n);}
return 0;
}
如果本篇题解还可以,就点个赞吧!
题解:P4586 [FJOI2015] 最小覆盖双圆问题的更多相关文章
- 洛谷P4586 [FJOI2015]最小覆盖双圆问题(最小圆覆盖)
题面 传送门 前置芝士 最小圆覆盖 题解 我们按照\(x\)坐标排序,然后二分中间点,把点分成左右两边,对两边都做一个最小圆覆盖,那么半径大一点的那个就是答案了.然后对半径大的那一边继续二分就行了 然 ...
- 【题解】FJOI2015火星商店问题
好几天之前做的题目了,一直想写一下博客也没腾出时间来,今天赶紧把坑给填上呼呼呼~ 这道题首先如果只考虑每个商店中没有时间限制的物品时,我们只需要使用一棵可持久化trie树来维护区间内的异或最大值即可, ...
- bzoj AC倒序
Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...
- 【题解】P4585 [FJOI2015]火星商店问题(线段树套Trie树)
[题解]P4585 [FJOI2015]火星商店问题(线段树套Trie树) 语文没学好不要写省选题面!!!! 题目大意: 有\(n\)个集合,每个集合有个任意时刻都可用的初始元素.现在有\(m\)个操 ...
- SCU 4439 Vertex Cover(二分图最小覆盖点)题解
题意:每一条边至少有一个端点要涂颜色,问最少涂几个点 思路:最小顶点覆盖:用最少的点,让每条边都至少和其中一个点关联,显然是道裸最小顶点覆盖题: 参考:二分图 代码: #include<iost ...
- poj 2226 Muddy Fields (转化成二分图的最小覆盖)
http://poj.org/problem?id=2226 Muddy Fields Time Limit: 1000MS Memory Limit: 65536K Total Submissi ...
- 【POJ3171】Cleaning Shifts 带权区间最小覆盖
题目大意:给定一个长度为 N 的序列,求带权区间最小覆盖. 题解:设 \(dp[i]\) 表示从左端点到 i 的最小权值是多少,则状态转移为:\(dp[e[i].ed]=min\{dp[j],j\in ...
- 【LG4585】[FJOI2015]火星商店问题
[LG4585][FJOI2015]火星商店问题 题面 bzoj权限题 洛谷 \(Notice:\) 关于题面的几个比较坑的地方: "一天"不是一个操作,而是有0操作就相当于一天开 ...
- poj 3308(二分图的点权最小覆盖)
Paratroopers Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8325 Accepted: 2502 Desc ...
- CSU-1531 Jewelry Exhibition —— 二分图匹配(最小覆盖点)
题目链接:https://vjudge.net/problem/CSU-1531 Input Output Sample Input 2 1 5 3 0.2 1.5 0.3 4.8 0.4 3.5 4 ...
随机推荐
- Flink学习(二) 应用场景和架构模型
实时计算最好的时代 在过去的十年里,面向数据时代的实时计算技术接踵而至.从我们最初认识的 Storm,再到 Spark 的异军突起,迅速占领了整个实时计算领域.直到 2019 年 1 月底,阿里巴巴内 ...
- CPrimerPlus
还没学 的 167页的wordcnt程序 199页的checking程序(太长了,不想看) 113页的第八章编程练习5(不想看) 125页的复习题9(有问题,有时间再来验证) 119页重定向和文件(n ...
- ABC391F题解
不加火车头(不吸氧)不开快读全部 long long 提交记录. 使用了我所知的三种优化后的提交记录(最慢点还是没有在一秒内跑过啊). 做法非常的妙,我们先将 \(A,B,C\) 这三个数组降序排序, ...
- php查询结果汉字乱码解决方法
问题描述:使用php查询数据显示,显示的结果中所有汉字乱码 问题及解决:这种情况是编码造成的,检查数据库及页面编码是否一致,也可在页面增加: header('Content-Type:text/htm ...
- manim边学边做--局部缩放的场景类
在动画制作中,尤其是数学和科学可视化领域,有时我们需要将观众的注意力集中在场景的某个特定部分. Manim提供了一个强大的工具 ZoomedScene,它允许我们在场景中创建一个独立的缩放视图,从而实 ...
- Vue2/Vue3 项目生产环境开启 vue devtools 插件线上调试 vue 组件
说到 vue 项目的调试工具,必然少不了 "vue devtools 插件",此插件就像"手术刀"一样,是开发环境下的一个利器,生产环境一般情况没办法使用. 要 ...
- antd+vue 中select组件的自定义后缀图标不显示问题记录
根据项目需求,需要使用select组件,并自定义后缀图标,但是设置了没的效果,这是我的代码和效果图 后来查看代码发现需要给select组件加上showArrow属性,然后实现了效果,看效果图 这里记录 ...
- JMeter 通过 BeanShell 脚本处理入参和回参
入参:可以通过该方式动态生成入参参数,如时间参数,随机参数等. 操作:右键 HTTP Request - Add - Pre Processor - BeanShell PreProcessor im ...
- 解决PyCharm提示Error: Please select a valid Python interpreter
前言 Pycharm运行Python3.7.8的程序时发现源程序运行报错(非语法错误) Error:please select a valid Python interpreter 解决 第一步:打开 ...
- go json omitempty 关键字 脱坑
用法 大家对于 json 和 struct 之间的转换一定不陌生,为了将代码中的结构体与 json 数据解耦,通常我们会在结构体的 field 类型后加上解释说明,例如在表示一个地址的时候, json ...