关闭

BZOJ2428: [HAOI2006]均分数据 模拟退火

234人阅读 评论(0) 收藏 举报
分类:

题意:N个数分成M组,要求均方差最小
N<=20,M<=6
随机一种分法,然后模拟退火,每次把一个数随机分到另外一组,若rand(0,1)< exp(dt/T)则判定为成功。然而WA了很久,最后知道温度高的时候需要贪心把它放入当前和最小的一组。。。还是不行,于是改成了从头模拟退火20次才终于过了,慢得垫底,真玄学

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
using namespace std;
int n,m;
int w[20],a[20][21],t[20],sum[20],pos[20];
inline double F()
{
    int asum=0;
    for(int i=0;i<m;++i)
    {
        sum[i]=0;
        for(int j=1;j<=t[i];++j)
        sum[i]+=w[a[i][j]];
        asum+=sum[i];
    }
    double dx=floor(asum)/m,res=0;
    for(int i=0;i<m;++i)
    res+=(sum[i]-dx)*(sum[i]-dx);
    return res;
}
inline int move(int x,int p)
{
    int b=pos[x];
    for(int i=t[b]--;;--i)
    {
        if(a[b][i]==x)
        {
            for(;i<=t[b];++i)
            a[b][i]=a[b][i+1];
            break;
        }
    }
    a[p][++t[pos[x]=p]]=x;
    return b;
}
inline bool failed(const double &dt,const double &T)
{
    return dt<0.0&&exp(dt/T)<=floor(rand())/RAND_MAX;
}
inline void random_shuffle()
{
    int aim;
    for(int i=0;i<n;++i)
    {
        pos[i]=aim=rand()%m;
        a[aim][++t[aim]]=i;
    }
}
inline int greedy_choice()
{
    int minn=0x7fffffff,res;
    for(int i=0;i<m;++i)
    {
        sum[i]=0;
        for(int j=1;j<=t[i];++j)
        sum[i]+=w[a[i][j]];
        if(sum[i]<minn) minn=sum[i],res=i;
    }
    return res;
}
inline double fire()
{
    memset(t,0,sizeof t);
    double T=1e16,ans,dt,res;
    int ele,aim,org;
    random_shuffle();
    res=ans=F();
    while(T>1e-10)
    {
        ele=rand()%n;
        if(T>1e2) aim=greedy_choice();
        else aim=rand()%m;
        org=move(ele,aim);
        dt=ans-F();
        if(ans-dt<res) res=ans-dt;
        if(failed(dt,T)) move(ele,org);
        else ans-=dt;
        T*=0.999;
    }
    return res;
}
inline void min(double &a,const double &b)
{
    if(b<a) a=b;
}
int main()
{
    srand(114514);
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;++i)
    scanf("%d",w+i);
    double ans=1e10;
    for(int i=1;i<=20;++i)
    min(ans,sqrt(fire()/m));
    printf("%.2lf\n",ans);
    return 0;
}
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:20006次
    • 积分:1096
    • 等级:
    • 排名:千里之外
    • 原创:90篇
    • 转载:0篇
    • 译文:0篇
    • 评论:18条
    博客专栏