【HAOI2006】【BZOJ2428】均分数据

Description

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

,其中σ为均方差,是各组数据和的平均值,xi为第i组数据的数值和。

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

Source

Day2

我本来以为黄学长的退火参数是没问题的…
就去偷了一发…
没想到WA了= =是BZOJ数据升级了?
然后调了调退火的次数和初温…
最后找到了一个跑的比较快而且能A的参数.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,m;
int a[30],grp[30];
double sum[30],tmp,minn=1e30,ave;
double sqr(double x)
{
    return x*x;
}
void solve()
{
    memset(sum,0,sizeof(sum));tmp=0;
    for (int i=1;i<=n;i++)  grp[i]=rand()%m+1;
    for (int i=1;i<=n;i++)  sum[grp[i]]+=a[i];
    for (int i=1;i<=m;i++)  tmp+=sqr(sum[i]-ave);
    double T=17500;
    while (T>0.1)
    {
        T*=0.9;
        int t=rand()%n+1,x=grp[t],y;
        if  (T>500) y=min_element(sum+1,sum+m+1)-sum;
        else    y=rand()%m+1;
        if (x==y)   continue;
        double last=tmp;
        tmp-=sqr(sum[x]-ave);tmp-=sqr(sum[y]-ave);
        sum[x]-=a[t];sum[y]+=a[t];
        tmp+=sqr(sum[x]-ave);tmp+=sqr(sum[y]-ave);
        if (rand()%10000>T&&tmp>last)   sum[x]+=a[t],sum[y]-=a[t],tmp=last;
        else    grp[t]=y;
    }
    minn=min(minn,tmp);
}
int main()
{
    //srand(time(0));
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)  scanf("%d",&a[i]),ave+=a[i],swap(a[i],a[rand()%i+1]);
    ave/=m;
    for (int i=1;i<=4000;i++)   solve();
    printf("%.2lf\n",sqrt(minn/m));
}

你可能感兴趣的:(模拟退火)