2428: [HAOI2006]均分数据
Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 2944 Solved: 916
[ 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
1 2 3 4 5 6
Sample Output
0.00
HINT
对于全部的数据,保证有K<=N <= 20,2<=K<=6
Source
模拟退火入门题。。。。
先随机生成一个解,
然后每次考虑产生一个新解,具体来说就是随机选一个数,再转移到随机一个组里,计算均方差
接下来考虑是否接纳这个解:如果这个解比当前解优,那么接纳这个解;否则按一定的概率接纳这个解,这个概率是随着时间的推移而逐渐减小的,
这样的过程中解趋于稳定,越来越趋近于正确答案
但是考虑到当当前温度很高时,解的走向不稳定,很有可能越变越劣,那么我们考虑在高温时加一个贪心,来保证前期解的方向一定是往更优的方向走,具体说来即把选出来的这个数放到和值最小的那一组里去
PS.蒟蒻做这题时题目看错调了一个小时。。。。。。初温设得不同在bzoj上的答案也是不一样的。。。。
代码:
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
typedef long double DL;
const DL d = 0.99,eps = 0.1;
const int INF = 2147483647;
const int maxn = 30;
int n,m,be[maxn],a[maxn];
DL sum[maxn],now,ave,ans,s;
inline int RAND()
{
return rand() % 10000;
}
inline int RANDTO(int t)
{
int k = rand() % m + 1;
while (k == t) k = rand() % m + 1;
}
inline DL sqr(DL x){return x * x;}
inline void backout()
{
memset(sum,0,sizeof(sum));
now = 0;
for (int i = 1; i <= n; i++)
{
int to = rand() % m + 1;
sum[to] += a[i]; be[i] = to;
}
for (int i = 1; i <= m; i++) now += sqr(sum[i] - ave);
DL T = 100000;
while (T > eps)
{
int u = rand() % n + 1,from = be[u],to;
if (T > 500)
{
DL minx = INF;
for (int i = 1; i <= m; i++)
{
if (i == from) continue;
if (sum[i] < minx)
{
minx = sum[i];
to = i;
}
}
}
else to = rand() % m + 1;
DL pre = now;
now -= sqr(sum[from] - ave);
now -= sqr(sum[to] - ave);
sum[from] -= a[u]; sum[to] += a[u];
now += sqr(sum[from] - ave);
now += sqr(sum[to] - ave);
if (pre < now && RAND() > T)
{
sum[from] += a[u];
sum[to] -= a[u];
now = pre;
}
else be[u] = to;
T *= d;
}
ans = min(ans,now);
}
inline LL getint()
{
LL ret = 0,f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
ret = ret * 10 + c - '0',c = getchar();
return ret * f;
}
int main()
{
#ifdef dyf
freopen("hyj1.txt","r",stdin);
#endif
n = getint(); m = getint();
for (int i = 1; i <= n; i++) a[i] = getint() , s += a[i];
ans = INF; ave = s / m;
for (int i = 1; i <= 1000; i++) backout();
printf("%0.2Lf",sqrt(ans / m));
return 0;
}