【转载】bzoj2428 均分数据 爬山算法or模拟退火
爬山法和模拟退火的本质是差不多的,在本题中就是得到一个初解后,随机一个数改变到哪一组(组别在步长较大的时候选择和最小的,步长较小的时候随机选择)中,然后比较是否更优,更优就跳过去;如果不更优,则模拟退火算法有一定概率接受。
注意由于模拟退火有一定概率接受,因此(很)可能得不到最好的结果,可以把步长定的小一点然后多跑几遍;爬山算法则不同,一定每次都是局部最优解,可以把步长定的大点,这样搜到最优解的可能性就大了,算法可以少跑几遍。
注意在x改变组别的时候不需要重新计算,只需要计算它带来的影响,然后这个影响可以用平方差公式后提取公因式化简。
AC代码如下:
(爬山算法)
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<cstdlib>
- #include<cmath>
- using namespace std;
- int n,m,blg[105]; double ave,a[105],sum[105];
- double solve(){
- sum[0]=1e100; int i;
- for (i=1; i<=m; i++) sum[i]=0;
- for (i=1; i<=n; i++){ blg[i]=rand()%m+1; sum[blg[i]]+=a[i]; }
- double t=1e5,last,ans=0;
- for (i=1; i<=m; i++) ans+=(sum[i]-ave)*(sum[i]-ave);
- for (; t>1e-3; t*=0.98){
- last=ans;
- int x=rand()%n+1,y=0;
- if (t<500) y=rand()%m+1; else
- for (i=1; i<=m; i++) if (sum[i]<sum[y]) y=i;
- ans+=a[x]*(a[x]+sum[y]-sum[blg[x]])*2;
- if (ans>last) ans=last;
- else{ sum[blg[x]]-=a[x]; blg[x]=y; sum[y]+=a[x]; }
- }
- return ans;
- }
- int main(){
- srand(20160310);
- scanf("%d%d",&n,&m); int i; double ans=1e100;
- for (i=1; i<=n; i++){
- scanf("%lf",&a[i]); ave+=a[i];
- }
- ave/=m;
- for (i=1; i<=1000; i++) ans=min(ans,solve());
- printf("%.2f\n",sqrt(ans/m));
- return 0;
- }
(模拟退火)
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<cstdlib>
- #include<cmath>
- using namespace std;
- int n,m,blg[105]; double ave,a[105],sum[105];
- double solve(){
- sum[0]=1e100; int i;
- for (i=1; i<=m; i++) sum[i]=0;
- for (i=1; i<=n; i++){ blg[i]=rand()%m+1; sum[blg[i]]+=a[i]; }
- double t=1e4,last,ans=0;
- for (i=1; i<=m; i++) ans+=(sum[i]-ave)*(sum[i]-ave);
- for (; t>1e-1; t*=0.75){
- last=ans;
- int x=rand()%n+1,y=0;
- if (t<500) y=rand()%m+1; else
- for (i=1; i<=m; i++) if (sum[i]<sum[y]) y=i;
- //ans+=(sum[blg[x]]-a[x]-ave)*(sum[blg[x]]-a[x]-ave)-(sum[blg[x]]-ave)*(sum[blg[x]]-ave);
- //ans+=(sum[y]+a[x]-ave)*(sum[y]+a[x]-ave)-(sum[y]-ave)*(sum[y]-ave);
- ans+=a[x]*(a[x]+sum[y]-sum[blg[x]])*2;
- if (ans>last && t<rand()%10000) ans=last;
- else{ sum[blg[x]]-=a[x]; blg[x]=y; sum[y]+=a[x]; }
- }
- return ans;
- }
- int main(){
- srand(20160310);
- scanf("%d%d",&n,&m); int i; double ans=1e100;
- for (i=1; i<=n; i++){
- scanf("%lf",&a[i]); ave+=a[i];
- }
- ave/=m;
- for (i=1; i<=50000; i++) ans=min(ans,solve());
- printf("%.2f\n",sqrt(ans/m));
- return 0;
- }
by lych
2016.3.10
转自:http://blog.csdn.net/lych_cys
传送门:http://blog.csdn.net/lych_cys/article/details/50843232

浙公网安备 33010602011771号