1. 程式人生 > >2428: [HAOI2006]均分數據

2428: [HAOI2006]均分數據

隨機 ace play 技術分享 || element bzoj blog return

模擬退火。
一種十分玄學的隨機算法,網上可以查到比較詳細的資料。

先隨機地把數分成m組,每次隨機地選擇一個數,一開始直接選最小的一組,後來就隨機一組,把這個數換到該組看看答案能不能變小,如果變小則換,如果沒有變小,按模擬退火的玄學方式判斷一下,也要交換。

srand(time(0))在bzoj會RE,不知為何。

非常玄學的正確性,沒拍多少數據就會出現差別,然而OJ上可以過的。

技術分享
//Twenty
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include
<cmath> #include<cstring> #include<queue> #include<vector> #include<ctime> #define Ti 1e4 const int maxn=30; using namespace std; int n,from[maxn],m; double tp,ave,ans,p[maxn],sum[maxn]; double pf(double x) {return x*x;} double solve() { double res=0; for(int i=1;i<=m;i++) sum[i]=0
; for(int i=1;i<=n;i++) { from[i]=rand()%m+1; sum[from[i]]+=p[i]; } for(int i=1;i<=m;i++) res+=pf(sum[i]-ave); double T=Ti; while(T>0.1) { int x=rand()%n+1,y; y=T>1000?min_element(sum+1,sum+m+1)-sum:rand()%m+1; tp=res-pf(sum[y]-ave)+pf(sum[y]+p[x]-ave)-pf(sum[from
[x]]-ave)+pf(sum[from[x]]-p[x]-ave); if(tp<res||exp((res-tp)*Ti/T)>(double)rand()/RAND_MAX) { res=tp; sum[y]+=p[x]; sum[from[x]]-=p[x]; from[x]=y; } T*=0.9; } return res; } int main() { srand(159159141); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%lf",&p[i]); ave+=p[i]; } ave/=m; ans=solve(); for(int i=1;i<=Ti;i++) ans=min(ans,solve()); printf("%.2lf\n",sqrt(ans/m)); return 0; }
View Code

這篇博客寫得十分好

其實不懂能不能隨便放別人博客,侵刪

2428: [HAOI2006]均分數據