[HAOI2006][BZOJ2428] 均分数据

2428: [HAOI2006]均分数据

Time Limit: 5 Sec  Memory Limit: 128 MB
Submit: 801  Solved: 231
[Submit][Status][Discuss]

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

 
第一次写模拟退火……常数设不好。试验了很多次。
971708 ws_fqk 2428 Accepted 1284 kb 596 ms C++/Edit 1238 B 2015-05-13 10:47:32
971707 ws_fqk 2428 Wrong_Answer 1284 kb 404 ms C++/Edit 1237 B 2015-05-13 10:47:10
971705 ws_fqk 2428 Wrong_Answer 1284 kb 368 ms C++/Edit 1240 B 2015-05-13 10:45:39
971704 ws_fqk 2428 Wrong_Answer 1284 kb 160 ms C++/Edit 1240 B 2015-05-13 10:45:21
971703 ws_fqk 2428 Accepted 1284 kb 608 ms C++/Edit 1242 B 2015-05-13 10:44:59
971701 ws_fqk 2428 Accepted 1284 kb 1172 ms C++/Edit 1243 B 2015-05-13 10:43:58

最后调到了600ms。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
int n,m,k,pos[21],a[21];
double ave,ans,mn=100000007,sum[21];
void solve()
{
    double t=10000; //初始温度
    ans=0;
    memset(sum,0,sizeof(sum));
    for (int i=1;i<=n;i++) pos[i]=rand()%m+1;
    for (int i=1;i<=n;i++) sum[pos[i]]+=a[i];
    for (int i=1;i<=m;i++) ans+=(sum[i]-ave)*(sum[i]-ave);
    while (t>0.1)
    {
        t*=0.91; //降温速率 0.9时WA。
//-----------------------------------------------------------------------
int t1=rand()%n+1,x=pos[t1],y; if (t>500) y=min_element(sum+1,sum+m+1)-sum; else y=rand()%m+1; //随机选取两个位置 为什么>500取最小我也很不解,求解释。
//-----------------------------------------------------------------------
if (x==y) continue; double pre=ans; ans-=(sum[x]-ave)*(sum[x]-ave); ans-=(sum[y]-ave)*(sum[y]-ave); sum[x]-=a[t1]; sum[y]+=a[t1]; ans+=(sum[x]-ave)*(sum[x]-ave); ans+=(sum[y]-ave)*(sum[y]-ave); //换位置,更新ans,sum。
//-----------------------------------------------------------------------
if (rand()%10000>t&&ans>pre) { sum[x]+=a[t1]; sum[y]-=a[t1]; ans=pre; } else pos[t1]=y; } //若不优,则返回原来的值,否则(小于概率或更优)更换位置。
//-----------------------------------------------------------------------
if (ans<mn) mn=ans; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) { scanf("%d",&a[i]); ave+=a[i]; } ave/=m; for (int i=1;i<=5000;i++) solve(); printf("%.2lf",sqrt(mn/m)); return 0; }
原文地址:https://www.cnblogs.com/ws-fqk/p/4499753.html