BZOJ 2428: [HAOI2006]均分数据 模拟退火

已知N个正整数:A1、A2、……、An 。今要将它们分成M组,使得各组数据的数值和最平均,即各组的均方差最小。

Input

第一行是两个整数,表示N,M的值(N是整数个数,M是要分成的组数)
第二行有N个整数,表示A1、A2、……、An。整数的范围是1–50。
(同一行的整数间用空格分开)

Output

这一行只包含一个数,表示最小均方差的值(保留小数点后两位数字)。

Sample Input

6 3

1 2 3 4 5 6

Sample Output

0.00

HINT

对于全部的数据,保证有K<=N <= 20,2<=K<=6

题解

第一次写模拟退火,总结一下就是看人品233,我换了好几个人的生日,结果五花八门,就当放个板子在这里了。

#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<iomanip>
using namespace std;
int ans[25];
int mid[25];
double sum[6];
int n,k;
int val[25];
double trueans=2147483647;
double ax;
double zong=0;
double get_fangcha(int a[])
{
    double re=0;
    memset(sum,0,sizeof(sum));
    for(int i=1;i<=n;i++) sum[a[i]]+=val[i];
    for(int i=0;i<k;i++) re+=(sum[i]-ax)*(sum[i]-ax);
    re/=k;
    return sqrt(re);
}
void SA(double T)
{
    double nowans=get_fangcha(ans);
    if(nowans<trueans) trueans=nowans;
    while(T>0.00001)
    {
        T*=0.99;
        get_fangcha(ans);
        int t=rand()%n+1;
        int y=rand()%k;
        if(ans[t]==y) continue;
        int yuan=ans[t];
        ans[t]=y;
        double newans=get_fangcha(ans);
        double rate=exp((nowans-newans)/T);
        double tt=rand()%65536;
        tt/=65536.0;
        if(tt<rate) nowans=newans;
        else ans[t]=yuan;
        if(nowans<trueans) trueans=nowans;
    }
}
int main()
{
    srand(19991005);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&val[i]),zong+=val[i];
    ax=zong/k;
    for(int hhh=1;hhh<=500;hhh++)
    {
        for(int i=1;i<=n;i++) 
        {
            ans[i]=rand()%k;
            mid[i]=ans[i];
        }
        SA(get_fangcha(ans));
    }
    printf("%.2lf\n",trueans);  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值