【Luogu P2503】均分数据

题目描述

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

σ=mi=1(Six)2m,x=mi=1xim σ = ∑ i = 1 m ( S i − x ) 2 m , x = ∑ i = 1 m x i m

题解

胡乱代一遍式子发现答案只与 S2i ∑ S i 2 有关。
于是要最小化它。又均值定理可知应使每个Si尽量相等。
于是构造新解的方法就出来了,每次从其他地方取一个加到当前最小组里。
我这里为了方便先分配了一个比较优的组。

但是单这样会全WA,你会发现你找出来的新解总是不比你当前这个解优,并且试一下发现自己总是接受不了比自己差的解,于是就找不到最优解了。

于是我把接受差解的不等式右边除以1000就AC了? (迷)

#include
#include
#include
#include
#include
#include
#include
#define POW(a) ((a)*(a))
using namespace std;
int n,m;
typedef double db;
const int N=40;
int a[N];
int K[N];
int Sum[10];
const db INF=1e99;
const db eps=1e-10;
const db delta=0.99;
const db T=1000000;
db ax;
db Ans=0;
int ans;
//使sigma xi^2 最小
int S=0;
inline int Rand(int mod){return rand()%mod+1;}
int top=0;
int st[N];
inline void Get(int k){top=0;for(register int i=1;i<=n;++i) if(K[i]==k) st[++top]=i;}
inline db calc(){
    register db S=0;
    for(register int i=1;i<=m;++i) S+=POW(Sum[i]);
    return S;
}
inline void SA()
{
    db t=T;
    int nans=ans;
    while(t>eps){
        bool flag=1;
        while(flag){
            flag=0;register int min_pos=-1;register int max_pos=-1;
            for(register int i=1;i<=m;++i) if(min_pos==-1||(Sum[min_pos]>Sum[i])) min_pos=i;
            register int p=Rand(n);
            while(K[p]==min_pos) p=Rand(n);
            max_pos=K[p];
            K[p]=min_pos;Sum[min_pos]+=a[p];Sum[max_pos]-=a[p];
            register int new_ans=calc();
            if(new_ansif(nans1;}
            else if(rand()>exp((new_ans-nans)/t)*RAND_MAX/1000.0000) nans=new_ans,flag=1;//....
            else{
                K[p]=max_pos;Sum[max_pos]+=a[p];Sum[min_pos]-=a[p];
            }
        }
        t*=delta;
    }

}
int main()
{
    srand(19260817);
    scanf("%d %d",&n,&m);register int SS=0;
    for(register int i=1;i<=n;++i) scanf("%d",&a[i]),SS+=a[i];
    ax=(db)(SS)/(m*1.0000);
    sort(a+1,a+1+n);
    Ans=1.000*m*POW(ax)-(2*SS*1.0000)*ax;
    register int num=(n-1)/m+1;
    register int k=0;
    bool g=0;int l=1;int r=n;
    register int tot=0;
    for(register int i=1;i<=m;++i)
    {
        while(k=l){
            if(!g) g=1,K[l]=i,Sum[i]+=a[l++];
            else g=0,K[r]=i,Sum[i]+=a[r--];
            ++k;++tot;
        }
        k=0;
    }
    for(register int i=1;i<=m;++i) S+=POW(Sum[i]);
    ans=S;SA();
    printf("%.2lf\n",sqrt((1.000*ans+Ans)/(1.000*m)));
}
/*
6 2
1 2 3 4 5 6
 */

你可能感兴趣的:(模拟退火,======题解======)