AT_abc332_e题解
AT_abc332_e random_shuffle解法
题意简述:把 $n$ 个数分成 $d$ 组,尽量减小每一组中所有数的和的方差(每一组可以是空的,但是每个数必须用上),这相当于一个模板题吧 逃。
方差的定义:
$$ V = \frac{1}{D} \sum_{i = 1}^{D}(x_i - \overline{x})^{2} $$
其中 $x_i$ 为第 $i$ 组的总和,$\overline{x} = \dfrac{1}{D}(x_1 + x_2 + x_3 + \cdot\cdot\cdot + x_D)$,也就是每组数的和的平均值。
每次我们每次用 random_shuffle
打乱数组,把前 $D$ 个数单独组作为 1 组放进去,然后把剩下的数,放到 当前 总和最小的一组,最后统计方差。
因为有随机化,所以我们用 while ((double)clock() / CLOCKS_PER_SEC < 1.9)
来卡时间,保证我们能更大概率随机到正解。
注意 $ans$ 的初值设的大一点!
代码:
#pragma GCC optimize(2)
#include <algorithm>
#include <iostream>
#include <cstring>
#include <random>
#include <time.h>
#include <climits>
#define endl "\n"
using namespace std;
const int N = 25;
double ans = LLONG_MAX, aver;
double x[N], s[N];
int n, m;
void solve()
{
random_shuffle(x + 1, x + 1 + n);
for (int i = 1; i <= m; i ++ ) s[i] = x[i];
for (int i = m + 1; i <= n; i ++ )
{
int p = min_element(s + 1, s + m + 1) - s; // 查找范围内最小的元素的地址
s[p] += x[i];
}
double res = 0;
for (int i = 1; i <= m; i ++ ) res += (s[i] - aver) * (s[i] - aver);
ans = min(ans, res);
}
int main()
{
srand(time(NULL));
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%lf", &x[i]), aver += x[i];
aver /= m;
while ((double)clock() / CLOCKS_PER_SEC < 1.9) solve();
printf("%.10lf\n", ans / m);
return 0;
}