BZOJ3680 吊打XXX##

有n个点,求一个点到所有点的加权距离和最小

模拟退火算法##

自然界普遍遵循着向着低能量发展的趋势,物体的降温就是一个这样的过程
物体在降温过程中,分子做剧烈的无规则运动,逐渐寻找到达一个能量比较低的状态,温度逐渐降低,而无规则运动的剧烈程度也逐渐降低,最后趋于稳定的状态便很可能是一个所有状态中能量几乎最低的状态

就比如爬山算法,我们想要找到一个最高点,便从当前状态出发,向邻近的比当前状态高的点移动,直至无法移动
显然这样是可以找到一个局部最高点的,但这样子就很可能忽视一些更高的点

为了解决这样算法的不足,我们模仿退火的过程,我们设定一个温度,随着时间乘一个(0,1)之间的数而逐渐减小
在这个过程中,我们不断找寻当前点周围的点,其相差距离也由温度而定,温度越大,这个距离可能就越大,温度越小,这个距离就会变得细微
①如果我们找到一个比当前点优的点,显然要转移过去
②如果我们找到一个比当前点差的点,以一定的概率转移过去,这个概率为\(e^{\frac{\Delta d}{k*T}}\),其中\(\Delta d\)是温度差【即答案优秀程度相差值】,k是一个常数。因为比当前差,所以\(\Delta d\)是一个负值,那么\(e^{\frac{\Delta d}{k*T}} \in (0,1)\),且随着T减小而减小,即T越小,越趋于稳定

综上我们就得到了这样一个算法:
①确定一初始解
②设定温度T,逐渐减小
1'随机跳跃到附近一个解,距离据T而定
2'比较新解与当前解,如果更优,则转移,否则按一定概率转移
③最后再随机尝试小幅度转移,逼近最后答案

#include<iostream>
#include<cmath>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 10005,maxm = 100005,INF = 1000000000;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
	return out * flag;
}
int n;
int x[maxn],y[maxn],w[maxn];
double ansx,ansy,D;
double R(){
	return  (double)(rand() % 10000) / 10000;
}
double cal(double nx,double ny){
	double ans = 0;
	for (int i = 1; i <= n; i++){
		ans += sqrt((nx - x[i]) * (nx - x[i]) + (ny - y[i]) * (ny - y[i])) * w[i];
	}
	if (ans < D){
		ansx = nx; ansy = ny; D = ans;
	}
	return ans;
}
void SA(){
	double dE,T = 100000;
	double Nowx = ansx,Nowy = ansy,nx,ny;
	while (T > 0.001){
		nx = Nowx + T * (2 * R() - 1);
		ny = Nowy + T * (2 * R() - 1);
		dE = cal(Nowx,Nowy) - cal(nx,ny);
		if (dE > 0 || exp(dE / T) > R()){
			Nowx = nx; Nowy = ny;
		}
		T *= 0.97;
	}
	REP(i,1000){
		nx = ansx + T * (2 * R() - 1);
		ny = ansy + T * (2 * R() - 1);
		cal(nx,ny);
	}
}
int main(){
	n = read();
	REP(i,n){
		x[i] = read(),y[i] = read(),w[i] = read();
		ansx += x[i]; ansy += y[i];
	}
	ansx /= n; ansy /= n; D = cal(ansx,ansy);
	SA();
	printf("%.3lf %.3lf\n",ansx,ansy);
	return 0;
}