在前进的路上,别忘了你前进的目的

【转载】bzoj2428 均分数据 爬山算法or模拟退火

爬山法和模拟退火的本质是差不多的,在本题中就是得到一个初解后,随机一个数改变到哪一组(组别在步长较大的时候选择和最小的,步长较小的时候随机选择)中,然后比较是否更优,更优就跳过去;如果不更优,则模拟退火算法有一定概率接受。

       注意由于模拟退火有一定概率接受,因此(很)可能得不到最好的结果,可以把步长定的小一点然后多跑几遍;爬山算法则不同,一定每次都是局部最优解,可以把步长定的大点,这样搜到最优解的可能性就大了,算法可以少跑几遍。

       注意在x改变组别的时候不需要重新计算,只需要计算它带来的影响,然后这个影响可以用平方差公式后提取公因式化简。

AC代码如下:

(爬山算法)

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<cstdlib>  
  5. #include<cmath>  
  6. using namespace std;  
  7.   
  8. int n,m,blg[105]; double ave,a[105],sum[105];  
  9. double solve(){  
  10.     sum[0]=1e100; int i;  
  11.     for (i=1; i<=m; i++) sum[i]=0;  
  12.     for (i=1; i<=n; i++){ blg[i]=rand()%m+1; sum[blg[i]]+=a[i]; }  
  13.     double t=1e5,last,ans=0;   
  14.     for (i=1; i<=m; i++) ans+=(sum[i]-ave)*(sum[i]-ave);  
  15.     for (; t>1e-3; t*=0.98){  
  16.         last=ans;  
  17.         int x=rand()%n+1,y=0;  
  18.         if (t<500) y=rand()%m+1; else  
  19.             for (i=1; i<=m; i++) if (sum[i]<sum[y]) y=i;  
  20.         ans+=a[x]*(a[x]+sum[y]-sum[blg[x]])*2;  
  21.         if (ans>last) ans=last;  
  22.         else{ sum[blg[x]]-=a[x]; blg[x]=y; sum[y]+=a[x]; }  
  23.     }  
  24.     return ans;  
  25. }  
  26. int main(){  
  27.     srand(20160310);  
  28.     scanf("%d%d",&n,&m); int i; double ans=1e100;  
  29.     for (i=1; i<=n; i++){  
  30.         scanf("%lf",&a[i]); ave+=a[i];  
  31.     }  
  32.     ave/=m;  
  33.     for (i=1; i<=1000; i++) ans=min(ans,solve());  
  34.     printf("%.2f\n",sqrt(ans/m));  
  35.     return 0;  
  36. }  

 

 

 

(模拟退火)

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<cstdlib>  
  5. #include<cmath>  
  6. using namespace std;  
  7.   
  8. int n,m,blg[105]; double ave,a[105],sum[105];  
  9. double solve(){  
  10.     sum[0]=1e100; int i;  
  11.     for (i=1; i<=m; i++) sum[i]=0;  
  12.     for (i=1; i<=n; i++){ blg[i]=rand()%m+1; sum[blg[i]]+=a[i]; }  
  13.     double t=1e4,last,ans=0;   
  14.     for (i=1; i<=m; i++) ans+=(sum[i]-ave)*(sum[i]-ave);  
  15.     for (; t>1e-1; t*=0.75){  
  16.         last=ans;  
  17.         int x=rand()%n+1,y=0;  
  18.         if (t<500) y=rand()%m+1; else  
  19.             for (i=1; i<=m; i++) if (sum[i]<sum[y]) y=i;  
  20.         //ans+=(sum[blg[x]]-a[x]-ave)*(sum[blg[x]]-a[x]-ave)-(sum[blg[x]]-ave)*(sum[blg[x]]-ave);  
  21.         //ans+=(sum[y]+a[x]-ave)*(sum[y]+a[x]-ave)-(sum[y]-ave)*(sum[y]-ave);  
  22.         ans+=a[x]*(a[x]+sum[y]-sum[blg[x]])*2;  
  23.         if (ans>last && t<rand()%10000) ans=last;  
  24.             else{ sum[blg[x]]-=a[x]; blg[x]=y; sum[y]+=a[x]; }  
  25.     }  
  26.     return ans;  
  27. }  
  28. int main(){  
  29.     srand(20160310);  
  30.     scanf("%d%d",&n,&m); int i; double ans=1e100;  
  31.     for (i=1; i<=n; i++){  
  32.         scanf("%lf",&a[i]); ave+=a[i];  
  33.     }  
  34.     ave/=m;  
  35.     for (i=1; i<=50000; i++) ans=min(ans,solve());  
  36.     printf("%.2f\n",sqrt(ans/m));  
  37.     return 0;  
  38. }  



 

by lych

2016.3.10

 

转自:http://blog.csdn.net/lych_cys

传送门:http://blog.csdn.net/lych_cys/article/details/50843232

posted @ 2017-02-07 16:31  尘世念想  阅读(134)  评论(0)    收藏  举报