【模拟退火】POJ 2420/ZOJ 1901 费马点

本文介绍了一种通过模拟退火算法求解n边形费马点的方法,详细解释了算法流程并提供了代码实现。算法通过逐步缩小精度,不断寻找最优解,最终在给定精度下得到费马点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

n 边形的费马点, 即某一点到n 边形的n 个顶点距离和最小的点。

模拟退火精度会受损。

模拟退火,就是一种贪心算法,但一定要注意得满足一些类似三角不等式的条件才能使用,否则不就不是损精度的问题了,而是根本上就是错误的。

以求费马点为例:

在平面上我先随便选出一点出发,向上下左右4个方向(这里只是以4方向为例,你也可以设8方向等,对于3维的可以设6方向)每个方向都尝试性的迈一步,这一步的大小为step,在所到的4个新地点选一个到所有点距离和最小的,然后你就走到那个点作为下一步的出发点,直到某一时刻四个方向都不如你当前所占的地点优,这是就将step/=2(除2也是灵活的,你也可以除1.5)之后再重复找四个方向。

不能更新的意义就是在当前step的精度下无法再移动,所以要再次缩小精度。这就像在google地图上找scnu公寓一样,先找到中国,再缩小精度(step/=2),再找到gz,在缩小精度,再找到scnu,再缩小精度,再找到xisan。----参考某blog

算法:

先拿一个点(该算法用n 个顶点的x, y 坐标和的均值所在的点)去计算距离和最小值, 然后拿它的四个方向上的点去测试比较哪个点更优, 不断迭代, 最终得到在精度允许下的费马点. 

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <deque>
#include <stack>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <limits.h>
#include <time.h>

using namespace std;

int lowbit(int t){return t&(-t);}
int countbit(int t){return (t==0)?0:(1+countbit(t&(t-1)));}
int gcd(int a,int b){return (b==0)?a:gcd(b,a%b);}
#define LL long long
#define PI acos(-1.0)
#define N  110
#define MAX INT_MAX
#define MIN INT_MIN
#define eps 1e-8
#define FRE freopen("a.txt","r",stdin)
struct node{
    double x,y;
}p[N];
int n;
double d[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
double dis(node a,node b){
    return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}

double dis_all(node a){
    int i;
    double sum=0;
    for(i=0;i<n;i++){
        sum+=dis(a,p[i]);
    }
    return sum;
}

int main(){
    while(scanf("%d",&n)!=EOF){
        int i,j,k;
        for(i=0;i<n;i++)scanf("%lf %lf",&p[i].x,&p[i].y);
        node p0=p[0];
        double minm=INT_MAX;
        double step;
        for(step=1000.0;step>eps;step/=2.0){
            int flag=1;
            while(flag){                //当再也找不到比当前小的就中止循环,把step缩小再找
                flag=0;
                for(i=0;i<4;i++){
                    node tmp;
                    tmp.x=p0.x+d[i][0]*step;
                    tmp.y=p0.y+d[i][1]*step;
                    double t=dis_all(tmp);
                    if(t<minm){
                        minm=t;
                        p0=tmp;
                        flag=1;
                    }
                }
            }
        }
        printf("%.0f\n",minm);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值