模拟退火.
具体实现的过程中有一些小技巧,题解见http://blog.csdn.net/qpswwww/article/details/44161053
AC code:
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
typedef double llf;
const int N=101;
const int TIME=5000;
const llf INF=2147483647;
int n,m;
int a[N];
llf ave,mn=INF;
void work(){
llf ans=0,T=10000;
int x[N]={0},bl[N]={0};
for(int i=1;i<=n;i++){
int grp=rand()%m+1;
bl[i]=grp;
x[grp]+=a[i];
}
for(int i=1;i<=m;i++) ans+=(x[i]-ave)*(x[i]-ave);
while(T>0.1){
int t=rand()%n+1,u=bl[t],v;
llf tmp=ans;
T*=0.9;
if(T>500){
int grp;
llf mnx=INF;
for(int i=1;i<=m;i++){
if(i==u) continue;
if(x[i]<mnx){
mnx=x[i];
grp=i;
}
}
v=grp;
}
else{
v=rand()%m+1;
while(u==v) v=rand()%m+1;
}
ans-=(x[u]-ave)*(x[u]-ave)+(x[v]-ave)*(x[v]-ave);
x[u]-=a[t];x[v]+=a[t];
ans+=(x[u]-ave)*(x[u]-ave)+(x[v]-ave)*(x[v]-ave);
if(ans>tmp&&(rand()%10000)>T){
ans=tmp;
x[u]+=a[t];x[v]-=a[t];
}
else bl[t]=v;
}
if(ans<mn) mn=ans;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) ave+=a[i];
ave/=m;
for(int i=1;i<=TIME;i++) work();
printf("%.2lf\n",sqrt(mn/m));
return 0;
}