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

  显然可以状压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[N],sum[N],id[N],size[N];
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;
}

 

posted @ 2018-09-02 11:42  Gloid  阅读(172)  评论(0编辑  收藏  举报