模拟退火算法

一些求解极值的问题不能通过函数特性直接求解,只能暴力枚举,但是单纯的枚举效率不高,通过模拟退火算法可以高效的找到答案。
学习好博文:
http://www.cnblogs.com/heaad/archive/2010/12/20/1911614.html

相关题目:
最小圆覆盖:
hdu 3007  Buried memory
http://acm.hdu.edu.cn/showproblem.php?pid=3007
大意:给出一些点,求出能覆盖他们的最小的圆。输出圆心和半径

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const double eps=1e-7,INF=1e99; // T: 初始温度  delta:下降速率
const double T=10,delta=0.88;    //一般地,T=100, delta=0.98
const int N=5e2+10;
struct point{
    double x,y;
}p[N],s;
double dis(point a,point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double solve(int n){
    s=p[0];
    double t=T;
    double r=INF;
    while(t>eps){
        int k=0;
        double d=0;
        for(int i=0;i<n;i++){
            double f=dis(s,p[i]);
            if(f>d){
                d=f;
                k=i;
            }
        }
        s.x+=(p[k].x-s.x)/d*t;
        s.y+=(p[k].y-s.y)/d*t;
        r=min(r,d);
        t*=delta;
    }
    return r;
}
int main()
{
    int n;
    while(~scanf("%d",&n)&&n){
        for(int i=0;i<n;i++){
            scanf("%lf %lf",&p[i].x,&p[i].y);
        }
        double r=solve(n);
        printf("%.2lf %.2lf %.2lf\n",s.x,s.y,r);
    }
    return 0;
}

最小球包含
POJ 2069 Super Star
http://poj.org/problem?id=2069
大意:给出一些三维空间的点,求最小的包含他们的球的半径

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const double T=100,delta=0.98; //下降速度一般设成0.98
const double eps=1e-7,INF=1e99;

struct point{
    double x,y,z;
}p[35],s;
double dis(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(int n){
    s=p[0];
    double t=T;
    double ans=INF;
    while(t>eps){
        int k=0;
        for(int i=0;i<n;i++){
            if(dis(s,p[i])>dis(s,p[k])){
                k=i;
            }
        }
        double d=dis(s,p[k]);
        ans=min(ans,d);
        s.x+=(p[k].x-s.x)/d*t;  //向量
        s.y+=(p[k].y-s.y)/d*t;
        s.z+=(p[k].z-s.z)/d*t;
        t*=delta;
    }
    return ans;
}
int main()
{
    int n;
    while(cin>>n&&n){
        for(int i=0;i<n;i++){
            scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z);
        }
        printf("%.5lf\n",solve(n));
    }
    return 0;
}

查找函数极值:
hdu 2899  Strange fuction
http://acm.hdu.edu.cn/showproblem.php?pid=2899
求解: F(x) = 6 * x^7+8*x^6+7*x^3+5*x^2-y*x (0 <= x <=100) 的最小值

#include <iostream>
#include <cstdio>
#include <cmath>
#include <stdlib.h>
#include <algorithm>
using namespace std;

const double eps=1e-7,INF=1e99;
const double T=100,delta=0.88;  // 0.98 太慢了
const int N=10;
double x[N];

double random(){
    double x=rand()*1.0/RAND_MAX;
    if(rand()&1) x=-x;
    return x;
}

int jud(double a,double b){
    if(fabs(a-b)<eps) return 0;
    if(a-b<-eps) return -1;
    return 1;
}

double calc(double x,double y){
    return 6*pow(x,7)+8*pow(x,6)+7*pow(x,3)+5*x*x-y*x;
}

double solve(double y){
    for(int i=0;i<N;i++) x[i]=fabs(random()*100);

    double ans=INF;
    double t=T;
    while(t>eps){
        for(int i=0;i<N;i++){ // change x[i]
            double temp=calc(x[i],y);
            for(int j=0;j<N;j++){  //times
                double xx=x[i]+random()*t;
                if(jud(xx,0)>=0 && jud(xx,100)<=0){
                    if(jud(temp,calc(xx,y))>0){
                        x[i]=xx;
                        break;
                    }
                }
            }
        }
        t=t*delta;
    }
    for(int i=0;i<N;i++) ans=min(ans,calc(x[i],y));
    return ans;
}
int main()
{
    int t;
    double y;
    cin>>t;
    while(t--){
        scanf("%lf",&y);
        printf("%.4lf\n",solve(y));
    }
    return 0;
}

/*
%.4lf\n
2
100
-74.4291
200
-178.8534
-------------
%lf\n
2
100
-74.429122
200
-178.853367
*/

本想尝试做三分题,zoj 3421  Error Curves,但是发现不是超时,就是WA。

你可能感兴趣的:(数学)