您的位置:首页 > 其它

BZOJ2428 HAOI2006均分数据(模拟退火)

2018-09-02 11:42 381 查看

  显然可以状压dp。显然过不了。

  考虑暴力模拟退火。每次随机改变一个数所属集合即可。

  并不明白要怎么调参。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
#define N 21
#define T0 1E5
#define T1 1E-6
#define v 0.996
int n,m,a
,sum
,id
,size
;
double ave,ans=100000000;
void anneal()
{
for (int i=1;i<=m;i++) id[i]=i,sum[i]=a[i],size[i]=1;
for (int i=m+1;i<=n;i++) id[i]=rand()%m+1,sum[id[i]]+=a[i],size[id[i]]++;
double T=T0,tot=0;
for (int i=1;i<=m;i++) tot+=(sum[i]-ave)*(sum[i]-ave);
if (m<n)
while (T>T1)
{
int x=rand()%n+1;
while (size[id[x]]==1) x=rand()%n+1;
int y=rand()%m+1;
while (id[x]==y) y=rand()%m+1;
if (rand()<exp((-2.0*a[x]*(a[x]-sum[id[x]]+sum[y]))/T)*RAND_MAX)
{
tot+=2*a[x]*(a[x]-sum[id[x]]+sum[y]);
sum[y]+=a[x],sum[id[x]]-=a[x];
size[y]++,size[id[x]]--;
id[x]=y;
}
T*=v;
ans=min(ans,tot);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj2428.in","r",stdin);
freopen("bzoj2428.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
srand(20020509);
n=read(),m=read();
for (int i=1;i<=n;i++) ave+=a[i]=read();
ave/=m;
for (int i=1;i<=200;i++) anneal();
printf("%.2lf",sqrt(ans/m));
return 0;
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: