博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
bzoj 3754: Tree之最小方差树 模拟退火+随机三分
阅读量:4485 次
发布时间:2019-06-08

本文共 2902 字,大约阅读时间需要 9 分钟。

题目大意:

求最小方差生成树.N<=100,M<=2000,Ci<=100

题解:


首先我们知道这么一个东西:

  • 一些数和另一个数的差的平方之和的最小值在这个数是这些数的平均值时取得

所以我们可以枚举这个平均数,然后计算所有边与该值的差的平方

然后扔下去跑一个最小生成树
然后我们通过枚举这个平均数发现这个平均数和答案的对应函数的图像是一个波形函数

所以我们可以直接在这个波形图像上找函数最低点:

相应的就有

  • 爬山算法
  • 模拟退火

两种算法


所以我们可以先在全局用模拟退火然后在局部用爬山算法。

然而还是每三组数据就Wa一次
然后发现这样的话极限数据只需要0.8s,还有1.2s可以用
所以可以在全局再三分找函数最低点.
随机化左右端点然后再三分.
随机化22次端点极限数据可以跑到1.3s

#include 
#include
#include
#include
using namespace std;typedef long long ll;inline void read(int &x){ x=0;char ch;bool flag = false; while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true; while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;}const int maxn = 128;const int maxm = 2048;const double eps = 1e-10;const double det = 0.99;struct Node{ int u,v,bed; double d; bool friend operator < (const Node &a,const Node &b){ return a.d < b.d; }}G[maxm];int fa[maxn];inline int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}int n,m;inline double work(double mid){ for(int i=1;i<=n;++i) fa[i] = i; for(int i=1;i<=m;++i) G[i].d = (G[i].bed - mid)*(G[i].bed - mid); sort(G+1,G+m+1); int cnt = 0;double sum = 0; for(int i=1;i<=m;++i){ int x = find(G[i].u); int y = find(G[i].v); if(x != y){ sum += G[i].d; fa[x] = y; if(++cnt == n-1) break; } } return sqrt(sum/(n-1));}inline double ran(){ return (1.0*rand())/1000.0;}double ans = 1e10,ans_p;inline double f(double mid){ double x = work(mid); if(ans > x){ ans = x; ans_p = mid; }return x;}int main(){ srand(2333); read(n);read(m); int minn = 0x7f7f7f7f,maxx = -0x7f7f7f7f; for(int i=1;i<=m;++i){ read(G[i].u); read(G[i].v); read(G[i].bed); minn = min(minn,G[i].bed); maxx = max(maxx,G[i].bed); } double l = minn,r = maxx,nx,t,val_nx; double T = 50.0,nw = (l+r)/2,val_nw; while(T > eps){ nx = ( rand() % 2 == 0 ? -1 : 1)*ran()*T; t = val_nw - (val_nx = f(nx)); if(t > 0 || exp(t/T) >= ran() ){ nw = nx; val_nw = val_nx; } T *= det; } while(r-l > eps){ double midx = (l+l+r)/3; double midy = (l+r+r)/3; double x = f(midx); double y = f(midy); if(x < y) r = midy; else l = midx; } ans = min(ans,f(l)); int num = 22; while(num--){ double l = minn + rand()*ran()*0.1; double r = maxx - rand()*ran()*0.1; if(l > r) swap(l,r); while(r-l > eps){ double midx = (l+l+r)/3; double midy = (l+r+r)/3; double x = f(midx); double y = f(midy); if(x < y) r = midy; else l = midx; }ans = min(ans,f(l)); } printf("%.4lf\n",ans); getchar();getchar(); return 0;}

转载于:https://www.cnblogs.com/Skyminer/p/6486133.html

你可能感兴趣的文章
bzoj 1578: [Usaco2009 Feb]Stock Market 股票市场【背包】
查看>>
hdu 3038 How Many Answers Are Wrong【带权并查集】
查看>>
二叉树的基本操作
查看>>
软件工程之寻找水王
查看>>
MSMQ 消息队列错误处理
查看>>
Prism for WPF 搭建一个简单的模块化开发框架(五)添加聊天、消息模块
查看>>
在VisualStudio 工具箱中隐藏用户控件
查看>>
C#.NET使用Task,await,async,异步执行控件耗时事件(event),不阻塞UI线程和不跨线程执行UI更新,以及其他方式比较...
查看>>
with(nolock) 与 with(readpast) 与不加此2个的区别
查看>>
零元学Expression Blend 4 - Chapter 11 用实例了解布局容器系列-「Border」
查看>>
Bootstrap<基础十> 响应式实用工具
查看>>
SQL Server :理解GAM和SGAM页
查看>>
恢复SQLServer实例连接
查看>>
Oralce 处理字符串函数
查看>>
Flex4 饼图样式(颜色渐变,点击分离,环形)
查看>>
28个Unix/Linux的命令行神器
查看>>
pb11.5破解补丁
查看>>
struts2下 数据转换器
查看>>
比特信 介绍
查看>>
ubuntu 16.04 Samba 匿名配置
查看>>