D.Country Meow 最小球覆盖 三分套三分套三分 && 模拟退火

// 2019.10.3
// 练习题:2018 ICPC 南京现场赛

D Country Meow

题目大意

给定空间内 N 个点,求某个点到 N 个点的距离最大值的最小值。
?

思路

非常裸的最小球覆盖问题啊,即找到半径最小的球包含全部的点。
最小圆覆盖问题上,可以使用随机增量法,这里没有四点确定球心的公式,所以板子失效了。
最小圆覆盖可以用三分套三分,这里空间有三维,假装证明得到在任意一维上都满足凸函数特性,那么再套一层维度三分就OK了。
?

AC代码

三分套三分套三分写法,复杂度O(n*log^3)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const double eps = 1e-3;
struct Point {
    double x, y, z;
    Point() {
        x = y = z = 0.0;
    }
    Point(double xx, double yy, double zz) {
        x = xx, y = yy, z = zz;
    }
    Point operator-(const Point& p) {
        return Point(x-p.x, y-p.y, z-p.z);
    }
    double dis() {
        return sqrt(x*x+y*y+z*z);
    }
}pt[110];
int n;

double cal(double x, double y, double z) {
    double res = 0;
    for(int i=1;i<=n;i++) {
        res = max(res, (pt[i]-Point(x, y, z)).dis());
    }
    return res;
}

double cal2(double x, double y) {
    double res = 1e18;
    double l = -100000, r = 100000;
    while(r-l>eps) {
        double m1 = (r-l)/3 + l;
        double m2 = (r-l)/3*2 + l;
        double res1 = cal(x, y, m1), res2 = cal(x, y, m2);
        res = min(res, min(res1, res2));
        if(res1<res2) r = m2;
        else l = m1;
    }
    return res;
}

double cal3(double x) {
    double res = 1e18;
    double l = -100000, r = 100000;
    while(r-l>eps) {
        double m1 = (r-l)/3 + l;
        double m2 = (r-l)/3*2 + l;
        double res1 = cal2(x, m1), res2 = cal2(x, m2);
        res = min(res, min(res1, res2));
        if(res1<res2) r = m2;
        else l = m1;
    }
    return res;
}

int main() {
    cin>>n;
    for(int i=1;i<=n;i++) {
        scanf("%lf %lf %lf", &pt[i].x, &pt[i].y, &pt[i].z);
    }

    double res = 1e18;
    double l = -100000, r = 100000;
    while(r-l>eps) {
        double m1 = (r-l)/3 + l;
        double m2 = (r-l)/3*2 + l;
        double res1 = cal3(m1), res2 = cal3(m2);
        res = min(res, min(res1, res2));
        if(res1<res2) r = m2;
        else l = m1;
    }
    printf("%.10lf\n", res);
    return 0;
}

?
模拟退火写法,对于三维复杂度更低:

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const double eps = 1e-5;
struct Point{
    double x, y, z;
}p[110], op;
int n;

inline double dist(Point &a, Point &b) {
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));
}
void solve() {
    double ans, delta = 10000.0;
    double maxDis, tempDis;
    while(delta>eps){
        int id = 0;
        maxDis = dist(op, p[id]);
        for(int i=1;i<n;i++){
            tempDis=dist(op,p[i]);
            if(tempDis>maxDis){
                maxDis = tempDis;
                id = i;
            }
        }
        ans = maxDis;
        op.x += (p[id].x-op.x)/maxDis*delta;
        op.y += (p[id].y-op.y)/maxDis*delta;
        op.z += (p[id].z-op.z)/maxDis*delta;
        delta *= 0.98;
    }
    printf("%.10lf\n", ans);
}

int main() {
    while(scanf("%d", &n)!=EOF && n) {
        op.x = op.y = op.z = 0;
        for(int i=0;i<n;i++) {
            scanf("%lf %lf %lf", &p[i].x, &p[i].y, &p[i].z);
        }
        solve();
    }
    return 0;
}

POJ2069 Super Star

这一题三分做法会T,只能用模拟退火才能过。
注意初始点选择。

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const double eps = 1e-5;
struct Point{
    double x, y, z;
}p[35], op;
int n;

inline double dist(Point &a, Point &b) {
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));
}
void solve() {
    double ans, delta = 100.0;
    double maxDis, tempDis;
    while(delta>eps){
        int id = 0;
        maxDis = dist(op, p[id]);
        for(int i=1;i<n;i++){
            tempDis=dist(op,p[i]);
            if(tempDis>maxDis){
                maxDis = tempDis;
                id = i;
            }
        }
        ans = maxDis;
        op.x += (p[id].x-op.x)/maxDis*delta;
        op.y += (p[id].y-op.y)/maxDis*delta;
        op.z += (p[id].z-op.z)/maxDis*delta;
        delta *= 0.98;
    }
    printf("%.5lf\n", ans);
}

int main() {
    while(scanf("%d", &n)!=EOF && n) {
        op.x = op.y = op.z = 0;
        for(int i=0;i<n;i++) {
            scanf("%lf %lf %lf", &p[i].x, &p[i].y, &p[i].z);
            op.x += p[i].x;
            op.y += p[i].y;
            op.z += p[i].z;
        }
        op.x /= n; op.y /= n; op.z /= n;
        solve();
    }
    return 0;
}


HDU3007 HDU3932 类似。
注意HDU3932 n==1采用模拟退火要特判。。。。

原文地址:https://www.cnblogs.com/izcat/p/11632683.html

时间: 2024-10-11 16:07:22

D.Country Meow 最小球覆盖 三分套三分套三分 && 模拟退火的相关文章

POJ2069 最小球覆盖 几何法和退火法

对这种问题不熟悉的读者 可以先去看一看最小圆覆盖的问题 ZOJ1450 现在我们来看最小球覆盖问题POJ2069 题目很裸,给30个点 求能覆盖所有点的最小球的半径. 先给出以下几个事实: 1.对于一个点,球心就是这个点且半径无穷小. 2.对于两个点,球心是两个点线段的中点,半径就是线段长度的一半. 3.对于三个点,三个点构成的平面必为球的大圆,所以球心是三角形的外心,半径就是球心到某个点的距离. 4.对于四个点,若四个点共面则转化到3,只需考虑某三个点的情况,若四点不共面,四面体可以唯一确定一

Country Meow

Country Meow 和这基本一样 https://www.cnblogs.com/Fighting-sh/p/9809518.html 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<string> 6 #include<algorithm> 7 #include<queue> 8 #in

Gym - 101981D Country Meow(模拟退火)

题意 三维空间有\(n\)个点,找到另外一个点,离所有点的最大距离最小.求这个距离. 题解 \(1\).最小球覆盖,要找的点为球心. \(2\).模拟退火. 还是补一下模拟退火的介绍吧. 模拟退火有一个初始温度,温度越高,接受较差的解的可能性就越大.每次走完后,都会降低温度,使得接受较差解的可能性变小.在走的过程中,更新最优解的值. 对这个题目来说,从空间中某一个点出发,如果每次都找离当前点最远的点,往那个点的方向走,大概率可以使结果变得更优. 随便设了个温度下降速率为\(0.97\),一遍就A

【模板】模拟退火 费马点以及最小球覆盖

最近学了一波模拟退火.个人感觉,就是随机算法,然后我们的目标点,一开始温度T高的时候会移动的剧烈,T小的时候移动的缓和(所以这就是为什么每一次移动的距离都是乘T).然后真正的模拟退火是如果当前的tem比ans优,那就毫不犹豫地接受,否则则以一定概率接受.也就是那个exp(dE/T)> rand 那个. 然后还有爬山算法,就是只会一直找更优解,不接受差解,具体就是在模拟退火基础上,一直找最优解,找不到就降温(所以会陷入局部最优解的坑).在网上嫖了一份代码(https://blog.csdn.net

HDU5126---stars (CDQ套CDQ套 树状数组)

题意:Q次操作,三维空间内 每个星星对应一个坐标,查询以(x1,y1,z1) (x2,y2,z2)为左下顶点 .右上顶点的立方体内的星星的个数. 注意Q的范围为50000,显然离散化之后用三维BIT会MLE. 我们可以用一次CDQ把三维变成二维,变成二维之后就有很多做法了,树套树,不会树套树的话还可以继续CDQ由二维变成一维,,变成一维了就好做了,,最基本的数据结构题目了.. 不得不说.CDQ真的很神奇. 下面做法就是CDQ套CDQ套树状数组. 1 #include <cstdio> 2 #i

POJ 2069 Super Star(模拟退火,最小球覆盖)

解题思路: 给出空间内的n个点,找出覆盖这n个点的最小球的半径.用模拟退火来做. #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <vector> #include <queue> #include <stack> #include <map> #include <cmath>

#1142 : 三分&#183;三分求极值 ( 三分极值 )

#1142 : 三分·三分求极值 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 这一次我们就简单一点了,题目在此: [week40_1.PNG] 在直角坐标系中有一条抛物线y=ax^2+bx+c和一个点P(x,y),求点P到抛物线的最短距离d. 提示:三分法 输入 第1行:5个整数a,b,c,x,y.前三个数构成抛物线的参数,后两个数x,y表示P点坐标.-200≤a,b,c,x,y≤200 输出 第1行:1个实数d,保留3位小数(四舍五入) 样例输入 2 8 2

hihocoder 1142 三分求极值【三分算法 模板应用】

#1142 : 三分·三分求极值 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 这一次我们就简单一点了,题目在此: 在直角坐标系中有一条抛物线y=ax^2+bx+c和一个点P(x,y),求点P到抛物线的最短距离d. 提示:三分法 输入 第1行:5个整数a,b,c,x,y.前三个数构成抛物线的参数,后两个数x,y表示P点坐标.-200≤a,b,c,x,y≤200 输出 第1行:1个实数d,保留3位小数(四舍五入) 样例输入 2 8 2 -2 6 样例输出 2.437

【dfs套dfs套dfs】算24点

Luogu P1236 算24点 值得纪念一下 1 #include<cstdio> 2 #include<iostream> 3 #define ri register int 4 #define ll long long 5 using namespace std; 6 int a, b, c, d; 7 int ans[20]; 8 char s[5]; 9 bool flag = 0; 10 bool dfs3(int x, int y) { 11 if(x < y)