https://blog.csdn.net/qq_42447015/article/details/98480009
https://www.cnblogs.com/flashhu/p/8884132.html
学习这两篇blog后,就大概会模拟退火的大概思想了。
这个算法主要就是利用结论进行调参,然后玄学过出比较难的函数最优解问题。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
const double PI= acos(-1.0);
const int M = 1e3+7;
ld x[M],y[M],w[M];
int n;
ld cal(ld a,ld b)
{
ld ans=0;
for(int i=1;i<=n;i++)
ans+=w[i]*sqrt((x[i]-a)*(x[i]-a)+(y[i]-b)*(y[i]-b));
return ans;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)scanf("%Lf%Lf%Lf",&x[i],&y[i],&w[i]);
srand(time(NULL));
//模拟退火的过程
int times=10;//模拟退火的次数
//delt设置 在 0.95 - 0.99 比较合适
ld best,T=100000,delt=0.97,eps=1e-14,lst;
ld x1=0,y1=0,x0=0,y0=0,xb=0,yb=0;
for(int i=1;i<=n;i++)x0+=x[i],y0+=y[i];
// cout<<x0<<" "<<y0<<endl;
x0/=n,y0/=n;
best=lst=cal(x0,y0);xb=x0,yb=y0;//预处理出较优解
// cout<<xb<<" - "<<yb<< " "<<best<<endl;
while(times)
{
T=100000;
x0 = xb,y0 = yb,lst = best;
while(T>eps)
{
//横向纵向 两个方向都要随机爬山!!
x1 = x0 + T*(rand()*2-RAND_MAX);
y1 = y0 + T*(rand()*2-RAND_MAX);
ld res = cal(x1,y1);
// printf("%.18Lf %Lf %Lf %Lf ----- %Lf %Lf %Lf\n",T,x1,y1,res,xb,yb,best);
//cout<<res<<" "<<T<<endl;
if(res < best)best = res , xb = x1, yb = y1;
if(res < lst || exp((lst - res) / T) > (ld)rand()/RAND_MAX)
lst = res , x0 = x1,y0 = y1;
T*=delt;
}
times--;
}
printf("%.3Lf %.3Lf\n",xb,yb);
return 0;
}