题面传送门

解决思路

本题数据范围较小,可以使用模拟退火算法(随机化)。

顾名思义,模拟退火就是一个类似于降温的过程。先设置一个较大的初温,每次随机改变状态,若使答案更优,则采取更优答案,否则根据其与当前最优答案的差值,一定概率保留这个较不优的答案。这时为了防止答案陷入局部最优的情况:

比如下图,陷入局部最优解 \(1\) 的状态后,需要一定的概率跳出来(蓝色虚线),到 \(2\) 处寻找全局最优解。

关于跳出的概率,遵循 『 \(\text{Metropolis}\) 接受准则 』:

设 \(delta=\) 之前最优答案 \(-\) 当前答案,\(T\) 为当前温度。

若 \(p=exp({delta}\div{T})\) 大于 \(\lbrack\ 0,1 )\) 区间的随机数,则仍接受当前状态。

注:\(exp(x)\) 函数:求 \(e\) 的 \(x\) 次方的函数。\(e\) 是一个常数,等于 \(2.718281828…\)

至于为什么,有兴趣可以自己搜索,我们暂且认为这是一种很好的更新方式。


那么再看本题,我们就可以用模拟退火的方法不断随机“费马点”的坐标,得到最优解。

说一下退火的一些基本套路:

  • 初温一般设为 \(1000\sim3000\),每次降温的系数一般在 \(0.95\sim0.9975\) 之间,温度下限一般取 \(1e-15\)。可根据数据范围需要和时限做调整。

  • 除非你是究极无敌大欧皇,在时间允许情况下,一般建议退火 \(5\sim10\) 次取最优解。

  • 对空间类问题,初始的 \(ans\) 一般设为所有点横、纵坐标的平均值。每次调整方法(以横坐标为例):\(new_x=ans_x+(rand()\times2-\texttt{RAND\_MAX})\times t\),其中 \(ans_x\) 为之前最优横坐标,\(rand()\times2-\texttt{RAND\_MAX}\) 可以取到 \(-\texttt{RAND\_MAX}\sim \texttt{RAND\_MAX}\) 之间的随机数。乘 \(t\) (当前温度)是为了控制调整幅度。

对于本题,可知 \(ans_x<=max_x\),\(ans_y<=max_y\),为了防止刚开始的几次随机到较大的无用结果,我们可以将 \(new_x\) 取模 \(max_x\),\(new_y\) 取模 \(max_y\),用 \(fmod()\) 函数即可。

根据笔者试验,在 初温 \(=1000\),降温系数 \(=0.975\),退火 \(5\) 次的情况下可以 \(0\ \text{ms}\) 通过本题。

还有,虽然答案要求保留整数,但直接用 \(\text{int}\) 会导致精度丢失。所以都用 \(\text{double}\),输出答案时四舍五入\((int)(ans+0.5)\) 即可。

最后,注意多测的清空与额外换行!

AC Code:

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define db double
using namespace std;
db n,ansx,ansy;
db x[105],y[105],ans,mxx,mxy;
int tt,T,ANS;
db dis(db x1,db y1,db x2,db y2){
db d1=(x1-x2),d2=(y1-y2);
return sqrt(d1*d1+d2*d2);
}
db calc(db xx,db yy){
db sum=0;
for(int i=1;i<=n;i++) sum+=dis(xx,yy,x[i],y[i]);
return sum;
}
void sa(){
db t=1000,dw=0.975;
while(t>1e-15){
db tx=fmod(ansx+(rand()*2.0-(db)RAND_MAX)*t,mxx);
db ty=fmod(ansy+(rand()*2.0-(db)RAND_MAX)*t,mxy);
db m=calc(tx,ty);
db delta=ans-m;
if(delta>0) ans=m,ansx=tx,ansy=ty;
else if((db)rand()<(db)RAND_MAX*(db)exp(delta/t)) ansx=tx,ansy=ty;
t*=dw;
}
}
void solve(){
cin>>n;
ansx=0,ansy=0;
for(int i=1;i<=n;i++){
cin>>x[i]>>y[i];
mxx=max(mxx,x[i]),mxy=max(mxy,y[i]);
ansx+=x[i],ansy+=y[i];
}
ansx/=n,ansy/=n;
ans=calc(ansx,ansy);
tt=5;
while(tt--) sa();
cout<<(int)(ans+0.5)<<endl;
if(T) cout<<endl;
}
int main(){
IOS;TIE;
srand(time(NULL));
cin>>T;
while(T--) solve();
return 0;
}

最新文章

  1. LoadRunner 参数和变量的区别(未完)
  2. JSP 新闻发布会
  3. nginx php rewrite配置
  4. Tomcat免安装配置2
  5. hdu 4272 2012长春赛区网络赛 dfs暴力 ***
  6. C语言中的位操作(14)--反转比特位
  7. 这些HTML、CSS知识点,面试和平时开发都需要 (转)
  8. Java问题汇集(1)
  9. VS2010数据库连接问题
  10. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(32)-swfupload多文件上传[附源码]
  11. docker基础入门之一
  12. C++静态局部对象
  13. Xamarin for Visual Studio下载后的文件路径
  14. isolate demo
  15. HBuilder-svn安装与使用【原创】
  16. netcore 发布 到 windows server IIS
  17. C# Owin初探 概念理解(一)
  18. React之函数中的this指向
  19. SQL语句创建数据库及表
  20. Swift3.0:Get/Post同步和异步请求

热门文章

  1. 【manim】含有add_updater更新函数become的物体移动方法
  2. Dapr中国社区活动之 分布式运行时开发者日 (2022.09.03)
  3. docker 匿名和具名挂载
  4. 【Vue学习笔记】—— vuex的语法 { }
  5. FastDFS配置文件思维导图(内含各配置文件详细参数说明)
  6. 给 SSH 启用二次身份验证
  7. MySQL一致性读原来是有条件的
  8. Elasticsearch:如何实现对 emoji 表情符号进行搜索
  9. Gitlab备份以及恢复
  10. PostgreSQL 删除数据库