bzoj 2428: [HAOI2006]均分数据

模拟退火;

#include
#define rep(i,k,n) for(int i=k;i<=n;i++)
using namespace std;
double ave=0.0,ans=0.0,tmp=0.0,T=10000.0,minans=1e30;
int n,m,sum[30],belong[30],a[30];
void fire(){T=10000.0;
memset(sum,0,sizeof(sum));
ans=0.0;
    for(int i=0;iint xx=rand()%m;sum[xx]+=a[i];belong[i]=xx;}
    for(int i=0;i<m;i++){ans+=(sum[i]*1.0-ave)*(sum[i]*1.0-ave);}
    while(T>0.1){
        T*=0.9;
        int t=rand()%n,y;int x=belong[t];
        if(T>500)
            y=min_element(sum,sum+m)-sum;
        else y=rand()%m;
        if(x==y)continue;
        tmp=ans;
        ans-=(sum[x]*1.0-ave)*(sum[x]*1.0-ave);
        ans-=(sum[y]*1.0-ave)*(sum[y]*1.0-ave);
        sum[x]-=a[t];sum[y]+=a[t];
        ans+=(sum[x]*1.0-ave)*(sum[x]*1.0-ave);
        ans+=(sum[y]*1.0-ave)*(sum[y]*1.0-ave);
        if(ans<=tmp)
            belong[t]=y;
            else if(rand()%10000>T){
                sum[x]+=a[t];sum[y]-=a[t];
                ans=tmp;
            }
            else belong[t]=y;
        minans=min(minans,ans); 
    }
}
int main(){
    //freopen("in.in","r",stdin);
    srand('L'+'T'+'Q');
    scanf("%d%d",&n,&m);
    for(int i=0;i"%d",&a[i]);ave+=(a[i]*1.0);
    }ave/=(double)m;
    rep(i,1,10000)
    fire();
    printf("%.2lf",sqrt(minans/m));
}

很神奇也很好玩的算法,我也就在bzoj上玩了20几遍罢了…尝试各种参数…

你可能感兴趣的:(bzoj 2428: [HAOI2006]均分数据)