模拟退火(D Country Meow,POJ 2420,POJ 1379)

D Country Meow

D Country Meow 最小球覆盖模板题,模拟退火算法

题意:

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

在这里插入图片描述
代码(含详细注释):

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int M=150;
const double eps=1e-3;       //精度
//模拟退火有一个初始温度,温度越高,接受较差的解的可能性就越大
const double start_T=1000;   //初始温度
const double rate=0.98;      //温度下降速率
struct point{
    double x,y,z;
} p[M];
int N;

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));
}

double solve(){
    double T=start_T;
    point ans_p={0,0,0};  //初始点
    double ans=1e99;      //预设一个较大值
    while(T>eps){
        //找到距离ans_p最远的点:maxd_p
        point maxd_p;
        double max_dis=-1;
        for(int i=1;i<=N;i++){
            if(dist(ans_p,p[i])>max_dis){
                maxd_p=p[i];
                max_dis=dist(ans_p,p[i]);
            }
        }
        //在走的过程中,更新最优解的值
        ans=min(ans,max_dis);
        //一直向离它最远的那个点靠近,但每次要以一定的概率去靠近
        // 那么使该概率为 p = T/start_T , 并不断减小
        ans_p.x+=(maxd_p.x-ans_p.x)*(T/start_T);
        ans_p.y+=(maxd_p.y-ans_p.y)*(T/start_T);
        ans_p.z+=(maxd_p.z-ans_p.z)*(T/start_T);
        //温度T越低时这一次则越可能达到更优解(温度不断下降,使得接受较差解的可能性变小)
        T*=rate;
    }
    return ans;
}

int main(){
    scanf("%d",&N);
    for(int i=1;i<=N;i++)
        scanf("%lf %lf %lf",&p[i].x,&p[i].y,&p[i].z);
    printf("%.8lf",solve());
    return 0;
}

A Star not a Tree?

A Star not a Tree?

题意:

求n边形的费马点,即找到一个点使得这个点到n个点的距离之和最小

代码(含详细注释):

#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
//往8个方向搜索
const int offx[]={1,1,0,-1,-1,-1,0,1};
const int offy[]={0,1,1,1,0,-1,-1,-1};
const int maxn=110;
const double eps=1e-2;

struct point{
    double x,y;
    point(double x0=0,double y0=0){x=x0;y=y0;}
    double getdis(point p){
        return sqrt( (x-p.x)*(x-p.x)+(y-p.y)*(y-p.y) );
    }
}p[maxn];

int n;

double getsum(point p0){
    double sum=0;
    for(int i=0;i<n;i++) sum+=p0.getdis(p[i]);
    return sum;
}

void solve(){
    for(int i=0;i<n;i++)
        scanf("%lf%lf",&p[i].x,&p[i].y);
    double step=100;//定义好步长
    double ans=getsum(p[0]);
    point s=p[0],tem_start;
    while(step>eps){
        bool flag=false;
        for(int i=0;i<8;i++){
            tem_start=point(s.x+step*offx[i],s.y+step*offy[i]);
            double tem_ans=getsum(tem_start);
            //如果找到比答案更小的点,那么以这个新的点为起点,重复
            if(tem_ans < ans){
                s=tem_start;//更新起点
                ans=tem_ans;//更新答案
                flag=true;
            }
        }
        //如果找不到比答案更小的点,那么步长减半,再尝试
        if(!flag) step/=2;
    }
    printf("%.0f\n",ans);
}

int main(){
    while(scanf("%d",&n)!=EOF){
        solve();
    }
    return 0;
}

Run Away

Run Away

题意:

平面上有一个矩形,在矩形内有一些陷阱 , 求得矩形内一个点,该点离与它最近的已知陷阱最远。

思路:

模拟退火算法+随机化算法(二者均属于概率算法)

RAND_MAX用法 : srand rand RAND_MAX 分别是什么意思?

代码(出现运行时错误,求解答qaq):
在这里插入图片描述

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstdlib>
const int shift=60;
const double inf=1e10;
const double pi=acos(-1.0);

struct Point{
    double x,y;
}p[1010],randp[4];

double Get_Dis(Point a,Point b){
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}

Point Get_Rand_Point(int a,int b){
    Point temp;
    temp.x=rand()%a+1;
    temp.y=rand()%b+1;
    return temp;
}

Point Goto_Rand_Dir(double key,Point temp){
    //rand()/(RAND_MAX+1.0) : 产生一个0到1之间的小数
    double delta=2*pi*(double)rand()/RAND_MAX;
    temp.x+=key*sin(delta);
    temp.y+=key*cos(delta);
    return temp;
}

int main(){
    double dis[5];
    int i,j,k,T,x,y,m;
    scanf("%d",&T);
    //只有设置成NULL才能获得系统的时间,不然数字不是随机的
    srand(time(NULL));
    while(T--){
        double now;
        Point temp;
        scanf("%d%d%d",&x,&y,&m);
        for(i=1;i<=m;i++)
            scanf("%lf%lf",&p[i].x,&p[i].y);
        for(i=1;i<=4;i++){//分别往四个方向找四个方向的最小值
            dis[i]=inf;
            randp[i]=Get_Rand_Point(x,y);//给randp[]数组随机取横纵坐标值
            for(j=1;j<=m;j++){
                now=Get_Dis(randp[i],p[j]);
                if(now<dis[i])
                    dis[i]=now;//dis[i]存的是最小值
            }
        }
        //关键,取对角线的一半,这是最大距离了
        double key=sqrt(1.0*(x*x+y*y))/2;
        while(key>=0.01){//精度要求
            for(i=1;i<=4;i++){
                for(j=1;j<=shift;j++){
                    temp=randp[i];//采用之前四个方向确定的随机横纵坐标值
                    temp=Goto_Rand_Dir(key,temp);
                    if(temp.x<0||temp.y<0||temp.x>x||temp.y>y)
                        continue;
                    now=inf;
                    for(k=1;k<=m;k++){
                        double dist=Get_Dis(temp,p[k]);
                        if(now>dist) now=dist;//now存的是最小值
                    }
                    if(now>dis[i]){
                        dis[i]=now;//dis[i]中存的是最小值最大
                        randp[i]=temp;
                    }
                }
            }
            key=key*0.8;
        }
        for(i=1,k=1;i<=4;i++)//在四个方向中找最小值最大
            if(dis[i]>dis[k]) k=i;
        printf("The safest point is (%.1lf, %.1lf).\n",randp[k].x,randp[k].y);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值