題意:有n個樓,給定xy座標和高度,有一個防護盾核心,以他為球心的半球內的都處於他的保護之下,求他的座標,使得他需要覆蓋全部樓的半徑最小。
題解:
類比退火。
第一個類比退火,哈哈。
傳統的類比退火如果目前狀態更有就接受,否則以一定機率接受,機率的存在保證不會跳進局部最優而出不來。
不過用機率搞貌似效果不是很好,然後就初始產生100組解,對著100組並行操作,只在更優的時候接受,這樣每組解最後都會到達他所在地區的局部最優解,而全域最優解很有可能就在這些局部最優解之中,對這100組取個最優作為全域最優值。
這個方法在資料不是特別針對的情況下效果還不錯,特別怕那種鋸齒形的有很多局部最優解的資料。
#include<stdio.h>
#include<memory.h>
#include<iostream>
#include<algorithm>
#include <stdlib.h>
#include <time.h>
#include<cmath>
using namespace std;
#define TN 180
int n;
struct pt
{
double x,y,h;
};
struct status
{
pt p;
double e,r;
};
pt a[105];
double calc_r(pt a,pt b)
{
return sqrt( (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y) +a.h*a.h );
}
double max_r(pt x)
{
int i;
double ans=0,t;
for(i=1;i<=n;i++)
if ((t=calc_r(a[i],x))>ans)
ans=t;
return ans;
}
status s0[2000],s[2000];
double t0;
double P(double se,double ee)
{
return exp(-(ee-se)/ee);
}
double fmax(double x,double y)
{
if (x>y) return x;
else return y;
}
int main()
{
int i,j,k,u;
double minx,miny,maxx,maxy;
srand(time(NULL));
while(cin>>n)
{
if (n==0) break;
minx=10000000;maxx=-100000000;miny=100000000;maxy=-10000000;
for(i=1;i<=n;i++)
{
cin>>a[i].x>>a[i].y>>a[i].h;
if (a[i].x<minx) minx=a[i].x;
if (a[i].x>maxx) maxx=a[i].x;
if (a[i].y<miny) miny=a[i].y;
if (a[i].y>maxy) maxy=a[i].y;
}
for(i=0;i<=TN;i++)
{
s0[i].p.x=minx+i*(maxx-minx)/TN;
s0[i].p.y=miny+i*(maxy-miny)/TN;
s0[i].e=max_r(s0[i].p);
}
for(double len=fmax(maxx-minx,maxy-miny);len>1e-7;len*=0.9)
for(u=0;u<=TN;u++)
{
double ang=(rand()%32768)*1.0/32768*6.283;
s[u].p.x=s0[u].p.x+sin(ang)*len;
s[u].p.y=s0[u].p.y+cos(ang)*len;
s[u].e=max_r(s[u].p);
if (s[u].e<s0[u].e) s0[u]=s[u];
}
int ans;
double anse;
anse=10000000;
for(i=0;i<=TN;i++)
if (anse>s0[i].e)
{
anse=s0[i].e;
ans=i;
}
printf("%.3lf %.3lf/n",s0[ans].p.x,s0[ans].p.y);
}
}