【BZOJ2428】[HAOI2006]均分数据
Description
已知N个正整数:A1、A2、……、An 。今要将它们分成M组,使得各组数据的数值和最平均,即各组的均方差最小。均方差公式如下:
,其中σ为均方差,是各组数据和的平均值,xi为第i组数据的数值和。
Input
第一行是两个整数,表示N,M的值(N是整数个数,M是要分成的组数)
第二行有N个整数,表示A1、A2、……、An。整数的范围是1--50。
(同一行的整数间用空格分开)
Output
这一行只包含一个数,表示最小均方差的值(保留小数点后两位数字)。
Sample Input
6 3
1 2 3 4 5 6
1 2 3 4 5 6
Sample Output
0.00
HINT
对于全部的数据,保证有K<=N <= 20,2<=K<=6
传说中的模拟退火裸题,此处OTZ CA 爷
在我看来模拟退火就是一种特别看人品的贪心,不停地取随机数,也不知道最后能不能用上,毕竟我人品超级差
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<ctime> 6 #include<cstdlib> 7 #include<algorithm> 8 using namespace std; 9 int n,m,sum1; 10 int a[30],grp[30]; 11 double sum[30],tmp,minn=10000000,ave=0; 12 double sqr(double x) 13 { 14 return x*x; 15 } 16 void solve(){ 17 memset(sum,0,sizeof(sum));tmp=0; 18 for (int i=1;i<=n;i++) grp[i]=rand()%m+1; 19 for (int i=1;i<=n;i++) sum[grp[i]]+=a[i]; 20 for (int i=1;i<=m;i++) tmp+=sqr(sum[i]-ave); 21 double T=17500; 22 while (T>0.1){ 23 T*=0.9; 24 int t=rand()%n+1,x=grp[t],y; 25 if (T>500) y=min_element(sum+1,sum+m+1)-sum; 26 else y=rand()%m+1; 27 if (x==y) continue; 28 double last=tmp; 29 tmp-=sqr(sum[x]-ave);tmp-=sqr(sum[y]-ave); 30 sum[x]-=a[t];sum[y]+=a[t]; 31 tmp+=sqr(sum[x]-ave);tmp+=sqr(sum[y]-ave); 32 if (rand()%10000>T&&tmp>last) sum[x]+=a[t],sum[y]-=a[t],tmp=last; 33 else grp[t]=y; 34 } 35 minn=min(minn,tmp); 36 } 37 38 int main(){ 39 scanf("%d%d",&n,&m); 40 for (int i=1;i<=n;i++)scanf("%d",&a[i]), 41 sum1+=a[i],swap(a[i],a[rand()%i+1]); 42 ave=(sum1+0.0)/m; 43 for (int i=1;i<=10000;i++) 44 solve(); 45 printf("%.2lf\n",sqrt(minn/m)); 46 }