BZOJ2428 [HAOI2006]均分数据(模拟退火算法)

【题解】
随机10000次左右,每次随机生成初始分组情况,判断将某个元素移入另一组是否会更优,记录全局最优答案 

注意:
1. 自己用平方取中法弄的伪随机数,效果远不如系统函数rand()
2. t*=0.9变为t*=0.99 耗时远远超过 solve()从10000次变为50000次 

3. 随机打乱原数组 


【代码】

拼RP终于AC了。。。

#include
#include
#include
#include
#include
double a[25]={0},sum[25]={0};
int pos[25]={0};
double ave=0,Mans=100000000.0;
int n,m,num=3257;
void jh(double* a,double* b)
{
	double t=*a;
	*a=*b;
	*b=t;
}
int getmin()
{
	int i,ans=1;
	for(i=1;i<=m;i++)
		if(sum[ans]>sum[i]) ans=i;
	return ans;
}
double change(int x,int posy)
{
	double S0,St;
	S0=(sum[pos[x]]-ave)*(sum[pos[x]]-ave)+(sum[posy]-ave)*(sum[posy]-ave);
	St=(sum[pos[x]]-a[x]-ave)*(sum[pos[x]]-a[x]-ave)+(sum[posy]+a[x]-ave)*(sum[posy]+a[x]-ave);
	return St-S0;
}
void solve()
{
	double T,ans=0.0;
	int i,x,posy;
	memset(sum,0,sizeof(sum)); 
	for(i=1;i<=n;i++)
		pos[i]=rand()%m+1;
	for(i=1;i<=n;i++)
		sum[pos[i]]+=a[i];
	for(i=1;i<=m;i++)
		ans+=(sum[i]-ave)*(sum[i]-ave);
	for(T=10000;T>0.01;T*=0.9)//模拟退火过程 
	{
		if(T>500) posy=getmin();
		else posy=rand()%m+1;
		x=rand()%n+1;
		if(pos[x]==posy) continue;
		if( rand()%10000ans) Mans=ans;
}
int main()
{
	int i;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	{
		scanf("%lf",&a[i]);
		ave+=a[i];
		jh(&a[i],&a[rand()%i+1]);//将原数组随机打乱 
	}
	ave/=m;
	for(i=1;i<=15000;i++)
		solve();
	printf("%.2lf",sqrt(Mans/m));
	return 0;
}


你可能感兴趣的:(近似算法)