思路:模拟退火随机交换两个数,暴力分组,稍微推一下公式
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const db down = 0.996;
const db eps = 1e-15;
const int N = 25;
int n, m, a[N], b[N], c[N];
db ans, ave;
db cal() {
memset(c, 0, sizeof(c));
for(int i = 1; i <= n; ++i) {
int pos = 1;
for(int j = 2; j <= m; ++j)
if(c[j] < c[pos]) pos = j;
c[pos] += b[i];
}
db ret = 0.0;
for(int i = 1; i <= m; ++i)
ret += (1.0 * c[i] - ave) * (1.0 * c[i] + ave);
return 1.0 * sqrt(ret / m);
}
void sa() {
db t = 3000;
while (t > eps) {
int x = rand() % n + 1, y = rand() % n + 1;
while(x == y) y = rand() % n + 1;
swap(b[x], b[y]);
db ew = cal();
db de = ew - ans;
if(de < 0) ans = ew;
else if(exp(-de / t) * RAND_MAX > rand()) swap(b[x], b[y]);
t *= down;
}
}
void solve() {
for(int i = 1; i <= 4; ++i) {
memcpy(b, a, sizeof(a));
sa();
}
}
int main() {
srand(19260817);
scanf("%d%d", &n, &m);
ave = 0.0;
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
ave += 1.0 * a[i];
}
ave = 1.0 * ave / m;
memcpy(b, a, sizeof(a));
ans = cal();
solve();
printf("%.2f\n", ans);
return 0;
}