luogu P2503 [HAOI2006]均分数据

背景:

模拟退火大法好 . . . ... ...

题意:

给出 n n n个数,将其分成 m m m组,求每组和的均方差(标准差)的最小值。

思路:

先随机出每一个数属于哪一组。
模拟退火,每一次选择一个数将其放在权值和最小的一组。

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define eps 1e-16
using namespace std;
	int n,m;
	double ave,ANS=2147483647;
	int a[50],belong[50],sum[50];
int findmin()
{
	double mi=2147483647;
	int pos;
	for(int i=1;i<=m;i++)
		if(sum[i]<mi) mi=sum[i],pos=i;
	return pos;
}
void fire()
{
	double ans=0;
	memset(sum,0,sizeof(sum));
	for(int i=1;i<=n;i++)
	{
		belong[i]=rand()%m+1;
		sum[belong[i]]+=a[i];
	}
	for(int i=1;i<=m;i++)
		ans+=(sum[i]-ave)*(sum[i]-ave);
	double t=200;
	while(t>eps)
	{
		int x=rand()%n+1,min_pos=findmin();
		double now=ans-(sum[belong[x]]-ave)*(sum[belong[x]]-ave)-(sum[min_pos]-ave)*(sum[min_pos]-ave);
		sum[belong[x]]-=a[x],sum[min_pos]+=a[x];
		now+=(sum[belong[x]]-ave)*(sum[belong[x]]-ave)+(sum[min_pos]-ave)*(sum[min_pos]-ave);
		double delta=now-ans;
		if(delta<0) ans=now,belong[x]=min_pos;
		else if(exp(-delta/t)*RAND_MAX>=rand()) ans=now,belong[x]=min_pos;
		else sum[belong[x]]+=a[x],sum[min_pos]-=a[x];
		t*=0.996;
	}
	ANS=min(ANS,ans);
}
int main()
{
	int op=0;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		op+=a[i];
	}
	ave=(double)op/m;
	for(int i=1;i<=100;i++)
		fire();
	printf("%.2lf",sqrt(ANS/m));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值