您的位置:首页 > 其它

【BZOJ2428】[HAOI2006]均分数据

2016-01-24 21:52 357 查看

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

Sample Output

0.00

HINT

对于全部的数据,保证有K<=N <= 20,2<=K<=6

传说中的模拟退火裸题,此处OTZ CA 爷
在我看来模拟退火就是一种特别看人品的贪心,不停地取随机数,也不知道最后能不能用上,毕竟我人品超级差

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,m,sum1;
int a[30],grp[30];
double sum[30],tmp,minn=10000000,ave=0;
double sqr(double x)
{
return x*x;
}
void solve(){
memset(sum,0,sizeof(sum));tmp=0;
for (int i=1;i<=n;i++) grp[i]=rand()%m+1;
for (int i=1;i<=n;i++) sum[grp[i]]+=a[i];
for (int i=1;i<=m;i++) tmp+=sqr(sum[i]-ave);
double T=17500;
while (T>0.1){
T*=0.9;
int t=rand()%n+1,x=grp[t],y;
if (T>500) y=min_element(sum+1,sum+m+1)-sum;
else y=rand()%m+1;
if (x==y) continue;
double last=tmp;
tmp-=sqr(sum[x]-ave);tmp-=sqr(sum[y]-ave);
sum[x]-=a[t];sum[y]+=a[t];
tmp+=sqr(sum[x]-ave);tmp+=sqr(sum[y]-ave);
if (rand()%10000>T&&tmp>last) sum[x]+=a[t],sum[y]-=a[t],tmp=last;
else grp[t]=y;
}
minn=min(minn,tmp);
}

int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)scanf("%d",&a[i]),
sum1+=a[i],swap(a[i],a[rand()%i+1]);
ave=(sum1+0.0)/m;
for (int i=1;i<=10000;i++)
solve();
printf("%.2lf\n",sqrt(minn/m));
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: