1. 程式人生 > >BZOJ2428 均分數據

BZOJ2428 均分數據

using alt getch img void brush amp 包含 getchar

2428: [HAOI2006]均分數據

Time Limit: 5 Sec Memory Limit: 128 MB

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


AC+1

模擬退火,隨機選點更換分組

當溫度太大不穩定,直接嘗試換到sum最小的組

由於模擬退火的不穩定,所以跑個幾萬遍就穩了233

當然如果您是yzh那樣的強者,您可以用DP每次算最優解

個人認為這樣並不優秀,如果您有心情$n ^ 2 m$DP,為何不多退$n ^ 2 m$次火<_<


#include<bits/stdc++.h>
using namespace std;
template <class _T> inline void read(_T &_x) {
	int _t; bool flag = false;
	while ((_t = getchar()) != ‘-‘ && (_t < ‘0‘ || _t > ‘9‘)) ;
	if (_t == ‘-‘) _t = getchar(), flag = true; _x = _t - ‘0‘;
	while ((_t = getchar()) >= ‘0‘ && _t <= ‘9‘) _x = _x * 10 + _t - ‘0‘;
	if (flag) _x = -_x;
}
using namespace std;
const int maxn = 30;
int n, m, a[maxn];
double ave;
int from[maxn], sum[maxn];
inline double sqr(double val) {return val * val; }
inline double getr() {return (double)rand() / RAND_MAX; }
inline double solve() {
	memset(sum, 0, sizeof sum);
	for (int i = 1; i <= n; ++i) {
		from[i] = rand() % m + 1;
		sum[from[i]] += a[i];
	}
	double ans = 0;
	for (int i = 1; i <= m; ++i)
		ans += sqr(sum[i] - ave);
	double T = 10000;
	while (T > 0.1) {
		int t = rand() % n + 1, x = from[t], y;
		if (T > 1000) y = min_element(sum + 1, sum + m + 1) - sum;
		else y = rand() % m + 1;
		double to = ans;
		to -= sqr(sum[x] - ave) + sqr(sum[y] - ave);
		to += sqr(sum[x] - a[t] - ave) + sqr(sum[y] + a[t] - ave);
		if (to < ans || exp((ans - to) * 1e4 / T) > getr()) {
			ans = to;
			from[t] = y;
			sum[x] -= a[t], sum[y] += a[t];
		}
		T *= 0.9;
	}
	return ans;
}
int main() {
	//freopen();
	//freopen();
	srand(19260817);
	read(n), read(m);
	for (int i = 1; i <= n; ++i) {
		read(a[i]);
		ave += a[i];
	}
	ave /= m;
	double ans = solve();
	for (int i = 1; i <= 10000; ++i)
		ans = min(ans, solve());
	printf("%.2lf\n", sqrt(ans / m));
	return 0;
}

BZOJ2428 均分數據