2428: [HAOI2006]均分數據
阿新 • • 發佈:2017-09-11
隨機 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]均分數據