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;
}
posted @ 2023-12-11 08:26  emo_male_god  阅读(20)  评论(0)    收藏  举报  来源