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

来源:互联网 发布:民生银行网络面试 编辑:程序博客网 时间:2024/06/05 03:35
【题解】
随机10000次左右,每次随机生成初始分组情况,判断将某个元素移入另一组是否会更优,记录全局最优答案 

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

3. 随机打乱原数组 


【代码】

拼RP终于AC了。。。

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<math.h>#include<time.h>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()%10000<T || change(x,posy)<0 )//将a[x]从pos[x]组移入posy组 {ans+=change(x,posy);sum[pos[x]]-=a[x];sum[posy]+=a[x];pos[x]=posy;}}if(Mans>ans) 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;}


0 0
原创粉丝点击