题目描述
已知N个正整数:A1、A2、……、An 。今要将它们分成M组,使得各组数据的数值和最平均,即各组的均方差最小。均方差公式如下:
σ=∑mi=1(Si−x)2m‾‾‾‾‾‾‾‾‾‾‾‾‾‾√,x=∑mi=1xim
σ
=
∑
i
=
1
m
(
S
i
−
x
)
2
m
,
x
=
∑
i
=
1
m
x
i
m
题解
胡乱代一遍式子发现答案只与
∑S2i
∑
S
i
2
有关。
于是要最小化它。又均值定理可知应使每个Si尽量相等。
于是构造新解的方法就出来了,每次从其他地方取一个加到当前最小组里。
我这里为了方便先分配了一个比较优的组。
但是单这样会全WA,你会发现你找出来的新解总是不比你当前这个解优,并且试一下发现自己总是接受不了比自己差的解,于是就找不到最优解了。
于是我把接受差解的不等式右边除以1000就AC了? (迷)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<ctime>
#define POW(a) ((a)*(a))
using namespace std;
int n,m;
typedef double db;
const int N=40;
int a[N];
int K[N];
int Sum[10];
const db INF=1e99;
const db eps=1e-10;
const db delta=0.99;
const db T=1000000;
db ax;
db Ans=0;
int ans;
//使sigma xi^2 最小
int S=0;
inline int Rand(int mod){return rand()%mod+1;}
int top=0;
int st[N];
inline void Get(int k){top=0;for(register int i=1;i<=n;++i) if(K[i]==k) st[++top]=i;}
inline db calc(){
register db S=0;
for(register int i=1;i<=m;++i) S+=POW(Sum[i]);
return S;
}
inline void SA()
{
db t=T;
int nans=ans;
while(t>eps){
bool flag=1;
while(flag){
flag=0;register int min_pos=-1;register int max_pos=-1;
for(register int i=1;i<=m;++i) if(min_pos==-1||(Sum[min_pos]>Sum[i])) min_pos=i;
register int p=Rand(n);
while(K[p]==min_pos) p=Rand(n);
max_pos=K[p];
K[p]=min_pos;Sum[min_pos]+=a[p];Sum[max_pos]-=a[p];
register int new_ans=calc();
if(new_ans<nans) {nans=new_ans;if(nans<ans) ans=nans;flag=1;}
else if(rand()>exp((new_ans-nans)/t)*RAND_MAX/1000.0000) nans=new_ans,flag=1;//....
else{
K[p]=max_pos;Sum[max_pos]+=a[p];Sum[min_pos]-=a[p];
}
}
t*=delta;
}
}
int main()
{
srand(19260817);
scanf("%d %d",&n,&m);register int SS=0;
for(register int i=1;i<=n;++i) scanf("%d",&a[i]),SS+=a[i];
ax=(db)(SS)/(m*1.0000);
sort(a+1,a+1+n);
Ans=1.000*m*POW(ax)-(2*SS*1.0000)*ax;
register int num=(n-1)/m+1;
register int k=0;
bool g=0;int l=1;int r=n;
register int tot=0;
for(register int i=1;i<=m;++i)
{
while(k<num&&r>=l){
if(!g) g=1,K[l]=i,Sum[i]+=a[l++];
else g=0,K[r]=i,Sum[i]+=a[r--];
++k;++tot;
}
k=0;
}
for(register int i=1;i<=m;++i) S+=POW(Sum[i]);
ans=S;SA();
printf("%.2lf\n",sqrt((1.000*ans+Ans)/(1.000*m)));
}
/*
6 2
1 2 3 4 5 6
*/